diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/CREDITS linux.14p6/CREDITS
--- linux.vanilla/CREDITS	Wed Oct 20 01:12:32 1999
+++ linux.14p6/CREDITS	Thu Nov 11 21:51:57 1999
@@ -789,12 +789,12 @@
 D: Selection mechanism
 
 N: Jochen Hein
-E: jochen.hein@delphi.central.de
+E: jochen@jochen.org
 P: 1024/4A27F015 25 72 FB E3 85 9F DE 3B  CB 0A DA DA 40 77 05 6C
 D: National Language Support
 D: Linux Internationalization Project
 D: German Localization for Linux and GNU software
-S: Frankenstraße
+S: Frankenstraße 33
 S: 34131 Kassel
 S: Germany
 
@@ -997,6 +997,7 @@
 
 N: Jan Kara
 E: jack@atrey.karlin.mff.cuni.cz
+E: jack@suse.cz
 D: Quota fixes for 2.2 kernel
 D: Few other fixes in filesystem area (isofs, loopback)
 W: http://atrey.karlin.mff.cuni.cz/~jack/
@@ -1713,8 +1714,8 @@
 S: Germany
 
 N: Stephen Rothwell
-E: Stephen.Rothwell@canb.auug.org.au
-W: http://www.canb.auug.org.au/~sfr
+E: sfr@linuxcare.com
+W: http://linuxcare.com.au/sfr
 P: 1024/BD8C7805 CD A4 9D 01 10 6E 7E 3B  91 88 FA D9 C8 40 AA 02
 D: Boot/setup/build work for setup > 2K
 D: Author, APM driver
@@ -2122,9 +2123,10 @@
 N: Tim Waugh
 E: tim@cyberelk.demon.co.uk
 D: Co-architect of the parallel-port sharing system
-S: 4 Fox Close
-S: Bishopstoke
-S: SO50 8NB
+S: 34 Bladon Close
+S: GUILDFORD
+S: Surrey
+S: GU1 1TY
 S: United Kingdom
 
 N: Juergen Weigert
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/Changes linux.14p6/Documentation/Changes
--- linux.vanilla/Documentation/Changes	Sat Aug 14 02:27:12 1999
+++ linux.14p6/Documentation/Changes	Tue Nov 16 15:10:47 1999
@@ -43,7 +43,7 @@
 
    for all your Linux kernel needs.
 
-Last updated: July 13, 1999
+Last updated: October 13, 1999
 Current Author: Chris Ricker (kaboom@gatech.edu or
 	chris.ricker@genetics.utah.edu).
 
@@ -61,19 +61,20 @@
 - Linux libc6 C Library  2.0.7pre6               ; ls -l /lib/libc*
 - Dynamic Linker (ld.so) 1.9.9                   ; ldd --version or ldd -v
 - Linux C++ Library	 2.7.2.8                 ; ls -l /usr/lib/libg++.so.*
-- Procps		 1.2.9                   ; ps --version
+- Procps		 2.0.3                   ; ps --version
 - Procinfo               16                      ; procinfo -v
 - Psmisc		 17			 ; pstree -V
 - Net-tools              1.52                    ; hostname -V
 - Loadlin                1.6a
 - Sh-utils               1.16                    ; basename --v
-- Autofs                 3.1.1                   ; automount --version
+- Autofs                 3.1.3                   ; automount --version
 - NFS                    2.2beta40               ; showmount --version
 - Bash                   1.14.7                  ; bash -version
 - Ncpfs                  2.2.0                   ; ncpmount -v
-- Pcmcia-cs              3.0.13                  ; cardmgr -V
-- PPP                    2.3.8                   ; pppd --version
-- Util-linux             2.9t                    ; chsh -v
+- Pcmcia-cs              3.0.14                  ; cardmgr -V
+- PPP                    2.3.10                  ; pppd --version
+- Util-linux             2.9z                    ; chsh -v
+- isdn4k-utils           v3.1beta7               ; isdnctrl 2>&1|grep version
 
 Upgrade notes
 *************
@@ -226,7 +227,7 @@
    Note that the latest compilers (egcs, pgcc, gcc 2.8) may do Bad
 Things while compiling your kernel, particularly if absurd
 optimizations (like -O9) are used.  Caveat emptor.  Currently, the only
-C compiler available in a binary distribution is egcs.  Version 1.0.3
+C compiler available in a binary distribution is egcs.  Version 1.1.2
 seems okay; if you have to have a binary, you may be successful using
 that.  In general, however, gcc-2.7.2.3 is known to be stable, while
 egcs and others have not been as thoroughly tested yet.
@@ -272,9 +273,6 @@
    DHCP clients for 2.0 do not work with the new networking code in the
 2.2 kernel.  You will need to upgrade your dhcpcd / dhcpclient.
 
-   The ISDN code in the stock 2.2 kernel may not work for you.  If it
-doesn't, look in ftp://ftp.suse.com/pub/isdn4linux for updated versions.
-
    In 2.0.x the kernel could be configured to drop source routed IP
 packets via a compile time configuration option.  In 2.2.x, this has
 been replaced by a sysctl.  See Documentation/networking/ip-sysctl.txt
@@ -284,8 +282,8 @@
 ======
 
    As of 2.1.41, the format of /proc/meminfo has changed.  This broke
-many memory utils, which have to be upgraded.  Get the new procps-1.2
-and you should be set.
+many memory utils, which have to be upgraded.  Get the new procps and
+you should be set.
 
 Network File System
 ===================
@@ -513,29 +511,26 @@
 
    so be sure to check that when you recompile.
 
+ISDN4Linux
+==========
+Older isdn4k-utils versions don't support EXTRAVERSION into kernel version
+string. A upgrade to isdn4k-utils.v3.1beta7 or later is recomented.
+
 Where to get the files
 **********************
 
 Binutils
 ========
 
-The 2.8.1.0.23 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.8.1.0.23.bin.tar.gz
-ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.8.1.0.23.bin.tar.gz
+The 2.9.1.0.25 release:
+ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz
 Installation notes:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.8.1.0.23
-ftp://metalab.unc.edu/pub/Linux/GCC/release.binutils-2.8.1.0.23
+ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/release.binutils-2.9.1.0.25
 
-The 2.9.1.0.15 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15-glibc.x86.tar.gz
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15-libc5.x86.tar.gz
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15.tar.gz
-ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15-glibc.x86.tar.gz
-ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15-libc5.x86.tar.gz
-ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15.tar.gz
+The 2.9.5.0.16 release:
+ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.16.tar.bz2
 Installation notes:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.9.1.0.15
-ftp://metalab.unc.edu/pub/Linux/GCC/release.binutils-2.9.1.0.15
+ftp://ftp.varesearch.com/pub/support/hjl/binutils/release.binutils-2.9.5.0.16
 
 Bin86
 =====
@@ -547,14 +542,12 @@
 Gnu C
 =====
 
-The egcs-1.0.3 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-glibc.x86.tar.bz2
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-libc5.x86.tar.bz2
-ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-glibc.x86.tar.bz2
-ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-libc5.x86.tar.bz2
+The egcs-1.1.2 release:
+ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-glibc.x86.tar.bz2
+ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-libc5.x86.tar.bz2
+ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-alpha.x86.tar.bz2
 Installation notes:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.egcs-1.0.3
-ftp://metalab.unc.edu/pub/Linux/GCC/release.egcs-1.0.3
+ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/release.egcs-1.1.2
 
 Gnu C 2.7.2.3 source:
 ftp://ftp.gnu.org/gnu/gcc/gcc-2.7.2.3.tar.gz
@@ -596,15 +589,14 @@
 Procps utilities
 ================
 
-The 1.2 release:
-ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.2.9.tar.gz
-ftp://metalab.unc.edu/pub/Linux/system/status/ps/procps-1.2.9.tgz
+The 2.0.3 release:
+ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-2.0.3.tar.gz
 
 Procinfo utilities
 ==================
 
-The 16 release:
-ftp://ftp.cistron.nl/pub/people/svm/procinfo-16.tar.gz
+The 17 release:
+ftp://ftp.cistron.nl/pub/people/svm/procinfo-17.tar.gz
 
 Psmisc utilities
 ================
@@ -624,7 +616,6 @@
 ======
 
 The 0.98.6 release:
-ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu-0.98.6.tgz
 ftp://ftp.dosemu.org/dosemu/dosemu-0.98.6.tgz
 
 Loadlin
@@ -645,7 +636,7 @@
 ==========
 
 The 2.9 release:
-ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9t.tar.gz
+ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9z.tar.gz
 
 Autofs
 ======
@@ -660,9 +651,9 @@
 ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
 ftp://linux.nrao.edu/mirrors/fb0429.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
 
-The kernel-level 1.4.4 release:
-ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.4.tar.gz
-ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.4.tar.gz
+The kernel-level 1.4.7 release:
+ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.7.tar.gz
+ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.7.tar.gz
 
 Net-tools
 =========
@@ -707,8 +698,8 @@
 Pcmcia-cs
 =========
 
-The 3.0.13 release:
-ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs.3.0.13.tar.gz
+The 3.0.14 release:
+ftp://sourceforge.org/pcmcia/pcmcia-cs.3.0.14.tar.gz
 
 Setserial
 =========
@@ -720,15 +711,15 @@
 PPP
 ===
 
-The 2.3.8 release:
-ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.8.tar.gz
+The 2.3.10 release:
+ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.10.tar.gz
 
 IP Chains
 =========
 
-The 1.3.8 release:
-ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.8.tar.gz
-ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.8.tar.bz2
+The 1.3.9 release:
+ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.9.tar.gz
+ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.9.tar.bz2
 
 IP Masq Adm
 ===========
@@ -739,11 +730,11 @@
 DHCP clients
 ============
 
-The 2.0b1pl27 ISC dhcpclient release:
-ftp://ftp.isc.org/isc/dhcp/test/dhcp-2.0b1pl27.tar.gz
+The 2.0 ISC dhcpclient release:
+ftp://ftp.isc.org/isc/dhcp/test/dhcp-2.0.tar.gz
 
-The 1.3.17-pl2 PhysTech dhcpcd release:
-ftp://ftp.phystech.com/pub/dhcpcd-1.3.17-pl2.tar.gz
+The 1.3.18-pl1 PhysTech dhcpcd release:
+ftp://ftp.phystech.com/pub/dhcpcd-1.3.18-pl1.tar.gz
 
 iBCS
 ====
@@ -754,8 +745,8 @@
 Asun netatalk
 =============
 
-The 2.0a18.2 release:
-ftp://ftp.u.washington.edu/pub/user-supported/asun/netatalk-1.4b2+asun2.0a18.2.tar.gz
+The 2.1.3 release:
+ftp://ftp.u.washington.edu/pub/user-supported/asun/netatalk-1.4b2+asun2.1.3.tar.gz
 
 Fbset
 =====
@@ -792,15 +783,20 @@
 IP utils
 ========
 
-The June 1999 release:
-ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss990630.tar.gz
-ftp://ftp.inr.ac.ru/ip-routing/iputils-ss990610.tar.gz
+The latest releases:
+ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss990824.tar.gz
+ftp://ftp.inr.ac.ru/ip-routing/iputils-ss990915.tar.gz
 
 Patch
 =====
 
 The 2.5 release:
 ftp://ftp.gnu.org/gnu/patch/patch-2.5.tar.gz
+
+ISDN4Linux
+==========
+The v3.1beta7 release:
+ftp://ftp.isdn4linux.de/pub/isdn4linux/utils/testing/isdn4k-utils.v3.1beta7.tar.gz
 
 Other Info
 ==========
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/Configure.help linux.14p6/Documentation/Configure.help
--- linux.vanilla/Documentation/Configure.help	Wed Oct 20 01:12:32 1999
+++ linux.14p6/Documentation/Configure.help	Tue Nov 16 15:23:52 1999
@@ -2532,6 +2532,8 @@
 
   from a boot time script after the /proc filesystem has been mounted.
 
+  Enabling masquerading automagically enables ip_always_defrag too.
+
   Details on how to set things up are contained in the IP Masquerade
   mini-HOWTO, available via FTP (user: anonymous) from
   ftp://metalab.unc.edu/pub/Linux/docs/HOWTO/mini; there's also some
@@ -2643,25 +2645,6 @@
   The module will be called ip_masq_markfw.o. If you want to compile
   it as a module, say M here and read Documentation/modules.txt.
 
-IP: always defragment (required for masquerading)
-CONFIG_IP_ALWAYS_DEFRAG
-  If you say Y here, then all incoming fragments (parts of IP packets
-  that arose when some host between origin and destination decided
-  that the packets were too large and cut them into pieces) will be
-  reassembled (defragmented) before being processed, even if they are
-  about to be forwarded.
-
-  You must say Y here if you want to enable "IP: masquerading" or "IP:
-  transparent proxying".
-
-  When using "IP: firewalling" support, you might also want to say Y
-  here, to have a more reliable firewall (otherwise second and further
-  fragments must be dealt with by the firewall, which can be tricky).
-
-  Only say Y here if running either a firewall that is the sole link
-  to your network or a transparent proxy; never ever say Y here for a
-  normal router or host.
-
 IP: aliasing support
 CONFIG_IP_ALIAS
   Sometimes it is useful to give several IP addresses to a single
@@ -3515,8 +3498,9 @@
   to work, choose Y. This driver is also available as a module called
   af_packet.o ( = code which can be inserted in and removed from the
   running kernel whenever you want). If you want to compile it as a
-  module, say M here and read Documentation/modules.txt. If unsure,
-  say Y.
+  module, say M here and read Documentation/modules.txt.  You will
+  need to add 'alias net-pf-17 af_packet' to your /etc/conf.modules
+  file for the module version to function automatically.  If unsure, say Y.
 
 Kernel/User network link driver
 CONFIG_NETLINK
@@ -5591,6 +5575,77 @@
   you say N, the PPP support will not be included in the driver (saves
   about 16 KB of kernel memory).
 
+MultiGate/COMX support
+CONFIG_COMX
+  Say Y if you want to use any board from the MultiGate (COMX) family. 
+  These boards are synchronous serial adapters for the PC, manufactured 
+  by ITConsult-Pro Co, Hungary. 
+
+  Read linux/Documentation/networking/comx.txt  for help on configuring 
+  and using COMX interfaces. Further info on these cards can be found at 
+  http://www.itc.hu or <info@itc.hu>.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx.o.
+
+COMX/CMX/HiCOMX board support
+CONFIG_COMX_HW_COMX
+  Hardware driver for the 'CMX', 'COMX' and 'HiCOMX' boards from the
+  MultiGate family. Say Y if you have one of these. 
+
+  You will need additional firmware to use these cards, which are
+  downloadable from ftp://ftp.itc.hu/.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-comx.o.
+
+LoCOMX board support
+CONFIG_COMX_HW_LOCOMX
+  Hardware driver for the 'LoCOMX' board from the MultiGate family. Say Y
+  if you have a board like this. 
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-locomx.o.
+
+MixCOM board support
+CONFIG_COMX_HW_MIXCOM
+  Hardware driver for the 'MixCOM' board from the MultiGate family. Say Y
+  if you have a board like this.
+
+  If you want to use the watchdog device on this card, you should
+  select it in the Watchdog Cards section of the Character Devices
+  configuration. The ISDN interface of this card is Teles 16.3 compatible,
+  you should enable it in the ISDN configuration menu. The driver for the 
+  flash ROM of this card is available separately on ftp://ftp.itc.hu/.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-mixcom.o.
+
+MultiGate Cisco-HDLC and synchronous PPP protocol support
+CONFIG_COMX_PROTO_PPP
+  Cisco-HDLC and synchronous PPP protocol driver for all MultiGate boards. 
+  Say Y if you want to use either protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called 
+  comx-proto-ppp.o.
+
+MultiGate LAPB protocol support
+CONFIG_COMX_PROTO_LAPB
+  LAPB protocol driver for all MultiGate boards. Say Y if you 
+  want to use this protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-proto-lapb.o.
+
+MultiGate Frame Relay protocol support
+CONFIG_COMX_PROTO_FR
+  Frame Relay protocol driver for all MultiGate boards. Say Y if you 
+  want to use this protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-proto-fr.o.
+
 Ethernet (10 or 100Mbit)
 CONFIG_NET_ETHERNET
   Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common
@@ -5802,6 +5857,24 @@
   say M here and read Documentation/modules.txt. This is recommended.
   The module will be called rtl8139.o.
 
+SiS 900/7016 support
+CONFIG_SIS900
+  This is a driver for the Fast Ethernet PCI network cards based on
+  the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in
+  SiS 630 and SiS 540 chipsets. If you have one of those, say Y and
+  read the Ethernet-HOWTO, available via FTP (user: anonymous) in   
+  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. Please read 
+  Documentation/networking/sis900.txt and comments at the beginning
+  of drivers/net/sis900.c for more information.
+
+  This driver also supports AMD 79C901 HomePNA such that you can use
+  your phone line as network cable.
+
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want), 
+  say M here and read Documentation/modules.txt. This is recommended. 
+  The module will be called sis900.o.
+
 Packet Engines Yellowfin Gigabit-NIC support
 CONFIG_YELLOWFIN
   Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet
@@ -8884,9 +8957,15 @@
   APM is a BIOS specification for saving power using several different
   techniques. This is mostly useful for battery powered laptops with
   APM compliant BIOSes. If you say Y here, the system time will be
-  reset after a USER RESUME operation, the /proc/apm device will
-  provide battery status information, and user-space programs will
-  receive notification of APM "events" (e.g., battery status change).
+  reset after a RESUME operation, the /proc/apm device will provide
+  battery status information, and user-space programs will receive
+  notification of APM "events" (e.g. battery status change).
+
+  If you select "Y" here, you can disable actual use of the APM
+  BIOS by passing the "apm=off" option to the kernel at boot time.
+
+  Note that the APM support is almost completely disabled for
+  machines with more than one CPU.
 
   Supporting software is available; for more information, read the
   Battery Powered Linux mini-HOWTO, available via FTP (user:
@@ -8899,9 +8978,7 @@
   This driver does not support the TI 4000M TravelMate and the ACER
   486/DX4/75 because they don't have compliant BIOSes. Many "green"
   desktop machines also don't have compliant BIOSes, and this driver
-  will cause those machines to panic during the boot phase (typically,
-  these machines are using a data segment of 0040, which is reserved
-  for the Linux kernel). 
+  may cause those machines to panic during the boot phase.
 
   If you are running Linux on a laptop, you may also want to read the
   Linux Laptop home page on the WWW at
@@ -9422,20 +9499,6 @@
   You can say M here to compile this driver as a module; the module is
   called sb.o.
 
-Generic OPL2/OPL3 FM synthesizer support
-CONFIG_SOUND_ADLIB
-  Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
-  Answering Y is usually a safe and recommended choice, however some
-  cards may have software (TSR) FM emulation. Enabling FM support with
-  these cards may cause trouble (I don't currently know of any such
-  cards, however). 
-
-  Please read the file Documentation/sound/OPL3 if your card has an
-  OPL3 chip.
-
-  If unsure, say Y.
-  
-
 #Loopback MIDI device support
 #CONFIG_SOUND_VMIDI
 ###
@@ -9715,7 +9778,15 @@
 
 FM synthesizer (YM3812/OPL-3) support
 CONFIG_SOUND_YM3812
-  Answer Y here, unless you know you will not need the option.
+  Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
+  Answering Y is usually a safe and recommended choice, however some
+  cards may have software (TSR) FM emulation. Enabling FM support with
+  these cards may cause trouble (I don't currently know of any such
+  cards, however).
+  Please read the file Documentation/sound/OPL3 if your card has an
+  OPL3 chip.
+
+  If unsure, say Y.
 
 Sun Audio support
 CONFIG_SUN_AUDIO
@@ -9953,11 +10024,6 @@
 
 ISDN subsystem
 CONFIG_ISDN
-  CAUTION: the ISDN driver shipped with this kernel distribution
-  is outdated and might not work without problems. An updated driver
-  is available for download. Please read http://www.isdn4linux.de
-  on the WWW for a list of servers.
-
   ISDN ("Integrated Services Digital Networks", called RNIS in France)
   is a special type of fully digital telephone service; it's mostly
   used to connect to your Internet service provider (with SLIP or
@@ -9966,9 +10032,7 @@
   conversations while downloading stuff. It only works if your
   computer is equipped with an ISDN card and both you and your service
   provider purchased an ISDN line from the phone company. For details,
-  read http://alumni.caltech.edu/~dank/isdn/ on the WWW. (To browse
-  the WWW, you need to have access to a machine on the Internet that
-  has a program like lynx or netscape.)
+  read http://alumni.caltech.edu/~dank/isdn/ on the WWW.
 
   This driver allows you to use an ISDN-card for networking
   connections and as dialin/out device. The isdn-tty's have a built in
@@ -10018,25 +10082,28 @@
   is the only voice-supporting driver. See
   Documentation/isdn/README.audio for more information.
 
-X.25 PLP on top of ISDN (EXPERIMENTAL)
+X.25 PLP on top of ISDN
 CONFIG_ISDN_X25
-  This experimental feature provides the X.25 protocol over ISDN
-  connections. See Documentation/isdn/README.x25 for more information
+  This feature provides the X.25 protocol over ISDN connections.
+  See Documentation/isdn/README.x25 for more information
   if you are thinking about using this.
 
 ISDN diversion services support
 CONFIG_ISDN_DIVERSION
   This option allows you to use some supplementary diversion
   services in conjunction with the HiSax driver on an EURO/DSS1
-  line. Supported options are CD (call deflection), CFU (Call 
-  forward unconditional), CFB (Call forward when busy) and CFNR 
-  (call forward not reachable).
-  Additionally the actual CFU, CFB and CFNR state may be 
-  interrogated. The use of CFU, CFB, CFNR and interrogation may
-  be limited to some countries. The keypad protocol is still not
-  implemented.
-  CD should work in all countries if this service has been sub-
-  scribed.
+  line. 
+
+  Supported options are CD (call deflection), CFU (Call forward
+  unconditional), CFB (Call forward when busy) and CFNR (call forward
+  not reachable). Additionally the actual CFU, CFB and CFNR state may
+  be interrogated.
+
+  The use of CFU, CFB, CFNR and interrogation may be limited to some
+  countries. The keypad protocol is still not implemented. CD should
+  work in all countries if the service has been subscribed to.
+
+  Please read the file Documentation/isdn/README.diversion.
 
 ICN 2B and 4B support
 CONFIG_ISDN_DRV_ICN
@@ -10278,7 +10345,7 @@
   
   For more informations see under Documentation/isdn/README.hfc-pci.
 
-HiSax Support for Winbond W6692 based cards (EXPERIMENTAL)
+HiSax Support for Winbond W6692 based cards
 CONFIG_HISAX_W6692
   This enables HiSax support for Winbond W6692 based PCI ISDN cards.
   
@@ -10311,35 +10378,33 @@
   can be inserted in and removed from the running kernel whenever you
   want, details in Documentation/modules.txt); the module will be
   called sc.o. See Documentation/isdn/README.sc and
-  http://www.spellcast.com for more information (to browse the WWW,
-  you need to have access to a machine on the Internet that has a
-  program like lynx or netscape).
+  http://www.spellcast.com for more information.
 
 Eicon.Diehl active card support
 CONFIG_ISDN_DRV_EICON
   Say Y here if you have an Eicon active ISDN card. In order to use
   this card, additional firmware is necessary, which has to be loaded
-  into the card using the eiconctrl utility which is part of the latest
-  isdn4k-utils package. Please read the file
+  into the card using the eiconctrl utility which is part of the
+  latest isdn4k-utils package. Please read the file
   Documentation/isdn/README.eicon for more information.
   
 Eicon old-type card support
 CONFIG_ISDN_DRV_EICON_ISA
-  Say Y here if you have an old-type Eicon active ISDN card. In order to
-  use this card, additional firmware is necessary, which has to be loaded
-  into the card using the eiconctrl utility which is part of the latest
-  isdn4k-utils package. Please read the file
+  Say Y here if you have an old-type Eicon active ISDN card. In order
+  to use this card, additional firmware is necessary, which has to be
+  loaded into the card using the eiconctrl utility which is part of
+  the latest isdn4k-utils package. Please read the file
   Documentation/isdn/README.eicon for more information.
 
 Support AT-Fax Class 2 commands
 CONFIG_ISDN_TTY_FAX
   If you say Y here, the modem-emulator will support a subset of the
   Fax Class 2 commands. Using a getty with fax-support
-  (mgetty+sendfax, hylafax), you will be able to use your Linux box 
-  as an ISDN-fax-machine. This must be supported by the lowlevel driver
+  (mgetty+sendfax, hylafax), you will be able to use your Linux box as
+  an ISDN-fax-machine. This must be supported by the lowlevel driver
   also. See Documentation/isdn/README.fax for more information.
 
-AVM-B1 with CAPI2.0 support
+AVM CAPI2.0 support
 CONFIG_ISDN_DRV_AVMB1
   This enables support for the AVM B1/T1 ISDN networking cards.In
   addition, a CAPI (Common ISDN Application Programming Interface, a
@@ -10350,7 +10415,7 @@
   additional firmware is necessary, which has to be downloaded into
   the card using a utility which is distributed separately. Please
   read the file Documentation/isdn/README.avmb1. 
-
+  
   This code is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
   The module will be called avmb1.o. If you want to compile it as a
@@ -10364,7 +10429,7 @@
 CONFIG_ISDN_DRV_AVMB1_B1PCI
   Enable support for the PCI version of the AVM B1 card.
 
-AVM T1/T1B ISA support
+AVM T1/T1-B ISA support
 CONFIG_ISDN_DRV_AVMB1_T1ISA
   Enable support for the AVM T1 T1B card.
   Note: This is a PRI card and handle 30 B-channels.
@@ -10373,11 +10438,17 @@
 CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
   Enable support for the PCMCIA version of the AVM B1 card.
 
+AVM T1/T1-B PCI support
+CONFIG_ISDN_DRV_AVMB1_T1PCI
+  Enable support for the AVM T1 T1B card.
+  Note: This is a PRI card and handle 30 B-channels.
+
 Verbose reason code reporting (kernel size +=7K)
 CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
   If you say Y here, the AVM B1 driver will give verbose reasons for
   disconnecting. This will increase the size of the kernel by 7 KB. If
   unsure, say Y.
+
 
 IBM Active 2000 support (EXPERIMENTAL)
 CONFIG_ISDN_DRV_ACT2000
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/isdn/00-INDEX linux.14p6/Documentation/isdn/00-INDEX
--- linux.vanilla/Documentation/isdn/00-INDEX	Thu Apr  2 01:20:56 1998
+++ linux.14p6/Documentation/isdn/00-INDEX	Tue Nov 16 15:10:47 1999
@@ -6,12 +6,18 @@
 	- description of Linklevel and Hardwarelevel ISDN interface.
 README
 	- general info on what you need and what to do for Linux ISDN.
+README.FAQ
+	- general info for FAQ.
 README.audio
 	- info for running audio over ISDN.
+README.fax
+	- info for using Fax over ISDN.
 README.icn
 	- info on the ICN-ISDN-card and its driver.
 README.HiSax
 	- info on the HiSax driver which replaces the old teles.
+README.hfc-pci
+	- info on hfc-pci based cards.
 README.pcbit
 	- info on the PCBIT-D ISDN adapter and driver.
 README.syncppp
@@ -22,8 +28,12 @@
 	- info on driver for AVM-B1 ISDN card.
 README.act2000
 	- info on driver for IBM ACT-2000 card.
+README.eicon
+	- info on driver for Eicon active cards.
 README.concap
 	- info on "CONCAP" ecapsulation protocol interface used for X.25.
+README.diversion
+	- info on module for isdn diversion services.
 README.sc
 	- info on driver for Spellcaster cards.
 README.x25
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/isdn/README linux.14p6/Documentation/isdn/README
--- linux.vanilla/Documentation/isdn/README	Wed Oct 20 01:12:32 1999
+++ linux.14p6/Documentation/isdn/README	Tue Nov 16 15:10:47 1999
@@ -11,7 +11,7 @@
   necessary. Those programs and some contributed utilities are available
   at
 
-   ftp.franken.de
+   ftp.isdn4linux.de
 
    /pub/isdn4linux/isdn4k-utils-<VersionNumber>.tar.gz
 
@@ -22,11 +22,11 @@
    reasons, the mailing-list's primary language is german. However mails
    written in english have been welcome all the time.
 
-   to subscribe: write a email to majordomo@hub-wue.franken.de,
+   to subscribe: write a email to majordomo@listserv.isdn4linux.de,
    Subject irrelevant, in the message body:
    subscribe isdn4linux <your_email_address>
 
-   To write to the mailing-list, write to isdn4linux@hub-wue.franken.de
+   To write to the mailing-list, write to isdn4linux@listserv.isdn4linux.de
 
    This mailinglist is bidirectionally gated to the newsgroup
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/isdn/README.FAQ linux.14p6/Documentation/isdn/README.FAQ
--- linux.vanilla/Documentation/isdn/README.FAQ	Thu Jan  1 01:00:00 1970
+++ linux.14p6/Documentation/isdn/README.FAQ	Tue Nov 16 15:10:47 1999
@@ -0,0 +1,26 @@
+
+The FAQ for isdn4linux
+======================
+
+Please note that there is a big FAQ available in the isdn4k-utils.
+You find it in:
+ isdn4k-utils/FAQ/i4lfaq.sgml
+
+In case you just want to see the FAQ online, or download the newest version,
+you can have a look at my website:
+http://www.mhessler.de/i4lfaq/ (view + download)
+or:
+http://www.isdn4linux.de/faq/ (view)
+
+As the extension tells, the FAQ is in SGML format, and you can convert it
+into text/html/... format by using the sgml2txt/sgml2html/... tools.
+Alternatively, you can also do a 'configure; make all' in the FAQ directory.
+
+
+Please have a look at the FAQ before posting anything in the Mailinglist,
+or the newsgroup!
+
+
+Matthias Hessler
+hessler@isdn4linux.de
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/isdn/README.avmb1 linux.14p6/Documentation/isdn/README.avmb1
--- linux.vanilla/Documentation/isdn/README.avmb1	Sat Aug 14 02:27:12 1999
+++ linux.14p6/Documentation/isdn/README.avmb1	Tue Nov 16 15:10:47 1999
@@ -169,7 +169,7 @@
 
 Questions
 ---------
-Check out the FAQ (ftp.franken.de) or subscribe to the
+Check out the FAQ (ftp.isdn4linux.de) or subscribe to the
 linux-avmb1@calle.in-berlin.de mailing list by sending
 a mail to majordomo@calle.in-berlin.de with
 subscribe linux-avmb1
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/isdn/README.eicon linux.14p6/Documentation/isdn/README.eicon
--- linux.vanilla/Documentation/isdn/README.eicon	Sat Aug 14 02:27:12 1999
+++ linux.14p6/Documentation/isdn/README.eicon	Tue Nov 16 15:10:47 1999
@@ -1,6 +1,6 @@
-$Id: README.eicon,v 1.4 1999/07/11 17:17:30 armin Exp $
+$Id: README.eicon,v 1.5 1999/10/11 18:13:25 armin Exp $
 
-(c) 1999 Cytronics & Melware
+(c) 1999 Cytronics & Melware (info@melware.de)
 
 This document describes the eicon driver for the
 Eicon.Diehl active ISDN cards.
@@ -24,17 +24,26 @@
 
 
 Supported Cards
----------------
+===============
 
+Old ISA type
+------------
 - S-Card ISA
 - SX-Card ISA
 - SXn-Card ISA
 - SCOM-Card ISA
 - Quadro-Card ISA
 - S2M-Card ISA
+
+DIVA Server family
+------------------
 - DIVA Server BRI/PCI 2M
 - DIVA Server PRI/PCI 2M (9M 23M 30M)
-  (Only analog modem functions of the DSPs are currently implemented)
+	supported functions of onboard DSPs:
+	- analog modem
+	- fax group 2/3 (Fax Class 2 commands)
+	- DTMF detection
+
 
 ISDN D-Channel Protocols
 ------------------------
@@ -76,6 +85,10 @@
 Details about using the eiconctrl utility are in 'man eiconctrl'
 or will be printed by starting eiconctrl without any parameters.
 
+Thanks to 
+	Deutsche Mailbox Saar-Lor-Lux GmbH
+	for sponsoring and testing fax
+	capabilities with Diva Server cards.
 
 
 Any reports about bugs, errors and even wishes are welcome.
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/isdn/README.fax linux.14p6/Documentation/isdn/README.fax
--- linux.vanilla/Documentation/isdn/README.fax	Sat Aug 28 20:00:52 1999
+++ linux.14p6/Documentation/isdn/README.fax	Tue Nov 16 15:10:47 1999
@@ -9,7 +9,7 @@
 
 - You need the commands as dummy, because you are using
   hylafax (with patch) for AVM capi.
-- You want to use the fax capabillities of your isdn-card.
+- You want to use the fax capabilities of your isdn-card.
   (supported cards are listed below)
 
 
@@ -21,8 +21,12 @@
 Supported ISDN-Cards
 --------------------
 
-Eicon DIVA Server BRI/PCI (will be ready soon)
-Eicon DIVA Server PRI/PCI (will be ready soon)
+Eicon DIVA Server BRI/PCI
+	- full support with both B-channels.
+
+Eicon DIVA Server PRI/PCI
+	- full support on amount of B-channels
+		depending on DSPs on board.
 
 
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/isdn/README.pcbit linux.14p6/Documentation/isdn/README.pcbit
--- linux.vanilla/Documentation/isdn/README.pcbit	Tue Apr 28 22:22:04 1998
+++ linux.14p6/Documentation/isdn/README.pcbit	Tue Nov 16 15:10:47 1999
@@ -32,7 +32,7 @@
 the manufacturer in order to solve this problem.
 
 Information/hints/help can be obtained in the linux isdn
-mailing list (isdn4linux@hub-wue.franken.de) or directly from me.
+mailing list (isdn4linux@listserv.isdn4linux.de) or directly from me.
 
 regards,
   Pedro.
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/kernel-docs.txt linux.14p6/Documentation/kernel-docs.txt
--- linux.vanilla/Documentation/kernel-docs.txt	Sat Aug 14 02:26:08 1999
+++ linux.14p6/Documentation/kernel-docs.txt	Wed Oct 27 18:53:51 1999
@@ -1,15 +1,14 @@
 
-   
- INDEX OF DOCUMENTATION FOR PEOPLE INTERESTED IN WRITING AND/OR UNDERSTANDING
-                               THE LINUX KERNEL.
-                                       
-		Juan-Mariano de Goyeneche <jmseyas@dit.upm.es>
-   
-   
-   /*
-    * The latest version of this document may be found at: 
-    *   http://www.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html
-    */
+       Index of Documentation for People Interested in Writing and/or
+                                      
+                      Understanding the Linux Kernel.
+                                      
+              Juan-Mariano de Goyeneche < jmseyas@dit.upm.es>
+                                      
+/*
+ * The latest version of this document may be found at:
+ *   http://www.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html
+ */
 
    The need for a document like this one became apparent in the
    linux-kernel mailing list as the same questions, asking for pointers
@@ -31,301 +30,411 @@
    corrections, ideas or comments are also welcomed.
    
    The papers that follow are listed in no particular order. All are
-   catalogued with the following fields: the document's "Title", the
-   "Author"/s, the "URL" where they can be found, some "Keywords"
-   helpful when searching for specific topics, and a brief "Description"
-   of the Document.
+   cataloged with the following fields: the document's "Title", the
+   "Author"/s, the "URL" where they can be found, some "Keywords" helpful
+   when searching for specific topics, and a brief "Description" of the
+   Document.
    
    Enjoy!
    
-   
-   ON-LINE DOCS:
-   
-          + Title: "The Linux Kernel"
-            Author: David A. Rusling.
-            URL: http://sunsite.unc.edu/linux/LDP/tlk/tlk.html
-            Keywords: everything!, book.
-            Description: On line, 200 pages book describing most
-            aspects of the Linux Kernel. Probably, the first reference
-            for beginners. Lots of illustrations explaining data
-            structures use and relationships in the purest Richard W.
-            Stevens' style. Contents: "1.-Hardware Basics, 2.-Software
-            Basics, 3.-Memory Management, 4.-Processes, 5.-Interprocess
-            Communication Mechanisms, 6.-PCI, 7.-Interrupts and Interrupt
-            Handling, 8.-Device Drivers, 9.-The File system,
-            10.-Networks, 11.-Kernel Mechanisms, 12.-Modules, 13.-The
-            Linux Kernel Sources, A.-Linux Data Structures, B.-The Alpha
-            AXP Processor, C.-Useful Web and FTP Sites, D.-The GNU
-            General Public License, Glossary". In short: a must have.
-   
-          + Title: "The Linux Kernel Hackers' Guide"
-            Author: Michael K.Johnson and others.
-            URL: http://www.redhat.com:8080/HyperNews/get/khg.html
-            Keywords: everything!
-            Description: No more Postscript book-like version. Only
-            HTML now. Many people have contributed. The interface is
-            similar to web available mailing lists archives. You can find
-            some articles and then some mails asking questions about them
-            and/or complementing previous contributions. A little bit
-            anarchic in this aspect, but with some valuable information
-            in some cases.
-       
-          + Title: "Tour Of the Linux Kernel Source"
-            Author: Vijo Cherian.
-            URL: http://www.svrec.ernet.in/~vijo/tolks/tolks.html
-            Keywords:
-            Description: The name says it all. A tour of the sources,
-            describing directories, files, variables, data structures...
-            It covers general stuff, device drivers, filesystems, IPC and
-            Network Code.
-       
-          + Title: "Overview of the Virtual File System"
-            Author: Richard Gooch.
-            URL: http://www.atnf.csiro.au/~rgooch/linux/vfs.txt
-            Keywords: VFS, File System, mounting filesystems, opening
-            files, dentries,
-            dcache. Description: Brief introduction to the Linux
-            Virtual File System. What is it, how it works, operations
-            taken when opening a file or mounting a file system and
-            description of important data structures explaining the
-            purpose of each of their entries.
-   
-          + Title: "The Linux RAID-1, 4, 5 Code"
-            Author: Ingo Molnar, Gadi Oxman and Miguel de Icaza.
-            URL: http://www.ssc.com/lj/issue44/2391.html
-            Keywords: RAID, MD driver.
-            Description: Linux Journal Kernel Korner article. Here is
-            it's abstract: "A description of the implementation of the
-            RAID-1, RAID-4 and RAID-5 personalities of the MD device
-            driver in the Linux kernel, providing users with high
-            performance and reliable, secondary-storage capability using
-            software".
-   
-          + Title: "Dynamic Kernels: Modularized Device Drivers"
-            Author: Alessandro Rubini.
-            URL: http://www.ssc.com/lj/issue23/1219.html
-            Keywords: device driver, module, loading/unloading modules,
-            allocating
-            resources. Description: Linux Journal Kernel Korner
-            article. Here is it's abstract: "This is the first of a
-            series of four articles co-authored by Alessandro Rubini and
-            Georg Zezchwitz which present a practical approach to writing
-            Linux device drivers as kernel loadable modules. This
-            installment presents an introduction to the topic, preparing
-            the reader to understand next month's installment".
-   
-          + Title: "Dynamic Kernels: Discovery"
-            Author: Alessandro Rubini.
-            URL: http://www.ssc.com/lj/issue24/kk24.html
-            Keywords: character driver, init_module, clean_up module,
-            autodetection,
-            mayor number, minor number, file operations, open(), close().
-            Description: Linux Journal Kernel Korner article. Here is
-            it's abstract: "This article, the second of four, introduces
-            part of the actual code to create custom module implementing
-            a character device driver. It describes the code for module
-            initialization and cleanup, as well as the open() and close()
-            system calls".
-   
-          + Title: "The Devil's in the Details"
-            Author: Georg v. Zezschwitz and Alessandro Rubini.
-            URL: http://www.ssc.com/lj/issue25/kk25.html
-            Keywords: read(), write(), select(), ioctl(), blocking/non
-            blocking mode,
-            interrupt handler. Description: Linux Journal Kernel Korner
-            article. Here is it's abstract: "This article, the third of
-            four on writing character device drivers, introduces concepts
-            of reading, writing, and using ioctl-calls".
-   
-          + Title: "Dissecting Interrupts and Browsing DMA"
-            Author: Alessandro Rubini and Georg v. Zezschwitz.
-            URL: http://www.ssc.com/lj/issue26/interrupt.html
-            Keywords: interrupts, irqs, DMA, bottom halves, task
-            queues.
-            Description: Linux Journal Kernel Korner article. Here is
-            it's abstract: "This is the fourth in a series of articles
-            about writing character device drivers as loadable kernel
-            modules. This month, we further investigate the field of
-            interrupt handling. Though it is conceptually simple,
-            practical limitations and constraints make this an
-            ``interesting'' part of device driver writing, and several
-            different facilities have been provided for different
-            situations. We also investigate the complex topic of DMA".
-   
-          + Title: "Network Buffers And Memory Management"
-            Author: Alan Cox.
-            URL: http://www.ssc.com/lj/issue30/kk30.html
-            Keywords: sk_buffs, network devices, protocol/link layer
-            variables, network
-            devices flags, transmit, receive, configuration, multicast.
-            Description: Linux Journal Kernel Korner. Here is the
-            abstract: "Writing a network device driver for Linux is
-            fundamentally simple---most of the complexity (other than
-            talking to the hardware) involves managing network packets in
-            memory".
-       
-          + Title: "An Introduction to the Linux 1.3.x Networking Code"
-            Author: Vipul Gupta.
-            URL:
-            http://anchor.cs.binghamton.edu/courses/cs628/linux-net.html
-            Keywords: files, sk_buffs.
-            Description: A short description of files under the net/
-            directory. Each file has a one- or two-line paragraph to
-            describe it. Also, sk_buffs is explained with some
-            beautiful pictures. A little bit outdated.
-   
-          + Title: "Linux ioctl() Primer"
-            Author: Vipul Gupta.
-            URL:
-            http://anchor.cs.binghamton.edu/courses/cs628/ioctl.html
-            Keywords: ioctl, socket.
-            Description: Little description and examples on the use and
-            implementation of the ioctl() system call. A little bit
-            biased towards sockets.
-       
-          + Title: "Writing Linux Device Drivers"
-            Author: Michael K. Johnson.
-            URL: http://www.redhat.com/~johnsonm/devices.html
-            Keywords: files, VFS, file operations, kernel interface,
-            character vs
-            block devices, I/O access, hardware interrupts, DMA, access
-            to user memory, memory allocation, timers. Description:
-            Introductory 50-minutes (sic) tutorial on writing device
-            drivers. 12 pages written by the same author of the "Kernel
-            Hackers' Guide" which give a very good overview of the topic.
-   
-          + Title: "The Venus kernel interface"
-            Author: Peter J. Braam.
-            URL:
-            http://www.coda.cs.cmu.edu/doc/html/kernel-venus-protocol.html
-            Keywords: coda, filesystem, venus, cache manager.
-            Description: "This document describes the communication
-            between Venus and kernel level file system code needed for
-            the operation of the Coda filesystem. This version document
-            is meant to describe the current interface (version 1.0) as
-            well as improvements we envisage".
-       
-          + Title: "Programming PCI-Devices under Linux"
-            Author: Claus Schroeter.
-            URL:
-            ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pc
-            ip.ps.gz
-            Keywords: PCI, device, busmastering.
-            Description: 6 pages tutorial on PCI programming under
-            Linux. Gives the basic concepts on the architecture of the
-            PCI subsystem, as long as basic functions and macros to
-            read/write the devices and perform busmastering.
-       
-          + Title: "Writing Character Device Driver for Linux"
-            Author: R. Baruch and C. Schroeter.
-            URL:
-            ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/dr
-            ivers.ps.gz
-            Keywords: character device drivers, I/O, signals, DMA,
-            accessing ports in user space, kernel environment.
-            Description: 68 pages paper on writing character drivers. A
-            little bit old (1.993, 1.994) although still useful.
-       
-       
-       
-     * BOOKS: (Not on-line)
-       
-          + Title: "Linux Device Drivers"
-            Author: Alessandro Rubini.
-            Publisher: O'Reilly &Associates.
-            Date: 1998.
-            ISBN: 1-56592-292-1
-       
-          + Title: "Linux Kernel Internals"
-            Author: Michael Beck.
-            Publisher: Addison-Wesley.
-            Date: 1997.
-            ISBN: 0-201-33143-8 (second edition)
-       
-          + Title: "The Design of the UNIX Operating System"
-            Author: Maurice J. Bach.
-            Publisher: Prentice Hall.
-            Date: 1986.
-            ISBN: ???
-   
-          + Title: "The Design and Implementation of the 4.3 BSD UNIX
-            Operating System"
-            Author: Samuel J. Leffler, Marshall Kirk McKusick, Michael
-            J. Karels, John S. Quarterman.
-            Publisher: Addison-Wesley.
-            Date: 1989 (reprinted with corrections on October, 1990).
-            ISBN: 0-201-06196-1
-       
-          + Title: "The Design and Implementation of the 4.4 BSD UNIX
-            Operating System"
-            Author: Marshall Kirk McKusick, Keith Bostic, Michael J.
-            Karels, John S. Quarterman.
-            Publisher: Addison-Wesley.
-            Date: 1996.
-            ISBN: 0-201-54979-4
-   
-          + Title: "Programmation Linux 2.0 API systeme et
-            fonctionnement du noyau"
-            Author: Remy Card, Eric Dumas, Franck Mevel.
-            Publisher: Eyrolles.
-            Date: 1997.
-            Pages: 520. ISBN: 2-212-08932-5
-       
-          + Title: "Unix internals -- the new frontiers"
-            Author: Uresh Vahalia.
-            Publisher: Prentice Hall.
-            Date: 1996.
-            Pages: 600. ISBN: 0-13-101908-2
-   
-       
-     * MISCELLANEOUS:
+     ON-LINE DOCS:
        
-          + Name: Linux Source Driver.
-            URL: http://lsd.linux.cz
-            Keywords: Browsing.
-            Description: "Linux Source Driver (LSD) is an application,
-            which can make browsing source codes of Linux kernel easier
-            than you can imagine. You can select between multiple
-            versions of kernel (e.g. 0.01, 1.0.0, 2.0.33, 2.0.34pre13,
-            2.0.0, 2.1.101 etc.). With LSD you can search Linux kernel
-            (fulltext, macros, types, functions and variables) and LSD
-            can generate patches for you on the fly (files, directories
-            or kernel)".
-       
-          + Name: Linux Weekly News.
-            URL: http://lwn.net
-            Keywords: last kernel news.
-            Description: The title says it all. There's a fixed kernel
-            section summarizing developers' work, bug fixes, new features
-            and versions produced during the week. Published every
-            Thursday.
-       
-          + Name: CuTTiNG.eDGe.LiNuX.
-            URL: http://edge.linuxhq.com
-            Keywords: changelist.
-            Description: Site which provides the changelist for every
-            kernel release. What's new, what's better, what's changed.
-            Myrdraal reads the patchs and describes them. Pointers to the
-            patches are there, too.
+     * Title: "The Linux Kernel"
+       Author: David A. Rusling.
+       URL: http://sunsite.unc.edu/linux/LDP/tlk/tlk.html
+       Keywords: everything!, book.
+       Description: On line, 200 pages book describing most aspects of
+       the Linux Kernel. Probably, the first reference for beginners.
+       Lots of illustrations explaining data structures use and
+       relationships in the purest Richard W. Stevens' style. Contents:
+       "1.-Hardware Basics, 2.-Software Basics, 3.-Memory Management,
+       4.-Processes, 5.-Interprocess Communication Mechanisms, 6.-PCI,
+       7.-Interrupts and Interrupt Handling, 8.-Device Drivers, 9.-The
+       File system, 10.-Networks, 11.-Kernel Mechanisms, 12.-Modules,
+       13.-The Linux Kernel Sources, A.-Linux Data Structures, B.-The
+       Alpha AXP Processor, C.-Useful Web and FTP Sites, D.-The GNU
+       General Public License, Glossary". In short: a must have.
+       
+     * Title: "The Linux Kernel Hackers' Guide"
+       Author: Michael K.Johnson and others.
+       URL: http://khg.redhat.com/HyperNews/get/khg.html
+       Keywords: everything!
+       Description: No more Postscript book-like version. Only HTML now.
+       Many people have contributed. The interface is similar to web
+       available mailing lists archives. You can find some articles and
+       then some mails asking questions about them and/or complementing
+       previous contributions. A little bit anarchic in this aspect, but
+       with some valuable information in some cases.
+       
+     * Title: "Conceptual Architecture of the Linux Kernel"
+       Author: Ivan T. Bowman.
+       URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a1.html
+       Keywords: conceptual software arquitecture, extracted design,
+       reverse engineering, system structure.
+       Description: Conceptual software arquitecture of the Linux kernel,
+       automatically extracted from the source code. Very detailed. Good
+       figures. Gives good overall kernel understanding.
+       
+     * Title: "Concrete Architecture of the Linux Kernel"
+       Author: Ivan T. Bowman, Saheem Siddiqi, and Meyer C. Tanuan.
+       URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a2.html
+       Keywords: concrete arquitecture, extracted design, reverse
+       engineering, system structure, dependencies.
+       Description: Concrete arquitecture of the Linux kernel,
+       automatically extracted from the source code. Very detailed. Good
+       figures. Gives good overall kernel understanding. This papers
+       focus on lower details than its predecessor (files, variables...).
+       
+     * Title: "Linux as a Case Study: Its Extracted Software
+       Architecture"
+       Author: Ivan T. Bowman, Richard C. Holt and Neil V. Brewster.
+       URL: http://plg.uwaterloo.ca/~itbowman/papers/linuxcase.html
+       Keywords: software architecture, architecture recovery,
+       redocumentation.
+       Description: Paper appeared at ICSE'99, Los Angeles, May 16-22,
+       1999. A mixture of the previous two documents from the same
+       author.
+       
+     * Title: "Overview of the Virtual File System"
+       Author: Richard Gooch.
+       URL: http://www.atnf.csiro.au/~rgooch/linux/vfs.txt
+       Keywords: VFS, File System, mounting filesystems, opening files,
+       dentries,
+       dcache. Description: Brief introduction to the Linux Virtual File
+       System. What is it, how it works, operations taken when opening a
+       file or mounting a file system and description of important data
+       structures explaining the purpose of each of their entries.
+       
+     * Title: "The Linux RAID-1, 4, 5 Code"
+       Author: Ingo Molnar, Gadi Oxman and Miguel de Icaza.
+       URL: http://www.ssc.com/lj/issue44/2391.html
+       Keywords: RAID, MD driver.
+       Description: Linux Journal Kernel Korner article. Here is it's
+       abstract: "A description of the implementation of the RAID-1,
+       RAID-4 and RAID-5 personalities of the MD device driver in the
+       Linux kernel, providing users with high performance and reliable,
+       secondary-storage capability using software".
+       
+     * Title: "Dynamic Kernels: Modularized Device Drivers"
+       Author: Alessandro Rubini.
+       URL: http://www.ssc.com/lj/issue23/1219.html
+       Keywords: device driver, module, loading/unloading modules,
+       allocating resources.
+       Description: Linux Journal Kernel Korner article. Here is it's
+       abstract: "This is the first of a series of four articles
+       co-authored by Alessandro Rubini and Georg Zezchwitz which present
+       a practical approach to writing Linux device drivers as kernel
+       loadable modules. This installment presents an introduction to the
+       topic, preparing the reader to understand next month's
+       installment".
+       
+     * Title: "Dynamic Kernels: Discovery"
+       Author: Alessandro Rubini.
+       URL: http://www.ssc.com/lj/issue24/kk24.html
+       Keywords: character driver, init_module, clean_up module,
+       autodetection,
+       mayor number, minor number, file operations, open(), close().
+       Description: Linux Journal Kernel Korner article. Here is it's
+       abstract: "This article, the second of four, introduces part of
+       the actual code to create custom module implementing a character
+       device driver. It describes the code for module initialization and
+       cleanup, as well as the open() and close() system calls".
+       
+     * Title: "The Devil's in the Details"
+       Author: Georg v. Zezschwitz and Alessandro Rubini.
+       URL: http://www.ssc.com/lj/issue25/kk25.html
+       Keywords: read(), write(), select(), ioctl(), blocking/non
+       blocking mode, interrupt handler.
+       Description: Linux Journal Kernel Korner article. Here is it's
+       abstract: "This article, the third of four on writing character
+       device drivers, introduces concepts of reading, writing, and using
+       ioctl-calls".
+       
+     * Title: "Dissecting Interrupts and Browsing DMA"
+       Author: Alessandro Rubini and Georg v. Zezschwitz.
+       URL: http://www.ssc.com/lj/issue26/interrupt.html
+       Keywords: interrupts, irqs, DMA, bottom halves, task queues.
+       Description: Linux Journal Kernel Korner article. Here is it's
+       abstract: "This is the fourth in a series of articles about
+       writing character device drivers as loadable kernel modules. This
+       month, we further investigate the field of interrupt handling.
+       Though it is conceptually simple, practical limitations and
+       constraints make this an ``interesting'' part of device driver
+       writing, and several different facilities have been provided for
+       different situations. We also investigate the complex topic of
+       DMA".
+       
+     * Title: "Network Buffers And Memory Management"
+       Author: Alan Cox.
+       URL: http://www.ssc.com/lj/issue30/kk30.html
+       Keywords: sk_buffs, network devices, protocol/link layer
+       variables, network devices flags, transmit, receive,
+       configuration, multicast.
+       Description: Linux Journal Kernel Korner. Here is the abstract:
+       "Writing a network device driver for Linux is fundamentally
+       simple---most of the complexity (other than talking to the
+       hardware) involves managing network packets in memory".
+       
+     * Title: "An Introduction to the Linux 1.3.x Networking Code"
+       Author: Vipul Gupta.
+       URL: http://anchor.cs.binghamton.edu/courses/cs628/linux-net.html
+       Keywords: files, sk_buffs.
+       Description: A short description of files under the net/
+       directory. Each file has a one or two lines paragraph description.
+       sk_buffs explained, too, with some beautiful pictures. A little
+       bit outdated.
+       
+     * Title: "Linux ioctl() Primer"
+       Author: Vipul Gupta.
+       URL: http://anchor.cs.binghamton.edu/courses/cs628/ioctl.html
+       Keywords: ioctl, socket.
+       Description: Little description and examples on the use and
+       implementation of the ioctl() system call. A little bit biased
+       towards sockets.
+       
+     * Title: "Writing Linux Device Drivers"
+       Author: Michael K. Johnson.
+       URL: http://www.redhat.com/~johnsonm/devices.html
+       Keywords: files, VFS, file operations, kernel interface, character
+       vs block devices, I/O access, hardware interrupts, DMA, access to
+       user memory, memory allocation, timers.
+       Description: Introductory 50-minutes (sic) tutorial on writing
+       device drivers. 12 pages written by the same author of the "Kernel
+       Hackers' Guide" which give a very good overview of the topic.
+       
+     * Title: "The Venus kernel interface"
+       Author: Peter J. Braam.
+       URL:
+       http://www.coda.cs.cmu.edu/doc/html/kernel-venus-protocol.html
+       Keywords: coda, filesystem, venus, cache manager.
+       Description: "This document describes the communication between
+       Venus and kernel level file system code needed for the operation
+       of the Coda filesystem. This version document is meant to describe
+       the current interface (version 1.0) as well as improvements we
+       envisage".
+       
+     * Title: "Programming PCI-Devices under Linux"
+       Author: Claus Schroeter.
+       URL:
+       ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pcip.ps
+       .gz
+       Keywords: PCI, device, busmastering.
+       Description: 6 pages tutorial on PCI programming under Linux.
+       Gives the basic concepts on the architecture of the PCI subsystem,
+       as long as basic functions and macros to read/write the devices
+       and perform busmastering.
+       
+     * Title: "Writing Character Device Driver for Linux"
+       Author: R. Baruch and C. Schroeter.
+       URL:
+       ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/drivers
+       .ps.gz
+       Keywords: character device drivers, I/O, signals, DMA, accesing
+       ports in user space, kernel environment.
+       Description: 68 pages paper on writing character drivers. A little
+       bit old (1.993, 1.994) although still useful.
+       
+     * Title: "The Second Extended Filesystem"
+       Author: Matthew Wilcox.
+       URL: http://pocket.fluff.org/~mrw/linux/ext2.txt
+       Keywords: ext2, filesystem.
+       Description: Description of ext2's blocks, directories, inodes ...
+       
+     * Title: "Analysis of the Ext2fs structure"
+       Author: Louis-Dominique Dubeau.
+       URL: http://step.polymtl.ca/~ldd/ext2fs/ext2fs_toc.html
+       Keywords: ext2, filesystem, ext2fs.
+       Description: Description of ext2's blocks, directories, inodes,
+       bitmaps, invariants ...
+       
+     * Title: "Kernel API changes from 2.0 to 2.2"
+       Author: Richard Gooch.
+       URL:
+       http://www.atnf.csiro.au/~rgooch/linux/docs/porting-to-2.2.html
+       Keywords: 2.2, changes.
+       Description: Kernel functions/structures/variables which changed
+       from 2.0.x to 2.2.x.
+       
+     * Title: "Kernel API changes from 2.2 to 2.3"
+       Author: Richard Gooch.
+       URL:
+       http://www.atnf.csiro.au/~rgooch/linux/docs/porting-to-2.2.html
+       Keywords: 2.3, changes.
+       Description: Kernel functions/structures/variables which changed
+       from 2.2.x to 2.3.x.
+       
+     * Title: "Linux Kernel Module Programming Guide"
+       Author: Ori Pomerantz.
+       URL:
+       http://www.leo.org/pub/comp/os/unix/linux/sunsite/docs/linux-doc-p
+       roject/module-programming-guide/index.html
+       Keywords: modules, free book, /proc, ioctls, system calls,
+       interrupt handlers .
+       Description: Very nice 92 pages free book on the topic of modules
+       programming. Lots of examples.
+       
+     * Title: "Device File System (devfs) Overview"
+       Author: Richard Gooch.
+       URL: http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.txt
+       Keywords: filesystem, /dev, devfs, dynamic devices, major/minor
+       allocation, device management.
+       Description: Document describing Richard Gooch's controversial
+       devfs, which allows for dynamic devices, only shows present
+       devices in /dev, gets rid of major/minor numbers allocation
+       problems, and allows for hundreds of identical devices (which some
+       USB systems might demand soon).
+       
+     * Title: "I/O Event Handling Under Linux"
+       Author: Richard Gooch.
+       URL: http://www.atnf.csiro.au/~rgooch/linux/docs/io-events.html
+       Keywords: IO, I/O, select(2), poll(2), FDs, aio_read(2), readiness
+       event queues.
+       Description: From the Introduction: "I/O Event handling is about
+       how your Operating System allows you to manage a large number of
+       open files (file descriptors in UNIX/POSIX, or FDs) in your
+       application. You want the OS to notify you when FDs become active
+       (have data ready to be read or are ready for writing). Ideally you
+       want a mechanism that is scalable. This means a large number of
+       inactive FDs cost very little in memory and CPU time to manage".
+       
+     BOOKS: (Not on-line)
+   
+     * Title: "Linux Device Drivers"
+       Author: Alessandro Rubini.
+       Publisher: O'Reilly &Associates.
+       Date: 1998.
+       ISBN: 1-56592-292-1
+       
+     * Title: "Linux Kernel Internals"
+       Author: Michael Beck.
+       Publisher: Addison-Wesley.
+       Date: 1997.
+       ISBN: 0-201-33143-8 (second edition)
+       
+     * Title: "The Design of the UNIX Operating System"
+       Author: Maurice J. Bach.
+       Publisher: Prentice Hall.
+       Date: 1986.
+       Pages: 471.
+       ISBN: 0-13-201757-1
+       
+     * Title: "The Design and Implementation of the 4.3 BSD UNIX
+       Operating System"
+       Author: Samuel J. Leffler, Marshall Kirk McKusick, Michael J.
+       Karels, John S. Quarterman.
+       Publisher: Addison-Wesley.
+       Date: 1989 (reprinted with corrections on October, 1990).
+       ISBN: 0-201-06196-1
+       
+     * Title: "The Design and Implementation of the 4.4 BSD UNIX
+       Operating System"
+       Author: Marshall Kirk McKusick, Keith Bostic, Michael J. Karels,
+       John S. Quarterman.
+       Publisher: Addison-Wesley.
+       Date: 1996.
+       ISBN: 0-201-54979-4
+       
+     * Title: "Programmation Linux 2.0 API systeme et fonctionnement du
+       noyau"
+       Author: Remy Card, Eric Dumas, Franck Mevel.
+       Publisher: Eyrolles.
+       Date: 1997.
+       Pages: 520.
+       ISBN: 2-212-08932-5
+       Notes: French.
+       
+     * Title: "The Linux Kernel Book"
+       Author: Remy Card, Eric Dumas, Franck Mevel.
+       Publisher: John Wiley & Sons.
+       Date: 1998.
+       ISBN: 0-471-98141-9
+       Notes: English translation.
+       
+     * Title: "Linux 2.0"
+       Author: Remy Card, Eric Dumas, Franck Mevel.
+       Publisher: Gestión 2000.
+       Date: 1997.
+       Pages: 501.
+       ISBN: 8-480-88208-5
+       Notes: Spanish translation.
+       
+     * Title: "Unix internals -- the new frontiers"
+       Author: Uresh Vahalia.
+       Publisher: Prentice Hall.
+       Date: 1996.
+       Pages: 600.
+       ISBN: 0-13-101908-2
+       
+     * Title: "Linux Core Kernel Commentary. Guide to Insider's Knowledge
+       on the Core Kernel od the Linux Code"
+       Author: Scott Maxwell.
+       Publisher: ???.
+       Date: 1999.
+       Pages: 592.
+       ISBN: 1-57610-469-9
+       Notes: CD-ROM included.
+       
+     MISCELLANEOUS:
+   
+     * Name: Linux Source Driver.
+       URL: http://lsd.linux.cz
+       Keywords: Browsing source code.
+       Description: "Linux Source Driver (LSD) is an application, which
+       can make browsing source codes of Linux kernel easier than you can
+       imagine. You can select between multiple versions of kernel (e.g.
+       0.01, 1.0.0, 2.0.33, 2.0.34pre13, 2.0.0, 2.1.101 etc.). With LSD
+       you can search Linux kernel (fulltext, macros, types, functions
+       and variables) and LSD can generate patches for you on the fly
+       (files, directories or kernel)".
+       
+     * Name: Cross-Referencing Linux.
+       URL: http://lxr.linux.no/source/
+       Keywords: Browsing source code.
+       Description: Another web-based Linux kernel source code browser.
+       Lots of cross references to variables and functions. You can see
+       where they are defined and where they are used.
+       
+     * Name: Linux Weekly News.
+       URL: http://lwn.net
+       Keywords: latest kernel news.
+       Description: The title says it all. There's a fixed kernel section
+       summarizing developers' work, bug fixes, new features and versions
+       produced during the week. Published every Thursday.
+       
+     * Name: Kernel Traffic.
+       URL: http://lwn.net
+       Keywords: linux-kernel mailing list, weekly kernel news.
+       Description: Weekly newsletter covering the most relevant
+       discussions of the linux-kernel mailing list.
+       
+     * Name: CuTTiNG.eDGe.LiNuX.
+       URL: http://edge.kernelnotes.org
+       Keywords: changelist.
+       Description: Site which provides the changelist for every kernel
+       release. What's new, what's better, what's changed. Myrdraal reads
+       the patches and describes them. Pointers to the patches are there,
+       too.
+       
+     * Name: New linux-kernel Mailing List FAQ.
+       URL: Original site:
+       http://www.altern.org/andrebalsa/doc/lkml-faq.html
+       URL: U.S. mirror site:
+       http://www.ececs.uc.edu/~rreilova/linux/lkml-faq.html
+       Keywords: linux-kernel mailing list FAQ.
+       Description: linux-kernel is a mailing list for developers to
+       communicate. This FAQ builds on the previous linux-kernel mailing
+       list FAQ maintained by Frohwalt Egerer, who no longer maintains
+       it. Read it to see how to join the mailing list. Dozens of
+       interesting questions regarding the list, Linux, developers (who
+       is ...?), terms (what is...?) are answered here too. Just read it.
+       
+     * Name: "Linux Virtual File System"
+       Author: Peter J. Braam.
+       URL: http://www.coda.cs.cmu.edu/doc/talks/linuxvfs
+       Keywords: slides, VFS, inode, superblock, dentry, dcache.
+       Description: Set of slides, presumably from a presentation on the
+       Linux VFS layer. Covers version 2.1.x, with dentries and the
+       dcache.
+     _________________________________________________________________
    
-          + Name: New linux-kernel Mailing List FAQ.
-            URL: Original site:
-            http://www.altern.org/andrebalsa/doc/lkml-faq.html
-            URL: U.S. mirror site:
-            http://www.ececs.uc.edu/~rreilova/linux/lkml-faq.html
-            Keywords: linux-kernel mailing list FAQ.
-            Description: linux-kernel is a mailing list for developers
-            to communicate. This FAQ builds on the previous linux-kernel
-            mailing list FAQ maintained by Frohwalt Egerer, who no longer
-            maintains it. Read it to see how to join the mailing list.
-            Dozens of interesting questions regarding the list, Linux,
-            developers (who is ...?), terms (what is...?) are answered
-            here too. Just read it.
-       
-          + Name: "Linux Virtual File System"
-            Author: Peter J. Braam.
-            URL: http://www.coda.cs.cmu.edu/doc/talks/linuxvfs
-            Keywords: slides, VFS, inode, superblock, dentry, dcache.
-            Description: Set of slides, presumably from a presentation
-            on the Linux VFS layer. Covers version 2.1.x, with dentries
-            and the dcache.
+   Document last updated on Wed Oct 27 17:14:03 CEST 1999
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/kernel-parameters.txt linux.14p6/Documentation/kernel-parameters.txt
--- linux.vanilla/Documentation/kernel-parameters.txt	Sat Aug 14 02:26:45 1999
+++ linux.14p6/Documentation/kernel-parameters.txt	Fri Oct 29 23:39:48 1999
@@ -11,7 +11,7 @@
 restrictions referred to are that the relevant option is valid if:
 
     APIC	APIC support is enabled.
-    APM 	Automatic Power Management support is enabled.
+    APM 	Advanced Power Management support is enabled.
     AX25	Appropriate AX.25 support is enabled.
     CD		Appropriate CD support is enabled.
     EIDE	EIDE/ATAPI support is enabled.
@@ -50,7 +50,7 @@
 it will appear as a kernel argument readable via /proc/cmdline by programs
 running once the system is up.
 
-    53c7xx=		[HW,SCSI]
+    53c7xx=		[HW,SCSI] Amiga SCSI controllers
 
     adb_buttons=	[HW,MOUSE]
 
@@ -64,10 +64,12 @@
 
     AM53C974=		[HW,SCSI]
 
-    apm=		[APM] Automatic Power Management.
+    apm=		[APM] Advanced Power Management.
 
     arcrimi=		[HW,NET]
 
+	ataflop=		[HW, M68k]
+
     atamouse=		[HW,MOUSE] Atari Mouse.
 
     atascsi=		[HW,SCSI] Atari SCSI.
@@ -96,22 +98,22 @@
 
     com90xx=		[HW,NET]
 
-    console=		
+    console=		[KNL] output console + comm spec (speed, control, parity)
 
     cyclades=		[HW,SERIAL] Cyclades multi-serial port adapter.
 
-    debug		[KNL] Enable kernel debugging.
+    debug		[KNL] Enable kernel debugging (events log level).
 
     decnet=		[HW,NET]
 
-    digi=		[HW,SERIAL]
+    digi=		[HW,SERIAL] io parameters + enable/disable command
 
     digiepca=		[HW,SERIAL]
 
     dmascc=		[HW,AX25,SERIAL] AX.25 Z80SCC driver with DMA
 			support available.
 
-    dmasound=		[HW,SOUND]
+    dmasound=		[HW,SOUND] (sound subsystem buffers)
 
     dtc3181e=		[HW,SCSI]
 
@@ -121,7 +123,7 @@
 
     edb=		[HW,PS2]
 
-    ether=		[HW,NET] Ethernet.
+    ether=		[HW,NET] Ethernet cards parameters (iomem,irq,dev_name).
 
     fd_mcs=		[HW,SCSI]
 
@@ -129,7 +131,7 @@
 
     floppy=		[HW]
 
-    ftape=		[HW] Floppy Tape subsystem.
+    ftape=		[HW] Floppy Tape subsystem debugging options.
 
     gdth=		[HW,SCSI]
 
@@ -137,7 +139,8 @@
 
     gvp11=		[HW,SCSI]
 
-    hd= 		[EIDE] IDE and EIDE hard drive subsystem.
+    hd= 		[EIDE] (E)IDE hard drive subsystem
+                geometry (Cyl/heads/sectors) or tune parameters.
 
     hfmodem=		[HW,AX25]
 
@@ -149,10 +152,18 @@
 
     icn=		[HW,ISDN]
 
+	ide?=		[HW] (E)IDE subsystem : config (iomem/irq), tuning or
+                     debugging (serialize,reset,no{dma,tune,probe}) or
+					chipset specific parameters
+
+	idebus=		[HW] (E)IDE subsystem : VLB/PCI bus speed
+
     in2000=		[HW,SCSI]
 
     init=		[KNL]
 
+	initrd=		[KNL] initial ramdisk path
+
     ip= 		[PNP]
 
     isp16=		[HW,CD]
@@ -185,7 +196,7 @@
 
     kbd-reset		[VT]
 
-    load_ramdisk=	[RAM]
+    load_ramdisk=	[RAM] initrd loading boolean
 
     lp= 		[LPT] Parallel Printer.
 
@@ -204,7 +215,7 @@
 
     mcdx=		[HW,CD]
 
-    md= 		[HW]
+    md= 		[HW] RAID subsystems devices and level
 
     mdacon=		[MDA]
 
@@ -224,6 +235,8 @@
 
     nfsroot=		[NFS]
 
+	nmi_watchdog=	[KNL, BUGS=ix86] debugging features for SMP kernels
+
     no387		[BUGS=ix86] Tells the kernel to use the 387 maths
 			emulation library even if a 387 maths coprocessor
 			is present.
@@ -231,6 +244,10 @@
     noapic		[SMP,APIC] Tells the kernel not to make use of any
 			APIC that may be present on the system.
 
+    noasync		[HW, M68K] Disables async and sync negotiation for all devices.
+
+    nodisconnect	[HW,SCSI, M68K] Disables SCSI disconnects.
+
     no-halt		[BUGS=ix86]
 
     noinitrd		[RAM] Tells the kernel not to load any configured
@@ -240,6 +257,8 @@
 
     nosmp		[SMP] Tells an SMP kernel to act as a UP kernel.
 
+    nosync		[HW, M68K] Disables sync negotiation for all devices.
+
     optcd=		[HW,CD]
 
     panic=		
@@ -260,7 +279,7 @@
 
     pg. 		[PARIDE]
 
-    pirq=		[SMP,APIC]
+    pirq=		[SMP,APIC] mp-table
 
     plip=		[LP,NET] Parallel port network link.
 
@@ -275,13 +294,14 @@
 
     ramdisk_size=	[RAM]
 
-    ramdisk_start=	[RAM]
+    ramdisk_start=	[RAM] offset of the initrd image when cohabiting with
+					a kernel image on a floppy
 
     reboot=		[BUGS=ix86]
 
     reserve=
 
-    riscom8=		[HW,SERIAL]
+    riscom8=		[HW,SERIAL] multi-port serial driver (io parameters)
 
     ro			[KNL] Mount root device read-only on boot.
 
@@ -303,12 +323,14 @@
 
     specialix=		[HW,SERIAL] Specialix multi-serial port adapter.
 
-    st= 		[HW]
+    st= 		[HW] SCSI tape parameters (buffers, ..)
 
     st0x=		[HW,SCSI]
 
     stram_swap= 	[HW]
 
+    switches=		[HW, M68K]
+
     sym53c416=		[HW,SCSI]
 
     sym53c8xx=		[HW,SCSI]
@@ -331,6 +353,6 @@
 
     wdt=		[HW]
 
-    xd= 		[HW,XT]
+    xd= 		[HW,XT] Original XT 8bit disk controllers
 
     xd_geo=		[HW,XT]
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/networking/comx.txt linux.14p6/Documentation/networking/comx.txt
--- linux.vanilla/Documentation/networking/comx.txt	Thu Jan  1 01:00:00 1970
+++ linux.14p6/Documentation/networking/comx.txt	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,248 @@
+
+		COMX drivers for the 2.2 kernel
+
+Originally written by: Tivadar Szemethy, <tiv@itc.hu>
+Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+
+Last change: 21/06/1999.
+
+INTRODUCTION
+
+This document describes the software drivers and their use for the 
+COMX line of synchronous serial adapters for Linux version 2.2.0 and
+above.
+The cards are produced and sold by ITC-Pro Ltd. Budapest, Hungary
+For further info contact <info@itc.hu> 
+or http://www.itc.hu (mostly in Hungarian).
+The firmware files and software are available from ftp://ftp.itc.hu
+
+Currently, the drivers support the following cards and protocols:
+
+COMX (2x64 kbps intelligent board)
+CMX (1x256 + 1x128 kbps intelligent board)
+HiCOMX (2x2Mbps intelligent board)
+LoCOMX (1x512 kbps passive board)
+MixCOM (1x512 or 2x512kbps passive board with a hardware watchdog an
+	optional BRI interface and optional flashROM (1-32M))
+
+At the moment of writing this document, the (Cisco)-HDLC, LAPB, SyncPPP and
+Frame Relay (DTE, rfc1294 IP encapsulation with partially implemented Q933a 
+LMI) protocols are available as link-level protocol. 
+X.25 support is being worked on.
+
+USAGE
+
+Load the comx.o module and the hardware-specific and protocol-specific 
+modules you'll need into the running kernel using the insmod utility.
+This creates the /proc/comx directory.
+See the example scripts in the 'etc' directory.
+
+/proc INTERFACE INTRO
+
+The COMX driver set has a new type of user interface based on the /proc 
+filesystem which eliminates the need for external user-land software doing 
+IOCTL calls. 
+Each network interface or device (i.e. those ones you configure with 'ifconfig'
+and 'route' etc.) has a corresponding directory under /proc/comx. You can
+dynamically create a new interface by saying 'mkdir /proc/comx/comx0' (or you
+can name it whatever you want up to 8 characters long, comx[n] is just a 
+convention).
+Generally the files contained in these directories are text files, which can
+be viewed by 'cat filename' and you can write a string to such a file by
+saying 'echo _string_ >filename'. This is very similar to the sysctl interface.
+Don't use a text editor to edit these files, always use 'echo' (or 'cat'
+where appropriate).
+When you've created the comx[n] directory, two files are created automagically
+in it: 'boardtype' and 'protocol'. You have to fill in these files correctly
+for your board and protocol you intend to use (see the board and protocol 
+descriptions in this file below or the example scripts in the 'etc' directory).
+After filling in these files, other files will appear in the directory for 
+setting the various hardware- and protocol-related informations (for example
+irq and io addresses, keepalive values etc.) These files are set to default 
+values upon creation, so you don't necessarily have to change all of them.
+
+When you're ready with filling in the files in the comx[n] directory, you can
+configure the corresponding network interface with the standard network 
+configuration utilites. If you're unble to bring the interfaces up, look up
+the various kernel log files on your system, and consult the messages for
+a probable reason.
+
+EXAMPLE
+
+To create the interface 'comx0' which is the first channel of a COMX card:
+
+insmod comx 
+# insmod comx-hw-comx ; insmod comx-proto-hdlc  (these are usually
+autoloaded if you use the kernel module loader)
+
+mkdir /proc/comx/comx0
+echo comx >/proc/comx/comx0/boardtype
+echo 0x360 >/proc/comx/comx0/io		<- jumper-selectable I/O port 
+echo 0x0a >/proc/comx/comx0/irq		<- jumper-selectable IRQ line
+echo 0xd000 >/proc/comx/comx0/memaddr	<- software-configurable memory
+					   address. COMX uses 64 KB, and this
+					   can be: 0xa000, 0xb000, 0xc000, 
+					   0xd000, 0xe000. Avoid conflicts
+					   with other hardware.
+cat </etc/siol1.rom >/proc/comx/comx0/firmware <- the firmware for the card
+echo HDLC >/proc/comx/comx0/protocol	<- the data-link protocol
+echo 10 >/proc/comx/comx0/keepalive	<- the keepalive for the protocol
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255 <-
+					   finally configure it with ifconfig
+Check its status:
+cat /proc/comx/comx0/status
+
+If you want to use the second channel of this board:
+
+mkdir /proc/comx/comx1
+echo comx >/proc/comx/comx1/boardtype
+echo 0x360 >/proc/comx/comx1/io	
+echo 10 >/proc/comx/comx1/irq		
+echo 0xd000 >/proc/comx/comx1/memaddr
+echo 1 >/proc/comx/comx1/channel	<- channels are numbered 
+					   as 0 (default) and 1
+
+Now, check if the driver recognized that you're going to use the other
+channel of the same adapter:
+
+cat /proc/comx/comx0/twin
+comx1
+cat /proc/comx/comx1/twin
+comx0
+
+You don't have to load the firmware twice, if you use both channels of
+an adapter, just write it into the channel 0's /proc firmware file.
+
+Default values: io 0x360 for COMX, 0x320 (HICOMX), irq 10, memaddr 0xd0000
+
+THE LOCOMX HARDWARE DRIVER
+
+The LoCOMX driver doesn't require firmware, and it doesn't use memory either,
+but it uses DMA channels 1 and 3. You can set the clock rate (if enabled by
+jumpers on the board) by writing the kbps value into the file named 'clock'.
+Set it to 'external' (it is the default) if you have external clock source.
+
+(Note: currently the LoCOMX driver does not support the internal clock)
+
+THE COMX, CMX AND HICOMX DRIVERS
+
+On the HICOMX, COMX and CMX, you have to load the firmware (it is different for
+the three cards!). All these adapters can share the same memory
+address (we usually use 0xd0000). On the CMX you can set the internal
+clock rate (if enabled by jumpers on the small adapter boards) by writing
+the kbps value into the 'clock' file. You have to do this before initializing
+the card. If you use both HICOMX and CMX/COMX cards, initialize the HICOMX
+first. The I/O address of the HICOMX board is not configurable by any
+method available to the user: it is hardwired to 0x320, and if you have to 
+change it, consult ITC-Pro Ltd.
+
+THE MIXCOM DRIVER
+
+The MixCOM board doesn't require firmware, the driver communicates with
+it through I/O ports. You can have three of these cards in one machine.
+
+THE HDLC LINE PROTOCOL DRIVER
+
+There's only one configurable parameter with this protocol: the 'keepalive'
+value. You can set this in seconds or set to 'off'. Agree with the administrator
+of your peer router on this setting. The default is 10 (seconds).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo hdlc >/proc/comx/comx0/protocol
+echo 10 >/proc/comx/comx0/keepalive	<- not necessary, 10 is the default
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE PPP LINE PROTOCOL DRIVER
+
+To use this driver, you have to have ppp-2.3.4, and have a modified version of
+pppd (this pppd will work as async pppd to, the modifiactions merely relax 
+some restricions in order to be able to use non-async lines too.
+If configured, this driver can use Van Jacobson TCP header compression (you'll
+need the slhc.o module for this).
+Additionaly to use this protocol, enable async ppp in your kernel config, and
+create the comx device special files in /dev. They're character special files
+with major 88, and their names must be the same as their network interface
+counterparts (i.e /dev/comx0 with minor 0 corresponds interface comx0 and so
+on).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo ppp >/proc/comx/comx0/protocol
+ifconfig comx0 up
+pppd comx0 1.2.3.4:5.6.7.8 persist	<- with this option pppd won't exit
+					   when the line goes down
+
+THE LAPB LINE PROTOCOL DRIVER
+
+For this, you'll need to configure LAPB support (See 'LAPB Data Link Driver' in
+'Network options' section) into your kernel (thanks to Jonathan Naylor for his 
+excellent implementation). 
+comxlapb.o provides the following files in the appropriate directory
+(the default values in parens): t1 (5), t2 (1), n2 (20), mode (DTE, STD) and
+window (7). Agree with the administrator of your peer router on these
+settings (most people use defaults, but you have to know if you are DTE or
+DCE).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo lapb >/proc/comx/comx0/protocol
+echo dce >/proc/comx/comx0/mode		<- DCE interface in this example
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE FRAME RELAY PROTOCOL DRIVER
+
+You DON'T need any other frame relay related modules from the kernel to use
+COMX-Frame Relay. This protocol is a bit more complicated than the others, 
+because it allows to use 'subinterfaces' or DLCIs within one physical device.
+First you have to create the 'master' device (the actual physical interface)
+as you would do for other protocols. Specify 'frad' as protocol type.
+Now you can bring this interface up by saying 'ifconfig comx0 up' (or whatever
+you've named the interface). Do not assign any IP address to this interface
+and do not set any routes through it.
+Then, set up your DLCIs the following way: create a comx interface for each
+DLCI you intend to use (with mkdir), and write 'dlci' to the 'boardtype' file, 
+and 'ietf-ip' to the 'protocol' file. Currently, the only supported 
+encapsulation type is this (also called as RFC1294/1490 IP encapsulation).
+Write the DLCI number to the 'dlci' file, and write the name of the physical
+COMX device to the file called 'master'. 
+Now you can assign an IP address to this interface and set routes using it.
+See the example file for further info and example config script.
+Notes: this driver implements a DTE interface with partially implemented
+Q933a LMI.
+You can find an extensively commented example in the 'etc' directory.
+
+FURTHER /proc FILES
+
+boardtype:
+Type of the hardware. Valid values are:
+ 'comx', 'hicomx', 'locomx', 'cmx'.
+
+protocol:
+Data-link protocol on this channel. Can be: HDLC, LAPB, PPP, FRAD
+
+status:
+You can read the channel's actual status from the 'status' file, for example
+'cat /proc/comx/comx3/status'.
+
+lineup_delay:
+Interpreted in seconds (default is 1). Used to avoid line jitter: the system
+will consider the line status 'UP' only if it is up for at least this number
+of seconds.
+
+debug: 
+You can set various debug options through this file. Valid options are:
+'comx_events', 'comx_tx', 'comx_rx', 'hw_events', 'hw_tx', 'hw_rx'.
+You can enable a debug options by writing its name prepended by a '+' into
+the debug file, for example 'echo +comx_rx >comx0/debug'. 
+Disabling an option happens similarly, use the '-' prefix 
+(e.g. 'echo -hw_rx >debug').
+Debug results can be read from the debug file, for example: 
+tail -f /proc/comx/comx2/debug
+
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/networking/sis900.txt linux.14p6/Documentation/networking/sis900.txt
--- linux.vanilla/Documentation/networking/sis900.txt	Thu Jan  1 01:00:00 1970
+++ linux.14p6/Documentation/networking/sis900.txt	Tue Nov 16 15:23:52 1999
@@ -0,0 +1,460 @@
+  SiS 900/7016 Fast Ethernet Device Driver
+  by Ollie Lho (ollie@sis.com.tw)
+  November 4, 1999. Document Revision: 0.1
+
+  This document gives some information on installation and usage of SiS
+  900/7016 device driver under Linux.
+  ______________________________________________________________________
+
+  Table of Contents
+
+
+  1. Introduction
+
+  2. License
+
+  3. Changes
+
+  4. Tested Environment
+
+  5. Files in This Rackage
+
+  6. Installation
+
+     6.1 Kernel version later than 2.2.11 and 2.3.15
+        6.1.1 Building the driver as loadable module
+        6.1.2 Building the driver into kernel
+     6.2 Earlier Kernel Version in 2.2.x and 2.3.x Series
+
+  7. Known Problems and Bugs
+
+  8. Revision History
+
+  9. Acknowledgements
+
+
+
+  ______________________________________________________________________
+
+  1.  Introduction
+
+  This document describes the revision 1.06 of SiS 900/7016 Fast
+  Ethernet device driver under Linux. The driver is developed by Silicon
+  Integrated System Corp. and distributed freely under the GNU General
+  Public License (GPL). The driver can be compiled as a loadable module
+  and used under Linux kernel version 2.2.x. With minimal changes, the
+  driver can also be used under 2.3.x kernel, please see section
+  ``Installation''. If you are intended to use the driver for earlier
+  kernels, you are on your own.
+
+  The driver is tested with usual TCP/IP applications including FTP,
+  Telnet, Netscape etc. and is used constantly by the developers.
+
+  Please send all comments/fixes/questions to Ollie Lho.
+
+
+  2.  License
+
+
+
+
+
+
+
+
+
+
+          Copyright (C) 1999 Silicon Integrated System Corp.
+
+          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
+
+
+
+
+
+  3.  Changes
+
+  Changes made in Revision 1.06
+
+  1. Separation of sis900.c and sis900.h in order to move most constant
+     definition to sis900.h (many of those constants were corrected)
+
+  2. Clean up PCI detection, the pci-scan from Donald Becker were not
+     used, just simple pci_find_*.
+
+  3. MII detection is modified to support multiple mii transceiver.
+
+  4. Bugs in read_eeprom, mdio_* were removed.
+
+  5. Lot of sis900 irrelevant comments were removed/changed and more
+     comments were added to reflect the real situation.
+
+  6. Clean up of physical/virtual address space mess in buffer
+     descriptors.
+
+  7. Better transmit/receive error handling.
+
+  8. The driver now uses zero-copy single buffer management scheme to
+     improve performance.
+
+  9. Names of variables were changed to be more consistent.
+
+ 10. Clean up of auo-negotiation and timer code.
+
+ 11. Automatic detection and change of PHY on the fly.
+
+
+  4.  Tested Environment
+
+  This driver is developed on the following hardware
+
+  o  Intel Celeron 336 with SiS 620 (rev 02) chipset
+
+  o  SiS 900 (rev 01) and SiS 7016/7014 Fast Ethernet Card
+
+     and tested with these software environments
+
+  o  Red Hat Linux version 6.0
+
+  o  Linux kernel version 2.2.13
+
+  o  Netscape version 4.6
+
+  o  NcFTP 3.0.0 beta 18
+
+  o  Samba version 2.0.3
+
+
+  5.  Files in This Rackage
+
+  In the package you can find these files:
+
+
+     sis900-2.2.x.c
+        Driver source for kernel 2.2.x
+
+     sis900-2.3.x.c
+        Driver source for kernel 2.3.x
+
+     sis900.h
+        Header file for both 2.2.x and 2.3.x kernel
+
+     sis900.sgml
+        Linux-Doc SGML source of the document
+
+
+  6.  Installation
+
+  Before trying to install the driver, be sure to get the latest
+  revision from SiS' Home Page.  If you have no prior experience in
+  networking under Linux, please read Ethernet HOWTO and Networking
+  HOWTO available from Linux Documentation Project (LDP).
+
+  The installation procedure are different according to your kernel
+  versions.
+
+
+  6.1.  Kernel version later than 2.2.11 and 2.3.15
+
+  The driver is bundled in release later than 2.2.11 and 2.3.15 so this
+  is the most easy case.  Be sure you have the appropriate packages for
+  compiling kernel source.  Those packages are listed in
+  Document/Changes in kernel source distribution. There are two
+  alternative ways to install the driver
+
+
+  6.1.1.  Building the driver as loadable module
+
+  To build the driver as a loadable kernel module you have to
+  reconfigure the kernel to activate network support by
+
+
+
+       make config
+
+
+
+
+  Choose "Network Device Support" to "Y" and "Ethernet Support" to "Y".
+  Then you have to choose "SiS 900 Fast Ethernet Adapter Support" to
+  "M".
+
+  After reconfiguring the kernel, you can make the driver module by
+
+
+       make modules
+
+
+
+
+  The driver should be compiled with no errors. After compiling the
+  driver, the driver can be installed to proper place by
+
+
+
+       make modules_install
+
+
+
+
+  Load the driver into kernel by
+
+
+
+       insmod sis900
+
+
+
+
+  When loading the driver into memory, some information message can be
+  view by
+
+
+
+       dmesg
+
+
+
+
+  or
+
+
+       cat /var/log/message
+
+
+
+
+  If the driver is loaded properly you will have messages similar to
+  this:
+
+
+
+       sis900.c: v1.06  11/04/99
+       eth0: SiS 900 PCI Fast Ethernet at 0xd000, IRQ 10, 00:00:e8:83:7f:a4.
+       eth0: SiS 900 Internal MII PHY transceiver found at address 1.
+
+
+
+
+  showing the version of the driver and the results of probing routine.
+
+  Once the driver is loaded, network can be brought up by
+
+
+
+       /sbin/ifconfig eth0 IPADDR broadcast BROADCAST netmask NETMASK
+
+
+
+
+
+  where IPADDR, BROADCAST, NETMASK are your IP address, broadcast
+  address and netmask respectively. For more information on how to
+  configure network interface, please refer to Networking HOWTO.
+
+  The link status is also shown by kernel messages. For example, after
+  the network interface is activated, you may have the message:
+
+
+
+       eth0: Using SiS 900 Internal MII PHY as default
+       eth0: Media Link On 100mbps full-duplex
+
+
+
+
+  If you try to unplug the twist pair (TP) cable you will get
+
+
+
+       eth0: Media Link Off
+
+
+
+
+  indicating that the link is failed.
+
+
+  6.1.2.  Building the driver into kernel
+
+  If you want to make the driver into kernel, choose "Y" rather than "M"
+  on "SiS 900 Fast Ethernet Adapter Support" when configuring the
+  kernel.  Build the kernel image in the usual way
+
+
+
+       make dep
+
+       make clean
+
+       make bzlilo
+
+
+
+
+  Next time the system reboot, you have the driver in memory.
+
+
+  6.2.  Earlier Kernel Version in 2.2.x and 2.3.x Series
+
+  Installing the driver for earlier kernels in 2.2.x and 2.3.x series
+  requires a little bit more work. First you have to copy sis900-2.x.x.c
+  to /usr/src/linux/drivers/net/ and you have to modify some files
+  manually (sorry !! no patch available !!)
+
+  in Space.c, add
+
+
+       extern int sis900_probe(struct device *dev);
+
+       ...
+
+       #ifdef CONFIG_SIS900
+               {sis900_probe,0},
+       #endif
+
+
+  in Config.in add
+
+
+       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+               ... //other adapter drivers
+               tristate 'SiS 900 PCI Fast Ethernet Adapter Support' CONFIG_SIS900
+               ... //other adapter drivers
+       fi
+
+
+
+
+  in Makefile add
+
+
+       ifeq ($(CONFIG_SIS900),y)
+               L_OBJS += sis900.o
+       else
+               ifeq ($(CONFIG_SIS900),m)
+                       M_OBJS += sis900.o
+               endif
+       endif
+
+
+
+
+  After modifying these files, the driver can be build as described in
+  the previous section.
+
+
+  7.  Known Problems and Bugs
+
+  There are some known problems and bugs. If you find any other bugs
+  please mail to ollie@sis.com.tw
+
+  1. AM79C901 HomePNA PHY is not thoroughly tested, there may be some
+     bugs in the "on the fly" change of transceiver.
+
+  2. A bug is hidden somewhere in the receive buffer management code,
+     the bug causes NULL pointer reference in the kernel. This fault is
+     caught before bad things happen and reported with the message:
+
+
+       eth0: NULL pointer encountered in Rx ring, skipping
+
+
+
+
+  which can be viewed with dmesg or cat /var/log/message.
+
+
+  8.  Revision History
+
+
+  o  November 4, 1999, Revision 1.06, Second release, lots of clean up
+     and optimization.
+
+  o  August 8, 1999, Revision 1.05, Initial Public Release
+
+
+  9.  Acknowledgements
+
+  This driver was originally derived form Donald Becker's pci-skeleton
+  and rtl8139 drivers. Donald also provided various suggestion regarded
+  with improvements made in revision 1.06.
+
+  The 1.05 revision was created by Jim Huang, AMD 79c901 support was
+  added by Chin-Shan Li.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/oops-tracing.txt linux.14p6/Documentation/oops-tracing.txt
--- linux.vanilla/Documentation/oops-tracing.txt	Tue Jan  5 19:14:24 1999
+++ linux.14p6/Documentation/oops-tracing.txt	Tue Nov 16 15:26:34 1999
@@ -15,6 +15,37 @@
 linux-kernel@vger.rutgers.edu. Thanks for your help in making Linux as
 stable as humanly possible.
 
+Where is the_oops.txt?
+----------------------
+
+Normally the Oops text is read from the kernel buffers by klogd and
+handed to syslogd which writes it to a syslog file, typically
+/var/log/messages (depends on /etc/syslog.conf).  Sometimes klogd dies,
+in which case you can run dmesg > file to read the data from the kernel
+buffers and save it.  Or you can cat /proc/kmsg > file, however you
+have to break in to stop the transfer, kmsg is a "never ending file".
+If the machine has crashed so badly that you cannot enter commands or
+the disk is not available then you have three options :-
+
+(1) Hand copy the text from the screen and type it in after the machine
+    has restarted.  Messy but it is the only option if you have not
+    planned for a crash.
+
+(2) Boot with a serial console (see Documentation/serial-console.txt),
+    run a null modem to a second machine and capture the output there
+    using your favourite communication program.  Minicom works well.
+
+(3) Patch the kernel with one of the crash dump patches.  These save
+    data to a floppy disk or video rom or a swap partition.  None of
+    these are standard kernel patches so you have to find and apply
+    them yourself.  Search kernel archives for kmsgdump, lkcd and
+    oops+smram.
+
+No matter how you capture the log output, feed the resulting file to
+ksymoops along with /proc/ksyms and /proc/modules that applied at the
+time of the crash.  /var/log/ksymoops can be useful to capture the
+latter, man ksymoops for details.
+
 
 Full Information
 ----------------
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/proc.txt linux.14p6/Documentation/proc.txt
--- linux.vanilla/Documentation/proc.txt	Sat Feb  6 20:46:20 1999
+++ linux.14p6/Documentation/proc.txt	Fri Oct 29 23:36:11 1999
@@ -1041,6 +1041,21 @@
 
 IP fragmentation settings
 
+ip_always_defrag
+   Replaces the former Kernel-Configuration option:
+	CONFIG_IP_ALWAYS_DEFRAG
+   All incoming fragments (parts of IP packets
+   that arose when some host between origin and destination decided
+   that the packets were too large and cut them into pieces) will be
+   reassembled (defragmented) before being processed, even if they are
+   about to be forwarded.
+
+   Only say Y here if running either a firewall that is the sole link
+   to your network or a transparent proxy; never ever say Y here for a
+   normal router or host.
+
+   This is automagically enabled when enabling masquerading.
+
 ipfrag_high_trash and ipfrag_low_trash
    Maximum memory used to reassemble IP fragments. When
    ipfrag_high_thresh bytes of memory is allocated for this purpose,
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/sound/Introduction linux.14p6/Documentation/sound/Introduction
--- linux.vanilla/Documentation/sound/Introduction	Sat Aug 14 02:27:12 1999
+++ linux.14p6/Documentation/sound/Introduction	Sun Oct 31 23:00:45 1999
@@ -25,7 +25,8 @@
                    added info on multiple sound cards of similar types,]
                    added more diagnostics info, added info about esd.
                    added info on OSS and ALSA.
-
+1.1.1  19991031	   Added notes on sound-slot- and sound-service.
+			(Alan Cox)
 
 Modular Sound Drivers:
 ======================
@@ -321,6 +322,12 @@
 
 7)  Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB).
 
+8)  If the system reports insuffcient DMA memory then you may want to
+    load sound with the "dmabufs=1" option. Or in /etc/conf.modules add
+	
+	preinstall sound dmabufs=1
+
+    This makes the sound system allocate its buffers and hang onto them.
 
 Configuring Sound:
 ==================
@@ -335,7 +342,7 @@
 
 3)  In /etc/conf.modules when using modprobe.
 
-4)  Via Red Hat's /usr/sbin/sndconfig program (text based).
+4)  Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based).
 
 5)  Via the OSS soundconf program (with the commercial version
     of the OSS driver.
@@ -344,6 +351,28 @@
 
 Anyone want to write a linuxconf module for configuring sound?
 
+Module Loading:
+===============
+
+When a sound card is first referenced and sound is modular the sound system
+will ask for the sound devices to be loaded. Initially it requests that
+the driver for the sound system is loaded. It then wwill ask for 
+sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and
+so on). Thus you can do
+
+alias sound-slot-0 sb
+
+To load a soundblaster at this point. If the slot loading does not provide
+the desired device - for example a soundblaster does not directly provide
+a midi synth in all cases then it will request "sound-service-0-n" where n
+is
+
+0	Mixer
+
+2  	MIDI
+
+3, 4	DSP audio
+
 
 For More Information (RTFM):
 ============================
@@ -373,6 +402,3 @@
 Contact Information:
 ====================
 Wade Hampton:  (whampton@staffnet.com)
-
-
-
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/sound/PSS linux.14p6/Documentation/sound/PSS
--- linux.vanilla/Documentation/sound/PSS	Thu Jan  1 01:00:00 1970
+++ linux.14p6/Documentation/sound/PSS	Sun Oct 31 23:02:27 1999
@@ -0,0 +1,41 @@
+The PSS cards and other ECHO based cards provide an onboard DSP with 
+downloadable programs and also has an AD1848 "Microsoft Sound System" 
+device. The PSS driver enables MSS and MPU401 modes of the card. SB 
+is not enabled since it doesn't work concurrently with MSS. 
+
+If you build this driver as a module then the driver takes the folowing
+parameters
+
+pss_io. 	The I/O base the PSS card is configured at (normally 0x220
+		or 0x240)
+
+mss_io		The base address of the Microsoft Sound System interface.
+		This is normally 0x530, but may be 0x604 or other addresses.
+
+mss_irq		The interrupt assigned to the Microsoft Sound System
+		emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you
+		get IRQ errors be sure to check the interrupt is set to 
+		"ISA/Legacy" in the BIOS on modern machines.
+
+mss_dma		The DMA channel used by the Microsoft Sound System.
+		This can be 0, 1, or 3. DMA 0 is not available on older 
+		machines and will cause a crash on them.
+
+mpu_io		The MPU emulation base address. This sets the base of the
+		synthesizer. It is typically 0x330 but can be altered.
+
+mpu_irq		The interrupt to use for the synthesizer. It must differ
+		from the IRQ used by the Microsoft Sound System port.
+
+
+The mpu_io/mpu_irq fields are optional. If they are not specified the 
+synthesizer parts are not configured.
+
+When the module is loaded it looks for a file called 
+/etc/sound/pss_synth. This is the firmware file from the DOS install disks.
+This fil holds a general MIDI emulation. The file expected is called
+genmidi.ld on newer DOS driver install disks and synth.ld on older ones.
+
+You can also load alternative DSP algorithms into the card if you wish. One
+alternative driver can be found at http://www.mpg123.de/
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/MAINTAINERS linux.14p6/MAINTAINERS
--- linux.vanilla/MAINTAINERS	Wed Oct 20 01:12:33 1999
+++ linux.14p6/MAINTAINERS	Wed Nov  3 00:25:58 1999
@@ -121,9 +121,10 @@
 
 APM DRIVER
 P:	Stephen Rothwell
-M:	Stephen.Rothwell@canb.auug.org.au
+M:	sfr@linuxcare.com
 L:	linux-laptop@vger.rutgers.edu
-S:	Maintained
+W:	http://www.linuxcare.com.au/apm/
+S:	Supported
 
 APPLETALK NETWORK LAYER
 P:	Jay Schulist
@@ -203,6 +204,11 @@
 W:	http://www.fi.muni.cz/~kas/cosa/
 S:	Maintained
 
+COMX/MULTIGATE SYNC SERIAL DRIVERS
+P:	Gergely Madarasz
+M:	Gergely Madarasz <gorgo@itc.hu>
+S:	Supported
+
 CREDITS FILE
 P:	John A. Martin
 M:	jam@acm.org
@@ -233,7 +239,7 @@
 M:	SteveW@ACM.org
 W:	http://www.sucs.swan.ac.uk/~rohan/
 W:	http://www-sigproc.eng.cam.ac.uk/~sjw44/
-L:	netdev@roxanne.nuclecu.unam.mx
+L:	netdev@oss.sgi.com
 S:	Maintained
 
 DEVICE NUMBER REGISTRY
@@ -573,7 +579,7 @@
 
 NETWORKING [GENERAL]
 P:	Networking Teak
-M:	netdev@nuclecu.unam.mx
+M:	netdev@oss.sgi.com
 L:	linux-net@vger.rutgers.edu
 W:	http://www.uk.linux.org/NetNews.html (2.0 only)
 S:	Maintained
@@ -585,7 +591,7 @@
 M:	ak@muc.de
 P:	Alexey Kuznetsov
 M:	kuznet@ms2.inr.ac.ru
-L:	netdev@roxanne.nuclecu.unam.mx
+L:	netdev@oss.sgi.com
 S:	Maintained
 
 NFS CLIENT
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Makefile linux.14p6/Makefile
--- linux.vanilla/Makefile	Wed Oct 20 01:12:33 1999
+++ linux.14p6/Makefile	Tue Nov 16 15:08:35 1999
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
-SUBLEVEL = 13
-EXTRAVERSION =
+SUBLEVEL = 14
+EXTRAVERSION = pre6
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -339,7 +339,7 @@
 	if [ -f FC4_MODULES   ]; then inst_mod FC4_MODULES   fc4;   fi; \
 	if [ -f IRDA_MODULES  ]; then inst_mod IRDA_MODULES  net;   fi; \
 	\
-	for f in *.o; do [ -r $$f ] && echo $$f; done > $$MODLIB/.allmods; \
+	for f in *.o; do [ -r $$f ] && echo $$f; done | sort > $$MODLIB/.allmods; \
 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \
 	if [ -s $$MODLIB/.misc ]; then inst_mod $$MODLIB/.misc misc; fi; \
 	rm -f $$MODLIB/.misc $$MODLIB/.allmods; \
@@ -381,6 +381,10 @@
 	rm -f drivers/net/hamradio/soundmodem/gentbl
 	rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h
 	rm -f drivers/sound/*_boot.h drivers/sound/.*.boot
+	rm -f drivers/sound/msndinit.c
+	rm -f drivers/sound/msndperm.c
+	rm -f drivers/sound/pndsperm.c
+	rm -f drivers/sound/pndspini.c
 	rm -f .version .config* config.in config.old
 	rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
 	rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/process.c linux.14p6/arch/alpha/kernel/process.c
--- linux.vanilla/arch/alpha/kernel/process.c	Sat Aug 28 20:00:53 1999
+++ linux.14p6/arch/alpha/kernel/process.c	Fri Oct 22 21:20:22 1999
@@ -117,58 +117,44 @@
 	}
 }
 
-void
-generic_kill_arch (int mode, char *restart_cmd)
-{
-	/* The following currently only has any effect on SRM.  We should
-	   fix MILO to understand it.  Should be pretty easy.  Also we can
-	   support RESTART2 via the ipc_buffer machinations pictured below,
-	   which SRM ignores.  */
-
-	if (alpha_using_srm) {
-		struct percpu_struct *cpup;
-		unsigned long flags;
-	
-		cpup = (struct percpu_struct *)
-		  ((unsigned long)hwrpb + hwrpb->processor_offset);
-
-		flags = cpup->flags;
-
-		/* Clear reason to "default"; clear "bootstrap in progress". */
-		flags &= ~0x00ff0001UL;
-
-		if (mode == LINUX_REBOOT_CMD_RESTART) {
-			if (!restart_cmd) {
-				flags |= 0x00020000UL; /* "cold bootstrap" */
-				cpup->ipc_buffer[0] = 0;
-			} else {
-				flags |=  0x00030000UL; /* "warm bootstrap" */
-				strncpy((char *)cpup->ipc_buffer, restart_cmd,
-					sizeof(cpup->ipc_buffer));
-			}
-		} else {
-			flags |=  0x00040000UL; /* "remain halted" */
-		}
-			
-		cpup->flags = flags;					       
-		mb();						
-
-		reset_for_srm();
-		set_hae(srm_hae);
+struct halt_info {
+	int	mode;
+	char *	restart_cmd;
+};
 
-#ifdef CONFIG_DUMMY_CONSOLE
-		/* This has the effect of reseting the VGA video origin.  */
-		take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1);
-#endif
+static void
+halt_processor(void * generic_ptr)
+{
+	struct percpu_struct * cpup;
+	struct halt_info * how = (struct halt_info *)generic_ptr;
+	unsigned long *flags;
+	int cpuid = smp_processor_id();
+
+	/* No point in taking interrupts anymore. */
+	__cli();
+
+	cpup = (struct percpu_struct *)
+			((unsigned long)hwrpb + hwrpb->processor_offset
+			 + hwrpb->processor_size * cpuid);
+	flags = &cpup->flags;
+
+	/* Clear reason to "default"; clear "bootstrap in progress". */
+	*flags &= ~0x00ff0001UL;
+
+#ifdef __SMP__
+	/* Secondaries halt here. */
+	if (cpuid != smp_boot_cpuid) {
+		*flags |= 0x00040000UL; /* "remain halted" */
+		clear_bit(cpuid, &cpu_present_mask);
+		halt();
 	}
+#endif /* __SMP__ */
 
 #ifdef CONFIG_RTC
 	/* Reset rtc to defaults.  */
 	{
 		unsigned char control;
 
-		cli();
-
 		/* Reset periodic interrupt frequency.  */
 		CMOS_WRITE(0x26, RTC_FREQ_SELECT);
 
@@ -177,22 +163,73 @@
 		control |= RTC_PIE;
 		CMOS_WRITE(control, RTC_CONTROL);	
 		CMOS_READ(RTC_INTR_FLAGS);
-
-		sti();
 	}
-#endif
+#endif /* CONFIG_RTC */
 
-	if (!alpha_using_srm && mode != LINUX_REBOOT_CMD_RESTART) {
+	if (how->mode == LINUX_REBOOT_CMD_RESTART) {
+		if (!how->restart_cmd) {
+			*flags |= 0x00020000UL; /* "cold bootstrap" */
+			cpup->ipc_buffer[0] = 0;
+		} else {
+		  /* NOTE: this could really only work when returning
+		     into MILO, rather than SRM console. The latter
+		     does NOT look at the ipc_buffer to get a new
+		     boot command. It could be done by using callbacks
+		     to change some of the SRM environment variables,
+		     but that is beyond our capabilities at this time.
+		     At the moment, SRM will use the last boot device,
+		     but the file and flags will be the defaults, when
+		     doing a "warm" bootstrap.
+		  */
+			*flags |=  0x00030000UL; /* "warm bootstrap" */
+			strncpy((char *)cpup->ipc_buffer,
+				how->restart_cmd,
+				sizeof(cpup->ipc_buffer));
+		}
+	} else
+		*flags |=  0x00040000UL; /* "remain halted" */
+
+#ifdef __SMP__
+	/* Wait for the secondaries to halt. */
+	clear_bit(smp_boot_cpuid, &cpu_present_mask);
+	while (cpu_present_mask)
+		/* Make sure we sample memory and not a register. */
+		barrier();
+#endif /* __SMP__ */
+
+        /* If booted from SRM, reset some of the original environment. */
+	if (alpha_using_srm) {
+#ifdef CONFIG_DUMMY_CONSOLE
+		/* This has the effect of resetting the VGA video origin.  */
+		take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1);
+#endif
+		reset_for_srm();
+		set_hae(srm_hae);
+	}
+	else if (how->mode != LINUX_REBOOT_CMD_RESTART &&
+		 how->mode != LINUX_REBOOT_CMD_RESTART2) {
 		/* Unfortunately, since MILO doesn't currently understand
 		   the hwrpb bits above, we can't reliably halt the 
 		   processor and keep it halted.  So just loop.  */
 		return;
 	}
 
-	if (alpha_using_srm)
-		srm_paging_stop();
-
+	/* PRIMARY */
 	halt();
+}
+
+void
+generic_kill_arch(int mode, char * restart_cmd)
+{
+	struct halt_info copy_of_args;
+
+	copy_of_args.mode = mode;
+	copy_of_args.restart_cmd = restart_cmd;
+#ifdef __SMP__
+	/* A secondary can't wait here for the primary to finish, can it now? */
+	smp_call_function(halt_processor, (void *)&copy_of_args, 1, 0);
+#endif /* __SMP__ */
+	halt_processor(&copy_of_args);
 }
 
 void
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/proto.h linux.14p6/arch/alpha/kernel/proto.h
--- linux.vanilla/arch/alpha/kernel/proto.h	Sat Aug 14 02:27:12 1999
+++ linux.14p6/arch/alpha/kernel/proto.h	Fri Oct 22 21:20:22 1999
@@ -153,6 +153,7 @@
 extern void handle_ipi(struct pt_regs *);
 extern void smp_percpu_timer_interrupt(struct pt_regs *);
 extern int smp_boot_cpuid;
+extern unsigned long cpu_present_mask;
 
 /* bios32.c */
 extern void reset_for_srm(void);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/ptrace.c linux.14p6/arch/alpha/kernel/ptrace.c
--- linux.vanilla/arch/alpha/kernel/ptrace.c	Wed Dec 30 00:17:00 1998
+++ linux.14p6/arch/alpha/kernel/ptrace.c	Tue Nov 16 15:18:13 1999
@@ -149,13 +149,18 @@
 	pmd_t * pgmiddle;
 	pte_t * pgtable;
 	unsigned long page;
+	int fault;
 
 	DBG(DBG_MEM_ALL, ("getting long at 0x%lx\n", addr));
  repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
 	if (pgd_none(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
@@ -164,8 +169,12 @@
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
 	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
@@ -174,8 +183,12 @@
 	}
 	pgtable = pte_offset(pgmiddle, addr);
 	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	page = pte_page(*pgtable);
 	/* this is a hack for non-kernel-mapped video buffers and similar */
@@ -202,12 +215,17 @@
 	pmd_t *pgmiddle;
 	pte_t *pgtable;
 	unsigned long page;
+	int fault;
 
  repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
 	if (!pgd_present(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
@@ -216,8 +234,12 @@
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
 	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
@@ -226,13 +248,21 @@
 	}
 	pgtable = pte_offset(pgmiddle, addr);
 	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	page = pte_page(*pgtable);
 	if (!pte_write(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 
 	/* This is a hack for non-kernel-mapped video buffers and similar.  */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/signal.c linux.14p6/arch/alpha/kernel/signal.c
--- linux.vanilla/arch/alpha/kernel/signal.c	Sat Aug 14 02:27:12 1999
+++ linux.14p6/arch/alpha/kernel/signal.c	Sat Oct 23 23:02:34 1999
@@ -437,6 +437,8 @@
 		err |= __copy_to_user(frame->extramask, &set->sig[1], 
 				      sizeof(frame->extramask));
 	}
+	if (err)
+		goto give_sigsegv;
 
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
@@ -499,6 +501,8 @@
 	err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, sw,
 				set->sig[0], oldsp);
 	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+	if (err)
+		goto give_sigsegv;
 
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/math-emu/fp-emul.c linux.14p6/arch/alpha/math-emu/fp-emul.c
--- linux.vanilla/arch/alpha/math-emu/fp-emul.c	Sat Aug 28 20:00:53 1999
+++ linux.14p6/arch/alpha/math-emu/fp-emul.c	Fri Oct 22 22:30:50 1999
@@ -85,15 +85,15 @@
 
 /* For 128-bit division.  */
 
-__complex__ unsigned long
+void
 udiv128(unsigned long divisor_f0, unsigned long divisor_f1,
-	unsigned long dividend_f0, unsigned long dividend_f1)
+	unsigned long dividend_f0, unsigned long dividend_f1,
+	unsigned long *quot, unsigned long *remd)
 {
 	_FP_FRAC_DECL_2(quo);
 	_FP_FRAC_DECL_2(rem);
 	_FP_FRAC_DECL_2(tmp);
 	unsigned long i, num_bits, bit;
-	__complex__ unsigned long ret;
 
 	_FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2);
 	_FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2);
@@ -139,9 +139,9 @@
 	}
 
 out:
-	__real__ ret = quo_f1;
-	__imag__ ret = rem_f1;
-	return ret;
+	*quot = quo_f1;
+	*remd = rem_f1;
+	return;
 }
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/math-emu/sfp-machine.h linux.14p6/arch/alpha/math-emu/sfp-machine.h
--- linux.vanilla/arch/alpha/math-emu/sfp-machine.h	Sat Aug 28 20:00:53 1999
+++ linux.14p6/arch/alpha/math-emu/sfp-machine.h	Fri Oct 22 22:30:50 1999
@@ -552,15 +552,17 @@
 	   : "r" ((UDItype)(u)),		\
 	     "r" ((UDItype)(v)))
 
-extern __complex__ unsigned long udiv128(unsigned long, unsigned long,
-					 unsigned long, unsigned long);
+extern void udiv128(unsigned long, unsigned long,
+		    unsigned long, unsigned long,
+		    unsigned long *,
+		    unsigned long *);
 
-#define udiv_qrnnd(q, r, n1, n0, d)	\
-  do {					\
-    __complex__ unsigned long x_;	\
-    x_ = udiv128((n0), (n1), 0, (d));	\
-    (q) = __real__ x_;			\
-    (r) = __imag__ x_;			\
+#define udiv_qrnnd(q, r, n1, n0, d)		\
+  do {						\
+    unsigned long xr, xi;			\
+    udiv128((n0), (n1), 0, (d), &xr, &xi);	\
+    (q) = xr; 					\
+    (r) = xi;					\
   } while (0)
 
 #define UDIV_NEEDS_NORMALIZATION 1  
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/mm/fault.c linux.14p6/arch/alpha/mm/fault.c
--- linux.vanilla/arch/alpha/mm/fault.c	Sat Aug 14 02:27:13 1999
+++ linux.14p6/arch/alpha/mm/fault.c	Sat Oct 23 23:02:34 1999
@@ -120,9 +120,18 @@
 		if (!(vma->vm_flags & VM_WRITE))
 			goto bad_area;
 	}
-	handle_mm_fault(current, vma, address, cause > 0);
+survive:
+	{
+		int fault = handle_mm_fault(current, vma, address, cause > 0);
+		if (!fault)
+			goto do_sigbus;
+		if (fault < 0)
+			goto out_of_memory;
+	}
 	up(&mm->mmap_sem);
-	goto out;
+ out_unlock:
+	unlock_kernel();
+	return;
 
 /*
  * Something tried to access memory that isn't in our memory map..
@@ -133,9 +142,10 @@
 
 	if (user_mode(regs)) {
 		force_sig(SIGSEGV, current);
-		goto out;
+		goto out_unlock;
 	}
 
+no_context:
 	/* Are we prepared to handle this fault as an exception?  */
 	if ((fixup = search_exception_table(regs->pc)) != 0) {
 		unsigned long newpc;
@@ -143,7 +153,7 @@
 		printk("%s: Exception at [<%lx>] (%lx)\n",
 		       current->comm, regs->pc, newpc);
 		regs->pc = newpc;
-		goto out;
+		goto out_unlock;
 	}
 
 /*
@@ -154,7 +164,37 @@
 	       "virtual address %016lx\n", address);
 	die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16);
 	do_exit(SIGKILL);
- out:
-	unlock_kernel();
-}
 
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+	if (current->pid == 1)
+	{
+		current->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	up(&mm->mmap_sem);
+	if (user_mode(regs))
+	{
+		printk("VM: killing process %s\n", current->comm);
+		do_exit(SIGKILL);
+	}
+	goto no_context;
+
+do_sigbus:
+	up(&mm->mmap_sem);
+
+	/*
+	 * Send a sigbus, regardless of whether we were in kernel
+	 * or user mode.
+	 */
+	force_sig(SIGBUS, current);
+
+	/* Kernel mode? Handle exceptions or die */
+	if (!user_mode(regs))
+		goto no_context;
+	goto out_unlock;
+}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/boot/compressed/Makefile linux.14p6/arch/i386/boot/compressed/Makefile
--- linux.vanilla/arch/i386/boot/compressed/Makefile	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/i386/boot/compressed/Makefile	Fri Oct 22 22:27:46 1999
@@ -37,7 +37,7 @@
 	tmppiggy=_tmp_$$$$piggy; \
 	rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \
 	$(OBJCOPY) $(SYSTEM) $$tmppiggy; \
-	gzip -f -3 < $$tmppiggy > $$tmppiggy.gz; \
+	gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \
 	echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \
 	$(LD) -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \
 	rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/boot/compressed/misc.c linux.14p6/arch/i386/boot/compressed/misc.c
--- linux.vanilla/arch/i386/boot/compressed/misc.c	Wed Jun 24 22:30:08 1998
+++ linux.14p6/arch/i386/boot/compressed/misc.c	Fri Oct 22 22:27:14 1999
@@ -104,7 +104,7 @@
 #define LOW_BUFFER_START      0x2000
 #define LOW_BUFFER_END       0x90000
 #define LOW_BUFFER_SIZE      ( LOW_BUFFER_END - LOW_BUFFER_START )
-#define HEAP_SIZE             0x2000
+#define HEAP_SIZE             0x2400
 static int high_loaded =0;
 static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/;
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/apm.c linux.14p6/arch/i386/kernel/apm.c
--- linux.vanilla/arch/i386/kernel/apm.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/i386/kernel/apm.c	Fri Oct 29 23:39:48 1999
@@ -1,8 +1,8 @@
 /* -*- linux-c -*-
  * APM BIOS driver for Linux
- * Copyright 1994-1998 Stephen Rothwell
- *                     (Stephen.Rothwell@canb.auug.org.au)
- * Development of this driver was funded by NEC Australia P/L
+ * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com)
+ *
+ * Initial development of this driver was funded by NEC Australia P/L
  *	and NEC Corporation
  *
  * This program is free software; you can redistribute it and/or modify it
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/bios32.c linux.14p6/arch/i386/kernel/bios32.c
--- linux.vanilla/arch/i386/kernel/bios32.c	Sat Aug 14 02:25:55 1999
+++ linux.14p6/arch/i386/kernel/bios32.c	Wed Oct 27 18:53:06 1999
@@ -1095,7 +1095,7 @@
  * for buggy PCI BIOS'es :-[).
  */
 
-extern int skip_ioapic_setup;
+extern int skip_ioapic_setup;	/* defined in arch/i386/kernel/smp.c */
 
 static void __init pcibios_fixup_devices(void)
 {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/io_apic.c linux.14p6/arch/i386/kernel/io_apic.c
--- linux.vanilla/arch/i386/kernel/io_apic.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/i386/kernel/io_apic.c	Fri Oct 22 21:21:27 1999
@@ -19,7 +19,7 @@
  * volatile is justified in this case, IO-APIC register contents
  * might change spontaneously, GCC should not cache it
  */
-#define IO_APIC_BASE ((volatile int *)fix_to_virt(FIX_IO_APIC_BASE))
+#define IO_APIC_BASE(idx) ((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx))
 
 /*
  * The structure of the IO-APIC:
@@ -47,7 +47,7 @@
 /*
  * # of IRQ routing registers
  */
-int nr_ioapic_registers = 0;
+int nr_ioapic_registers[MAX_IO_APICS];
 
 enum ioapic_irq_destination_types {
 	dest_Fixed = 0,
@@ -94,6 +94,8 @@
 	mp_ExtINT = 3
 };
 
+int mp_apic_entries = 0;			/* # of I/O APIC entries */
+struct mpc_config_ioapic mp_apics[MAX_IO_APICS];/* I/O APIC entries */
 int mp_irq_entries = 0;				/* # of MP IRQ source entries */
 struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
 						/* MP IRQ source entries */
@@ -108,34 +110,34 @@
  * between pins and IRQs.
  */
 
-static inline unsigned int io_apic_read(unsigned int reg)
+static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg)
 {
-	*IO_APIC_BASE = reg;
-	return *(IO_APIC_BASE+4);
+	*IO_APIC_BASE(apic) = reg;
+	return *(IO_APIC_BASE(apic)+4);
 }
 
-static inline void io_apic_write(unsigned int reg, unsigned int value)
+static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value)
 {
-	*IO_APIC_BASE = reg;
-	*(IO_APIC_BASE+4) = value;
+	*IO_APIC_BASE(apic) = reg;
+	*(IO_APIC_BASE(apic)+4) = value;
 }
 
 /*
  * Re-write a value: to be used for read-modify-write
  * cycles where the read already set up the index register.
  */
-static inline void io_apic_modify(unsigned int value)
+static inline void io_apic_modify(unsigned int apic, unsigned int value)
 {
-	*(IO_APIC_BASE+4) = value;
+	*(IO_APIC_BASE(apic)+4) = value;
 }
 
 /*
  * Synchronize the IO-APIC and the CPU by doing
  * a dummy read from the IO-APIC
  */
-static inline void io_apic_sync(void)
+static inline void io_apic_sync(unsigned int apic)
 {
-	(void) *(IO_APIC_BASE+4);
+	(void) *(IO_APIC_BASE(apic)+4);
 }
 
 /*
@@ -146,7 +148,7 @@
 #define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS)
 
 static struct irq_pin_list {
-	int pin, next;
+	int apic, pin, next;
 } irq_2_pin[PIN_MAP_SIZE];
 
 /*
@@ -154,7 +156,7 @@
  * shared ISA-space IRQs, so we have to support them. We are super
  * fast in the common case, and fast for shared ISA-space IRQs.
  */
-static void add_pin_to_irq(unsigned int irq, int pin)
+static void add_pin_to_irq(unsigned int irq, int apic, int pin)
 {
 	static int first_free_entry = NR_IRQS;
 	struct irq_pin_list *entry = irq_2_pin + irq;
@@ -168,6 +170,7 @@
 		if (++first_free_entry >= PIN_MAP_SIZE)
 			panic("io_apic.c: whoops");
 	}
+	entry->apic = apic;
 	entry->pin = pin;
 }
 
@@ -183,9 +186,9 @@
 		pin = entry->pin;					\
 		if (pin == -1)						\
 			break;						\
-		reg = io_apic_read(0x10 + R + pin*2);			\
+		reg = io_apic_read(entry->apic, 0x10 + R + pin*2);	\
 		reg ACTION;						\
-		io_apic_modify(reg);					\
+		io_apic_modify(entry->apic, reg);			\
 		if (!entry->next)					\
 			break;						\
 		entry = irq_2_pin + entry->next;			\
@@ -197,12 +200,12 @@
  * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
  * zero. Trick by Ramesh Nalluri.
  */
-DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync())		/* destination = 0x00 */
+DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync(entry->apic))/* destination = 0x00 */
 DO_ACTION( enable,  1, |= 0xff000000, )				/* destination = 0xff */
-DO_ACTION( mask,    0, |= 0x00010000, io_apic_sync())		/* mask = 1 */
+DO_ACTION( mask,    0, |= 0x00010000, io_apic_sync(entry->apic))/* mask = 1 */
 DO_ACTION( unmask,  0, &= 0xfffeffff, )				/* mask = 0 */
 
-static void clear_IO_APIC_pin(unsigned int pin)
+static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
 {
 	struct IO_APIC_route_entry entry;
 
@@ -211,16 +214,17 @@
 	 */
 	memset(&entry, 0, sizeof(entry));
 	entry.mask = 1;
-	io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
-	io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1));
+	io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry) + 0));
+	io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry) + 1));
 }
 
 static void clear_IO_APIC (void)
 {
-	int pin;
+	int apic, pin;
 
-	for (pin = 0; pin < nr_ioapic_registers; pin++)
-		clear_IO_APIC_pin(pin);
+	for (apic = 0; apic < mp_apic_entries; apic++)
+		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
+			clear_IO_APIC_pin(apic, pin);
 }
 
 /*
@@ -270,12 +274,13 @@
 /*
  * Find the IRQ entry number of a certain pin.
  */
-static int __init find_irq_entry(int pin, int type)
+static int __init find_irq_entry(int apic, int pin, int type)
 {
 	int i;
 
 	for (i = 0; i < mp_irq_entries; i++)
 		if ( (mp_irqs[i].mpc_irqtype == type) &&
+			(mp_irqs[i].mpc_dstapic == mp_apics[apic].mpc_apicid) &&
 			(mp_irqs[i].mpc_dstirq == pin))
 
 			return i;
@@ -307,21 +312,26 @@
  * Find a specific PCI IRQ entry.
  * Not an initfunc, possibly needed by modules
  */
+static int __init pin_2_irq(int idx, int apic, int pin);
 int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin)
 {
-	int i;
+	int apic, i;
 
 	for (i = 0; i < mp_irq_entries; i++) {
 		int lbus = mp_irqs[i].mpc_srcbus;
 
-		if (IO_APIC_IRQ(mp_irqs[i].mpc_dstirq) &&
+		for (apic = 0; apic < mp_apic_entries; apic++)
+			if (mp_apics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic)
+				break;
+
+		if ((apic || IO_APIC_IRQ(mp_irqs[i].mpc_dstirq)) &&
 		    (mp_bus_id_to_type[lbus] == MP_BUS_PCI) &&
 		    !mp_irqs[i].mpc_irqtype &&
 		    (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) &&
 		    (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f)) &&
 		    (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3)))
 
-			return mp_irqs[i].mpc_dstirq;
+			return pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq);
 	}
 	return -1;
 }
@@ -491,9 +501,9 @@
 	return MPBIOS_trigger(idx);
 }
 
-static int __init pin_2_irq(int idx, int pin)
+static int __init pin_2_irq(int idx, int apic, int pin)
 {
-	int irq;
+	int irq, i;
 	int bus = mp_irqs[idx].mpc_srcbus;
 
 	/*
@@ -513,9 +523,12 @@
 		case MP_BUS_PCI: /* PCI pin */
 		{
 			/*
-			 * PCI IRQs are 'directly mapped'
+			 * PCI IRQs are mapped in order
 			 */
-			irq = pin;
+			i = irq = 0;
+			while (i < apic)
+				irq += nr_ioapic_registers[i++];
+			irq += pin;
 			break;
 		}
 		default:
@@ -545,12 +558,14 @@
 
 static inline int IO_APIC_irq_trigger(int irq)
 {
-	int idx, pin;
+	int apic, idx, pin;
 
-	for (pin = 0; pin < nr_ioapic_registers; pin++) {
-		idx = find_irq_entry(pin,mp_INT);
-		if ((idx != -1) && (irq == pin_2_irq(idx,pin)))
-			return irq_trigger(idx);
+	for (apic = 0; apic < mp_apic_entries; apic++) {
+		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
+			idx = find_irq_entry(apic,pin,mp_INT);
+			if ((idx != -1) && (irq == pin_2_irq(idx,apic,pin)))
+				return irq_trigger(idx);
+		}
 	}
 	/*
 	 * nonexistent IRQs are edge default
@@ -582,11 +597,12 @@
 void __init setup_IO_APIC_irqs(void)
 {
 	struct IO_APIC_route_entry entry;
-	int pin, idx, bus, irq, first_notcon = 1;
+	int apic, pin, idx, irq, first_notcon = 1;
 
 	printk("init IO_APIC IRQs\n");
 
-	for (pin = 0; pin < nr_ioapic_registers; pin++) {
+	for (apic = 0; apic < mp_apic_entries; apic++) {
+	for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
 
 		/*
 		 * add it to the IO-APIC irq-routing table:
@@ -598,13 +614,13 @@
 		entry.mask = 0;				/* enable IRQ */
 		entry.dest.logical.logical_dest = 0;	/* but no route */
 
-		idx = find_irq_entry(pin,mp_INT);
+		idx = find_irq_entry(apic,pin,mp_INT);
 		if (idx == -1) {
 			if (first_notcon) {
-				printk(" IO-APIC pin %d", pin);
+				printk(" IO-APIC (apicid-pin) %d-%d", mp_apics[apic].mpc_apicid, pin);
 				first_notcon = 0;
 			} else
-				printk(", %d", pin);
+				printk(", %d-%d", mp_apics[apic].mpc_apicid, pin);
 			continue;
 		}
 
@@ -617,18 +633,17 @@
 			entry.dest.logical.logical_dest = 0xff;
 		}
 
-		irq = pin_2_irq(idx,pin);
-		add_pin_to_irq(irq, pin);
+		irq = pin_2_irq(idx,apic,pin);
+		add_pin_to_irq(irq, apic, pin);
 
-		if (!IO_APIC_IRQ(irq))
+		if (!apic && !IO_APIC_IRQ(irq))
 			continue;
 
 		entry.vector = assign_irq_vector(irq);
 
-		bus = mp_irqs[idx].mpc_srcbus;
-
-		io_apic_write(0x11+2*pin, *(((int *)&entry)+1));
-		io_apic_write(0x10+2*pin, *(((int *)&entry)+0));
+		io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1));
+		io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0));
+	}
 	}
 
 	if (!first_notcon)
@@ -638,7 +653,7 @@
 /*
  * Set up a certain pin as ExtINT delivered interrupt
  */
-void __init setup_ExtINT_pin(unsigned int pin, int irq)
+void __init setup_ExtINT_pin(unsigned int apic, unsigned int pin, int irq)
 {
 	struct IO_APIC_route_entry entry;
 
@@ -662,8 +677,8 @@
 	entry.polarity = 0;
 	entry.trigger = 0;
 
-	io_apic_write(0x10+2*pin, *(((int *)&entry)+0));
-	io_apic_write(0x11+2*pin, *(((int *)&entry)+1));
+	io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0));
+	io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1));
 }
 
 void __init UNEXPECTED_IO_APIC(void)
@@ -674,17 +689,14 @@
 
 void __init print_IO_APIC(void)
 {
-	int i;
+	int apic, i;
 	struct IO_APIC_reg_00 reg_00;
 	struct IO_APIC_reg_01 reg_01;
 	struct IO_APIC_reg_02 reg_02;
 
  	printk("number of MP IRQ sources: %d.\n", mp_irq_entries);
- 	printk("number of IO-APIC registers: %d.\n", nr_ioapic_registers);
-
-	*(int *)&reg_00 = io_apic_read(0);
-	*(int *)&reg_01 = io_apic_read(1);
-	*(int *)&reg_02 = io_apic_read(2);
+	for (i = 0; i < mp_apic_entries; i++)
+		printk("number of IO-APIC #%d registers: %d.\n", mp_apics[i].mpc_apicid, nr_ioapic_registers[i]);
 
 	/*
 	 * We are a bit conservative about what we expect.  We have to
@@ -692,6 +704,12 @@
 	 */
 	printk("testing the IO APIC.......................\n");
 
+	for (apic = 0; apic < mp_apic_entries; apic++) {
+
+	*(int *)&reg_00 = io_apic_read(apic, 0);
+	*(int *)&reg_01 = io_apic_read(apic, 1);
+	*(int *)&reg_02 = io_apic_read(apic, 2);
+	printk("\nIO APIC #%d......\n", mp_apics[apic].mpc_apicid);
 	printk(".... register #00: %08X\n", *(int *)&reg_00);
 	printk(".......    : physical APIC id: %02X\n", reg_00.ID);
 	if (reg_00.__reserved_1 || reg_00.__reserved_2)
@@ -731,8 +749,8 @@
 	for (i = 0; i <= reg_01.entries; i++) {
 		struct IO_APIC_route_entry entry;
 
-		*(((int *)&entry)+0) = io_apic_read(0x10+i*2);
-		*(((int *)&entry)+1) = io_apic_read(0x11+i*2);
+		*(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2);
+		*(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2);
 
 		printk(" %02x %03X %02X  ",
 			i,
@@ -751,7 +769,7 @@
 			entry.vector
 		);
 	}
-
+	}
 	printk(KERN_DEBUG "IRQ to pin mappings:\n");
 	for (i = 0; i < NR_IRQS; i++) {
 		struct irq_pin_list *entry = irq_2_pin + i;
@@ -796,9 +814,12 @@
 	 */
 	{
 		struct IO_APIC_reg_01 reg_01;
+		int i;
 
-		*(int *)&reg_01 = io_apic_read(1);
-		nr_ioapic_registers = reg_01.entries+1;
+		for (i = 0; i < mp_apic_entries; i++) {
+			*(int *)&reg_01 = io_apic_read(i, 1);
+			nr_ioapic_registers[i] = reg_01.entries+1;
+		}
 	}
 
 	/*
@@ -897,15 +918,15 @@
 	/*
 	 * Set the ID
 	 */
-	*(int *)&reg_00 = io_apic_read(0);
+	*(int *)&reg_00 = io_apic_read(0, 0);
 	printk("...changing IO-APIC physical APIC ID to 2...\n");
 	reg_00.ID = 0x2;
-	io_apic_write(0, *(int *)&reg_00);
+	io_apic_write(0, 0, *(int *)&reg_00);
 
 	/*
 	 * Sanity check
 	 */
-	*(int *)&reg_00 = io_apic_read(0);
+	*(int *)&reg_00 = io_apic_read(0, 0);
 	if (reg_00.ID != 0x2)
 		panic("could not set ID");
 }
@@ -1227,7 +1248,10 @@
 
 		if (pin2 != -1) {
 			printk(".. (found pin %d) ...", pin2);
-			setup_ExtINT_pin(pin2, 0);
+			/*
+			 * legacy devices should be connected to IO APIC #0
+			 */
+			setup_ExtINT_pin(0, pin2, 0);
 			make_8259A_irq(0);
 		}
 
@@ -1238,9 +1262,9 @@
 			 * Just in case ...
 			 */
 			if (pin1 != -1)
-				clear_IO_APIC_pin(pin1);
+				clear_IO_APIC_pin(0, pin1);
 			if (pin2 != -1)
-				clear_IO_APIC_pin(pin2);
+				clear_IO_APIC_pin(0, pin2);
 
 			make_8259A_irq(0);
 
@@ -1282,7 +1306,8 @@
 	 * - those for which the user has specified a pirq= parameter
 	 */
 	if (	ioapic_whitelisted() ||
-		(nr_ioapic_registers == 16) ||
+		(mp_apic_entries == 1 && nr_ioapic_registers[0] == 16) ||
+		(mp_apic_entries > 1) ||
 		pirqs_enabled)
 	{
 		printk("ENABLING IO-APIC IRQs\n");
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/irq.h linux.14p6/arch/i386/kernel/irq.h
--- linux.vanilla/arch/i386/kernel/irq.h	Sat Aug 14 02:27:13 1999
+++ linux.14p6/arch/i386/kernel/irq.h	Fri Oct 22 23:30:31 1999
@@ -112,6 +112,11 @@
 
 extern char _stext, _etext;
 
+/*
+ * IF YOU CHANGE THIS, PLEASE ALSO CHANGE
+ * FIX_IO_APIC_BASE_* in fixmap.h
+ */
+#define MAX_IO_APICS 4
 #define MAX_IRQ_SOURCES 128
 #define MAX_MP_BUSSES 32
 enum mp_bustype {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/ptrace.c linux.14p6/arch/i386/kernel/ptrace.c
--- linux.vanilla/arch/i386/kernel/ptrace.c	Wed Mar 24 21:18:46 1999
+++ linux.14p6/arch/i386/kernel/ptrace.c	Tue Nov 16 15:18:13 1999
@@ -80,12 +80,17 @@
 	pmd_t * pgmiddle;
 	pte_t * pgtable;
 	unsigned long page;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
 	if (pgd_none(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
@@ -94,8 +99,12 @@
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
 	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
@@ -104,8 +113,12 @@
 	}
 	pgtable = pte_offset(pgmiddle, addr);
 	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
@@ -131,12 +144,17 @@
 	pmd_t *pgmiddle;
 	pte_t *pgtable;
 	unsigned long page;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
 	if (!pgd_present(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
@@ -145,8 +163,12 @@
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
 	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
@@ -155,13 +177,21 @@
 	}
 	pgtable = pte_offset(pgmiddle, addr);
 	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	page = pte_page(*pgtable);
 	if (!pte_write(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	if (MAP_NR(page) < max_mapnr)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/setup.c linux.14p6/arch/i386/kernel/setup.c
--- linux.vanilla/arch/i386/kernel/setup.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/i386/kernel/setup.c	Fri Oct 29 23:41:43 1999
@@ -17,6 +17,12 @@
  *
  *  IDT Winchip tweaks, misc clean ups.
  *	Dave Jones <dave@powertweak.com>, August 1999
+ *
+ *	Added proper L2 cache detection for Coppermine
+ *	Dragan Stancevic <visitor@valinux.com>, October 1999
+ *
+ *	Improved Intel cache detection.
+ *	Dave Jones <dave@powertweak.com>, October 1999
  */
 
 /*
@@ -366,7 +372,7 @@
 			/* Zero is valid according to the BIOS weenies */
 			if(i386_endbase)
 			{
-				printk(KERN_NOTICE "Ignoring bogus EBDA pointer %X\n", 
+				printk(KERN_NOTICE "Ignoring bogus EBDA pointer %lX\n", 
 					i386_endbase);
 			}
 			i386_endbase = BIOS_ENDBASE;
@@ -755,8 +761,8 @@
 	{ X86_VENDOR_INTEL,	6,
 	  { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", 
 	    NULL, "Pentium II (Deschutes)", "Mobile Pentium II", 
-            "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, 
-            NULL, NULL, NULL, NULL }},
+	    "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, 
+	    NULL, NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_AMD,	4,
 	  { NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB",
 	    "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT",
@@ -818,49 +824,48 @@
 		}
 	}
 
-	for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) {
-		if (c->cpuid_level > 1) {
-			/* supports eax=2  call */
-			int edx, cache_size, dummy;
+	if (c->cpuid_level > 1) {
+		/* supports eax=2  call */
+		int edx, dummy;
 			
-			cpuid(2, &dummy, &dummy, &dummy, &edx);
+		cpuid(2, &dummy, &dummy, &dummy, &edx);
 
-			/* We need only the LSB */
-			edx &= 0xff;
+		/* We need only the LSB */
+		edx &= 0xff;
 
-			switch (edx) {
-				case 0x40:
-					cache_size = 0;
-					break;
-
-				case 0x41:
-					cache_size = 128;
-					break;
-
-				case 0x42:
-					cache_size = 256;
-					break;
-
-				case 0x43:
-					cache_size = 512;
-					break;
-
-				case 0x44:
-					cache_size = 1024;
-					break;
-
-				case 0x45:
-					cache_size = 2048;
-					break;
-
-				default:
-					cache_size = 0;
-					break;
-			}
+		switch (edx) {
+			case 0x40:
+				c->x86_cache_size = 0;
+				break;
 
-			c->x86_cache_size = cache_size; 
+			case 0x41:
+				c->x86_cache_size = 128;
+				break;
+
+			case 0x42:
+			case 0x82: /*Detect 256-Kbyte cache on Coppermine*/ 
+				c->x86_cache_size = 256;
+				break;
+
+			case 0x43:
+				c->x86_cache_size = 512;
+				break;
+
+			case 0x44:
+				c->x86_cache_size = 1024;
+				break;
+
+			case 0x45:
+				c->x86_cache_size = 2048;
+				break;
+
+			default:
+				c->x86_cache_size = 0;
+				break;
 		}
+	}
 
+	for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) {
 		if (cpu_models[i].vendor == c->x86_vendor &&
 		    cpu_models[i].x86 == c->x86) {
 			if (c->x86_model <= 16)
@@ -1019,8 +1024,9 @@
 			x86_cap_flags[14] = "mca";
 			x86_cap_flags[16] = "pat";
 			x86_cap_flags[17] = "pse36";
-			x86_cap_flags[18] = "psn";
-			x86_cap_flags[24] = "osfxsr";
+			x86_cap_flags[18] = "pn";
+			x86_cap_flags[24] = "fxsr";
+			x86_cap_flags[25] = "xmm";
 			break;
 
 		    case X86_VENDOR_CENTAUR:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/signal.c linux.14p6/arch/i386/kernel/signal.c
--- linux.vanilla/arch/i386/kernel/signal.c	Sat Aug 14 02:26:46 1999
+++ linux.14p6/arch/i386/kernel/signal.c	Sat Oct 23 23:02:34 1999
@@ -419,13 +419,19 @@
 		           ? current->exec_domain->signal_invmap[sig]
 		           : sig),
 		          &frame->sig);
+	if (err)
+		goto give_sigsegv;
 
 	err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
+	if (err)
+		goto give_sigsegv;
 
 	if (_NSIG_WORDS > 1) {
 		err |= __copy_to_user(frame->extramask, &set->sig[1],
 				      sizeof(frame->extramask));
 	}
+	if (err)
+		goto give_sigsegv;
 
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
@@ -486,6 +492,8 @@
 	err |= __put_user(&frame->info, &frame->pinfo);
 	err |= __put_user(&frame->uc, &frame->puc);
 	err |= __copy_to_user(&frame->info, info, sizeof(*info));
+	if (err)
+		goto give_sigsegv;
 
 	/* Create the ucontext.  */
 	err |= __put_user(0, &frame->uc.uc_flags);
@@ -497,6 +505,8 @@
 	err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
 			        regs, set->sig[0]);
 	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+	if (err)
+		goto give_sigsegv;
 
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/smp.c linux.14p6/arch/i386/kernel/smp.c
--- linux.vanilla/arch/i386/kernel/smp.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/i386/kernel/smp.c	Fri Oct 22 21:23:03 1999
@@ -128,6 +128,8 @@
 const char lk_lockmsg[] = "lock from interrupt context at %p\n"; 
 
 int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, };
+extern int mp_apic_entries;
+extern struct mpc_config_ioapic mp_apics [MAX_IO_APICS];
 extern int mp_irq_entries;
 extern struct mpc_config_intsrc mp_irqs [MAX_IRQ_SOURCES];
 extern int mpc_default_type;
@@ -367,11 +369,9 @@
 					printk("I/O APIC #%d Version %d at 0x%lX.\n",
 						m->mpc_apicid,m->mpc_apicver,
 						m->mpc_apicaddr);
-					/*
-					 * we use the first one only currently
-					 */
-					if (ioapics == 1)
-						mp_ioapic_addr = m->mpc_apicaddr;
+					mp_apics [mp_apic_entries] = *m;
+					if (++mp_apic_entries > MAX_IO_APICS)
+						--mp_apic_entries;
 				}
 				mpt+=sizeof(*m);
 				count+=sizeof(*m);
@@ -403,9 +403,9 @@
 			}
 		}
 	}
-	if (ioapics > 1)
+	if (ioapics > MAX_IO_APICS)
 	{
-		printk("Warning: Multiple IO-APICs not yet supported.\n");
+		printk("Warning: Max I/O APICs exceeded (max %d, found %d).\n", MAX_IO_APICS, ioapics);
 		printk("Warning: switching to non APIC mode.\n");
 		skip_ioapic_setup=1;
 	}
@@ -772,18 +772,22 @@
 
 #ifdef CONFIG_X86_IO_APIC
 	{
-		unsigned long ioapic_phys;
+		unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0;
+		int i;
 
-		if (smp_found_config) {
-			ioapic_phys = mp_ioapic_addr;
-		} else {
-			ioapic_phys = __pa(memory_start);
-			memset((void *)memory_start, 0, PAGE_SIZE);
-			memory_start += PAGE_SIZE;
+		for (i = 0; i < mp_apic_entries; i++) {
+			if (smp_found_config) {
+				ioapic_phys = mp_apics[i].mpc_apicaddr;
+			} else {
+				ioapic_phys = __pa(memory_start);
+				memset((void *)memory_start, 0, PAGE_SIZE);
+				memory_start += PAGE_SIZE;
+			}
+			set_fixmap(idx,ioapic_phys);
+			printk("mapped IOAPIC to %08lx (%08lx)\n",
+					__fix_to_virt(idx), ioapic_phys);
+			idx++;
 		}
-		set_fixmap(FIX_IO_APIC_BASE,ioapic_phys);
-		printk("mapped IOAPIC to %08lx (%08lx)\n",
-				fix_to_virt(FIX_IO_APIC_BASE), ioapic_phys);
 	}
 #endif
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/README linux.14p6/arch/i386/math-emu/README
--- linux.vanilla/arch/i386/math-emu/README	Wed Jun 24 22:30:08 1998
+++ linux.14p6/arch/i386/math-emu/README	Thu Nov  4 23:43:12 1999
@@ -1,9 +1,9 @@
  +---------------------------------------------------------------------------+
  |  wm-FPU-emu   an FPU emulator for 80386 and 80486SX microprocessors.      |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1995,1996,1997                               |
+ | Copyright (C) 1992,1993,1994,1995,1996,1997,1999                          |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@suburbia.net               |
+ |                       Australia.  E-mail billm@melbpc.org.au              |
  |                                                                           |
  |    This program is free software; you can redistribute it and/or modify   |
  |    it under the terms of the GNU General Public License version 2 as      |
@@ -42,14 +42,15 @@
 some differences.
 
 Please report bugs, etc to me at:
-       billm@suburbia.net
+       billm@melbpc.org.au
+or     b.metzenthen@medoto.unimelb.edu.au
 
 For more information on the emulator and on floating point topics, see
 my web pages, currently at  http://www.suburbia.net/~billm/
 
 
 --Bill Metzenthen
-  December 1997
+  December 1999
 
 
 ----------------------- Internals of wm-FPU-emu -----------------------
@@ -98,7 +99,7 @@
 ----------------------- Limitations of wm-FPU-emu -----------------------
 
 There are a number of differences between the current wm-FPU-emu
-(version 2.00) and the 80486 FPU (apart from bugs).  The differences
+(version 2.01) and the 80486 FPU (apart from bugs).  The differences
 are fewer than those which applied to the 1.xx series of the emulator.
 Some of the more important differences are listed below:
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/fpu_trig.c linux.14p6/arch/i386/math-emu/fpu_trig.c
--- linux.vanilla/arch/i386/math-emu/fpu_trig.c	Wed Dec 10 01:57:09 1997
+++ linux.14p6/arch/i386/math-emu/fpu_trig.c	Thu Nov  4 23:43:12 1999
@@ -3,9 +3,9 @@
  |                                                                           |
  | Implementation of the FPU "transcendental" functions.                     |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1997                                         |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@suburbia.net             |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -85,7 +85,8 @@
 	  FPU_normalize(&tmp);
 	  tmptag =
 	    FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS,
-		      exponent16(&CONST_PI2extra) + exponent16(&tmp));
+		      exponent(&CONST_PI2extra) + exponent(&tmp));
+	  setsign(&tmp, getsign(&CONST_PI2extra));
 	  st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION);
 	  if ( signnegative(st0_ptr) )
 	    {
@@ -117,7 +118,8 @@
 	  FPU_normalize(&tmp);         /* This must return TAG_Valid */
 	  tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION,
 			     SIGN_POS,
-			     exponent16(&CONST_PI2extra) + exponent16(&tmp));
+			     exponent(&CONST_PI2extra) + exponent(&tmp));
+	  setsign(&tmp, getsign(&CONST_PI2extra));
 	  st0_tag = FPU_sub(LOADED|(tmptag & 0x0f), (int)&tmp,
 			    FULL_PRECISION);
 	  if ( (exponent(st0_ptr) == exponent(&CONST_PI2)) &&
@@ -827,18 +829,29 @@
 		       unsigned long long st1,
 		       unsigned long long q, int n)
 {
+  int dummy;
   unsigned long long x;
 
   x = st0 << n;
 
   /* Do the required multiplication and subtraction in the one operation */
-  asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
-                 movl %3,%%eax; mull %4; subl %%eax,%1;
-                 movl %2,%%eax; mull %5; subl %%eax,%1;"
-		:"=m" (x), "=m" (((unsigned *)&x)[1])
-		:"m" (st1),"m" (((unsigned *)&st1)[1]),
-		 "m" (q),"m" (((unsigned *)&q)[1])
-		:"%ax","%dx");
+
+  /* lsw x -= lsw st1 * lsw q */
+  asm volatile ("mull %4; subl %%eax,%0; sbbl %%edx,%1"
+		:"=m" (((unsigned *)&x)[0]), "=m" (((unsigned *)&x)[1]),
+		"=a" (dummy)
+		:"2" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[0])
+		:"%dx");
+  /* msw x -= msw st1 * lsw q */
+  asm volatile ("mull %3; subl %%eax,%0"
+		:"=m" (((unsigned *)&x)[1]), "=a" (dummy)
+		:"1" (((unsigned *)&st1)[1]), "m" (((unsigned *)&q)[0])
+		:"%dx");
+  /* msw x -= lsw st1 * msw q */
+  asm volatile ("mull %3; subl %%eax,%0"
+		:"=m" (((unsigned *)&x)[1]), "=a" (dummy)
+		:"1" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[1])
+		:"%dx");
 
   *y = x;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/poly.h linux.14p6/arch/i386/math-emu/poly.h
--- linux.vanilla/arch/i386/math-emu/poly.h	Tue Apr  7 15:48:54 1998
+++ linux.14p6/arch/i386/math-emu/poly.h	Thu Nov  4 23:43:12 1999
@@ -3,9 +3,9 @@
  |                                                                           |
  |  Header file for the FPU-emu poly*.c source files.                        |
  |                                                                           |
- | Copyright (C) 1994                                                        |
+ | Copyright (C) 1994,1999                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  | Declarations and definitions for functions operating on Xsig (12-byte     |
  | extended-significand) quantities.                                         |
@@ -55,15 +55,20 @@
    actually be in-line.
    */
 
-/* Multiply two fixed-point 32 bit numbers. */
-extern inline void mul_32_32(const unsigned long arg1,
-			     const unsigned long arg2,
-			     unsigned long *out)
+/* Multiply two fixed-point 32 bit numbers, producing a 32 bit result.
+   The answer is the ms word of the product. */
+/* Some versions of gcc make it difficult to stop eax from being clobbered.
+   Merely specifying that it is used doesn't work...
+ */
+extern inline unsigned long mul_32_32(const unsigned long arg1,
+				      const unsigned long arg2)
 {
-  asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \
-		:"=g" (*out) \
-		:"g" (arg1), "g" (arg2) \
-		:"ax","dx");
+  int retval;
+  asm volatile ("mull %2; movl %%edx,%%eax" \
+		:"=a" (retval) \
+		:"0" (arg1), "g" (arg2) \
+		:"dx");
+  return retval;
 }
 
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/poly_sin.c linux.14p6/arch/i386/math-emu/poly_sin.c
--- linux.vanilla/arch/i386/math-emu/poly_sin.c	Wed Dec 10 01:57:09 1997
+++ linux.14p6/arch/i386/math-emu/poly_sin.c	Thu Nov  4 23:43:12 1999
@@ -4,9 +4,9 @@
  |  Computation of an approximation of the sin function and the cosine       |
  |  function by a polynomial.                                                |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1997                                         |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
- |                  E-mail   billm@suburbia.net                              |
+ |                  E-mail   billm@melbpc.org.au                             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -132,6 +132,9 @@
 	}
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+      /* There is a special case which arises due to rounding, to fix here. */
+      if ( fixed_arg == 0xffffffffffffffffLL )
+	fixed_arg = 0;
 
       XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0;
       mul64_Xsig(&argSqrd, &fixed_arg);
@@ -172,10 +175,9 @@
       if ( argSqrd.msw & 0xffc00000 )
 	{
 	  /* Get about 32 bit precision in these: */
-	  mul_32_32(0x898cc517, argSqrd.msw, &adj);
-	  fix_up -= adj/6;
+	  fix_up -= mul_32_32(0x898cc517, argSqrd.msw) / 6;
 	}
-      mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up);
+      fix_up = mul_32_32(fix_up, LL_MSW(fixed_arg));
 
       adj = accumulator.lsw;    /* temp save */
       accumulator.lsw -= fix_up;
@@ -211,7 +213,6 @@
   FPU_REG	      result;
   long int            exponent, exp2, echange;
   Xsig                accumulator, argSqrd, fix_up, argTo4;
-  unsigned long       adj;
   unsigned long long  fixed_arg;
 
 #ifdef PARANOID
@@ -300,6 +301,9 @@
 	}
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+      /* There is a special case which arises due to rounding, to fix here. */
+      if ( fixed_arg == 0xffffffffffffffffLL )
+	fixed_arg = 0;
 
       exponent = -1;
       exp2 = -1;
@@ -363,10 +367,8 @@
       if ( argSqrd.msw & 0xffc00000 )
 	{
 	  /* Get about 32 bit precision in these: */
-	  mul_32_32(0x898cc517, argSqrd.msw, &adj);
-	  fix_up.msw -= adj/2;
-	  mul_32_32(0x898cc517, argTo4.msw, &adj);
-	  fix_up.msw += adj/24;
+	  fix_up.msw -= mul_32_32(0x898cc517, argSqrd.msw) / 2;
+	  fix_up.msw += mul_32_32(0x898cc517, argTo4.msw) / 24;
 	}
 
       exp2 += norm_Xsig(&accumulator);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/poly_tan.c linux.14p6/arch/i386/math-emu/poly_tan.c
--- linux.vanilla/arch/i386/math-emu/poly_tan.c	Wed Dec 10 01:57:09 1997
+++ linux.14p6/arch/i386/math-emu/poly_tan.c	Thu Nov  4 23:43:12 1999
@@ -3,9 +3,9 @@
  |                                                                           |
  | Compute the tan of a FPU_REG, using a polynomial approximation.           |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1997                                         |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@suburbia.net             |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -84,6 +84,14 @@
 	}
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum);
+      /* This is a special case which arises due to rounding. */
+      if ( XSIG_LL(accum) == 0xffffffffffffffffLL )
+	{
+	  FPU_settag0(TAG_Valid);
+	  significand(st0_ptr) = 0x8a51e04daabda360LL;
+	  setexponent16(st0_ptr, 0x41 + EXTENDED_Ebias | SIGN_Negative);
+	  return;
+	}
 
       argSignif.lsw = accum.lsw;
       XSIG_LL(argSignif) = XSIG_LL(accum);
@@ -177,11 +185,11 @@
       else if ( exponent > -30 )
 	{
 	  adj = accum.msw >> -(exponent+1);      /* tan */
-	  mul_32_32(adj, adj, &adj);           /* tan^2 */
+	  adj = mul_32_32(adj, adj);             /* tan^2 */
 	}
       else
 	adj = 0;
-      mul_32_32(0x898cc517, adj, &adj);        /* delta * tan^2 */
+      adj = mul_32_32(0x898cc517, adj);          /* delta * tan^2 */
 
       fix_up.msw += adj;
       if ( !(fix_up.msw & 0x80000000) )   /* did fix_up overflow ? */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/reg_round.S linux.14p6/arch/i386/math-emu/reg_round.S
--- linux.vanilla/arch/i386/math-emu/reg_round.S	Wed Dec 10 01:57:09 1997
+++ linux.14p6/arch/i386/math-emu/reg_round.S	Thu Nov  4 23:43:12 1999
@@ -365,7 +365,7 @@
 	jne	LDo_64_round_up
 
 	/* Now test for round-to-even */
-	testb	$1,%ebx
+	testb	$1,%bl
 	jz	LCheck_truncate_64
 
 LDo_64_round_up:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/mm/fault.c linux.14p6/arch/i386/mm/fault.c
--- linux.vanilla/arch/i386/mm/fault.c	Sun Nov 22 17:38:19 1998
+++ linux.14p6/arch/i386/mm/fault.c	Sat Oct 23 23:02:34 1999
@@ -50,7 +50,14 @@
 	start &= PAGE_MASK;
 
 	for (;;) {
-		handle_mm_fault(current,vma, start, 1);
+	survive:
+		{
+			int fault = handle_mm_fault(current,vma, start, 1);
+			if (!fault)
+				goto do_sigbus;
+			if (fault < 0)
+				goto out_of_memory;
+		}
 		if (!size)
 			break;
 		size--;
@@ -73,6 +80,19 @@
 
 bad_area:
 	return 0;
+
+do_sigbus:
+	force_sig(SIGBUS, current);
+	goto bad_area;
+
+out_of_memory:
+	if (current->pid == 1)
+	{
+		current->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	goto bad_area;
 }
 
 asmlinkage void do_invalid_op(struct pt_regs *, unsigned long);
@@ -162,8 +182,14 @@
 	 * make sure we exit gracefully rather than endlessly redo
 	 * the fault.
 	 */
-	if (!handle_mm_fault(tsk, vma, address, write))
-		goto do_sigbus;
+survive:
+	{
+		int fault = handle_mm_fault(tsk, vma, address, write);
+		if (!fault)
+			goto do_sigbus;
+		if (fault < 0)
+			goto out_of_memory;
+	}
 
 	/*
 	 * Did it hit the DOS screen memory VA from vm86 mode?
@@ -255,6 +281,34 @@
  * We ran out of memory, or some other thing happened to us that made
  * us unable to handle the page fault gracefully.
  */
+out_of_memory:
+	if (tsk->pid == 1)
+	{
+		tsk->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	up(&mm->mmap_sem);
+	if (error_code & 4)
+	{
+		if (!((regs->eflags >> 12) & 3))
+		{
+			printk("VM: killing process %s\n", tsk->comm);
+			do_exit(SIGKILL);
+		}
+		else
+		{
+			/*
+			 * The task is running with privilegies and so we
+			 * trust it and we give it a chance to die gracefully.
+			 */
+			printk("VM: terminating process %s\n", tsk->comm);
+			force_sig(SIGTERM, current);
+			return;
+		}
+	}
+	goto no_context;
+
 do_sigbus:
 	up(&mm->mmap_sem);
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/Makefile linux.14p6/arch/ppc/Makefile
--- linux.vanilla/arch/ppc/Makefile	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/ppc/Makefile	Sat Nov 13 15:14:56 1999
@@ -112,6 +112,10 @@
 	rm -f .config arch/ppc/defconfig
 	ln -s apus_defconfig arch/ppc/defconfig
 
+gemini_config:
+	rm -f .config arch/ppc/defconfig
+	ln -s gemini_defconfig arch/ppc/defconfig
+
 tags:
 	etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h}
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/boot/Makefile linux.14p6/arch/ppc/boot/Makefile
--- linux.vanilla/arch/ppc/boot/Makefile	Sat Aug 28 20:00:53 1999
+++ linux.14p6/arch/ppc/boot/Makefile	Sat Nov 13 15:14:56 1999
@@ -25,12 +25,22 @@
 IOFF = 0
 ISZ = 0
 
+ifeq ($(CONFIG_ALL_PPC),y)
+CONFIG_PREP=y
+endif
+
 ifeq ($(CONFIG_SMP),y)
 TFTPIMAGE=/tftpboot/zImage.prep.smp$(MSIZE)
 else
 TFTPIMAGE=/tftpboot/zImage.prep$(MSIZE)
 endif
 
+ifeq ($(CONFIG_SMP),y)
+TFTPSIMAGE=/tftpboot/sImage.smp
+else
+TFTPSIMAGE=/tftpboot/sImage
+endif
+
 ifeq ($(CONFIG_PPC64),y)
 MSIZE=.64
 else
@@ -46,14 +56,10 @@
 OBJCOPY = $(CROSS_COMPILE)objcopy
 OBJCOPY_ARGS = -O elf32-powerpc
 
-ifeq ($(CONFIG_SMP),y)
-CFLAGS += -D__SMP__
-endif
-
 OBJECTS += vreset.o kbd.o of1275.o
- ifeq ($(CONFIG_SERIAL_CONSOLE),y)
-  OBJECTS += ns16550.o
- endif
+ifeq ($(CONFIG_SERIAL_CONSOLE),y)
+OBJECTS += ns16550.o
+endif
 
 all:	zImage
 
@@ -75,11 +81,20 @@
 		zvmlinux.initrd.tmp $@
 	rm zvmlinux.initrd.tmp
 
-zImage: zvmlinux mkprep
+zImage: zvmlinux mkprep sImage
+ifdef CONFIG_PREP
 	./mkprep -pbp zvmlinux zImage
+endif
+
+sImage: ../../../vmlinux
+ifdef CONFIG_GEMINI
+	$(OBJCOPY) -I elf32-powerpc -O binary ../../../vmlinux sImage
+endif
 
 zImage.initrd: zvmlinux.initrd mkprep
+ifdef CONFIG_PREP
 	./mkprep -pbp zvmlinux.initrd zImage.initrd
+endif
 
 zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz
 #
@@ -108,7 +123,12 @@
 	$(HOSTCC) -o mkprep mkprep.c
 
 znetboot : zImage
+ifdef CONFIG_PREP
 	cp zImage $(TFTPIMAGE)
+endif
+ifdef CONFIG_GEMINI
+	cp sImage $(TFTPSIMAGE)
+endif
 
 znetboot.initrd : zImage.initrd
 	cp zImage.initrd $(TFTPIMAGE)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/boot/head.S linux.14p6/arch/ppc/boot/head.S
--- linux.vanilla/arch/ppc/boot/head.S	Sat Aug 14 02:26:11 1999
+++ linux.14p6/arch/ppc/boot/head.S	Sat Nov 13 15:14:56 1999
@@ -6,7 +6,7 @@
 	.text
 
 /*
- * $Id: head.S,v 1.31 1999/04/22 06:32:00 davem Exp $
+ * $Id: head.S,v 1.33 1999/09/08 01:06:58 cort Exp $
  *	
  * Boot loader philosophy:
  *      ROM loads us to some arbitrary location
@@ -125,12 +125,11 @@
 	 * get start address of kernel code which is stored as a coff
 	 * entry.  see boot/head.S -- Cort 
 	 */
-	li	r9,0x0
-	lwz	r9,0(r9)
+	li	r9,0x4
 	mtlr	r9
-	li	r9,0
 	lis	r10,0xdeadc0de@h
 	ori	r10,r10,0xdeadc0de@l
+	li	r9,0
 	stw	r10,0(r9)
 /*
  * The Radstone firmware maps PCI memory at 0xc0000000 using BAT2
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/chrpboot/main.c linux.14p6/arch/ppc/chrpboot/main.c
--- linux.vanilla/arch/ppc/chrpboot/main.c	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/ppc/chrpboot/main.c	Sat Nov 13 15:14:56 1999
@@ -68,7 +68,7 @@
 
     flush_cache(dst, len);
     
-    sa = *(unsigned long *)PROG_START+PROG_START;
+    sa = (unsigned long)PROG_START;
     printf("start address = 0x%x\n\r", sa);
 
     (*(void (*)())sa)(0, 0, prom, a1, a2);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/coffboot/main.c linux.14p6/arch/ppc/coffboot/main.c
--- linux.vanilla/arch/ppc/coffboot/main.c	Wed Aug  5 00:06:36 1998
+++ linux.14p6/arch/ppc/coffboot/main.c	Sat Nov 13 15:14:56 1999
@@ -100,7 +100,7 @@
 
     flush_cache(dst, len);
 
-    sa = *(unsigned *)dst + PROG_START;
+    sa = (unsigned long)dst;
     printf("start address = 0x%x\n", sa);
 
 #if 0
@@ -165,7 +165,6 @@
 	printf("gunzip: ran out of data in header\n");
 	exit();
     }
-
     s.zalloc = zalloc;
     s.zfree = zfree;
     r = inflateInit2(&s, -MAX_WBITS);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/common_defconfig linux.14p6/arch/ppc/common_defconfig
--- linux.vanilla/arch/ppc/common_defconfig	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/ppc/common_defconfig	Sat Nov 13 15:14:56 1999
@@ -7,13 +7,13 @@
 #
 CONFIG_PPC=y
 CONFIG_6xx=y
-# CONFIG_PPC64 is not set
 # CONFIG_8xx is not set
 # CONFIG_PMAC is not set
 # CONFIG_PREP is not set
 # CONFIG_CHRP is not set
 CONFIG_ALL_PPC=y
 # CONFIG_APUS is not set
+# CONFIG_GEMINI is not set
 # CONFIG_MBX is not set
 # CONFIG_SMP is not set
 CONFIG_6xx=y
@@ -91,8 +91,10 @@
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
 CONFIG_PARIDE_PARPORT=y
 # CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -155,7 +157,7 @@
 CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
-# CONFIG_CHR_DEV_SG is not set
+CONFIG_CHR_DEV_SG=y
 
 #
 # Some SCSI devices (e.g. CD jukebox) support multiple LUNs
@@ -177,6 +179,7 @@
 CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
 CONFIG_AIC7XXX_PROC_STATS=y
 CONFIG_AIC7XXX_RESET_DELAY=15
+# CONFIG_SCSI_IPS is not set
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
@@ -195,6 +198,7 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_SIM710 is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 # CONFIG_SCSI_NCR53C8XX is not set
 CONFIG_SCSI_SYM53C8XX=y
@@ -226,26 +230,38 @@
 # Network device support
 #
 CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
 # CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
 CONFIG_NET_ETHERNET=y
 CONFIG_MACE=y
 CONFIG_BMAC=y
+# CONFIG_NCR885E is not set
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
 # CONFIG_YELLOWFIN is not set
-# CONFIG_ACENIC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
 CONFIG_PCNET32=y
+# CONFIG_ACENIC is not set
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
+# CONFIG_DM9102 is not set
 CONFIG_DE4X5=y
 # CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
@@ -261,7 +277,10 @@
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_HIPPI is not set
-# CONFIG_DLCI is not set
+
+#
+# Appletalk devices
+#
 # CONFIG_LTPC is not set
 # CONFIG_COPS is not set
 # CONFIG_IPDDP is not set
@@ -272,11 +291,24 @@
 #
 # CONFIG_SLIP is not set
 # CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
 # CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
 # CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
 # CONFIG_HOSTESS_SV11 is not set
 # CONFIG_COSA is not set
-# CONFIG_RCPCI is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_COMX is not set
+# CONFIG_DLCI is not set
+# CONFIG_SBNI is not set
 
 #
 # Amateur Radio support
@@ -407,6 +439,7 @@
 CONFIG_EXT2_FS=y
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
+# CONFIG_EFS_FS is not set
 
 #
 # Network File Systems
@@ -458,6 +491,7 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_14 is not set
 # CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
@@ -466,8 +500,11 @@
 #
 CONFIG_SOUND=y
 CONFIG_DMASOUND=y
+# CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_ES1370 is not set
 # CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
 # CONFIG_SOUND_SONICVIBES is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -475,13 +512,13 @@
 # CONFIG_SOUND_DMAP is not set
 # CONFIG_SOUND_PAS is not set
 # CONFIG_SOUND_SB is not set
-# CONFIG_SOUND_ADLIB is not set
 # CONFIG_SOUND_GUS is not set
 # CONFIG_SOUND_MPU401 is not set
 # CONFIG_SOUND_PSS is not set
 # CONFIG_SOUND_MSS is not set
 # CONFIG_SOUND_SSCAPE is not set
 # CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_VIA82CXXX is not set
 # CONFIG_SOUND_MAD16 is not set
 # CONFIG_SOUND_WAVEFRONT is not set
 CONFIG_SOUND_CS4232=m
@@ -494,6 +531,7 @@
 # CONFIG_SOUND_YM3812 is not set
 # CONFIG_SOUND_VMIDI is not set
 # CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_NM256 is not set
 
 #
 # Additional low level sound drivers
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/config.in linux.14p6/arch/ppc/config.in
--- linux.vanilla/arch/ppc/config.in	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/ppc/config.in	Sat Nov 13 15:14:56 1999
@@ -9,7 +9,6 @@
 define_bool CONFIG_PPC y
 choice 'Processor type' \
 	"6xx/7xx		CONFIG_6xx \
-	 630/Power3(64-Bit) 	CONFIG_PPC64 \
 	 860/821		CONFIG_8xx" 6xx/7xx
 
 choice 'Machine Type' \
@@ -18,6 +17,7 @@
 	 CHRP		CONFIG_CHRP \
 	 PowerMac/PReP/CHRP	CONFIG_ALL_PPC \
   	 APUS           CONFIG_APUS \
+	 Gemini		CONFIG_GEMINI \
 	 MBX		CONFIG_MBX" PowerMac
 
 bool 'Symmetric multi-processing support' CONFIG_SMP
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/defconfig linux.14p6/arch/ppc/defconfig
--- linux.vanilla/arch/ppc/defconfig	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/ppc/defconfig	Sat Nov 13 15:14:56 1999
@@ -7,16 +7,15 @@
 #
 CONFIG_PPC=y
 CONFIG_6xx=y
-# CONFIG_PPC64 is not set
 # CONFIG_8xx is not set
-CONFIG_PMAC=y
+# CONFIG_PMAC is not set
 # CONFIG_PREP is not set
 # CONFIG_CHRP is not set
-# CONFIG_ALL_PPC is not set
+CONFIG_ALL_PPC=y
 # CONFIG_APUS is not set
+# CONFIG_GEMINI is not set
 # CONFIG_MBX is not set
 # CONFIG_SMP is not set
-CONFIG_MACH_SPECIFIC=y
 CONFIG_6xx=y
 
 #
@@ -24,7 +23,7 @@
 #
 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
@@ -35,11 +34,10 @@
 # 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=m
-# CONFIG_PARPORT_PC is not set
-# CONFIG_VGA_CONSOLE is not set
+# CONFIG_PARPORT is not set
+CONFIG_VGA_CONSOLE=y
 CONFIG_FB=y
 CONFIG_FB_COMPAT_XPMAC=y
 CONFIG_PMAC_PBOOK=y
@@ -52,6 +50,7 @@
 # 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
@@ -61,7 +60,7 @@
 #
 # Block devices
 #
-# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_FD=y
 CONFIG_BLK_DEV_IDE=y
 
 #
@@ -75,16 +74,8 @@
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA=y
-# CONFIG_BLK_DEV_OFFBOARD is not set
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_TRM290 is not set
-# CONFIG_BLK_DEV_NS87415 is not set
-# CONFIG_BLK_DEV_VIA82C586 is not set
-CONFIG_BLK_DEV_CMD646=y
-# CONFIG_BLK_DEV_SL82C105 is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+CONFIG_BLK_DEV_SL82C105=y
 CONFIG_BLK_DEV_IDE_PMAC=y
 CONFIG_BLK_DEV_IDEDMA_PMAC=y
 CONFIG_BLK_DEV_IDEDMA=y
@@ -101,7 +92,7 @@
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_BLK_DEV_XD is not set
 # CONFIG_BLK_DEV_DAC960 is not set
-CONFIG_PARIDE_PARPORT=m
+CONFIG_PARIDE_PARPORT=y
 # CONFIG_PARIDE is not set
 # CONFIG_BLK_CPQ_DA is not set
 # CONFIG_BLK_DEV_HD is not set
@@ -125,7 +116,7 @@
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=y
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
 
 #
 # (it is safe to leave these untouched)
@@ -166,7 +157,7 @@
 CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
-# CONFIG_CHR_DEV_SG is not set
+CONFIG_CHR_DEV_SG=y
 
 #
 # Some SCSI devices (e.g. CD jukebox) support multiple LUNs
@@ -188,6 +179,7 @@
 CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
 CONFIG_AIC7XXX_PROC_STATS=y
 CONFIG_AIC7XXX_RESET_DELAY=15
+# CONFIG_SCSI_IPS is not set
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
@@ -200,14 +192,15 @@
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
 # CONFIG_SCSI_GDTH is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_G_NCR5380_PORT is not set
+# CONFIG_SCSI_G_NCR5380_MEM is not set
 # CONFIG_SCSI_INITIO is not set
 # CONFIG_SCSI_INIA100 is not set
-# CONFIG_SCSI_PPA is not set
-# CONFIG_SCSI_IMM is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_SIM710 is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX is not set
 CONFIG_SCSI_SYM53C8XX=y
 CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
@@ -215,7 +208,7 @@
 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set
 # CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT=y
+# 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
@@ -253,6 +246,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MACE=y
 CONFIG_BMAC=y
+# CONFIG_NCR885E is not set
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
@@ -262,11 +256,12 @@
 # CONFIG_YELLOWFIN is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
-# CONFIG_PCNET32 is not set
+CONFIG_PCNET32=y
 # CONFIG_ACENIC is not set
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
+# CONFIG_DM9102 is not set
 CONFIG_DE4X5=y
 # CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
@@ -289,7 +284,6 @@
 # CONFIG_LTPC is not set
 # CONFIG_COPS is not set
 # CONFIG_IPDDP is not set
-# CONFIG_PLIP is not set
 CONFIG_PPP=y
 
 #
@@ -312,7 +306,9 @@
 # CONFIG_HOSTESS_SV11 is not set
 # CONFIG_COSA is not set
 # CONFIG_SEALEVEL_4021 is not set
+# CONFIG_COMX is not set
 # CONFIG_DLCI is not set
+# CONFIG_SBNI is not set
 
 #
 # Amateur Radio support
@@ -338,12 +334,16 @@
 CONFIG_FB_CONTROL=y
 CONFIG_FB_PLATINUM=y
 CONFIG_FB_VALKYRIE=y
-CONFIG_FB_ATY=y
+# CONFIG_FB_ATY is not set
 CONFIG_FB_IMSTT=y
 CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
-# CONFIG_FB_MATROX is not set
-CONFIG_FB_ATY=y
+CONFIG_FB_MATROX=y
+# CONFIG_FB_MATROX_MILLENIUM is not set
+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_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
@@ -365,13 +365,22 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-# CONFIG_SERIAL is not set
+CONFIG_SERIAL=m
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-# CONFIG_PRINTER is not set
-# CONFIG_MOUSE is not set
+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_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
 CONFIG_NVRAM=y
@@ -392,6 +401,14 @@
 # Ftape, the floppy tape device driver
 #
 # CONFIG_FTAPE is not set
+# CONFIG_FT_NORMAL_DEBUG is not set
+# CONFIG_FT_FULL_DEBUG is not set
+# CONFIG_FT_NO_TRACE is not set
+# CONFIG_FT_NO_TRACE_AT_ALL is not set
+# CONFIG_FT_STD_FDC is not set
+# CONFIG_FT_MACH2 is not set
+# CONFIG_FT_PROBE_FC10 is not set
+# CONFIG_FT_ALT_FDC is not set
 
 #
 # USB drivers - not for the faint of heart
@@ -406,10 +423,10 @@
 # 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_ISO9660_FS=y
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
@@ -475,7 +492,7 @@
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
 # CONFIG_NLS_ISO8859_14 is not set
-CONFIG_NLS_ISO8859_15=y
+# CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
 #
@@ -483,13 +500,43 @@
 #
 CONFIG_SOUND=y
 CONFIG_DMASOUND=y
+# CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_ES1370 is not set
 # CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_MAESTRO is not set
 # CONFIG_SOUND_ESSSOLO1 is not set
 # CONFIG_SOUND_SONICVIBES is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
-# CONFIG_SOUND_OSS is not set
+CONFIG_SOUND_OSS=y
+# CONFIG_SOUND_DMAP is not set
+# CONFIG_SOUND_PAS is not set
+# CONFIG_SOUND_SB is not set
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+CONFIG_SOUND_CS4232=m
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_NM256 is not set
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
 
 #
 # Kernel hacking
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/gemini_defconfig linux.14p6/arch/ppc/gemini_defconfig
--- linux.vanilla/arch/ppc/gemini_defconfig	Thu Jan  1 01:00:00 1970
+++ linux.14p6/arch/ppc/gemini_defconfig	Sat Nov 13 15:14:56 1999
@@ -0,0 +1,380 @@
+#
+# Automatically generated make config: don't edit
+#
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+CONFIG_6xx=y
+# CONFIG_8xx is not set
+# CONFIG_PMAC is not set
+# CONFIG_PREP is not set
+# CONFIG_CHRP is not set
+# CONFIG_ALL_PPC is not set
+# CONFIG_APUS is not set
+CONFIG_GEMINI=y
+# 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=y
+CONFIG_KMOD=y
+CONFIG_PCI=y
+# CONFIG_PCI_QUIRKS is not set
+# CONFIG_PCI_OLD_PROC is not set
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_FB is not set
+# CONFIG_PMAC_PBOOK is not set
+# CONFIG_MAC_KEYBOARD is not set
+# CONFIG_MAC_FLOPPY is not set
+# CONFIG_MAC_SERIAL is not set
+# CONFIG_ADBMOUSE is not set
+# CONFIG_PROC_DEVICETREE is not set
+# CONFIG_TOTALMP is not set
+# CONFIG_BOOTX_TEXT is not set
+# CONFIG_MOTOROLA_HOTSWAP is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_ONLY is not set
+
+#
+# Additional Block Devices
+#
+# 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 is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=y
+CONFIG_SYN_COOKIES=y
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+# CONFIG_CHR_DEV_SG is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_G_NCR5380_PORT is not set
+# CONFIG_SCSI_G_NCR5380_MEM is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_SIM710 is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+CONFIG_SCSI_SYM53C8XX=y
+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_PQS_PDS 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
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_MESH is not set
+# CONFIG_SCSI_MAC53C94 is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MACE is not set
+# CONFIG_BMAC is not set
+CONFIG_NCR885E=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_COMX is not set
+# CONFIG_DLCI is not set
+# CONFIG_SBNI is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+# CONFIG_MOUSE is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_FT_NORMAL_DEBUG is not set
+# CONFIG_FT_FULL_DEBUG is not set
+# CONFIG_FT_NO_TRACE is not set
+# CONFIG_FT_NO_TRACE_AT_ALL is not set
+# CONFIG_FT_STD_FDC is not set
+# CONFIG_FT_MACH2 is not set
+# CONFIG_FT_PROBE_FC10 is not set
+# CONFIG_FT_ALT_FDC is not set
+
+#
+# USB drivers - not for the faint of heart
+#
+# CONFIG_USB is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_FAT_FS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_EFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
+# CONFIG_SUNRPC is not set
+# CONFIG_LOCKD is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_BSD_DISKLABEL is not set
+CONFIG_MAC_PARTITION=y
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_NLS is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/Makefile linux.14p6/arch/ppc/kernel/Makefile
--- linux.vanilla/arch/ppc/kernel/Makefile	Sat Aug 28 20:00:53 1999
+++ linux.14p6/arch/ppc/kernel/Makefile	Sat Nov 13 15:14:56 1999
@@ -40,7 +40,8 @@
 	  pmac_setup.o pmac_support.o \
 	  prep_pci.o pmac_pci.o chrp_pci.o \
 	  residual.o prom.o openpic.o feature.o \
-	  prep_nvram.o open_pic.o i8259.o pmac_pic.o indirect_pci.o
+	  prep_nvram.o open_pic.o i8259.o pmac_pic.o indirect_pci.o \
+	  gemini_pci.o gemini_prom.o gemini_setup.o
 OX_OBJS += chrp_setup.o prep_setup.o
 endif
 endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/chrp_pci.c linux.14p6/arch/ppc/kernel/chrp_pci.c
--- linux.vanilla/arch/ppc/kernel/chrp_pci.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/ppc/kernel/chrp_pci.c	Fri Oct 22 22:26:41 1999
@@ -287,9 +287,6 @@
 	{
 		if ( dev->irq )
 			dev->irq = openpic_to_irq( dev->irq );
-		/* adjust the io_port for the NCR cards for busses other than 0 -- Cort */
-		if ( (dev->bus->number > 0) && (dev->vendor == PCI_VENDOR_ID_NCR) )
-			dev->base_address[0] += (dev->bus->number*0x08000000);
 		/* these need to be absolute addrs for OF and Matrox FB -- Cort */
 		if ( dev->vendor == PCI_VENDOR_ID_MATROX )
 		{
@@ -306,6 +303,10 @@
 			pcibios_write_config_word(dev->bus->number,
 			  dev->devfn, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD);
 		}
+		if ( (dev->bus->number > 0) &&
+		     ((dev->vendor == PCI_VENDOR_ID_NCR) ||
+		      (dev->vendor == PCI_VENDOR_ID_AMD)))
+			dev->base_address[0] += (dev->bus->number*0x08000000);
 	}
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/chrp_setup.c linux.14p6/arch/ppc/kernel/chrp_setup.c
--- linux.vanilla/arch/ppc/kernel/chrp_setup.c	Sat Aug 28 20:00:53 1999
+++ linux.14p6/arch/ppc/kernel/chrp_setup.c	Sat Nov 13 15:14:56 1999
@@ -446,6 +446,7 @@
 			(*(unsigned long *)get_property(np,
 							"8259-interrupt-acknowledge", NULL));
 	}
+	open_pic.irq_offset = 16;
 	for ( i = 16 ; i < NR_IRQS ; i++ )
 		irq_desc[i].ctl = &open_pic;
 	/* openpic knows that it's at irq 16 offset
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/gemini_pci.c linux.14p6/arch/ppc/kernel/gemini_pci.c
--- linux.vanilla/arch/ppc/kernel/gemini_pci.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/arch/ppc/kernel/gemini_pci.c	Sat Nov 13 15:14:56 1999
@@ -0,0 +1,273 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+
+#include <asm/machdep.h>
+#include <asm/gemini.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "pci.h"
+
+#define pci_config_addr(bus,dev,offset) \
+        (0x80000000 | (bus<<16) | (dev<<8) | offset)
+
+
+int
+gemini_pcibios_read_config_byte(unsigned char bus, unsigned char dev,
+                                unsigned char offset, unsigned char *val)
+{
+	unsigned long reg;
+	reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3))));
+	*val = ((reg >> ((offset & 0x3) << 3)) & 0xff);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+gemini_pcibios_read_config_word(unsigned char bus, unsigned char dev,
+                                unsigned char offset, unsigned short *val)
+{
+	unsigned long reg;
+	reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3))));
+	*val = ((reg >> ((offset & 0x3) << 3)) & 0xffff);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+gemini_pcibios_read_config_dword(unsigned char bus, unsigned char dev,
+                                 unsigned char offset, unsigned int *val)
+{
+	*val = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3))));
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+gemini_pcibios_write_config_byte(unsigned char bus, unsigned char dev,
+                                 unsigned char offset, unsigned char val)
+{
+	unsigned long reg;
+	int shifts = offset & 0x3;
+
+	reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3))));
+	reg = (reg & ~(0xff << (shifts << 3))) | (val << (shifts << 3));
+	grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), reg );
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+gemini_pcibios_write_config_word(unsigned char bus, unsigned char dev,
+                                 unsigned char offset, unsigned short val)
+{
+	unsigned long reg;
+	int shifts = offset & 0x3;
+
+	reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3))));
+	reg = (reg & ~(0xffff << (shifts << 3))) | (val << (shifts << 3));
+	grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), reg );
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+gemini_pcibios_write_config_dword(unsigned char bus, unsigned char dev,
+                                  unsigned char offset, unsigned int val)
+{
+	grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), val );
+	return PCIBIOS_SUCCESSFUL;
+}
+
+struct gemini_device {
+	unsigned short vendor, device;
+	unsigned char irq;
+	unsigned short cmd;
+	unsigned char cache_line, latency;
+	void (*init)(struct pci_dev *dev);
+};
+
+static struct gemini_device gemini_map[] = {
+	{ PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C885, 11, 0x15, 32, 248, NULL },
+	{ PCI_VENDOR_ID_NCR, 0x701, 10, 0, 0, 0, NULL },
+	{ PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C042, 3, 0, 0, 0, NULL },
+	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_MPIC, 0xff, 0, 0, 0, NULL },
+	{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_670, 0xff, 0, 0, 0, NULL },
+	{ PCI_VENDOR_ID_MOTOROLA, PCI_DEVICE_ID_MOTOROLA_MPC106, 0xff, 0, 0, 0, NULL },
+};
+
+static int gemini_map_count = (sizeof( gemini_map ) /
+                               sizeof( gemini_map[0] ));
+
+ 
+
+/*  This just sets up the known devices on the board. */
+__initfunc(static void mapin_device( struct pci_dev *dev ))
+{
+	struct gemini_device *p;
+	unsigned short cmd;
+	int i;
+  
+
+	for( i=0; i < gemini_map_count; i++ ) {
+		p = &(gemini_map[i]);
+    
+		if ( p->vendor == dev->vendor && 
+		     p->device == dev->device ) {
+     
+			if (p->irq != 0xff) {
+				pci_write_config_byte( dev, PCI_INTERRUPT_LINE, p->irq );
+				dev->irq = p->irq;
+			}
+
+			if (p->cmd) {
+				pci_read_config_word( dev, PCI_COMMAND, &cmd );
+				pci_write_config_word( dev, PCI_COMMAND, (p->cmd|cmd));
+			}
+
+			if (p->cache_line)
+				pci_write_config_byte( dev, PCI_CACHE_LINE_SIZE, p->cache_line );
+ 
+			if (p->latency)
+				pci_write_config_byte( dev, PCI_LATENCY_TIMER, p->latency );
+		}
+	}
+}
+
+#define KB 1024
+#define MB (KB*KB)
+
+#define ALIGN(val,align) (((val) + ((align) -1))&(~((align) -1)))
+#define MAX(a,b)   (((a) > (b)) ? (a) : (b))
+
+#define FIRST_IO_ADDR 0x10000
+#define FIRST_MEM_ADDR 0x02000000
+
+#define GEMINI_PCI_MEM_BASE (0xf0000000)
+#define GEMINI_PCI_IO_BASE  (0xfe800000)
+
+static unsigned long pci_mem_base = GEMINI_PCI_MEM_BASE;
+static unsigned long pci_io_base = GEMINI_PCI_IO_BASE;
+
+static unsigned int io_base = FIRST_IO_ADDR;
+static unsigned int mem_base = FIRST_MEM_ADDR;
+
+
+
+__initfunc(static void layout_dev( struct pci_dev *dev ))
+{
+	int i;
+	struct pci_bus *bus;
+	unsigned short cmd;
+	unsigned int reg, base, mask, size, alignto, type;
+
+	bus = dev->bus;
+
+	/* make any known settings happen */
+	mapin_device( dev );
+
+	gemini_pcibios_read_config_word( bus->number, dev->devfn, PCI_COMMAND, &cmd );
+
+	for( reg = PCI_BASE_ADDRESS_0, i=0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++ ) {
+    
+		/* MPIC already done */
+		if (dev->vendor == PCI_VENDOR_ID_IBM &&
+		    dev->device == PCI_DEVICE_ID_IBM_MPIC)
+			return;
+
+		gemini_pcibios_write_config_dword( bus->number, dev->devfn, reg, 0xffffffff );
+		gemini_pcibios_read_config_dword( bus->number, dev->devfn, reg, &base );
+		if (!base) {
+			dev->base_address[i] = 0;
+			continue;
+		}
+
+		if (base & PCI_BASE_ADDRESS_SPACE_IO) {
+			cmd |= PCI_COMMAND_IO;
+			base &= PCI_BASE_ADDRESS_IO_MASK;
+			mask = (~base << 1) | 0x1;
+			size = (mask & base) & 0xffffffff;
+			alignto = MAX(0x400, size);
+			base = ALIGN(io_base, alignto);
+			io_base = base + size;
+			gemini_pcibios_write_config_dword( bus->number, dev->devfn, reg, 
+							   ((pci_io_base + base) & 0x00ffffff) | 0x1);
+			dev->base_address[i] = (pci_io_base + base) | 0x1;
+		}
+
+		else {
+			cmd |= PCI_COMMAND_MEMORY;
+			type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+			mask = (~base << 1) | 0x1;
+			size = (mask & base) & 0xffffffff;
+			switch( type ) {
+
+			case PCI_BASE_ADDRESS_MEM_TYPE_32:
+				break;
+			case PCI_BASE_ADDRESS_MEM_TYPE_64:
+				printk("Warning: Ignoring 64-bit device; slot %d, function %d.\n",
+				       PCI_SLOT( dev->devfn ), PCI_FUNC( dev->devfn ));
+				reg += 4;
+				continue;
+			}
+
+			alignto = MAX(0x1000, size);
+			base = ALIGN(mem_base, alignto);
+			mem_base = base + size;
+			gemini_pcibios_write_config_dword( bus->number, dev->devfn,
+							   reg, (pci_mem_base + base));
+			dev->base_address[i] = pci_mem_base + base;
+		}
+	}
+
+	if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+		cmd |= PCI_COMMAND_IO;
+
+	gemini_pcibios_write_config_word( bus->number, dev->devfn, PCI_COMMAND,
+					  (cmd|PCI_COMMAND_MASTER));
+
+}
+
+__initfunc(static void layout_bus( struct pci_bus *bus ))
+{
+	struct pci_dev *dev;
+
+	if (!bus->devices && !bus->children)
+		return;
+
+	io_base = ALIGN(io_base, 4*KB);
+	mem_base = ALIGN(mem_base, 4*KB);
+
+	for( dev = bus->devices; dev; dev = dev->sibling ) {
+		if (((dev->class >> 16) != PCI_BASE_CLASS_BRIDGE) ||
+		    ((dev->class >> 8) == PCI_CLASS_BRIDGE_OTHER))
+			layout_dev( dev );
+	}
+}
+
+
+__initfunc(void gemini_pcibios_fixup(void))
+{
+	struct pci_bus *bus;
+	unsigned long orig_mem_base, orig_io_base;
+
+	orig_mem_base = pci_mem_base;
+	orig_io_base = pci_io_base;
+
+	for( bus = &pci_root; bus; bus = bus->children )
+		layout_bus( bus );
+
+	pci_mem_base = orig_mem_base;
+	pci_io_base = orig_io_base;
+
+
+}
+
+decl_config_access_method(gemini);
+
+/* The "bootloader" for Synergy boards does none of this for us, so we need to
+   lay it all out ourselves... --Dan */
+__initfunc(void gemini_setup_pci_ptrs(void))
+{
+
+	set_config_access_method(gemini);
+	ppc_md.pcibios_fixup = gemini_pcibios_fixup;
+}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/gemini_prom.S linux.14p6/arch/ppc/kernel/gemini_prom.S
--- linux.vanilla/arch/ppc/kernel/gemini_prom.S	Thu Jan  1 01:00:00 1970
+++ linux.14p6/arch/ppc/kernel/gemini_prom.S	Sat Nov 13 15:14:56 1999
@@ -0,0 +1,94 @@
+/*
+ *  arch/ppc/kernel/gemini_prom.S
+ *
+ *  Not really prom support code (yet), but sort of anti-prom code.  The current
+ *  bootloader does a number of things it shouldn't and doesn't do things that it
+ *  should.  The stuff in here is mainly a hodge-podge collection of setup code
+ *  to get the board up and running.
+ *    ---Dan
+ */
+
+#include "ppc_asm.tmpl"
+#include "ppc_defs.h"
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/gemini.h>
+
+#define HID0_ABE (1<<3)
+
+/*
+ *  On 750's the MMU is on when Linux is booted, so we need to clear out the
+ *  bootloader's BAT settings, make sure we're in supervisor state (gotcha!),
+ *  and turn off the MMU.
+ *
+ */
+
+_GLOBAL(gemini_prom_init)
+#ifdef __SMP__
+	/* Since the MMU's on, get stuff in rom space that we'll need */
+	lis	r4,GEMINI_CPUSTAT@h
+	ori	r4,r4,GEMINI_CPUSTAT@l
+	lbz	r5,0(r4)
+	andi.	r5,r5,3
+	mr	r24,r5		/* cpu # used later on */
+#endif
+	mfmsr	r4
+	li	r3,MSR_PR	/* ensure supervisor! */
+	ori	r3,r3,MSR_IR|MSR_DR
+	andc	r4,r4,r3
+	mtmsr	r4
+#if 0
+	/* zero out the bats now that the MMU is off */
+prom_no_mmu:	
+	li	r3,0
+        mtspr   IBAT0U,r3
+        mtspr   IBAT0L,r3
+        mtspr   IBAT1U,r3
+        mtspr   IBAT1L,r3
+        mtspr   IBAT2U,r3
+        mtspr   IBAT2L,r3
+        mtspr   IBAT3U,r3
+        mtspr   IBAT3L,r3
+
+        mtspr   DBAT0U,r3
+        mtspr   DBAT0L,r3
+        mtspr   DBAT1U,r3
+        mtspr   DBAT1L,r3
+        mtspr   DBAT2U,r3
+	mtspr   DBAT2L,r3
+        mtspr   DBAT3U,r3
+        mtspr   DBAT3L,r3
+#endif
+
+	/* the bootloader (as far as I'm currently aware) doesn't mess with page
+	   tables, but since we're already here, might as well zap these, too */
+	li	r4,0
+	mtspr	SDR1,r4
+
+	li	r4,16
+	mtctr	r4
+	li	r3,0
+	li	r4,0
+3:	mtsrin	r3,r4
+	addi	r3,r3,1
+	bdnz	3b
+
+#ifdef __SMP__
+	/* The 750 book (and Mot/IBM support) says that this will "assist" snooping
+	   when in SMP.  Not sure yet whether this should stay or leave... */
+	mfspr	r4,HID0
+	ori	r4,r4,HID0_ABE
+	mtspr	HID0,r4
+	sync
+#endif /* __SMP__ */
+	blr
+
+/*  apparently, SMon doesn't pay attention to HID0[SRST].  Disable the MMU and
+    branch to 0xfff00100 */
+_GLOBAL(_gemini_reboot)
+	lis	r5,GEMINI_BOOT_INIT@h
+	ori	r5,r5,GEMINI_BOOT_INIT@l
+	li	r6,MSR_IP
+	mtspr	SRR0,r5
+	mtspr	SRR1,r6
+	rfi
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/gemini_setup.c linux.14p6/arch/ppc/kernel/gemini_setup.c
--- linux.vanilla/arch/ppc/kernel/gemini_setup.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/arch/ppc/kernel/gemini_setup.c	Sat Nov 13 15:14:56 1999
@@ -0,0 +1,582 @@
+/*
+ *  linux/arch/ppc/kernel/setup.c
+ *
+ *  Copyright (C) 1995 Linus Torvalds
+ *  Adapted from 'alpha' version by Gary Thomas
+ *  Modified by Cort Dougan (cort@cs.nmt.edu)
+ *  Synergy Microsystems board support by Dan Cox (dan@synergymicro.com)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h> 
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+#include <linux/console.h>
+#include <linux/openpic.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/m48t35.h>
+#include <asm/gemini.h>
+
+#include "time.h"
+#include "local_irq.h"
+#include "open_pic.h"
+
+void gemini_setup_pci_ptrs(void);
+
+static int l2_printed = 0;
+static unsigned char gemini_switch_map = 0;
+static char *gemini_board_families[] = {
+	"VGM", "VSS", "KGM", "VGR", "KSS"
+};
+
+static char *gemini_memtypes[] = {
+	"EDO DRAM, 60nS", "SDRAM, 15nS, CL=2", "SDRAM, 15nS, CL=2 with ECC"
+};
+
+static unsigned int cpu_7xx[16] = {
+	0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0
+};
+static unsigned int cpu_6xx[16] = {
+	0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0
+};
+
+void
+gemini_do_IRQ(struct pt_regs *regs, int cpu, int isfake)
+{
+	int irq, openpic_eoi_done = 0;
+	unsigned long bits = 0;
+	
+#ifdef __SMP__
+	if (cpu != 0) {
+		if (!isfake) {
+			smp_message_recv();
+			goto out;
+		}
+		goto out;
+	}
+	
+	{
+		unsigned int loops = 1000000;
+		while(test_bit(0, &global_irq_lock)) {
+			if (smp_processor_id() == global_irq_holder) {
+				printk("uh oh, it chokes!!! interrupt while we hold irq lock\n");
+				break;
+			}
+			if (loops-- == 0) {
+				printk("do_IRQ waiting for irq lock (holder=%d)\n", 
+				       global_irq_holder);
+			}
+		}
+	}
+#endif
+
+	irq = openpic_irq(cpu);
+	
+	if (irq == OPENPIC_VEC_SPURIOUS) {
+		ppc_spurious_interrupts++;
+		openpic_eoi_done = 1;
+		goto out;
+	}
+	
+	bits = 1UL<<irq;
+	irq -= OPENPIC_VEC_SOURCE;
+	
+	if (irq < 0) {
+		printk(KERN_DEBUG "Bogus interrupt %d from pc=%lx\n", irq,
+		       regs->nip);
+		ppc_spurious_interrupts++;
+	}
+	
+	else
+		ppc_irq_dispatch_handler(regs, irq);
+ out:
+	if (!openpic_eoi_done)
+		openpic_eoi(0);
+}
+
+static inline unsigned long _get_HID1(void)
+{
+	unsigned long val;
+
+	__asm__ __volatile__("mfspr %0,1009" : "=r" (val));
+	return val;
+}
+
+int
+gemini_get_cpuinfo(char *buffer)
+{
+	int i, len;
+	unsigned char reg, rev;
+	char *family;
+	unsigned int type;
+
+	reg = readb(GEMINI_FEAT);
+	family = gemini_board_families[((reg>>4) & 0xf)];
+	if (((reg>>4) & 0xf) > 2)
+		printk(KERN_ERR "cpuinfo(): unable to determine board family\n");
+
+	reg = readb(GEMINI_BREV);
+	type = (reg>>4) & 0xf;
+	rev = reg & 0xf;
+
+	reg = readb(GEMINI_BECO);
+
+	len = sprintf( buffer, "machine\t\t: Gemini %s%d, rev %c, eco %d\n", 
+		       family, type, (rev + 'A'), (reg & 0xf));
+
+	len += sprintf( buffer+len, "vendor\t\t: %s\n", 
+			(_get_PVR() & (1<<15)) ? "IBM" : "Motorola");
+
+	reg = readb(GEMINI_MEMCFG);
+	len += sprintf( buffer+len, "memory type\t: %s\n", 
+			gemini_memtypes[(reg & 0xc0)>>6]);
+	len += sprintf( buffer+len, "switches on\t: ");
+	for( i=0; i < 8; i++ ) {
+		if ( gemini_switch_map & (1<<i))
+			len += sprintf(buffer+len, "%d ", i);
+	}
+	len += sprintf(buffer+len, "\n");
+
+	return len;
+}
+
+static u_char gemini_openpic_initsenses[] = {
+	1,
+	1,
+	1,
+	1,
+	0,
+	0,
+	1, /* remainder are level-triggered */
+};
+
+#define GEMINI_MPIC_ADDR (0xfcfc0000)
+#define GEMINI_MPIC_PCI_CFG (0x80005800)
+
+void __init gemini_openpic_init(void)
+{
+	grackle_write(GEMINI_MPIC_PCI_CFG + PCI_BASE_ADDRESS_0, 
+		      GEMINI_MPIC_ADDR);
+	grackle_write(GEMINI_MPIC_PCI_CFG + PCI_COMMAND, PCI_COMMAND_MEMORY);
+
+	OpenPIC = (volatile struct OpenPIC *) GEMINI_MPIC_ADDR;
+	OpenPIC_InitSenses = gemini_openpic_initsenses;
+	OpenPIC_NumInitSenses = sizeof( gemini_openpic_initsenses );
+
+	ioremap( GEMINI_MPIC_ADDR, sizeof( struct OpenPIC ));
+}
+
+
+extern unsigned long loops_per_sec;
+extern int root_mountflags;
+extern char cmd_line[];
+
+
+void __init
+gemini_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+{
+	unsigned int cpu;
+	extern char cmd_line[];
+
+
+	loops_per_sec = 50000000;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+	/* bootable off CDROM */
+	if (initrd_start)
+		ROOT_DEV = MKDEV(SCSI_CDROM_MAJOR, 0);
+	else
+#endif
+		ROOT_DEV = to_kdev_t(0x0801);
+
+	/* nothing but serial consoles... */  
+	sprintf(cmd_line, "%s console=ttyS0", cmd_line);
+
+
+	/* The user switches on the front panel can be used as follows:
+
+	   Switch 0 - adds "debug" to the command line for verbose boot info,
+	   Switch 7 - boots in single-user mode 
+
+	*/
+
+	gemini_switch_map = readb( GEMINI_USWITCH );
+  
+	if ( gemini_switch_map & (1<<GEMINI_SWITCH_VERBOSE))
+		sprintf(cmd_line, "%s debug", cmd_line);
+
+	if ( gemini_switch_map & (1<<GEMINI_SWITCH_SINGLE_USER))
+		sprintf(cmd_line, "%s single", cmd_line);
+
+	printk("Boot arguments: %s\n", cmd_line);
+
+	/* mutter some kind words about who made the CPU */
+	cpu = _get_PVR();
+	printk("CPU manufacturer: %s [rev=%04x]\n", (cpu & (1<<15)) ? "IBM" :
+	       "Motorola", (cpu & 0xffff));
+
+	/* take special pains to map the MPIC, since it isn't mapped yet */
+	gemini_openpic_init();
+
+	/* start the L2 */
+	gemini_init_l2();
+
+}
+
+
+int
+gemini_get_clock_speed(void)
+{
+	unsigned long hid1;
+	int clock;
+	unsigned char reg;
+  
+	hid1 = _get_HID1();
+	if ((_get_PVR()>>16) == 8)
+		hid1 = cpu_7xx[hid1];
+	else
+		hid1 = cpu_6xx[hid1];
+
+	reg = readb(GEMINI_BSTAT) & 0xc0;
+
+	switch( reg >> 2 ) {
+
+	case 0:
+	default:
+		clock = (hid1*100)/3;
+		break;
+  
+	case 1:
+		clock = (hid1*125)/3;
+		break;
+  
+	case 2:
+		clock = (hid1*50)/3;
+		break;
+	}
+
+	return clock;
+}
+
+
+#define L2CR_PIPE_LATEWR   (0x01800000)   /* late-write SRAM */
+#define L2CR_L2CTL         (0x00100000)   /* RAM control */
+#define L2CR_INST_DISABLE  (0x00400000)   /* disable for insn's */
+#define L2CR_L2I           (0x00200000)   /* global invalidate */
+#define L2CR_L2E           (0x80000000)   /* enable */
+#define L2CR_L2WT          (0x00080000)   /* write-through */
+
+void __init gemini_init_l2(void)
+{
+	unsigned char reg;
+	unsigned long cache;
+	int speed;
+
+	reg = readb(GEMINI_L2CFG);
+
+	/* 750's L2 initializes differently from a 604's.  Also note that a Grackle
+	   bug will hang a dual-604 board, so make sure that doesn't happen by not
+	   turning on the L2 */
+	if ( _get_PVR() >> 16 != 8 ) {
+     
+		/* check for dual cpus and cry sadly about the loss of an L2... */
+		if ((( readb(GEMINI_CPUSTAT) & 0x0c ) >> 2) != 1) 
+			printk("Sorry. Your dual-604 does not allow the L2 to be enabled due "
+			       "to a Grackle bug.\n");
+		else if ( reg & GEMINI_L2_SIZE_MASK ) {
+			printk("Enabling 604 L2 cache: %dKb\n", 
+			       (128<<((reg & GEMINI_L2_SIZE_MASK)>>6)));
+			writeb( 1, GEMINI_L2CFG );
+		}
+	}
+
+	/* do a 750 */
+	else {
+		/* Synergy's first round of 750 boards had the L2 size stuff into the
+		   board register above.  If it's there, it's used; if not, the
+		   standard default is 1Mb.  The L2 type, I'm told, is "most likely
+		   probably always going to be late-write".  --Dan */
+
+		if (reg & 0xc0) {
+			if (!l2_printed) {
+				printk("Enabling 750 L2 cache: %dKb\n", 
+				       (128 << ((reg & 0xc0)>>6)));
+				l2_printed=1;
+			}
+    
+			/* take the size given */
+			cache = (((reg>>6) & 0x3)<<28);
+		}
+		else
+			/* default of 1Mb */
+			cache = 0x3<<28;
+ 
+		reg &= 0x3;
+
+		/* a cache ratio of 1:1 and CPU clock speeds in excess of 300Mhz are bad
+		   things.  If found, tune it down to 1:1.5.  -- Dan */
+		if (!reg) {
+
+			speed = gemini_get_clock_speed();
+   
+			if (speed >= 300) {
+				printk("Warning:  L2 ratio is 1:1 on a %dMhz processor.  Dropping to 1:1.5.\n",
+				       speed );
+				printk("Contact Synergy Microsystems for an ECO to fix this problem\n");
+				reg = 0x1;
+			}
+		}
+
+		/* standard stuff */
+		cache |= ((1<<reg)<<25);
+#ifdef __SMP__
+		/* A couple errata for the 750's (both IBM and Motorola silicon)
+		   note that you can get missed cache lines on MP implementations.
+		   The workaround - if you call it that - is to make the L2
+		   write-through.  This is fixed in IBM's 3.1 rev (I'm told), but
+		   for now, always make 2.x versions use L2 write-through. --Dan */
+		if (((_get_PVR()>>8) & 0xf) <= 2)
+			cache |= L2CR_L2WT;
+#endif
+		cache |= L2CR_PIPE_LATEWR|L2CR_L2CTL|L2CR_INST_DISABLE;
+		_set_L2CR(0);
+		_set_L2CR(cache|L2CR_L2I|L2CR_L2E);
+	}
+}
+
+void
+gemini_restart(char *cmd)
+{
+	__cli();
+	/* make a clean restart, not via the MPIC */
+	_gemini_reboot();
+	for(;;);
+}
+
+void
+gemini_power_off(void)
+{
+	for(;;);
+}
+
+void
+gemini_halt(void)
+{
+	gemini_restart(NULL);
+}
+
+void __init gemini_init_IRQ(void)
+{
+	int i;
+
+	/* gemini has no 8259 */
+	open_pic.irq_offset = 0;
+	for( i=0; i < OPENPIC_VEC_SPURIOUS; i++ ) 
+		irq_desc[i].ctl = &open_pic;
+	openpic_init(1);
+#ifdef __SMP__
+	request_irq(OPENPIC_VEC_IPI, openpic_ipi_action, 0, "IPI0", 0);
+#endif	/* __SMP__ */
+}
+
+#define gemini_rtc_read(x)       (readb(GEMINI_RTC+(x)))
+#define gemini_rtc_write(val,x)  (writeb((val),(GEMINI_RTC+(x))))
+
+/* ensure that the RTC is up and running */
+void __init gemini_time_init(void)
+{
+	unsigned char reg;
+
+	reg = gemini_rtc_read(M48T35_RTC_CONTROL);
+
+	if ( reg & M48T35_RTC_STOPPED ) {
+		printk(KERN_INFO "M48T35 real-time-clock was stopped. Now starting...\n");
+		gemini_rtc_write((reg & ~(M48T35_RTC_STOPPED)), M48T35_RTC_CONTROL);
+		gemini_rtc_write((reg | M48T35_RTC_SET), M48T35_RTC_CONTROL);
+	}
+}
+
+#undef DEBUG_RTC
+
+unsigned long
+gemini_get_rtc_time(void)
+{
+	unsigned int year, mon, day, hour, min, sec;
+	unsigned char reg;
+
+	reg = gemini_rtc_read(M48T35_RTC_CONTROL);
+	gemini_rtc_write((reg|M48T35_RTC_READ), M48T35_RTC_CONTROL);
+#ifdef DEBUG_RTC
+	printk("get rtc: reg = %x\n", reg);
+#endif
+  
+	do {
+		sec = gemini_rtc_read(M48T35_RTC_SECONDS);
+		min = gemini_rtc_read(M48T35_RTC_MINUTES);
+		hour = gemini_rtc_read(M48T35_RTC_HOURS);
+		day = gemini_rtc_read(M48T35_RTC_DOM);
+		mon = gemini_rtc_read(M48T35_RTC_MONTH);
+		year = gemini_rtc_read(M48T35_RTC_YEAR);
+	} while( sec != gemini_rtc_read(M48T35_RTC_SECONDS));
+#ifdef DEBUG_RTC
+	printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", 
+	       sec, min, hour, day, mon, year);
+#endif
+
+	gemini_rtc_write(reg, M48T35_RTC_CONTROL);
+
+	BCD_TO_BIN(sec);
+	BCD_TO_BIN(min);
+	BCD_TO_BIN(hour);
+	BCD_TO_BIN(day);
+	BCD_TO_BIN(mon);
+	BCD_TO_BIN(year);
+
+	if ((year += 1900) < 1970)
+		year += 100;
+#ifdef DEBUG_RTC
+	printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", 
+	       sec, min, hour, day, mon, year);
+#endif
+
+	return mktime( year, mon, day, hour, min, sec );
+}
+
+
+int
+gemini_set_rtc_time( unsigned long now )
+{
+	unsigned char reg;
+	struct rtc_time tm;
+
+	to_tm( now, &tm );
+
+	reg = gemini_rtc_read(M48T35_RTC_CONTROL);
+#if DEBUG_RTC
+	printk("set rtc: reg = %x\n", reg);
+#endif
+  
+	gemini_rtc_write((reg|M48T35_RTC_SET), M48T35_RTC_CONTROL);
+#if DEBUG_RTC
+	printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n",
+	       tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year);
+#endif
+  
+	tm.tm_year -= 1900;
+	BIN_TO_BCD(tm.tm_sec);
+	BIN_TO_BCD(tm.tm_min);
+	BIN_TO_BCD(tm.tm_hour);
+	BIN_TO_BCD(tm.tm_mon);
+	BIN_TO_BCD(tm.tm_mday);
+	BIN_TO_BCD(tm.tm_year);
+#ifdef DEBUG_RTC
+	printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n",
+	       tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year);
+#endif
+
+	gemini_rtc_write(tm.tm_sec, M48T35_RTC_SECONDS);
+	gemini_rtc_write(tm.tm_min, M48T35_RTC_MINUTES);
+	gemini_rtc_write(tm.tm_hour, M48T35_RTC_HOURS);
+	gemini_rtc_write(tm.tm_mday, M48T35_RTC_DOM);
+	gemini_rtc_write(tm.tm_mon, M48T35_RTC_MONTH);
+	gemini_rtc_write(tm.tm_year, M48T35_RTC_YEAR);
+
+	/* done writing */
+	gemini_rtc_write(reg, M48T35_RTC_CONTROL);
+
+	if ((time_state == TIME_ERROR) || (time_state == TIME_BAD))
+		time_state = TIME_OK;
+  
+	return 0;
+}
+
+/*  use the RTC to determine the decrementer count */
+void __init gemini_calibrate_decr(void)
+{
+	int freq, divisor;
+	unsigned char reg;
+
+	/* determine processor bus speed */
+	reg = readb(GEMINI_BSTAT);
+
+	switch(((reg & 0x0c)>>2)&0x3) {
+	case 0:
+	default:
+		freq = 66;
+		break;
+	case 1:
+		freq = 83;
+		break;
+	case 2:
+		freq = 100;
+		break;
+	}
+
+	freq *= 1000000;
+	divisor = 4;
+	decrementer_count = freq / HZ / divisor;
+	count_period_num = divisor;
+	count_period_den = freq / 1000000;
+}
+
+
+void __init gemini_init(unsigned long r3, unsigned long r4, unsigned long r5,
+			unsigned long r6, unsigned long r7)
+{
+	void layout_bus( struct pci_bus * );
+
+	gemini_setup_pci_ptrs();
+
+	ISA_DMA_THRESHOLD = 0;
+	DMA_MODE_READ = 0;
+	DMA_MODE_WRITE = 0;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+	if ( r4 )
+	{
+		initrd_start = r4 + KERNELBASE;
+		initrd_end = r5 + KERNELBASE;
+	}
+#endif
+
+	ppc_md.setup_arch = gemini_setup_arch;
+	ppc_md.setup_residual = NULL;
+	ppc_md.get_cpuinfo = gemini_get_cpuinfo;
+	ppc_md.irq_cannonicalize = NULL;
+	ppc_md.init_IRQ = gemini_init_IRQ;
+	ppc_md.do_IRQ = gemini_do_IRQ;
+	ppc_md.init = NULL;
+
+	ppc_md.restart = gemini_restart;
+	ppc_md.power_off = gemini_power_off;
+	ppc_md.halt = gemini_halt;
+
+	ppc_md.time_init = gemini_time_init;
+	ppc_md.set_rtc_time = gemini_set_rtc_time;
+	ppc_md.get_rtc_time = gemini_get_rtc_time;
+	ppc_md.calibrate_decr = gemini_calibrate_decr;
+
+	/* no keyboard/mouse/video stuff yet.. */
+	ppc_md.kbd_setkeycode = NULL;
+	ppc_md.kbd_getkeycode = NULL;
+	ppc_md.kbd_translate = NULL;
+	ppc_md.kbd_unexpected_up = NULL;
+	ppc_md.kbd_leds = NULL;
+	ppc_md.kbd_init_hw = NULL;
+#ifdef CONFIG_MAGIC_SYSRQ
+	ppc_md.ppc_kbd_sysrq_xlate = NULL;
+#endif
+}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/head.S linux.14p6/arch/ppc/kernel/head.S
--- linux.vanilla/arch/ppc/kernel/head.S	Sat Aug 28 20:00:53 1999
+++ linux.14p6/arch/ppc/kernel/head.S	Sat Nov 13 15:14:56 1999
@@ -1,7 +1,7 @@
 /*
  *  arch/ppc/kernel/head.S
  *
- *  $Id: head.S,v 1.130.2.3 1999/08/10 21:36:48 cort Exp $
+ *  $Id: head.S,v 1.130.2.6 1999/10/12 01:03:34 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -97,21 +97,12 @@
 	bdnz	0b
 #endif
 
-#ifdef CONFIG_PPC64
+/* 601 only have IBAT cr0.eq is set on 601 when using this macro */ 
 #define LOAD_BAT(n, offset, reg, RA, RB) \
-	ld	RA,offset+0(reg);	\
-	ld	RB,offset+8(reg);	\
+	/* see the comment for clear_bats() -- Cort */ \
+	li	RA,0;			\
 	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	RB,offset+4(reg);	\
 	mtspr	IBAT##n##U,RA;		\
@@ -122,7 +113,6 @@
 	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
@@ -149,7 +139,15 @@
 	.text
 	.globl	_start
 _start:
-	.long	TOPHYS(__start),0,0
+	/* 
+	 * These are here for legacy reasons, the kernel used to
+	 * need to look like a coff function entry for the pmac
+	 * but we're always started by some kind of bootloader now.
+	 *  -- Cort
+	 */
+	nop
+	nop
+	nop
 
 /* PMAC
  * Enter here with the kernel text, data and bss loaded starting at
@@ -220,16 +218,6 @@
 	
 	.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
@@ -251,7 +239,6 @@
  * 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
@@ -265,7 +252,6 @@
 	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 */
@@ -277,14 +263,15 @@
 #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 */
+	/*
+	 * If the MMU is off clear the bats.  See clear_bat() -- Cort
+	 */
+#ifndef CONFIG_GEMINI	
+	mfmsr	r20
+	andi.	r20,r20,MSR_DR
+	bne	100f
+	bl	clear_bats
+100:
 	/* 
 	 * allow secondary cpus to get at all of ram in early bootup
 	 * since their init_task may be up there -- Cort
@@ -302,14 +289,13 @@
 	mtspr	DBAT2U,r21		/* bit in upper BAT register */
 	mtspr	IBAT2L,r18
 	mtspr	IBAT2U,r21
-#endif /* CONFIG_PPC64 */
+#endif /* ndef CONFIG_GEMINI */	
 #endif
 	mtspr	DBAT0L,r8		/* N.B. 6xx (not 601) have valid */
 	mtspr	DBAT0U,r11		/* bit in upper BAT register */
 	mtspr	IBAT0L,r8
 	mtspr	IBAT0U,r11
 5:	isync
-
 #ifdef CONFIG_APUS
 	/* Unfortunately the APUS specific instructions bloat the
 	 * code so it cannot fit in the 0x100 bytes available. We have
@@ -499,7 +485,12 @@
 
 /* System reset */
 #ifdef CONFIG_SMP /* MVME/MTX start the secondary here */
+#ifdef CONFIG_GEMINI
+	. = 0x100
+	b	__secondary_start_gemini
+#else /* CONFIG_GEMINI */
 	STD_EXCEPTION(0x100, Reset, __secondary_start_psurge)
+#endif /* CONFIG_GEMINI */
 #else
 	STD_EXCEPTION(0x100, Reset, UnknownException)
 #endif	
@@ -1269,6 +1260,7 @@
 	.globl	hash_page
 hash_page:
 #ifdef __SMP__
+	SAVE_2GPRS(7,r21)
 	eieio
 	lis	r2,hash_table_lock@h
 	ori	r2,r2,hash_table_lock@l
@@ -1282,10 +1274,18 @@
 	bne-	12f
 	stwcx.	r0,0,r2
 	beq+	11f
-12:	cmpw	r6,r0
+	/* spin here a bit */
+12:	mfctr	r7
+	li	r8,1000
+	mtctr	r8
+13:
+	bdnz	13b
+	mtctr	r7
+	cmpw	r6,r0
 	bdnzf	2,10b
 	tw	31,31,31
 11:	eieio
+	REST_2GPRS(7, r21)
 #endif
 	/* Get PTE (linux-style) and check access */
 	lwz	r5,PG_TABLES(r5)		
@@ -1312,7 +1312,6 @@
 #else
 	bnelr-
 #endif
-
 	ori	r6,r6,0x100		/* set _PAGE_ACCESSED in pte */
 	rlwinm	r5,r4,5,24,24		/* _PAGE_RW access -> _PAGE_DIRTY */
 	rlwimi	r5,r4,7,22,22		/* _PAGE_RW -> _PAGE_HWWRITE */
@@ -1320,11 +1319,6 @@
 	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 */
@@ -1332,34 +1326,17 @@
 
 	/* 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 */
@@ -1724,33 +1701,26 @@
 	mfmsr	r0
 	ori	r1,r0,MSR_DR|MSR_IR
 	mtspr	SRR1,r1
-#ifdef CONFIG_SMP
-	/* see the function start_here_ibm_hack for explanation -- Cort */
-	andi.	0,r1,MSR_DR	/* check if the MMU is already on */
-	bne	10f
-	lis	r5,smp_ibm_chrp_hack@h
-	ori	r5,r5,smp_ibm_chrp_hack@l
-	tophys(r5,r5,r6)
-	lwz	r5,0(r5)
-	cmpi	0,r5,0
-	beq	10f
-	lis	r5,first_cpu_booted@h
-	ori	r5,r5,first_cpu_booted@l
-	tophys(r5,r5,r6)
-	lwz	r5,0(r5)
-	cmpi	0,r5,0
-	beq	10f
-	lis	r0,start_here_ibm_hack@h
-	ori	r0,r0,start_here_ibm_hack@l
-	b	1010f
-10:	
-#endif /* CONFIG_SMP */
 	lis	r0,start_here@h
 	ori	r0,r0,start_here@l
-1010:	mtspr	SRR0,r0
+	mtspr	SRR0,r0
 	SYNC
 	rfi				/* enables MMU */
 
+#ifdef CONFIG_GEMINI
+	.globl	__secondary_start_gemini
+__secondary_start_gemini:
+        mfspr   r4,HID0
+        ori     r4,r4,HID0_ICFI
+        li      r3,0
+        ori     r3,r3,HID0_ICE
+        andc    r4,r4,r3
+        mtspr   HID0,r4
+        sync
+        bl      prom_init
+        b       __secondary_start
+#endif /* CONFIG_GEMINI */
+
 #ifdef CONFIG_SMP
 	.globl	__secondary_start_psurge
 __secondary_start_psurge:
@@ -1780,25 +1750,6 @@
 	blr
 #endif /* CONFIG_SMP */
 	
-/* 
- * We get _strange_ behavior on the new IBM chrp firmware.
- * We end up here with the MMU disabled, even though we enable
- * it with the rfi that takes us here.  Somehow, a physical address
- * access fixes this by enabling the MMU.
- *
- * The IBM engineers can't explain this behavior and AIX doesn't 
- * seem to find it.  This hack gets around it for now.
- *          -- Cort
- */
-start_here_ibm_hack:
-	mfmsr	r1
-	lis	r5,smp_ibm_chrp_hack@h
-	ori	r5,r5,smp_ibm_chrp_hack@l
-	tophys(r5,r5,r6)
-	mfmsr	r1
-	stw	r1,_MSR-16(r5)
-	addi	r5,r5,_MSR-16
-	dcbf	0,r5
 #ifndef CONFIG_8xx
 start_here:
 	/*
@@ -1885,6 +1836,8 @@
 	mr	r6,r28
 	mr	r7,r27
 	bl	identify_machine
+lis r20,0xc0000000@h
+stw	r20,0(r20)	
 	bl	MMU_init
 /*
  * Go back to running unmapped so we can load up new values
@@ -1895,11 +1848,7 @@
  */
 #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
@@ -1928,14 +1877,6 @@
 #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 */
@@ -1952,17 +1893,10 @@
 	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 */
@@ -2733,3 +2667,36 @@
 	.globl	cmd_line
 cmd_line:
 	.space	512
+
+/* 
+ * An undocumented "feature" of 604e requires that the v bit
+ * be cleared before changing BAT values.
+ *
+ * Also, newer IBM firmware does not clear bat3 and 4 so
+ * this makes sure it's done.
+ *  -- Cort 
+ */
+clear_bats:
+	li	r20,0
+	
+	mtspr	DBAT0U,r20
+	mtspr	DBAT0L,r20
+	mtspr	IBAT0U,r20
+	mtspr	IBAT0L,r20
+	
+	mtspr	DBAT1U,r20
+	mtspr	DBAT1L,r20
+	mtspr	IBAT1U,r20
+	mtspr	IBAT1L,r20
+	
+	mtspr	DBAT2U,r20
+	mtspr	DBAT2L,r20
+	mtspr	IBAT2U,r20
+	mtspr	IBAT2L,r20
+	
+	mtspr	DBAT3U,r20
+	mtspr	DBAT3L,r20
+	mtspr	IBAT3U,r20
+	mtspr	IBAT3L,r20
+
+	blr
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/open_pic.c linux.14p6/arch/ppc/kernel/open_pic.c
--- linux.vanilla/arch/ppc/kernel/open_pic.c	Sat Aug 14 02:26:13 1999
+++ linux.14p6/arch/ppc/kernel/open_pic.c	Sat Nov 13 15:14:56 1999
@@ -16,24 +16,24 @@
 
 void chrp_mask_and_ack_irq(unsigned int irq_nr)
 {
-	if (is_8259_irq(irq_nr))
-	    i8259_pic.mask_and_ack(irq_nr);
+	if ((_machine != _MACH_gemini) && is_8259_irq(irq_nr))
+		i8259_pic.mask_and_ack(irq_nr);
 }
 
 static void chrp_mask_irq(unsigned int irq_nr)
 {
-	if (is_8259_irq(irq_nr))
+	if ((_machine != _MACH_gemini) && is_8259_irq(irq_nr))
 		i8259_pic.disable(irq_nr);
 	else
-		openpic_disable_irq(irq_to_openpic(irq_nr));
+		openpic_disable_irq(irq_nr-open_pic.irq_offset);
 }
 
 static void chrp_unmask_irq(unsigned int irq_nr)
 {
-	if (is_8259_irq(irq_nr))
+	if ((_machine != _MACH_gemini) && is_8259_irq(irq_nr))
 		i8259_pic.enable(irq_nr);
 	else
-		openpic_enable_irq(irq_to_openpic(irq_nr));
+		openpic_enable_irq(irq_nr-open_pic.irq_offset);
 }
 
 struct hw_interrupt_type open_pic = {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/openpic.c linux.14p6/arch/ppc/kernel/openpic.c
--- linux.vanilla/arch/ppc/kernel/openpic.c	Sat Aug 14 02:26:13 1999
+++ linux.14p6/arch/ppc/kernel/openpic.c	Sat Nov 13 15:14:56 1999
@@ -241,10 +241,13 @@
 	    
 	    /* Initialize the spurious interrupt */
 	    openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
-	    
-	    if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT,
-			    "82c59 cascade", NULL))
-		    printk("Unable to get OpenPIC IRQ 0 for cascade\n");
+
+	    if ( _machine != _MACH_gemini )
+	    {
+		    if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT,
+				    "82c59 cascade", NULL))
+			    printk("Unable to get OpenPIC IRQ 0 for cascade\n");
+	    }
 	    openpic_set_priority(0, 0);
 	    openpic_disable_8259_pass_through();
     }
@@ -508,4 +511,11 @@
     openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority,
     			    OPENPIC_SENSE_LEVEL,
 			    (sense ? OPENPIC_SENSE_LEVEL : 0));
+}
+
+void openpic_enable_IPI(u_int ipi)
+{
+	check_arg_ipi(ipi);
+	openpic_clearfield(&OpenPIC->Global.IPI_Vector_Priority(ipi),
+			   OPENPIC_MASK);
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/prep_pci.c linux.14p6/arch/ppc/kernel/prep_pci.c
--- linux.vanilla/arch/ppc/kernel/prep_pci.c	Sat Aug 28 20:00:54 1999
+++ linux.14p6/arch/ppc/kernel/prep_pci.c	Sat Nov 13 15:14:57 1999
@@ -170,7 +170,7 @@
 	0,	/* Slot 8  - unused */
 	0,	/* Slot 9  - unused */
 	0,	/* Slot 10 - unxued */
-	0,	/* Slot 11 - unused */
+	0x1e,	/* Slot 11 - PCI-ISA/IDE/USB */
 	0,	/* Slot 12 - unused */
 	0,	/* Slot 13 - unused */
 	2,	/* Slot 14 - Ethernet */
@@ -198,7 +198,7 @@
 	0,      /* Slot 8  - unused */
 	0,      /* Slot 9  - unused */
 	0,      /* Slot 10 - unxued */
-	0,      /* Slot 11 - unused */
+	0x1e,   /* Slot 11 - PCI-ISA/IDE/USB */
 	0,      /* Slot 12 - unused */
 	0,      /* Slot 13 - unused */
 	2,      /* Slot 14 - Ethernet */
@@ -224,7 +224,7 @@
 	0,	/* Slot 8  - unused */
 	0,	/* Slot 9  - unused */
 	0,	/* Slot 10 - unused */
-	0,	/* Slot 11 - unused */
+	0x1e,	/* Slot 11 - PCI-ISA/IDE/USB */
 	3,	/* Slot 12 - SCSI */
 	0,	/* Slot 13 - unused */
 	2,	/* Slot 14 - Ethernet */
@@ -253,7 +253,7 @@
         0,      /* Slot 8  - unused */
         0,      /* Slot 9  - unused */
         0,      /* Slot 10 - unused */
-        0,      /* Slot 11 - unused */
+        0x1e,   /* Slot 11 - PCI-ISA/IDE/USB */
         3,      /* Slot 12 - SCSI */
         0,      /* Slot 13 - unused */
         2,      /* Slot 14 - Ethernet 1 */
@@ -1076,40 +1076,91 @@
 	return;
 }
 
+int motopenpic_to_irq(int n)
+{
+       if (n & 0xF0) {
+               return (n & 0x0F);
+       } else {
+               return(openpic_to_irq(n));
+       }
+}
+
+void prep_pib_init(void)
+{
+unsigned char   reg;
+unsigned short  short_reg;
+
+struct pci_dev *dev = NULL;
+
+	if (( _prep_type == _PREP_Motorola) && (OpenPIC)) {
+		/*
+		 * Perform specific configuration for the Via Tech or
+		 * or Winbond PCI-ISA-Bridge part.
+		 */
+		if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, 
+					PCI_DEVICE_ID_VIA_82C586_1, dev))) {
+			/*
+			 * PPCBUG does not set the enable bits
+			 * for the IDE device. Force them on here.
+			 */
+			pcibios_read_config_byte(dev->bus->number, 
+					dev->devfn, 0x40, &reg);
+
+			reg |= 0x03; /* IDE: Chip Enable Bits */
+			pcibios_write_config_byte(dev->bus->number, 
+					dev->devfn, 0x40, reg);
+
+		} else if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, 
+					PCI_DEVICE_ID_WINBOND_83C553, dev))) {
+			/*
+			 * Clear the PCI Interrupt Routing Control Register.
+			 */
+			short_reg = 0x0000;
+			pcibios_write_config_word(dev->bus->number, 
+					dev->devfn, 0x44, short_reg);
+		}
+	}
+}
 __initfunc(
 void
 prep_pcibios_fixup(void))
 {
-        struct pci_dev *dev;
-        extern unsigned char *Motherboard_map;
-        extern unsigned char *Motherboard_routes;
-        unsigned char i;
-
-        if ( _prep_type == _PREP_Radstone )
-        {
-                printk("Radstone boards require no PCI fixups\n");
+extern unsigned char *Motherboard_map;
+extern unsigned char *Motherboard_routes;
+unsigned char	i;
+struct pci_dev  *dev;
+
+	if (_prep_type == _PREP_Radstone) {
+		printk("Radstone boards require no PCI fixups\n");
 		return;
-        }
+	}
 
 	prep_route_pci_interrupts();
 
+	prep_pib_init();
+
 	printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name);
+
 	if (OpenPIC) {
-		/* PCI interrupts are controlled by the OpenPIC */
-		for(dev=pci_devices; dev; dev=dev->next) {
-			if (dev->bus->number == 0) {
-                       		dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]);
-				pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, dev->irq);
-			} else {
-				if (Motherboard_non0 != NULL)
-					Motherboard_non0(dev);
-			}
+
+	    /* PCI interrupts are controlled by the OpenPIC */
+	    for(dev=pci_devices; dev; dev=dev->next) {
+		if (dev->bus->number == 0) {
+		    dev->irq = 
+		       motopenpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]);
+
+		    pcibios_write_config_byte(dev->bus->number, dev->devfn, 
+					      PCI_INTERRUPT_PIN, dev->irq);
+		} else {
+			if (Motherboard_non0 != NULL)
+				Motherboard_non0(dev);
 		}
-		return;
+
+	    }
+	return;
 	}
 
-	for(dev=pci_devices; dev; dev=dev->next)
-	{
+	for(dev=pci_devices; dev; dev=dev->next) {
 		/*
 		 * Use our old hard-coded kludge to figure out what
 		 * irq this device uses.  This is necessary on things
@@ -1118,29 +1169,26 @@
 		unsigned char d = PCI_SLOT(dev->devfn);
 		dev->irq = Motherboard_routes[Motherboard_map[d]];
 
-		for ( i = 0 ; i <= 5 ; i++ )
-		{
-		        if ( dev->base_address[i] > 0x10000000 )
-		        {
-		                printk("Relocating PCI address %lx -> %lx\n",
-		                       dev->base_address[i],
-		                       (dev->base_address[i] & 0x00FFFFFF)
-		                       | 0x01000000);
-		                dev->base_address[i] =
-		                  (dev->base_address[i] & 0x00FFFFFF) | 0x01000000;
-		                pci_write_config_dword(dev,
-		                        PCI_BASE_ADDRESS_0+(i*0x4),
-		                       dev->base_address[i] );
-		        }
+		for ( i = 0 ; i <= 5 ; i++ ) {
+			if ( dev->base_address[i] > 0x10000000 ) {
+				printk("Relocating PCI address %lx -> %lx\n",
+				dev->base_address[i],
+				(dev->base_address[i] & 0x00FFFFFF)|0x01000000);
+
+				dev->base_address[i] = (dev->base_address[i] & 
+							0x00FFFFFF)|0x01000000;
+				pci_write_config_dword(dev, 
+						PCI_BASE_ADDRESS_0+(i*0x4),
+						dev->base_address[i] );
+			}
 		}
 #if 0
-		/*
-		 * If we have residual data and if it knows about this
-		 * device ask it what the irq is.
-		 *  -- Cort
-		 */
-		ppcd = residual_find_device_id( ~0L, dev->device,
-		                                -1,-1,-1, 0);
+	/*
+	 * If we have residual data and if it knows about this
+	 * device ask it what the irq is.
+	 *  -- Cort
+	 */
+	ppcd = residual_find_device_id( ~0L, dev->device, -1,-1,-1, 0);
 #endif
 	}
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/prep_setup.c linux.14p6/arch/ppc/kernel/prep_setup.c
--- linux.vanilla/arch/ppc/kernel/prep_setup.c	Sat Aug 28 20:00:54 1999
+++ linux.14p6/arch/ppc/kernel/prep_setup.c	Sat Nov 13 15:14:57 1999
@@ -628,25 +628,36 @@
 void
 prep_ide_insw(ide_ioreg_t port, void *buf, int ns)
 {
-	_insw((unsigned short *)((port)+_IO_BASE), buf, ns);
+	ide_insw(((port)+(_IO_BASE)), buf, ns);
 }
 
 void
 prep_ide_outsw(ide_ioreg_t port, void *buf, int ns)
 {
-	_outsw((unsigned short *)((port)+_IO_BASE), buf, ns);
+	ide_outsw(((port)+_IO_BASE), buf, ns);
 }
 
 int
 prep_ide_default_irq(ide_ioreg_t base)
 {
-	switch (base) {
-		case 0x1f0: return 13;
-		case 0x170: return 13;
-		case 0x1e8: return 11;
-		case 0x168: return 10;
-		default:
-                        return 0;
+	if ( _prep_type == _PREP_IBM ) {
+		switch (base) {
+			case 0x1f0: return 13;
+			case 0x170: return 13;
+			case 0x1e8: return 11;
+			case 0x168: return 10;
+			default:
+				return 0;
+		}
+	} else {
+		switch (base) {
+			case 0x1f0: return 14;
+			case 0x170: return 14;
+			case 0x1e8: return 15;
+			case 0x168: return 15;
+			default:
+				return 0;
+		}
 	}
 }
 
@@ -687,6 +698,7 @@
 void
 prep_ide_fix_driveid(struct hd_driveid *id)
 {
+	ppc_generic_ide_fix_driveid(id);
 }
 
 __initfunc(void
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/prom.c linux.14p6/arch/ppc/kernel/prom.c
--- linux.vanilla/arch/ppc/kernel/prom.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/ppc/kernel/prom.c	Sat Nov 13 15:14:57 1999
@@ -25,6 +25,7 @@
 #include <asm/smp.h>
 #include <asm/bootx.h>
 #include <asm/system.h>
+#include <asm/gemini.h>
 
 /*
  * Properties whose value is longer than this get excluded from our
@@ -276,6 +277,11 @@
 	unsigned long offset = reloc_offset();
 	int l;
 	char *p, *d;
+	
+#ifdef CONFIG_GEMINI
+	gemini_prom_init();
+	return;
+#endif /* CONFIG_GEMINI */
 	
 	/* check if we're apus, return if we are */
 	if ( r3 == 0x61707573 )
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/setup.c linux.14p6/arch/ppc/kernel/setup.c
--- linux.vanilla/arch/ppc/kernel/setup.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/ppc/kernel/setup.c	Sat Nov 13 15:14:57 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: setup.c,v 1.132.2.5 1999/09/11 03:32:50 paulus Exp $
+ * $Id: setup.c,v 1.132.2.6 1999/10/19 04:32:33 paulus Exp $
  * Common prep/pmac/chrp boot and setup code.
  */
 
@@ -62,6 +62,12 @@
                       unsigned long r6,
                       unsigned long r7);
 
+extern void gemini_init(unsigned long r3,
+                      unsigned long r4,
+                      unsigned long r5,
+                      unsigned long r6,
+                      unsigned long r7);
+
 extern boot_infos_t *boot_infos;
 extern char cmd_line[512];
 char saved_command_line[256];
@@ -342,7 +348,6 @@
 identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
 		 unsigned long r6, unsigned long r7)
 {
-	
 #ifdef __SMP__
 	if ( first_cpu_booted ) return 0;
 #endif /* __SMP__ */
@@ -403,6 +408,8 @@
 	_machine = _MACH_fads;
 #elif defined(CONFIG_APUS)
 	_machine = _MACH_apus;
+#elif defined(CONFIG_GEMINI)
+	_machine = _MACH_gemini;
 #else
 #error "Machine not defined correctly"
 #endif /* CONFIG_APUS */
@@ -488,6 +495,11 @@
                 mbx_init(r3, r4, r5, r6, r7);
                 break;
 #endif
+#ifdef CONFIG_GEMINI
+	case _MACH_gemini:
+                gemini_init(r3, r4, r5, r6, r7);
+		break;
+#endif
 	default:
 		printk("Unknown machine type in identify_machine!\n");
 	}
@@ -497,6 +509,28 @@
 		extern int __map_without_bats;
 		__map_without_bats = 1;
 	}
+
+	/* Look for mem= option on command line */
+	if (strstr(cmd_line, "mem=")) {
+		char *p, *q;
+		unsigned long maxmem = 0;
+		extern unsigned long __max_memory;
+
+		for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) {
+			q = p + 4;
+			if (p > cmd_line && p[-1] != ' ')
+				continue;
+			maxmem = simple_strtoul(q, &q, 0);
+			if (*q == 'k' || *q == 'K') {
+				maxmem <<= 10;
+				++q;
+			} else if (*q == 'm' || *q == 'M') {
+				maxmem <<= 20;
+				++q;
+			}
+		}
+		__max_memory = maxmem;
+	}
 	
 	return 0;
 }
@@ -522,7 +556,7 @@
 }
 
 __initfunc(void setup_arch(char **cmdline_p,
-			   unsigned long * memory_start_p, unsigned long * memory_end_p))
+		unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
 	extern int panic_timeout;
 	extern char _etext[], _edata[];
@@ -532,8 +566,11 @@
 
 #ifdef CONFIG_XMON
 	extern void xmon_map_scc(void);
+	char *p;
+
 	xmon_map_scc();
-	if (strstr(cmd_line, "xmon"))
+	p = strstr(cmd_line, "xmon");
+	if (p != NULL && (p == cmd_line || p[-1] == ' '))
 		xmon(0);
 #endif /* CONFIG_XMON */
  
@@ -547,6 +584,7 @@
 
 	/* Save unparsed command line copy for /proc/cmdline */
 	strcpy(saved_command_line, cmd_line);
+
 	*cmdline_p = cmd_line;
 
 	*memory_start_p = find_available_memory();
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/smp.c linux.14p6/arch/ppc/kernel/smp.c
--- linux.vanilla/arch/ppc/kernel/smp.c	Sat Aug 28 20:00:54 1999
+++ linux.14p6/arch/ppc/kernel/smp.c	Sat Nov 13 15:14:57 1999
@@ -34,6 +34,7 @@
 #include <asm/init.h>
 #include <asm/io.h>
 #include <asm/prom.h>
+#include <asm/gemini.h>
 
 #include "time.h"
 int first_cpu_booted = 0;
@@ -175,7 +176,7 @@
 void smp_message_pass(int target, int msg, unsigned long data, int wait)
 {
 	int i;
-	if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_prep)) )
+	if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_prep|_MACH_gemini)) )
 		return;
 
 	spin_lock(&mesg_pass_lock);
@@ -296,6 +297,12 @@
 			cpu_nr = 2;
 			break;
 		}
+	case _MACH_gemini:
+		for ( i = 0; i < 4 ; i++ )
+			openpic_enable_IPI(i);
+                cpu_nr = (readb(GEMINI_CPUSTAT) & GEMINI_CPU_COUNT_MASK)>>2;
+                cpu_nr = (cpu_nr == 0) ? 4 : cpu_nr;
+		break;
 	default:
 		printk("SMP not supported on this machine.\n");
 		return;
@@ -356,6 +363,10 @@
 			*MotSave_SmpIar = (unsigned long)__secondary_start_psurge - KERNELBASE;
 			*MotSave_CpusState[1] = CPU_GOOD;
 			printk("CPU1 reset, waiting\n");
+			break;
+		case _MACH_gemini:
+			openpic_init_processor( 1<<i );
+			openpic_init_processor( 0 );
 			break;
 		}
 		
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/mm/init.c linux.14p6/arch/ppc/mm/init.c
--- linux.vanilla/arch/ppc/mm/init.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/ppc/mm/init.c	Sat Nov 13 15:14:57 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: init.c,v 1.164.2.5 1999/09/07 00:59:22 paulus Exp $
+ *  $Id: init.c,v 1.164.2.7 1999/10/19 04:32:39 paulus Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -54,6 +54,7 @@
 #include <asm/setup.h>
 #include <asm/amigahw.h>
 /* END APUS includes */
+#include <asm/gemini.h>
 
 int prom_trashed;
 atomic_t next_mmu_context;
@@ -83,6 +84,7 @@
 unsigned long *prep_find_end_of_memory(void);
 unsigned long *pmac_find_end_of_memory(void);
 unsigned long *apus_find_end_of_memory(void);
+unsigned long *gemini_find_end_of_memory(void);
 extern unsigned long *find_end_of_memory(void);
 #ifdef CONFIG_MBX
 unsigned long *mbx_find_end_of_memory(void);
@@ -149,6 +151,9 @@
  */
 int __map_without_bats = 0;
 
+/* max amount of RAM to use */
+unsigned long __max_memory;
+
 /* optimization for 603 to load the tlb directly from the linux table -- Cort */
 #define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */
 
@@ -984,6 +989,10 @@
 		FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
 		FREESEC(__prep_begin,__prep_end,num_prep_pages);
 		break;
+	case _MACH_gemini:
+		FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
+		FREESEC(__prep_begin,__prep_end,num_prep_pages);
+		break;
 	}
 
 	if ( !have_of )
@@ -1021,9 +1030,13 @@
 	else if (_machine == _MACH_apus )
 		end_of_DRAM = apus_find_end_of_memory();
 #endif
+#ifdef CONFIG_GEMINI	
+	else if ( _machine == _MACH_gemini )
+		end_of_DRAM = gemini_find_end_of_memory();
+#endif /* CONFIG_GEMINI	*/
 	else /* prep */
 		end_of_DRAM = prep_find_end_of_memory();
-
+*(unsigned long *)(KERNELBASE) = 0xdeadbeef;	
         hash_init();
         _SDR1 = __pa(Hash) | (Hash_mask >> 10);
 	ioremap_base = 0xf8000000;
@@ -1066,6 +1079,10 @@
 		   (kernel_map) remaps individual IO regions to
 		   0x90000000. */
 		break;
+	case _MACH_gemini:
+		setbat(0, 0xf0000000, 0xf0000000, 0x10000000, IO_PAGE);
+		setbat(1, 0x80000000, 0x80000000, 0x10000000, IO_PAGE);
+		break;
 	}
 	ioremap_bot = ioremap_base;
 #else /* CONFIG_8xx */
@@ -1296,7 +1313,7 @@
 	int i;
 	
 	/* max amount of RAM we allow -- Cort */
-#define RAM_LIMIT (256<<20)
+#define RAM_LIMIT (768<<20)
 
 	memory_node = find_devices("memory");
 	if (memory_node == NULL) {
@@ -1329,8 +1346,12 @@
 	 * to our nearest IO area.
 	 * -- Cort
 	 */
-	if ( phys_mem.regions[0].size >= RAM_LIMIT )
-		phys_mem.regions[0].size = RAM_LIMIT;
+	if (__max_memory == 0 || __max_memory > RAM_LIMIT)
+		__max_memory = RAM_LIMIT;
+	if (phys_mem.regions[0].size >= __max_memory) {
+		phys_mem.regions[0].size = __max_memory;
+		phys_mem.n_regions = 1;
+	}
 	total = phys_mem.regions[0].size;
 	
 	if (phys_mem.n_regions > 1) {
@@ -1343,20 +1364,15 @@
 	if (boot_infos == 0) {
 		/* record which bits the prom is using */
 		get_mem_prop("available", &phys_avail);
+		prom_mem = phys_mem;
+		for (i = 0; i < phys_avail.n_regions; ++i)
+			remove_mem_piece(&prom_mem,
+					 phys_avail.regions[i].address,
+					 phys_avail.regions[i].size, 0);
 	} else {
 		/* booted from BootX - it's all available (after klimit) */
 		phys_avail = phys_mem;
-	}
-	prom_mem = phys_mem;
-	for (i = 0; i < phys_avail.n_regions; ++i)
-	{
-		if ( phys_avail.regions[i].address >= RAM_LIMIT )
-			continue;
-		if ( (phys_avail.regions[i].address+phys_avail.regions[i].size)
-		     >= RAM_LIMIT )
-			phys_avail.regions[i].size = RAM_LIMIT - phys_avail.regions[i].address;
-		remove_mem_piece(&prom_mem, phys_avail.regions[i].address,
-				 phys_avail.regions[i].size, 1);
+		prom_mem.n_regions = 0;
 	}
 
 	/*
@@ -1406,6 +1422,32 @@
 
 	return (__va(total));
 }
+
+#if defined(CONFIG_GEMINI)
+unsigned long __init *gemini_find_end_of_memory(void)
+{
+	unsigned long total, kstart, ksize, *ret;
+	unsigned char reg;
+*(unsigned long *)(KERNELBASE) = 0x1;
+	reg = readb(GEMINI_MEMCFG);
+*(unsigned long *)(KERNELBASE) = 0x2;
+	total = ((1<<((reg & 0x7) - 1)) *
+		 (8<<((reg >> 3) & 0x7)));
+	total *= (1024*1024);
+	phys_mem.regions[0].address = 0;
+	phys_mem.regions[0].size = total;
+	phys_mem.n_regions = 1;
+	
+	ret = __va(phys_mem.regions[0].size);
+	phys_avail = phys_mem;
+	kstart = __pa(_stext);
+	ksize = PAGE_ALIGN( _end - _stext );
+*(unsigned long *)(KERNELBASE) = 0x3;
+	remove_mem_piece( &phys_avail, kstart, ksize, 0 );
+*(unsigned long *)(KERNELBASE) = 0x4;
+	return ret;
+}
+#endif /* defined(CONFIG_GEMINI) */
 
 #ifdef CONFIG_APUS
 #define HARDWARE_MAPPED_SIZE (512*1024)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/xmon/xmon.c linux.14p6/arch/ppc/xmon/xmon.c
--- linux.vanilla/arch/ppc/xmon/xmon.c	Sat Aug 28 20:00:54 1999
+++ linux.14p6/arch/ppc/xmon/xmon.c	Fri Oct 22 22:26:41 1999
@@ -115,9 +115,13 @@
 {
 	struct pt_regs regs;
 	int msr, cmd;
+	static int entered = 0;
+
+	if (!entered) {
+		entered = 1;
+		printk("Entering xmon kernel debugger.\n");
+	}
 
-	printk("Entering xmon kernel debugger.\n");
-	
 	if (excp == NULL) {
 		asm volatile ("stw	0,0(%0)\n\
 			lwz	0,0(1)\n\
@@ -230,7 +234,7 @@
 
 	if (dabr.enabled && pc == dabr.instr)
 		return &dabr;
-	if (iabr.enabled && pc == iabr.address)
+	if (iabr.enabled && ((pc ^ iabr.address) & ~3) == 0)
 		return &iabr;
 	bp = bpts;
 	for (i = 0; i < NBPTS; ++i, ++bp)
@@ -254,7 +258,9 @@
 			printf("Couldn't insert breakpoint at %x, disabling\n",
 			       bp->address);
 			bp->enabled = 0;
+			continue;
 		}
+		store_inst((void *) bp->address);
 	}
 	if (dabr.enabled)
 		set_dabr(dabr.address);
@@ -277,9 +283,12 @@
 			continue;
 		if (mread(bp->address, &instr, 4) == 4
 		    && instr == bpinstr
-		    && mwrite(bp->address, &bp->instr, 4) != 4)
+		    && mwrite(bp->address, &bp->instr, 4) != 4) {
 			printf("Couldn't remove breakpoint at %x\n",
 			       bp->address);
+			continue;
+		}
+		store_inst((void *) bp->address);
 	}
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/entry.S linux.14p6/arch/sparc/kernel/entry.S
--- linux.vanilla/arch/sparc/kernel/entry.S	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/sparc/kernel/entry.S	Tue Nov 16 15:13:26 1999
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.159.2.2 1999/09/22 11:37:29 jj Exp $
+/* $Id: entry.S,v 1.159.2.6 1999/10/11 08:24:35 davem Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -829,13 +829,13 @@
 	.globl	C_LABEL(invalid_segment_patch1_ff)
 	.globl	C_LABEL(invalid_segment_patch2_ff)
 C_LABEL(invalid_segment_patch1_ff):	cmp	%l4, 0xff
-C_LABEL(invalid_segment_patch2_ff):	mov	0xff, %l4
+C_LABEL(invalid_segment_patch2_ff):	mov	0xff, %l3
 
 	.align	4
 	.globl	C_LABEL(invalid_segment_patch1_1ff)
 	.globl	C_LABEL(invalid_segment_patch2_1ff)
 C_LABEL(invalid_segment_patch1_1ff):	cmp	%l4, 0x1ff
-C_LABEL(invalid_segment_patch2_1ff):	mov	0x1ff, %l4
+C_LABEL(invalid_segment_patch2_1ff):	mov	0x1ff, %l3
 
 	.align	4
 	.globl	C_LABEL(num_context_patch1_16), C_LABEL(num_context_patch2_16)
@@ -856,7 +856,7 @@
 #ifdef CONFIG_SUN4
 C_LABEL(vac_hwflush_patch1_on):		nop
 #else
-C_LABEL(vac_hwflush_patch1_on):		subcc	%l7, (PAGE_SIZE - 4), %l7
+C_LABEL(vac_hwflush_patch1_on):		addcc	%l7, -PAGE_SIZE, %l7
 #endif
 
 C_LABEL(vac_hwflush_patch2_on):		sta	%g0, [%l3 + %l7] ASI_HWFLUSHSEG
@@ -972,12 +972,12 @@
 	bne	1f
 	 sethi	%hi(C_LABEL(sun4c_kfree_ring)), %l4
 	or	%l4, %lo(C_LABEL(sun4c_kfree_ring)), %l4
-	ld	[%l4 + 0x10], %l3
+	ld	[%l4 + 0x18], %l3
 	deccc	%l3			! do we have a free entry?
 	bcs,a	2f			! no, unmap one.
 	 sethi	%hi(C_LABEL(sun4c_kernel_ring)), %l4
 
-	st	%l3, [%l4 + 0x10]	! sun4c_kfree_ring.num_entries--
+	st	%l3, [%l4 + 0x18]	! sun4c_kfree_ring.num_entries--
 
 	ld	[%l4 + 0x00], %l6	! entry = sun4c_kfree_ring.ringhd.next
 	st	%l5, [%l6 + 0x08]	! entry->vaddr = address
@@ -1000,10 +1000,11 @@
 
 	st	%l6, [%l4 + 0x00]	! head->next = entry
 
-	ld	[%l4 + 0x10], %l3
+	ld	[%l4 + 0x18], %l3
 	inc	%l3			! sun4c_kernel_ring.num_entries++
+	st	%l3, [%l4 + 0x18]
 	b	4f
-	 st	%l3, [%l4 + 0x10]
+	 ld	[%l6 + 0x08], %l5
 
 2:
 	or	%l4, %lo(C_LABEL(sun4c_kernel_ring)), %l4
@@ -1023,7 +1024,7 @@
 C_LABEL(vac_hwflush_patch1):
 C_LABEL(vac_linesize_patch):
 	subcc	%l7, 16, %l7
-	bg	9b
+	bne	9b
 C_LABEL(vac_hwflush_patch2):
 	 sta	%g0, [%l3 + %l7] ASI_FLUSHSEG
 
@@ -1044,47 +1045,36 @@
 
 	mov	%l3, %l5		! address = tmp
 
+4:
 C_LABEL(num_context_patch1):
 	mov	0x08, %l7
 
-C_LABEL(invalid_segment_patch2):
-	mov	0x7f, %l4
+	ld	[%l6 + 0x08], %l4
+	ldub	[%l6 + 0x0c], %l3
+	or	%l4, %l3, %l4		! encode new vaddr/pseg into l4
 
 	sethi	%hi(AC_CONTEXT), %l3
 	lduba	[%l3] ASI_CONTROL, %l6
 
-3:
-	deccc	%l7
-	stba	%l7, [%l3] ASI_CONTROL
-	bne	3b
-	 stXa	%l4, [%l5] ASI_SEGMAP
-
-	stba	%l6, [%l3] ASI_CONTROL
-
-	! reload the entry
-
-	sethi	%hi(C_LABEL(sun4c_kernel_ring)), %l4
-	ld	[%l4 + %lo(C_LABEL(sun4c_kernel_ring))], %l6
-
-	ld	[%l6 + 0x08], %l5	! restore address from entry->vaddr
-
-4:
-C_LABEL(num_context_patch2):
-	mov	0x08, %l7
-
-	ldub	[%l6 + 0x0c], %l4	! entry->pseg
-
+	/* Invalidate old mapping, instantiate new mapping,
+	 * for each context.  Registers l6/l7 are live across
+	 * this loop.
+	 */
+3:	deccc	%l7
 	sethi	%hi(AC_CONTEXT), %l3
-	lduba	[%l3] ASI_CONTROL, %l6
-
-3:
-	deccc	%l7
 	stba	%l7, [%l3] ASI_CONTROL
+C_LABEL(invalid_segment_patch2):
+	mov	0x7f, %l3
+	stXa	%l3, [%l5] ASI_SEGMAP
+	andn	%l4, 0x1ff, %l3
 	bne	3b
-	 stXa	%l4, [%l5] ASI_SEGMAP
+	 stXa	%l4, [%l3] ASI_SEGMAP
 
+	sethi	%hi(AC_CONTEXT), %l3
 	stba	%l6, [%l3] ASI_CONTROL
 
+	andn	%l4, 0x1ff, %l5
+
 1:
 	sethi	%hi(SUN4C_VMALLOC_START), %l4
 	cmp	%l5, %l4
@@ -1152,6 +1142,7 @@
 
 sun4c_fault_fromuser:
 	SAVE_ALL
+	 nop
 	
 	mov	%l7, %o1		! Decode the info from %l7
 	mov	%l7, %o2
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/head.S linux.14p6/arch/sparc/kernel/head.S
--- linux.vanilla/arch/sparc/kernel/head.S	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/sparc/kernel/head.S	Tue Nov 16 15:13:26 1999
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.95.2.2 1999/09/23 09:53:18 anton Exp $
+/* $Id: head.S,v 1.95.2.3 1999/11/12 15:45:45 davem Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -450,7 +450,7 @@
  */
 	.ascii	"HdrS"
 	.word	LINUX_VERSION_CODE
-	.half	0x0201		/* HdrS version */
+	.half	0x0203		/* HdrS version */
 C_LABEL(root_flags):
 	.half	1
 C_LABEL(root_dev):
@@ -462,6 +462,8 @@
 C_LABEL(sparc_ramdisk_size):
 	.word	0
 	.word	C_LABEL(reboot_command)
+	.word	0, 0, 0
+	.word	_end
 
 /* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
  * %g7 and at prom_vector_p. And also quickly check whether we are on
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/process.c linux.14p6/arch/sparc/kernel/process.c
--- linux.vanilla/arch/sparc/kernel/process.c	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/sparc/kernel/process.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.137.2.1 1999/08/07 10:42:45 davem Exp $
+/*  $Id: process.c,v 1.137.2.2 1999/10/01 01:32:53 anton Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -55,7 +55,6 @@
 {
 	int ret = -EPERM;
 
-	lock_kernel();
 	if (current->pid != 0)
 		goto out;
 
@@ -100,7 +99,6 @@
 	}
 	ret = 0;
 out:
-	unlock_kernel();
 	return ret;
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/ptrace.c linux.14p6/arch/sparc/kernel/ptrace.c
--- linux.vanilla/arch/sparc/kernel/ptrace.c	Wed Mar 24 23:10:40 1999
+++ linux.14p6/arch/sparc/kernel/ptrace.c	Tue Nov 16 15:13:27 1999
@@ -37,12 +37,17 @@
 	pmd_t * pgmiddle;
 	pte_t * pgtable;
 	unsigned long page, retval;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
 	if (pgd_none(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
@@ -51,8 +56,12 @@
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
 	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
@@ -61,8 +70,12 @@
 	}
 	pgtable = pte_offset(pgmiddle, addr);
 	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 0);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
@@ -90,12 +103,17 @@
 	pmd_t *pgmiddle;
 	pte_t *pgtable;
 	unsigned long page;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
 	if (!pgd_present(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
@@ -104,8 +122,12 @@
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
 	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
@@ -114,13 +136,21 @@
 	}
 	pgtable = pte_offset(pgmiddle, addr);
 	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 	page = pte_page(*pgtable);
 	if (!pte_write(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, 1);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return;
 	}
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	flush_cache_page(vma, addr);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/setup.c linux.14p6/arch/sparc/kernel/setup.c
--- linux.vanilla/arch/sparc/kernel/setup.c	Sat Aug 14 02:25:55 1999
+++ linux.14p6/arch/sparc/kernel/setup.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.105 1999/04/13 14:17:08 jj Exp $
+/*  $Id: setup.c,v 1.105.2.1 1999/11/16 06:29:31 davem Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -424,7 +424,9 @@
 				initrd_start -= KERNBASE;
 				initrd_end -= KERNBASE;
 				break;
-			}
+			default:
+				break;
+			};
 		}
 	}
 #endif	
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/sparc_ksyms.c linux.14p6/arch/sparc/kernel/sparc_ksyms.c
--- linux.vanilla/arch/sparc/kernel/sparc_ksyms.c	Wed Oct 20 01:12:33 1999
+++ linux.14p6/arch/sparc/kernel/sparc_ksyms.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.77.2.3 1999/09/22 17:06:50 jj Exp $
+/* $Id: sparc_ksyms.c,v 1.77.2.4 1999/09/28 16:47:30 davem Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -63,6 +63,7 @@
 
 extern void bcopy (const char *, char *, int);
 extern int __ashrdi3(int, int);
+extern int __ashldi3(int, int);
 extern int __lshrdi3(int, int);
 
 extern void dump_thread(struct pt_regs *, struct user *);
@@ -269,6 +270,7 @@
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memmove);
 EXPORT_SYMBOL_NOVERS(__ashrdi3);
+EXPORT_SYMBOL_NOVERS(__ashldi3);
 EXPORT_SYMBOL_NOVERS(__lshrdi3);
 
 EXPORT_SYMBOL_DOT(rem);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/sun4d_smp.c linux.14p6/arch/sparc/kernel/sun4d_smp.c
--- linux.vanilla/arch/sparc/kernel/sun4d_smp.c	Sat Aug 14 02:26:14 1999
+++ linux.14p6/arch/sparc/kernel/sun4d_smp.c	Tue Nov 16 15:13:27 1999
@@ -99,6 +99,14 @@
 	local_flush_cache_all();
 	local_flush_tlb_all();
 
+	/*
+	 * Unblock the master CPU _only_ when the scheduler state
+	 * of all secondary CPUs will be up-to-date, so after
+	 * the SMP initialization the master will be just allowed
+	 * to call the scheduler code.
+	 */
+	init_idle();
+
 	/* Get our local ticker going. */
 	smp_setup_percpu_timer();
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/sun4m_smp.c linux.14p6/arch/sparc/kernel/sun4m_smp.c
--- linux.vanilla/arch/sparc/kernel/sun4m_smp.c	Sat Aug 14 02:26:14 1999
+++ linux.14p6/arch/sparc/kernel/sun4m_smp.c	Tue Nov 16 15:13:27 1999
@@ -93,6 +93,14 @@
 	local_flush_cache_all();
 	local_flush_tlb_all();
 
+	/*
+	 * Unblock the master CPU _only_ when the scheduler state
+	 * of all secondary CPUs will be up-to-date, so after
+	 * the SMP initialization the master will be just allowed
+	 * to call the scheduler code.
+	 */
+	init_idle();
+
 	/* Allow master to continue. */
 	swap((unsigned long *)&cpu_callin_map[cpuid], 1);
 	local_flush_cache_all();
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/sys_sparc.c linux.14p6/arch/sparc/kernel/sys_sparc.c
--- linux.vanilla/arch/sparc/kernel/sys_sparc.c	Sat Aug 14 02:26:14 1999
+++ linux.14p6/arch/sparc/kernel/sys_sparc.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.52 1999/05/08 08:09:48 anton Exp $
+/* $Id: sys_sparc.c,v 1.52.2.1 1999/10/04 10:36:15 davem Exp $
  * linux/arch/sparc/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -192,24 +192,23 @@
 	}
 	retval = -ENOMEM;
 	len = PAGE_ALIGN(len);
-	if(!(flags & MAP_FIXED) && !addr) {
-		addr = get_unmapped_area(addr, len);
+	if(!(flags & MAP_FIXED) &&
+	   (!addr || (ARCH_SUN4C_SUN4 &&
+		      (addr >= 0x20000000 && addr < 0xe0000000)))) {
+		addr = get_unmapped_area(0, len);
 		if(!addr)
 			goto out_putf;
+		if (ARCH_SUN4C_SUN4 &&
+		    (addr >= 0x20000000 && addr < 0xe0000000)) {
+			retval = -EINVAL;
+			goto out_putf;
+		}
 	}
 
 	/* See asm-sparc/uaccess.h */
 	retval = -EINVAL;
 	if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
 		goto out_putf;
-
-	if(ARCH_SUN4C_SUN4) {
-		if(((addr >= 0x20000000) && (addr < 0xe0000000))) {
-			/* VM hole */
-			retval = current->mm->brk;
-			goto out_putf;
-		}
-	}
 
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	retval = do_mmap(file, addr, len, prot, flags, off);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/kernel/sys_sunos.c linux.14p6/arch/sparc/kernel/sys_sunos.c
--- linux.vanilla/arch/sparc/kernel/sys_sunos.c	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/sparc/kernel/sys_sunos.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.94.2.2 1999/08/07 10:42:49 davem Exp $
+/* $Id: sys_sunos.c,v 1.94.2.3 1999/10/04 10:36:20 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -85,10 +85,17 @@
 	}
 
 	retval = -ENOMEM;
-	if(!(flags & MAP_FIXED) && !addr) {
-		addr = get_unmapped_area(addr, len);
+	if(!(flags & MAP_FIXED) &&
+	   (!addr || (ARCH_SUN4C_SUN4 &&
+		      (addr >= 0x20000000 && addr < 0xe0000000)))) {
+		addr = get_unmapped_area(0, len);
 		if(!addr)
 			goto out_putf;
+		if (ARCH_SUN4C_SUN4 &&
+		    (addr >= 0x20000000 && addr < 0xe0000000)) {
+			retval = -EINVAL;
+			goto out_putf;
+		}
 	}
 	/* If this is ld.so or a shared library doing an mmap
 	 * of /dev/zero, transform it into an anonymous mapping.
@@ -110,13 +117,6 @@
 	retval = -EINVAL;
 	if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
 		goto out_putf;
-
-	if(ARCH_SUN4C_SUN4) {
-		if(((addr >= 0x20000000) && (addr < 0xe0000000))) {
-			retval = current->mm->brk;
-			goto out_putf;
-		}
-	}
 
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	retval = do_mmap(file, addr, len, prot, flags, off);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/lib/Makefile linux.14p6/arch/sparc/lib/Makefile
--- linux.vanilla/arch/sparc/lib/Makefile	Sun Mar 21 15:23:38 1999
+++ linux.14p6/arch/sparc/lib/Makefile	Tue Nov 16 15:13:27 1999
@@ -1,11 +1,12 @@
-# $Id: Makefile,v 1.28 1999/03/21 06:37:44 davem Exp $
+# $Id: Makefile,v 1.28.2.1 1999/09/28 16:47:36 davem Exp $
 # Makefile for Sparc library files..
 #
 
 OBJS  = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
         strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
 	strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
-	copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o
+	copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o \
+	ashldi3.o
 
 ifdef CONFIG_SMP
 OBJS += irqlock.o
@@ -88,6 +89,9 @@
 
 ashrdi3.o: ashrdi3.S
 	$(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S
+
+ashldi3.o: ashldi3.S
+	$(CC) -D__ASSEMBLY__ -c -o ashldi3.o ashldi3.S
 
 lshrdi3.o: lshrdi3.S
 	$(CC) -D__ASSEMBLY__ -c -o lshrdi3.o lshrdi3.S
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/lib/ashldi3.S linux.14p6/arch/sparc/lib/ashldi3.S
--- linux.vanilla/arch/sparc/lib/ashldi3.S	Thu Jan  1 01:00:00 1970
+++ linux.14p6/arch/sparc/lib/ashldi3.S	Tue Nov 16 15:13:27 1999
@@ -0,0 +1,36 @@
+/* $Id: ashldi3.S,v 1.1.2.1 1999/09/28 16:47:39 davem Exp $
+ * ashldi3.S:	GCC emits these for certain drivers playing
+ *		with long longs.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include <asm/cprefix.h>
+
+	.text
+	.align	4
+	.globl	C_LABEL(__ashldi3)
+C_LABEL(__ashldi3):
+	cmp	%o2, 0
+	be	9f
+	 mov	0x20, %g2
+
+	sub	%g2, %o2, %g2
+	cmp	%g2, 0
+	bg	7f
+	 sll	%o0, %o2, %g3
+
+	neg	%g2
+	clr	%o5
+	b	8f
+	 sll	%o1, %g2, %o4
+7:
+	srl	%o1, %g2, %g2
+	sll	%o1, %o2, %o5
+	or	%g3, %g2, %o4
+8:
+	mov	%o4, %o0
+	mov	%o5, %o1
+9:
+	retl
+	 nop
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/lib/ashrdi3.S linux.14p6/arch/sparc/lib/ashrdi3.S
--- linux.vanilla/arch/sparc/lib/ashrdi3.S	Sat Nov  9 08:12:00 1996
+++ linux.14p6/arch/sparc/lib/ashrdi3.S	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: ashrdi3.S,v 1.3 1996/09/07 23:18:10 davem Exp $
+/* $Id: ashrdi3.S,v 1.3.12.1 1999/09/28 16:47:42 davem Exp $
  * ashrdi3.S:	The filesystem code creates all kinds of references to
  *              this little routine on the sparc with gcc.
  *
@@ -7,7 +7,9 @@
 
 #include <asm/cprefix.h>
 
-		.globl C_LABEL(__ashrdi3)
+	.text
+	.align	4
+	.globl C_LABEL(__ashrdi3)
 C_LABEL(__ashrdi3):
 	tst	%o2
 	be	3f
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/Makefile linux.14p6/arch/sparc/mm/Makefile
--- linux.vanilla/arch/sparc/mm/Makefile	Thu Mar 11 00:53:36 1999
+++ linux.14p6/arch/sparc/mm/Makefile	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.33 1999/01/02 16:45:47 davem Exp $
+# $Id: Makefile,v 1.33.2.1 1999/10/06 15:36:20 davem Exp $
 # Makefile for the linux Sparc-specific parts of the memory manager.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -12,7 +12,7 @@
 ifeq ($(CONFIG_SUN4),y)
 O_OBJS	 += nosrmmu.o
 else
-O_OBJS	 += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o
+O_OBJS	 += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o
 endif
 ifdef CONFIG_SMP
 O_OBJS   += nosun4c.o
@@ -30,3 +30,6 @@
 
 tsunami.o: tsunami.S
 	$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o tsunami.o tsunami.S
+
+swift.o: swift.S
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o swift.o swift.S
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/asyncd.c linux.14p6/arch/sparc/mm/asyncd.c
--- linux.vanilla/arch/sparc/mm/asyncd.c	Sun Oct  4 18:22:42 1998
+++ linux.14p6/arch/sparc/mm/asyncd.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/*  $Id: asyncd.c,v 1.12 1998/09/13 04:30:30 davem Exp $
+/*  $Id: asyncd.c,v 1.12.2.1 1999/11/16 06:29:36 davem Exp $
  *  The asyncd kernel daemon. This handles paging on behalf of 
  *  processes that receive page faults due to remote (async) memory
  *  accesses. 
@@ -152,7 +152,9 @@
 	if(!pte)
 		goto no_memory;
 	if(!pte_present(*pte)) {
-		handle_mm_fault(tsk, vma, address, write);
+		int fault = handle_mm_fault(tsk, vma, address, write);
+		if (fault < 0)
+			goto no_memory;
 		goto finish_up;
 	}
 	set_pte(pte, pte_mkyoung(*pte));
@@ -164,7 +166,11 @@
 		flush_tlb_page(vma, address);
 		goto finish_up;
 	}
-	handle_mm_fault(tsk, vma, address, write);
+	{
+		int fault = handle_mm_fault(tsk, vma, address, write);
+		if (fault < 0)
+			goto no_memory;
+	}
 
 	/* Fall through for do_wp_page */
 finish_up:
@@ -173,7 +179,7 @@
 
 no_memory:
 	stats.failure++;
-	oom(tsk);
+	force_sig(SIGKILL, tsk);
 	return 1;
 	
 bad_area:	  
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/fault.c linux.14p6/arch/sparc/mm/fault.c
--- linux.vanilla/arch/sparc/mm/fault.c	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/sparc/mm/fault.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.101.2.2 1999/08/07 10:42:53 davem Exp $
+/* $Id: fault.c,v 1.101.2.5 1999/11/16 06:29:39 davem Exp $
  * fault.c:  Page fault handlers for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -237,8 +237,14 @@
 		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
 			goto bad_area;
 	}
-	if (!handle_mm_fault(current, vma, address, write))
-		goto do_sigbus;
+survive:
+	{
+		int fault = handle_mm_fault(current, vma, address, write);
+		if (!fault)
+			goto do_sigbus;
+		if (fault < 0)
+			goto out_of_memory;
+	}
 	up(&mm->mmap_sem);
 	return;
 	/*
@@ -288,6 +294,17 @@
 	unhandled_fault (address, tsk, regs);
 	return;
 
+out_of_memory:
+	if (tsk->pid == 1) {
+		tsk->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	up(&mm->mmap_sem);
+	printk("VM: killing process %s\n", tsk->comm);
+	do_exit(SIGKILL);
+	return;
+
 do_sigbus:
 	up(&mm->mmap_sem);
 	tsk->tss.sig_address = address;
@@ -309,8 +326,18 @@
 	pgd_t *pgdp;
 	pte_t *ptep;
 
-	if (text_fault)
+	if (text_fault) {
 		address = regs->pc;
+	} else if (!write &&
+		   !(regs->psr & PSR_PS)) {
+		unsigned int insn, *ip;
+
+		ip = (unsigned int *)regs->pc;
+		if (! get_user(insn, ip)) {
+			if ((insn & 0xc1680000) == 0xc0680000)
+				write = 1;
+		}
+	}
 
 	pgdp = sun4c_pgd_offset(mm, address);
 	ptep = sun4c_pte_offset((pmd_t *) pgdp, address);
@@ -319,28 +346,36 @@
 	    if (write) {
 		if ((pte_val(*ptep) & (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT))
 				   == (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) {
+			unsigned long flags;
 
 			*ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED |
 				      _SUN4C_PAGE_MODIFIED |
 				      _SUN4C_PAGE_VALID |
 				      _SUN4C_PAGE_DIRTY);
 
+			save_and_cli(flags);
 			if (sun4c_get_segmap(address) != invalid_segment) {
 				sun4c_put_pte(address, pte_val(*ptep));
+				restore_flags(flags);
 				return;
 			}
+			restore_flags(flags);
 		}
 	    } else {
 		if ((pte_val(*ptep) & (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT))
 				   == (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) {
+			unsigned long flags;
 
 			*ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED |
 				      _SUN4C_PAGE_VALID);
 
+			save_and_cli(flags);
 			if (sun4c_get_segmap(address) != invalid_segment) {
 				sun4c_put_pte(address, pte_val(*ptep));
+				restore_flags(flags);
 				return;
 			}
+			restore_flags(flags);
 		}
 	    }
 	}
@@ -389,8 +424,14 @@
 		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
 			goto bad_area;
 	}
-	if (!handle_mm_fault(current, vma, address, write))
-		goto do_sigbus;
+survive:
+	{
+		int fault = handle_mm_fault(current, vma, address, write);
+		if (!fault)
+			goto do_sigbus;
+		if (fault < 0)
+			goto out_of_memory;
+	}
 	up(&mm->mmap_sem);
 	return;
 bad_area:
@@ -404,6 +445,17 @@
 	send_sig(SIGSEGV, tsk, 1);
 	return;
 
+out_of_memory:
+	if (tsk->pid == 1) {
+		tsk->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	up(&mm->mmap_sem);
+	printk("VM: killing process %s\n", tsk->comm);
+	do_exit(SIGKILL);
+	return;
+
 do_sigbus:
 	up(&mm->mmap_sem);
 	tsk->tss.sig_address = address;
@@ -415,31 +467,25 @@
 {
 	unsigned long sp;
 
-	lock_kernel();
 	sp = current->tss.rwbuf_stkptrs[0];
 	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
 		force_user_fault(sp + 0x38, 1);
 	force_user_fault(sp, 1);
-	unlock_kernel();
 }
 
 void window_underflow_fault(unsigned long sp)
 {
-	lock_kernel();
 	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
 		force_user_fault(sp + 0x38, 0);
 	force_user_fault(sp, 0);
-	unlock_kernel();
 }
 
 void window_ret_fault(struct pt_regs *regs)
 {
 	unsigned long sp;
 
-	lock_kernel();
 	sp = regs->u_regs[UREG_FP];
 	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
 		force_user_fault(sp + 0x38, 0);
 	force_user_fault(sp, 0);
-	unlock_kernel();
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/iommu.c linux.14p6/arch/sparc/mm/iommu.c
--- linux.vanilla/arch/sparc/mm/iommu.c	Sat Aug 28 20:00:54 1999
+++ linux.14p6/arch/sparc/mm/iommu.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: iommu.c,v 1.10.2.1 1999/08/13 12:35:35 davem Exp $
+/* $Id: iommu.c,v 1.10.2.2 1999/11/03 03:05:55 davem Exp $
  * iommu.c:  IOMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -16,6 +16,7 @@
 #include <asm/sbus.h>
 #include <asm/io.h>
 #include <asm/mxcc.h>
+#include <asm/mbus.h>
 
 /* srmmu.c */
 extern int viking_mxcc_present;
@@ -201,16 +202,19 @@
 #ifdef CONFIG_SBUS
 static void iommu_map_dma_area(unsigned long addr, int len)
 {
-	unsigned long page, end;
+	unsigned long page, end, ipte_cache;
 	pgprot_t dvma_prot;
 	struct iommu_struct *iommu = SBus_chain->iommu;
 	iopte_t *iopte = iommu->page_table;
 	iopte_t *first;
 
-	if(viking_mxcc_present)
+	if(viking_mxcc_present || srmmu_modtype == HyperSparc) {
 		dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
-	else
+		ipte_cache = 1;
+	} else {
 		dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
+		ipte_cache = 0;
+	}
 
 	iopte += ((addr - iommu->start) >> PAGE_SHIFT);
 	first = iopte;
@@ -229,13 +233,20 @@
 				viking_mxcc_flush_page(page);
 			else if (viking_flush)
 				viking_flush_page(page);
+			else
+				flush_page_to_ram(page);
 
 			pgdp = pgd_offset(init_task.mm, addr);
 			pmdp = pmd_offset(pgdp, addr);
 			ptep = pte_offset(pmdp, addr);
 
 			set_pte(ptep, pte_val(mk_pte(page, dvma_prot)));
-			iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page));
+			if (ipte_cache != 0) {
+				iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page));
+			} else {
+				iopte_val(*iopte++) =
+					MKIOPTE(mmu_v2p(page)) & ~IOPTE_CACHE;
+			}
 		}
 		addr += PAGE_SIZE;
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/nosrmmu.c linux.14p6/arch/sparc/mm/nosrmmu.c
--- linux.vanilla/arch/sparc/mm/nosrmmu.c	Sat Aug 14 02:25:55 1999
+++ linux.14p6/arch/sparc/mm/nosrmmu.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: nosrmmu.c,v 1.2 1999/03/30 10:17:39 jj Exp $
+/* $Id: nosrmmu.c,v 1.2.2.1 1999/10/06 10:52:37 anton Exp $
  * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, 
  *         so that it does not need srmmu and avoid ifdefs.
  *
@@ -13,6 +13,8 @@
 static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n";
 
 enum mbus_module srmmu_modtype;
+
+int vac_cache_size = 0;
 
 __initfunc(static void should_not_happen(void))
 {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/srmmu.c linux.14p6/arch/sparc/mm/srmmu.c
--- linux.vanilla/arch/sparc/mm/srmmu.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc/mm/srmmu.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.187.2.2 1999/09/21 11:24:15 anton Exp $
+/* $Id: srmmu.c,v 1.187.2.7 1999/11/16 06:29:44 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -114,7 +114,9 @@
 #define srmmu_ahashfn(addr)	((addr) >> 24)
 
 int viking_mxcc_present = 0;
+#ifdef __SMP__
 static spinlock_t srmmu_context_spinlock = SPIN_LOCK_UNLOCKED;
+#endif
 
 /* Physical memory can be _very_ non-contiguous on the sun4m, especially
  * the SS10/20 class machines and with the latest openprom revisions.
@@ -725,6 +727,17 @@
 	srmmu_set_entry(ptep, pte_val(pteval));
 }
 
+extern void swift_flush_chunk(unsigned long chunk);
+
+static void srmmu_set_pte_nocache_swift(pte_t *ptep, pte_t pteval)
+{
+	unsigned long page;
+
+	srmmu_set_entry(ptep, pte_val(pteval));
+	page = ((unsigned long)ptep) & PAGE_MASK;
+	swift_flush_chunk(page);
+}
+
 static void srmmu_set_pte_nocache_cypress(pte_t *ptep, pte_t pteval)
 {
 	register unsigned long a, b, c, d, e, f, g;
@@ -909,7 +922,8 @@
 /* tsunami.S */
 extern void tsunami_flush_cache_all(void);
 extern void tsunami_flush_cache_mm(struct mm_struct *mm);
-extern void tsunami_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end);
+extern void tsunami_flush_cache_range(struct mm_struct *mm,
+				      unsigned long start, unsigned long end);
 extern void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
 extern void tsunami_flush_page_to_ram(unsigned long page);
 extern void tsunami_flush_page_for_dma(unsigned long page);
@@ -917,111 +931,54 @@
 extern void tsunami_flush_chunk(unsigned long chunk);
 extern void tsunami_flush_tlb_all(void);
 extern void tsunami_flush_tlb_mm(struct mm_struct *mm);
-extern void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end);
+extern void tsunami_flush_tlb_range(struct mm_struct *mm,
+				    unsigned long start, unsigned long end);
 extern void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
+extern void tsunami_setup_blockops(void);
 
-/* Workaround, until we find what's going on with Swift. When low on memory, it sometimes
- * loops in fault/handle_mm_fault incl. flush_tlb_page to find out it is already in page tables/
- * fault again on the same instruction. I really don't understand it, have checked it and contexts
- * are right, flush_tlb_all is done as well, and it faults again... Strange. -jj
- */
-static void swift_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
-{
-	static unsigned long last;
-
-	if (last == address) viking_hwprobe(address);
-	last = address;
-}
-
-/* Swift flushes.  It has the recommended SRMMU specification flushing
- * facilities, so we can do things in a more fine grained fashion than we
- * could on the tsunami.  Let's watch out for HARDWARE BUGS...
- */
+/* swift.S */
+extern void swift_flush_cache_all(void);
+extern void swift_flush_cache_mm(struct mm_struct *mm);
+extern void swift_flush_cache_range(struct mm_struct *mm,
+				    unsigned long start, unsigned long end);
+extern void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
+extern void swift_flush_page_to_ram(unsigned long page);
+extern void swift_flush_page_for_dma(unsigned long page);
+extern void swift_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+extern void swift_flush_tlb_all(void);
+extern void swift_flush_tlb_mm(struct mm_struct *mm);
+extern void swift_flush_tlb_range(struct mm_struct *mm,
+				  unsigned long start, unsigned long end);
+extern void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
 
-static void swift_flush_cache_all(void)
-{
-	flush_user_windows();
-	swift_idflash_clear();
-}
-
-static void swift_flush_cache_mm(struct mm_struct *mm)
-{
-	FLUSH_BEGIN(mm)
-	flush_user_windows();
-	swift_idflash_clear();
-	FLUSH_END
-}
-
-static void swift_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
-{
-	FLUSH_BEGIN(mm)
-	flush_user_windows();
-	swift_idflash_clear();
-	FLUSH_END
-}
-
-static void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
-{
-	FLUSH_BEGIN(vma->vm_mm)
-	flush_user_windows();
-	if(vma->vm_flags & VM_EXEC)
-		swift_flush_icache();
-	swift_flush_dcache();
-	FLUSH_END
-}
-
-/* Not copy-back on swift. */
-static void swift_flush_page_to_ram(unsigned long page)
-{
-}
-
-/* But not IO coherent either. */
-static void swift_flush_page_for_dma(unsigned long page)
-{
-	swift_flush_dcache();
-}
-
-/* Again, Swift is non-snooping split I/D cache'd just like tsunami,
- * so have to punt the icache for on-stack signal insns.  Only the
- * icache need be flushed since the dcache is write-through.
- */
-static void swift_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
-{
-	swift_flush_icache();
-}
-
-static void swift_flush_chunk(unsigned long chunk)
+static void swift_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
+	if(pgdp != swapper_pg_dir)
+		swift_flush_chunk((unsigned long)pgdp);
+	if(tsk->mm->context != NO_CONTEXT &&
+	   tsk->mm->pgd != pgdp) {
+		swift_flush_cache_mm(tsk->mm);
+		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+		swift_flush_tlb_mm(tsk->mm);
+	}
 }
 
-static void swift_flush_tlb_all(void)
+static void swift_init_new_context(struct mm_struct *mm)
 {
-	srmmu_flush_whole_tlb();
-	module_stats.invall++;
-}
+	ctxd_t *ctxp;
 
-static void swift_flush_tlb_mm(struct mm_struct *mm)
-{
-	FLUSH_BEGIN(mm)
-	srmmu_flush_whole_tlb();
-	module_stats.invmm++;
-	FLUSH_END
-}
+	spin_lock(&srmmu_context_spinlock);
+	alloc_context(mm);
+	spin_unlock(&srmmu_context_spinlock);
 
-static void swift_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
-{
-	FLUSH_BEGIN(mm)
-	srmmu_flush_whole_tlb();
-	module_stats.invrnge++;
-	FLUSH_END
-}
+	ctxp = &srmmu_context_table[mm->context];
+	srmmu_set_entry((pte_t *)ctxp,
+			__pte((SRMMU_ET_PTD |
+			       (srmmu_v2p((unsigned long) mm->pgd) >> 4))));
+	swift_flush_chunk(((unsigned long)ctxp) & PAGE_MASK);
 
-static void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
-{
-	FLUSH_BEGIN(vma->vm_mm)
-	srmmu_flush_whole_tlb();
-	module_stats.invpg++;
-	FLUSH_END
+	if(mm == current->mm)
+		srmmu_set_context(mm->context);
 }
 
 /* The following are all MBUS based SRMMU modules, and therefore could
@@ -1306,7 +1263,8 @@
 /* hypersparc.S */
 extern void hypersparc_flush_cache_all(void);
 extern void hypersparc_flush_cache_mm(struct mm_struct *mm);
-extern void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end);
+extern void hypersparc_flush_cache_range(struct mm_struct *mm,
+					 unsigned long start, unsigned long end);
 extern void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
 extern void hypersparc_flush_page_to_ram(unsigned long page);
 extern void hypersparc_flush_chunk(unsigned long chunk);
@@ -1314,7 +1272,8 @@
 extern void hypersparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
 extern void hypersparc_flush_tlb_all(void);
 extern void hypersparc_flush_tlb_mm(struct mm_struct *mm);
-extern void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end);
+extern void hypersparc_flush_tlb_range(struct mm_struct *mm,
+				       unsigned long start, unsigned long end);
 extern void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
 extern void hypersparc_setup_blockops(void);
 
@@ -1328,7 +1287,8 @@
 
 static void hypersparc_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
 {
-	srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4))));
+	srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD |
+					      (srmmu_v2p((unsigned long) pgdp) >> 4))));
 	hypersparc_flush_page_to_ram((unsigned long)ctxp);
 	hyper_flush_whole_icache();
 }
@@ -1407,7 +1367,9 @@
 		alloc_context(tsk->mm);
 		spin_unlock(&srmmu_context_spinlock);
 		ctxp = &srmmu_context_table[tsk->mm->context];
-		srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4))));
+		srmmu_set_entry((pte_t *)ctxp,
+				__pte((SRMMU_ET_PTD |
+				       (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4))));
 		hypersparc_flush_page_to_ram((unsigned long)ctxp);
 	}
 	hyper_flush_whole_icache();
@@ -1423,7 +1385,9 @@
 	spin_unlock(&srmmu_context_spinlock);
 
 	ctxp = &srmmu_context_table[mm->context];
-	srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) mm->pgd) >> 4))));
+	srmmu_set_entry((pte_t *)ctxp,
+			__pte((SRMMU_ET_PTD |
+			       (srmmu_v2p((unsigned long) mm->pgd) >> 4))));
 	hypersparc_flush_page_to_ram((unsigned long)ctxp);
 
 	if(mm == current->mm) {
@@ -1450,12 +1414,14 @@
 
 static inline void srmmu_early_pgd_set(pgd_t *pgdp, pmd_t *pmdp)
 {
-	set_pte((pte_t *)pgdp, __pte((SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) pmdp) >> 4))));
+	set_pte((pte_t *)pgdp, __pte((SRMMU_ET_PTD |
+				      (srmmu_early_paddr((unsigned long) pmdp) >> 4))));
 }
 
 static inline void srmmu_early_pmd_set(pmd_t *pmdp, pte_t *ptep)
 {
-	set_pte((pte_t *)pmdp, __pte((SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) ptep) >> 4))));
+	set_pte((pte_t *)pmdp, __pte((SRMMU_ET_PTD |
+				      (srmmu_early_paddr((unsigned long) ptep) >> 4))));
 }
 
 static inline unsigned long srmmu_early_pgd_page(pgd_t pgd)
@@ -1470,12 +1436,14 @@
 
 static inline pmd_t *srmmu_early_pmd_offset(pgd_t *dir, unsigned long address)
 {
-	return (pmd_t *) srmmu_early_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+	return (pmd_t *) srmmu_early_pgd_page(*dir) +
+		((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
 }
 
 static inline pte_t *srmmu_early_pte_offset(pmd_t *dir, unsigned long address)
 {
-	return (pte_t *) srmmu_early_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+	return (pte_t *) srmmu_early_pmd_page(*dir) +
+		((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
 }
 
 static inline void srmmu_allocate_ptable_skeleton(unsigned long start, unsigned long end)
@@ -2245,6 +2213,7 @@
 __initfunc(static void init_hypersparc(void))
 {
 	srmmu_name = "ROSS HyperSparc";
+	srmmu_modtype = HyperSparc;
 
 	init_vac_layout();
 
@@ -2373,21 +2342,14 @@
 
 __initfunc(static void poke_swift(void))
 {
-	unsigned long mreg = srmmu_get_mmureg();
+	unsigned long mreg;
 
 	/* Clear any crap from the cache or else... */
-	swift_idflash_clear();
-	mreg |= (SWIFT_IE | SWIFT_DE); /* I & D caches on */
+	swift_flush_cache_all();
 
-	/* The Swift branch folding logic is completely broken.  At
-	 * trap time, if things are just right, if can mistakenly
-	 * think that a trap is coming from kernel mode when in fact
-	 * it is coming from user mode (it mis-executes the branch in
-	 * the trap code).  So you see things like crashme completely
-	 * hosing your machine which is completely unacceptable.  Turn
-	 * this shit off... nice job Fujitsu.
-	 */
-	mreg &= ~(SWIFT_BF);
+	/* Enable I & D caches */
+	mreg = srmmu_get_mmureg();
+	mreg |= (SWIFT_IE | SWIFT_DE);
 	srmmu_set_mmureg(mreg);
 }
 
@@ -2444,18 +2406,24 @@
 	BTFIXUPSET_CALL(flush_cache_page, swift_flush_cache_page, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_cache_range, swift_flush_cache_range, BTFIXUPCALL_NORM);
 
-	BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
+	BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
 
 	BTFIXUPSET_CALL(flush_tlb_all, swift_flush_tlb_all, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_tlb_mm, swift_flush_tlb_mm, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_tlb_page, swift_flush_tlb_page, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_tlb_range, swift_flush_tlb_range, BTFIXUPCALL_NORM);
 
-	BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_sig_insns, swift_flush_sig_insns, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_page_for_dma, swift_flush_page_for_dma, BTFIXUPCALL_NORM);
 
-	BTFIXUPSET_CALL(update_mmu_cache, swift_update_mmu_cache, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(update_mmu_cache, srmmu_update_mmu_cache, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(sparc_update_rootmmu_dir, swift_update_rootmmu_dir, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_swift, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(init_new_context, swift_init_new_context, BTFIXUPCALL_NORM);
+
+	flush_page_for_dma_global = 0;
 
 	/* Are you now convinced that the Swift is one of the
 	 * biggest VLSI abortions of all time?  Bravo Fujitsu!
@@ -2613,7 +2581,7 @@
 	BTFIXUPSET_CALL(flush_chunk, turbosparc_flush_chunk, BTFIXUPCALL_NORM);
 
 	BTFIXUPSET_CALL(flush_sig_insns, turbosparc_flush_sig_insns, BTFIXUPCALL_NOP);
-	BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NORM);
 
 	poke_srmmu = poke_turbosparc;
 }
@@ -2644,7 +2612,7 @@
 	BTFIXUPSET_CALL(flush_cache_page, tsunami_flush_cache_page, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_cache_range, tsunami_flush_cache_range, BTFIXUPCALL_NORM);
 
-	BTFIXUPSET_CALL(flush_chunk, tsunami_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
+	BTFIXUPSET_CALL(flush_chunk, tsunami_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
 
 	BTFIXUPSET_CALL(flush_tlb_all, tsunami_flush_tlb_all, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_tlb_mm, tsunami_flush_tlb_mm, BTFIXUPCALL_NORM);
@@ -2656,6 +2624,8 @@
 	BTFIXUPSET_CALL(flush_page_for_dma, tsunami_flush_page_for_dma, BTFIXUPCALL_NORM);
 
 	poke_srmmu = poke_tsunami;
+
+	tsunami_setup_blockops();
 }
 
 __initfunc(static void poke_viking(void))
@@ -2738,8 +2708,7 @@
 		 * which we use the IOMMU.
 		 */
 		BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page, BTFIXUPCALL_NORM);
-		/* Also, this is so far the only chip which actually uses
-		   the page argument to flush_page_for_dma */
+
 		flush_page_for_dma_global = 0;
 	} else {
 		srmmu_name = "TI Viking/MXCC";
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/sun4c.c linux.14p6/arch/sparc/mm/sun4c.c
--- linux.vanilla/arch/sparc/mm/sun4c.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc/mm/sun4c.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: sun4c.c,v 1.173.2.1 1999/09/08 00:32:02 davem Exp $
+/* $Id: sun4c.c,v 1.173.2.5 1999/10/11 08:24:44 davem Exp $
  * sun4c.c: Doing in software what should be done in hardware.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -27,42 +27,19 @@
 #include <asm/mmu_context.h>
 #include <asm/sun4paddr.h>
 
-/* TODO: Make it such that interrupt handlers cannot dick with
- *       the user segment lists, most of the cli/sti pairs can
- *       disappear once that is taken care of.
- */
-
-/* XXX Ok the real performance win, I figure, will be to use a combined hashing
- * XXX and bitmap scheme to keep track of what we have mapped where.  The whole
- * XXX incentive is to make it such that the range flushes can be serviced
- * XXX always in near constant time. --DaveM
+/* Because of our dynamic kernel TLB miss strategy, and how
+ * our DVMA mapping allocation works, you _MUST_:
+ *
+ * 1) Disable interrupts _and_ not touch any dynamic kernel
+ *    memory while messing with kernel MMU state.  By
+ *    dynamic memory I mean any object which is not in
+ *    the kernel image itself or a task_struct (both of
+ *    which are locked into the MMU).
+ * 2) Disable interrupts while messing with user MMU state.
  */
 
 extern int num_segmaps, num_contexts;
 
-/* Define this to get extremely anal debugging, undefine for performance. */
-/* #define DEBUG_SUN4C_MM */
-
-#define UWINMASK_OFFSET (const unsigned long)(&(((struct task_struct *)0)->tss.uwinmask))
-
-/* This is used in many routines below. */
-#define FUW_INLINE do {							\
-	register int ctr asm("g5");					\
-	ctr = 0;							\
-	__asm__ __volatile__("\n"					\
-	"1:	ld	[%%g6 + %2], %%g4	! flush user windows\n"	\
-	"	orcc	%%g0, %%g4, %%g0\n"				\
-	"	add	%0, 1, %0\n"					\
-	"	bne	1b\n"						\
-	"	 save	%%sp, -64, %%sp\n"				\
-	"2:	subcc	%0, 1, %0\n"					\
-	"	bne	2b\n"						\
-	"	 restore %%g0, %%g0, %%g0\n"				\
-	: "=&r" (ctr)							\
-	: "0" (ctr), "i" (UWINMASK_OFFSET)				\
-	: "g4", "cc");							\
-} while(0);
-
 #ifdef CONFIG_SUN4
 #define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes
 #else
@@ -82,58 +59,21 @@
 #define MIN(a,b) ((a)<(b)?(a):(b))
 #endif
 
-
-#define KGPROF_PROFILING 0
-#if KGPROF_PROFILING
-#define KGPROF_DEPTH 3 /* this needs to match the code below */
-#define KGPROF_SIZE 100
-static struct {
-	unsigned addr[KGPROF_DEPTH];
-	unsigned count;
-} kgprof_counters[KGPROF_SIZE];
-
-/* just call this function from whatever function you think needs it then
-   look at /proc/cpuinfo to see where the function is being called from
-   and how often. This gives a type of "kernel gprof" */
-#define NEXT_PROF(prev,lvl) (prev>PAGE_OFFSET?__builtin_return_address(lvl):0)
-static inline void kgprof_profile(void)
-{
-	unsigned ret[KGPROF_DEPTH];
-	int i,j;
-	/* you can't use a variable argument to __builtin_return_address() */
-	ret[0] = (unsigned)__builtin_return_address(0);
-	ret[1] = (unsigned)NEXT_PROF(ret[0],1);
-	ret[2] = (unsigned)NEXT_PROF(ret[1],2);
-
-	for (i=0;i<KGPROF_SIZE && kgprof_counters[i].addr[0];i++) {
-		for (j=0;j<KGPROF_DEPTH;j++) 
-			if (ret[j] != kgprof_counters[i].addr[j]) break;
-		if (j==KGPROF_DEPTH) break;
-	}
-	if (i<KGPROF_SIZE) {		
-		for (j=0;j<KGPROF_DEPTH;j++)
-			kgprof_counters[i].addr[j] = ret[j];
-		kgprof_counters[i].count++;
-	}
-}
-#endif
-
-
 /* Flushing the cache. */
 struct sun4c_vac_props sun4c_vacinfo;
-static int ctxflushes, segflushes, pageflushes;
 unsigned long sun4c_kernel_faults;
 
 /* convert a virtual address to a physical address and vice
-   versa. Easy on the 4c */
+ * versa. Easy on the 4c
+ */
 static unsigned long sun4c_v2p(unsigned long vaddr)
 {
-  return(vaddr - PAGE_OFFSET);
+	return (vaddr - PAGE_OFFSET);
 }
 
 static unsigned long sun4c_p2v(unsigned long vaddr)
 {
-  return(vaddr + PAGE_OFFSET);
+	return (vaddr + PAGE_OFFSET);
 }
 
 
@@ -142,44 +82,64 @@
 {
 	unsigned long begin, end;
 
-	if(sun4c_vacinfo.on)
+	if (sun4c_vacinfo.on)
 		panic("SUN4C: AIEEE, trying to invalidate vac while"
                       " it is on.");
 
 	/* Clear 'valid' bit in all cache line tags */
 	begin = AC_CACHETAGS;
 	end = (AC_CACHETAGS + SUN4C_VAC_SIZE);
-	while(begin < end) {
+	while (begin < end) {
 		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 				     "r" (begin), "i" (ASI_CONTROL));
 		begin += sun4c_vacinfo.linesize;
 	}
 }
 
-/* Context level flush. */
-static inline void sun4c_flush_context_hw(void)
+static __inline__ void sun4c_flush_context_hw(void)
 {
 	unsigned long end = SUN4C_VAC_SIZE;
-	unsigned pgsz = PAGE_SIZE;
 
-	ctxflushes++;
-	__asm__ __volatile__("
-1:	subcc	%0, %2, %0
-	bg	1b
-	 sta	%%g0, [%0] %3
-	nop; nop; nop;		! Weitek hwbug
-"	: "=&r" (end)
-	: "0" (end), "r" (pgsz), "i" (ASI_HWFLUSHCONTEXT)
+	__asm__ __volatile__(
+		"1:	addcc	%0, -4096, %0\n\t"
+		"	bne	1b\n\t"
+		"	 sta	%%g0, [%0] %2"
+	: "=&r" (end)
+	: "0" (end), "i" (ASI_HWFLUSHCONTEXT)
 	: "cc");
 }
 
+/* Must be called minimally with IRQs disabled. */
+static void sun4c_flush_segment_hw(unsigned long addr)
+{
+	if (sun4c_get_segmap(addr) != invalid_segment) {
+		unsigned long vac_size = SUN4C_VAC_SIZE;
+
+		__asm__ __volatile__(
+			"1:	addcc	%0, -4096, %0\n\t"
+			"	bne	1b\n\t"
+			"	 sta	%%g0, [%2 + %0] %3"
+			: "=&r" (vac_size)
+			: "0" (vac_size), "r" (addr), "i" (ASI_HWFLUSHSEG)
+			: "cc");
+	}
+}
+
+/* Must be called minimally with interrupts disabled. */
+static __inline__ void sun4c_flush_page_hw(unsigned long addr)
+{
+	addr &= PAGE_MASK;
+	if ((int)sun4c_get_pte(addr) < 0)
+		__asm__ __volatile__("sta %%g0, [%0] %1"
+				     : : "r" (addr), "i" (ASI_HWFLUSHPAGE));
+}
+
 /* Don't inline the software version as it eats too many cache lines if expanded. */
 static void sun4c_flush_context_sw(void)
 {
 	unsigned long nbytes = SUN4C_VAC_SIZE;
 	unsigned long lsize = sun4c_vacinfo.linesize;
 
-	ctxflushes++;
 	__asm__ __volatile__("
 	add	%2, %2, %%g1
 	add	%2, %%g1, %%g2
@@ -203,72 +163,13 @@
 	: "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc");
 }
 
-/* Scrape the segment starting at ADDR from the virtual cache. */
-static inline void sun4c_flush_segment(unsigned long addr)
-{
-	if(sun4c_get_segmap(addr) == invalid_segment)
-		return;
-
-	segflushes++;
-	if(sun4c_vacinfo.do_hwflushes) {
-		unsigned long end = (addr + SUN4C_VAC_SIZE);
-
-		for( ; addr < end; addr += PAGE_SIZE)
-			__asm__ __volatile__("sta %%g0, [%0] %1;nop;nop;nop;\n\t" : :
-					     "r" (addr), "i" (ASI_HWFLUSHSEG));
-	} else {
-		unsigned long nbytes = SUN4C_VAC_SIZE;
-		unsigned long lsize = sun4c_vacinfo.linesize;
-
-		__asm__ __volatile__("add	%2, %2, %%g1\n\t"
-				     "add	%2, %%g1, %%g2\n\t"
-				     "add	%2, %%g2, %%g3\n\t"
-				     "add	%2, %%g3, %%g4\n\t"
-				     "add	%2, %%g4, %%g5\n\t"
-				     "add	%2, %%g5, %%o4\n\t"
-				     "add	%2, %%o4, %%o5\n"
-				     "1:\n\t"
-				     "subcc	%1, %%o5, %1\n\t"
-				     "sta	%%g0, [%0] %6\n\t"
-				     "sta	%%g0, [%0 + %2] %6\n\t"
-				     "sta	%%g0, [%0 + %%g1] %6\n\t"
-				     "sta	%%g0, [%0 + %%g2] %6\n\t"
-				     "sta	%%g0, [%0 + %%g3] %6\n\t"
-				     "sta	%%g0, [%0 + %%g4] %6\n\t"
-				     "sta	%%g0, [%0 + %%g5] %6\n\t"
-				     "sta	%%g0, [%0 + %%o4] %6\n\t"
-				     "bg	1b\n\t"
-				     " add	%0, %%o5, %0\n\t"
-				     : "=&r" (addr), "=&r" (nbytes), "=&r" (lsize)
-				     : "0" (addr), "1" (nbytes), "2" (lsize),
-				       "i" (ASI_FLUSHSEG)
-				     : "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc");
-	}
-}
-
-/* Call this version when you know hardware flushes are available. */
-static inline void sun4c_flush_segment_hw(unsigned long addr)
-{
-	if(sun4c_get_segmap(addr) != invalid_segment) {
-		unsigned long end;
-
-		segflushes++;
-		for(end = addr + SUN4C_VAC_SIZE; addr < end; addr += PAGE_SIZE)
-			__asm__ __volatile__("sta %%g0, [%0] %1"
-					     : : "r" (addr), "i" (ASI_HWFLUSHSEG));
-		/* Weitek POWER-UP hwbug workaround. */
-		__asm__ __volatile__("nop;nop;nop;	! Weitek hwbug");
-	}
-}
-
 /* Don't inline the software version as it eats too many cache lines if expanded. */
 static void sun4c_flush_segment_sw(unsigned long addr)
 {
-	if(sun4c_get_segmap(addr) != invalid_segment) {
+	if (sun4c_get_segmap(addr) != invalid_segment) {
 		unsigned long nbytes = SUN4C_VAC_SIZE;
 		unsigned long lsize = sun4c_vacinfo.linesize;
 
-		segflushes++;
 		__asm__ __volatile__("
 		add	%2, %2, %%g1
 		add	%2, %%g1, %%g2
@@ -300,12 +201,11 @@
 {
 	addr &= PAGE_MASK;
 
-	if((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) !=
-	   _SUN4C_PAGE_VALID)
+	if ((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) !=
+	    _SUN4C_PAGE_VALID)
 		return;
 
-	pageflushes++;
-	if(sun4c_vacinfo.do_hwflushes) {
+	if (sun4c_vacinfo.do_hwflushes) {
 		__asm__ __volatile__("sta %%g0, [%0] %1;nop;nop;nop;\n\t" : :
 				     "r" (addr), "i" (ASI_HWFLUSHPAGE));
 	} else {
@@ -338,30 +238,15 @@
 	}
 }
 
-/* Again, hw-only and sw-only cache page-level flush variants. */
-static inline void sun4c_flush_page_hw(unsigned long addr)
-{
-	addr &= PAGE_MASK;
-	if((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) ==
-	   _SUN4C_PAGE_VALID) {
-		pageflushes++;
-		__asm__ __volatile__("sta %%g0, [%0] %1"
-				     : : "r" (addr), "i" (ASI_HWFLUSHPAGE));
-		/* Weitek POWER-UP hwbug workaround. */
-		__asm__ __volatile__("nop;nop;nop;	! Weitek hwbug");
-	}
-}
-
 /* Don't inline the software version as it eats too many cache lines if expanded. */
 static void sun4c_flush_page_sw(unsigned long addr)
 {
 	addr &= PAGE_MASK;
-	if((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) ==
-	   _SUN4C_PAGE_VALID) {
+	if ((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) ==
+	    _SUN4C_PAGE_VALID) {
 		unsigned long left = PAGE_SIZE;
 		unsigned long lsize = sun4c_vacinfo.linesize;
 
-		pageflushes++;
 		__asm__ __volatile__("
 		add	%2, %2, %%g1
 		add	%2, %%g1, %%g2
@@ -411,7 +296,7 @@
 	unsigned long vaddr;
 
 	sun4c_put_segmap(0, pseg);
-	for(vaddr = 0; vaddr < SUN4C_REAL_PGDIR_SIZE; vaddr+=PAGE_SIZE)
+	for (vaddr = 0; vaddr < SUN4C_REAL_PGDIR_SIZE; vaddr += PAGE_SIZE)
 		sun4c_put_pte(vaddr, 0);
 	sun4c_put_segmap(0, invalid_segment);
 }
@@ -423,15 +308,15 @@
 
 	savectx = sun4c_get_context();
 	kernel_end = SUN4C_REAL_PGDIR_ALIGN(kernel_end);
-	for(ctx = 0; ctx < num_contexts; ctx++) {
+	for (ctx = 0; ctx < num_contexts; ctx++) {
 		sun4c_set_context(ctx);
-		for(vaddr = 0; vaddr < 0x20000000; vaddr += SUN4C_REAL_PGDIR_SIZE)
+		for (vaddr = 0; vaddr < 0x20000000; vaddr += SUN4C_REAL_PGDIR_SIZE)
 			sun4c_put_segmap(vaddr, invalid_segment);
-		for(vaddr = 0xe0000000; vaddr < KERNBASE; vaddr += SUN4C_REAL_PGDIR_SIZE)
+		for (vaddr = 0xe0000000; vaddr < KERNBASE; vaddr += SUN4C_REAL_PGDIR_SIZE)
 			sun4c_put_segmap(vaddr, invalid_segment);
-		for(vaddr = kernel_end; vaddr < KADB_DEBUGGER_BEGVM; vaddr += SUN4C_REAL_PGDIR_SIZE)
+		for (vaddr = kernel_end; vaddr < KADB_DEBUGGER_BEGVM; vaddr += SUN4C_REAL_PGDIR_SIZE)
 			sun4c_put_segmap(vaddr, invalid_segment);
-		for(vaddr = LINUX_OPPROM_ENDVM; vaddr; vaddr += SUN4C_REAL_PGDIR_SIZE)
+		for (vaddr = LINUX_OPPROM_ENDVM; vaddr; vaddr += SUN4C_REAL_PGDIR_SIZE)
 			sun4c_put_segmap(vaddr, invalid_segment);
 	}
 	sun4c_set_context(savectx);
@@ -442,7 +327,7 @@
 	sun4c_disable_vac();
 
 	if (ARCH_SUN4) {
-		switch(idprom->id_machtype) {
+		switch (idprom->id_machtype) {
 
 		case (SM_SUN4|SM_4_110):
 			sun4c_vacinfo.type = NONE;
@@ -477,12 +362,12 @@
 		default:
 			prom_printf("Cannot initialize VAC - wierd sun4 model idprom->id_machtype = %d", idprom->id_machtype);
 			prom_halt();
-		}
+		};
 	} else {
 		sun4c_vacinfo.type = WRITE_THROUGH;
 
-		if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
-		   (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+		if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+		    (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
 			/* PROM on SS1 lacks this info, to be super safe we
 			 * hard code it here since this arch is cast in stone.
 			 */
@@ -497,7 +382,7 @@
 		sun4c_vacinfo.do_hwflushes =
 		 prom_getintdefault(prom_root_node, "vac-hwflush", 0);
 
-		if(sun4c_vacinfo.do_hwflushes == 0)
+		if (sun4c_vacinfo.do_hwflushes == 0)
 			sun4c_vacinfo.do_hwflushes =
 			 prom_getintdefault(prom_root_node, "vac_hwflush", 0);
 
@@ -509,7 +394,7 @@
 
 	sun4c_vacinfo.num_lines =
 		(sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize);
-	switch(sun4c_vacinfo.linesize) {
+	switch (sun4c_vacinfo.linesize) {
 	case 16:
 		sun4c_vacinfo.log2lsize = 4;
 		break;
@@ -566,7 +451,7 @@
 			prom_printf("Unhandled number of segmaps: %d\n",
 				    num_segmaps);
 			prom_halt();
-	}
+	};
 	switch (num_contexts) {
 		case 8:
 			/* Default, nothing to do. */
@@ -574,19 +459,22 @@
 		case 16:
 			PATCH_INSN(num_context_patch1_16,
 				   num_context_patch1);
+#if 0
 			PATCH_INSN(num_context_patch2_16,
 				   num_context_patch2);
+#endif
 			break;
 		default:
 			prom_printf("Unhandled number of contexts: %d\n",
 				    num_contexts);
 			prom_halt();
-	}
-	if(sun4c_vacinfo.do_hwflushes != 0) {
+	};
+
+	if (sun4c_vacinfo.do_hwflushes != 0) {
 		PATCH_INSN(vac_hwflush_patch1_on, vac_hwflush_patch1);
 		PATCH_INSN(vac_hwflush_patch2_on, vac_hwflush_patch2);
 	} else {
-		switch(sun4c_vacinfo.linesize) {
+		switch (sun4c_vacinfo.linesize) {
 		case 16:
 			/* Default, nothing to do. */
 			break;
@@ -604,7 +492,7 @@
 __initfunc(static void sun4c_probe_mmu(void))
 {
 	if (ARCH_SUN4) {
-		switch(idprom->id_machtype) {
+		switch (idprom->id_machtype) {
 		case (SM_SUN4|SM_4_110):
 			prom_printf("No support for 4100 yet\n");
 			prom_halt();
@@ -631,10 +519,10 @@
 		default:
 			prom_printf("Invalid SUN4 model\n");
 			prom_halt();
-		}
+		};
 	} else {
-		if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
-	   	(idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+		if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+		    (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
 			/* Hardcode these just to be safe, PROM on SS1 does
 		 	* not have this info available in the root node.
 		 	*/
@@ -679,10 +567,10 @@
 {
 	extern unsigned long start;
 
-	if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) ||
-	   (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) ||
-	   (idprom->id_machtype == (SM_SUN4 | SM_4_330)) ||
-	   (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) {
+	if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) ||
+	    (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) ||
+	    (idprom->id_machtype == (SM_SUN4 | SM_4_330)) ||
+	    (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) {
 		/* Whee.. */
 		printk("SS2 cache bug detected, uncaching trap table page\n");
 		sun4c_flush_page((unsigned int) &start);
@@ -697,9 +585,9 @@
 	unsigned long page, end;
 
 	end = PAGE_ALIGN((addr + len));
-	while(addr < end) {
+	while (addr < end) {
 		page = get_free_page(GFP_KERNEL);
-		if(!page) {
+		if (!page) {
 			prom_printf("alloc_dvma: Cannot get a dvma page\n");
 			prom_halt();
 		}
@@ -726,6 +614,13 @@
 	unsigned long vaddr;
 	unsigned char pseg;
 	unsigned char locked;
+
+	/* For user mappings only, and completely hidden from kernel
+	 * TLB miss code.
+	 */
+	unsigned char ctx;
+	struct sun4c_mmu_entry *lru_next;
+	struct sun4c_mmu_entry *lru_prev;
 };
 
 static struct sun4c_mmu_entry mmu_entry_pool[SUN4C_MAX_SEGMAPS];
@@ -734,12 +629,15 @@
 {
 	int i;
 
-	for(i=0; i < SUN4C_MAX_SEGMAPS; i++) {
+	for (i = 0; i < SUN4C_MAX_SEGMAPS; i++) {
 		mmu_entry_pool[i].pseg = i;
 		mmu_entry_pool[i].next = 0;
 		mmu_entry_pool[i].prev = 0;
 		mmu_entry_pool[i].vaddr = 0;
 		mmu_entry_pool[i].locked = 0;
+		mmu_entry_pool[i].ctx = 0;
+		mmu_entry_pool[i].lru_next = 0;
+		mmu_entry_pool[i].lru_prev = 0;
 	}
 	mmu_entry_pool[invalid_segment].locked = 1;
 }
@@ -750,8 +648,8 @@
 	unsigned long start, end;
 
 	end = vaddr + SUN4C_REAL_PGDIR_SIZE;
-	for(start = vaddr; start < end; start += PAGE_SIZE)
-		if(sun4c_get_pte(start) & _SUN4C_PAGE_VALID)
+	for (start = vaddr; start < end; start += PAGE_SIZE)
+		if (sun4c_get_pte(start) & _SUN4C_PAGE_VALID)
 			sun4c_put_pte(start, (sun4c_get_pte(start) | bits_on) &
 				      ~bits_off);
 }
@@ -762,16 +660,16 @@
 	unsigned char pseg, ctx;
 #ifdef CONFIG_SUN4
 	/* sun4/110 and 260 have no kadb. */
-	if((idprom->id_machtype != (SM_SUN4 | SM_4_260)) && 
-	   (idprom->id_machtype != (SM_SUN4 | SM_4_110))) {
+	if ((idprom->id_machtype != (SM_SUN4 | SM_4_260)) && 
+	    (idprom->id_machtype != (SM_SUN4 | SM_4_110))) {
 #endif
-	for(vaddr = KADB_DEBUGGER_BEGVM;
-	    vaddr < LINUX_OPPROM_ENDVM;
-	    vaddr += SUN4C_REAL_PGDIR_SIZE) {
+	for (vaddr = KADB_DEBUGGER_BEGVM;
+	     vaddr < LINUX_OPPROM_ENDVM;
+	     vaddr += SUN4C_REAL_PGDIR_SIZE) {
 		pseg = sun4c_get_segmap(vaddr);
-		if(pseg != invalid_segment) {
+		if (pseg != invalid_segment) {
 			mmu_entry_pool[pseg].locked = 1;
-			for(ctx = 0; ctx < num_contexts; ctx++)
+			for (ctx = 0; ctx < num_contexts; ctx++)
 				prom_putsegment(ctx, vaddr, pseg);
 			fix_permissions(vaddr, _SUN4C_PAGE_PRIV, 0);
 		}
@@ -779,10 +677,10 @@
 #ifdef CONFIG_SUN4
 	}
 #endif
-	for(vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) {
+	for (vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) {
 		pseg = sun4c_get_segmap(vaddr);
 		mmu_entry_pool[pseg].locked = 1;
-		for(ctx = 0; ctx < num_contexts; ctx++)
+		for (ctx = 0; ctx < num_contexts; ctx++)
 			prom_putsegment(ctx, vaddr, pseg);
 		fix_permissions(vaddr, _SUN4C_PAGE_PRIV, _SUN4C_PAGE_NOCACHE);
 	}
@@ -792,13 +690,13 @@
 {
 	int i, ctx;
 
-	while(start < end) {
-		for(i=0; i < invalid_segment; i++)
-			if(!mmu_entry_pool[i].locked)
+	while (start < end) {
+		for (i = 0; i < invalid_segment; i++)
+			if (!mmu_entry_pool[i].locked)
 				break;
 		mmu_entry_pool[i].locked = 1;
 		sun4c_init_clean_segmap(i);
-		for(ctx = 0; ctx < num_contexts; ctx++)
+		for (ctx = 0; ctx < num_contexts; ctx++)
 			prom_putsegment(ctx, start, mmu_entry_pool[i].pseg);
 		start += SUN4C_REAL_PGDIR_SIZE;
 	}
@@ -815,13 +713,15 @@
 
 static struct sun4c_mmu_ring sun4c_context_ring[SUN4C_MAX_CONTEXTS]; /* used user entries */
 static struct sun4c_mmu_ring sun4c_ufree_ring;       /* free user entries */
+static struct sun4c_mmu_ring sun4c_ulru_ring;	     /* LRU user entries */
 struct sun4c_mmu_ring sun4c_kernel_ring;      /* used kernel entries */
 struct sun4c_mmu_ring sun4c_kfree_ring;       /* free kernel entries */
 
 static inline void sun4c_init_rings(unsigned long *mempool)
 {
 	int i;
-	for(i=0; i<SUN4C_MAX_CONTEXTS; i++) {
+
+	for (i = 0; i<SUN4C_MAX_CONTEXTS; i++) {
 		sun4c_context_ring[i].ringhd.next =
 			sun4c_context_ring[i].ringhd.prev =
 			&sun4c_context_ring[i].ringhd;
@@ -830,6 +730,9 @@
 	sun4c_ufree_ring.ringhd.next = sun4c_ufree_ring.ringhd.prev =
 		&sun4c_ufree_ring.ringhd;
 	sun4c_ufree_ring.num_entries = 0;
+	sun4c_ulru_ring.ringhd.lru_next = sun4c_ulru_ring.ringhd.lru_prev =
+		&sun4c_ulru_ring.ringhd;
+	sun4c_ulru_ring.num_entries = 0;
 	sun4c_kernel_ring.ringhd.next = sun4c_kernel_ring.ringhd.prev =
 		&sun4c_kernel_ring.ringhd;
 	sun4c_kernel_ring.num_entries = 0;
@@ -838,8 +741,8 @@
 	sun4c_kfree_ring.num_entries = 0;
 }
 
-static inline void add_ring(struct sun4c_mmu_ring *ring,
-			    struct sun4c_mmu_entry *entry)
+static void add_ring(struct sun4c_mmu_ring *ring,
+		     struct sun4c_mmu_entry *entry)
 {
 	struct sun4c_mmu_entry *head = &ring->ringhd;
 
@@ -849,49 +752,58 @@
 	ring->num_entries++;
 }
 
-static inline void add_ring_ordered(struct sun4c_mmu_ring *ring,
-				    struct sun4c_mmu_entry *entry)
+static __inline__ void add_lru(struct sun4c_mmu_entry *entry)
+{
+	struct sun4c_mmu_ring *ring = &sun4c_ulru_ring;
+	struct sun4c_mmu_entry *head = &ring->ringhd;
+
+	entry->lru_next = head;
+	(entry->lru_prev = head->lru_prev)->lru_next = entry;
+	head->lru_prev = entry;
+}
+
+static void add_ring_ordered(struct sun4c_mmu_ring *ring,
+			     struct sun4c_mmu_entry *entry)
 {
 	struct sun4c_mmu_entry *head = &ring->ringhd;
 	unsigned long addr = entry->vaddr;
 
-	if(head->next != &ring->ringhd) {
-		while((head->next != &ring->ringhd) && (head->next->vaddr < addr))
-			head = head->next;
-	}
+	while ((head->next != &ring->ringhd) && (head->next->vaddr < addr))
+		head = head->next;
+
 	entry->prev = head;
 	(entry->next = head->next)->prev = entry;
 	head->next = entry;
 	ring->num_entries++;
+
+	add_lru(entry);
 }
 
-static inline void remove_ring(struct sun4c_mmu_ring *ring,
-			       struct sun4c_mmu_entry *entry)
+static __inline__ void remove_ring(struct sun4c_mmu_ring *ring,
+				   struct sun4c_mmu_entry *entry)
 {
 	struct sun4c_mmu_entry *next = entry->next;
 
 	(next->prev = entry->prev)->next = next;
 	ring->num_entries--;
-#ifdef DEBUG_SUN4C_MM
-	if(ring->num_entries < 0)
-		panic("sun4c: Ring num_entries < 0!");
-#endif
 }
 
-static inline void free_user_entry(int ctx, struct sun4c_mmu_entry *entry)
+static void remove_lru(struct sun4c_mmu_entry *entry)
 {
-        remove_ring(sun4c_context_ring+ctx, entry);
-        add_ring(&sun4c_ufree_ring, entry);
+	struct sun4c_mmu_entry *next = entry->lru_next;
+
+	(next->lru_prev = entry->lru_prev)->lru_next = next;
 }
 
-static inline void assign_user_entry(int ctx, struct sun4c_mmu_entry *entry) 
+static void free_user_entry(int ctx, struct sun4c_mmu_entry *entry)
 {
-        remove_ring(&sun4c_ufree_ring, entry);
-        add_ring_ordered(sun4c_context_ring+ctx, entry);
+        remove_ring(sun4c_context_ring+ctx, entry);
+	remove_lru(entry);
+        add_ring(&sun4c_ufree_ring, entry);
 }
 
-static inline void free_kernel_entry(struct sun4c_mmu_entry *entry,
-				     struct sun4c_mmu_ring *ring)
+static void free_kernel_entry(struct sun4c_mmu_entry *entry,
+			      struct sun4c_mmu_ring *ring)
 {
         remove_ring(ring, entry);
         add_ring(&sun4c_kfree_ring, entry);
@@ -901,9 +813,9 @@
 {
 	int i;
 
-	while(howmany) {
-		for(i=0; i < invalid_segment; i++)
-			if(!mmu_entry_pool[i].locked)
+	while (howmany) {
+		for (i = 0; i < invalid_segment; i++)
+			if (!mmu_entry_pool[i].locked)
 				break;
 		mmu_entry_pool[i].locked = 1;
 		sun4c_init_clean_segmap(i);
@@ -916,54 +828,40 @@
 {
 	int i;
 
-	for(i=0; i < invalid_segment; i++) {
-		if(mmu_entry_pool[i].locked)
+	for (i = 0; i < invalid_segment; i++) {
+		if (mmu_entry_pool[i].locked)
 			continue;
 		sun4c_init_clean_segmap(i);
 		add_ring(&sun4c_ufree_ring, &mmu_entry_pool[i]);
 	}
 }
 
-static inline void sun4c_kernel_unmap(struct sun4c_mmu_entry *kentry)
+static void sun4c_kernel_unmap(struct sun4c_mmu_entry *kentry)
 {
 	int savectx, ctx;
 
 	savectx = sun4c_get_context();
-	for(ctx = 0; ctx < num_contexts; ctx++) {
+	for (ctx = 0; ctx < num_contexts; ctx++) {
 		sun4c_set_context(ctx);
 		sun4c_put_segmap(kentry->vaddr, invalid_segment);
 	}
 	sun4c_set_context(savectx);
 }
 
-static inline void sun4c_kernel_map(struct sun4c_mmu_entry *kentry)
+static void sun4c_kernel_map(struct sun4c_mmu_entry *kentry)
 {
 	int savectx, ctx;
 
 	savectx = sun4c_get_context();
-	for(ctx = 0; ctx < num_contexts; ctx++) {
+	for (ctx = 0; ctx < num_contexts; ctx++) {
 		sun4c_set_context(ctx);
 		sun4c_put_segmap(kentry->vaddr, kentry->pseg);
 	}
 	sun4c_set_context(savectx);
 }
 
-static inline void sun4c_user_unmap(struct sun4c_mmu_entry *uentry)
-{
-	sun4c_put_segmap(uentry->vaddr, invalid_segment);
-}
-
-static inline void sun4c_user_map(struct sun4c_mmu_entry *uentry)
-{
-	unsigned long start = uentry->vaddr;
-	unsigned long end = start + SUN4C_REAL_PGDIR_SIZE;
-
-	sun4c_put_segmap(uentry->vaddr, uentry->pseg);
-	while(start < end) {
-		sun4c_put_pte(start, 0);
-		start += PAGE_SIZE;
-	}
-}
+#define sun4c_user_unmap(__entry) \
+	sun4c_put_segmap((__entry)->vaddr, invalid_segment)
 
 static void sun4c_demap_context_hw(struct sun4c_mmu_ring *crp, unsigned char ctx)
 {
@@ -971,11 +869,11 @@
 	unsigned long flags;
 
 	save_and_cli(flags);
-	if(head->next != head) {
+	if (head->next != head) {
 		struct sun4c_mmu_entry *entry = head->next;
 		int savectx = sun4c_get_context();
 
-		FUW_INLINE
+		flush_user_windows();
 		sun4c_set_context(ctx);
 		sun4c_flush_context_hw();
 		do {
@@ -985,7 +883,7 @@
 			free_user_entry(ctx, entry);
 
 			entry = next;
-		} while(entry != head);
+		} while (entry != head);
 		sun4c_set_context(savectx);
 	}
 	restore_flags(flags);
@@ -997,11 +895,11 @@
 	unsigned long flags;
 
 	save_and_cli(flags);
-	if(head->next != head) {
+	if (head->next != head) {
 		struct sun4c_mmu_entry *entry = head->next;
 		int savectx = sun4c_get_context();
 
-		FUW_INLINE
+		flush_user_windows();
 		sun4c_set_context(ctx);
 		sun4c_flush_context_sw();
 		do {
@@ -1011,49 +909,31 @@
 			free_user_entry(ctx, entry);
 
 			entry = next;
-		} while(entry != head);
+		} while (entry != head);
 		sun4c_set_context(savectx);
 	}
 	restore_flags(flags);
 }
 
-static inline void sun4c_demap_one(struct sun4c_mmu_ring *crp, unsigned char ctx)
-{
-	/* by using .prev we get a kind of "lru" algorithm */
-	struct sun4c_mmu_entry *entry = crp->ringhd.prev;
-	unsigned long flags;
-	int savectx = sun4c_get_context();
-
-#ifdef DEBUG_SUN4C_MM
-	if(entry == &crp->ringhd)
-		panic("sun4c_demap_one: Freeing from empty ctx ring.");
-#endif
-	FUW_INLINE
-	save_and_cli(flags);
-	sun4c_set_context(ctx);
-	sun4c_flush_segment(entry->vaddr);
-	sun4c_user_unmap(entry);
-	free_user_entry(ctx, entry);
-	sun4c_set_context(savectx);
-	restore_flags(flags);
-}
-
 static int sun4c_user_taken_entries = 0;  /* This is how much we have.             */
 static int max_user_taken_entries = 0;    /* This limits us and prevents deadlock. */
 
-static inline struct sun4c_mmu_entry *sun4c_kernel_strategy(void)
+static struct sun4c_mmu_entry *sun4c_kernel_strategy(void)
 {
 	struct sun4c_mmu_entry *this_entry;
 
 	/* If some are free, return first one. */
-	if(sun4c_kfree_ring.num_entries) {
+	if (sun4c_kfree_ring.num_entries) {
 		this_entry = sun4c_kfree_ring.ringhd.next;
 		return this_entry;
 	}
 
 	/* Else free one up. */
 	this_entry = sun4c_kernel_ring.ringhd.prev;
-	sun4c_flush_segment(this_entry->vaddr);
+	if (sun4c_vacinfo.do_hwflushes)
+		sun4c_flush_segment_hw(this_entry->vaddr);
+	else
+		sun4c_flush_segment_sw(this_entry->vaddr);
 	sun4c_kernel_unmap(this_entry);
 	free_kernel_entry(this_entry, &sun4c_kernel_ring);
 	this_entry = sun4c_kfree_ring.ringhd.next;
@@ -1061,141 +941,73 @@
 	return this_entry;
 }
 
-void sun4c_shrink_kernel_ring(void)
-{
-	struct sun4c_mmu_entry *entry;
-	unsigned long flags;
-
-	/* If an interrupt comes in here, we die... */
-	save_and_cli(flags);
-
-	if (sun4c_user_taken_entries) {
-		entry = sun4c_kernel_strategy();
-        	remove_ring(&sun4c_kfree_ring, entry);
-		add_ring(&sun4c_ufree_ring, entry);
-		sun4c_user_taken_entries--;
-#if 0
-		printk("shrink: ufree= %d, kfree= %d, kernel= %d\n",
-			sun4c_ufree_ring.num_entries,
-			sun4c_kfree_ring.num_entries,
-			sun4c_kernel_ring.num_entries);
-#endif
-#ifdef DEBUG_SUN4C_MM
-		if(sun4c_user_taken_entries < 0)
-			panic("sun4c_shrink_kernel_ring: taken < 0.");
-#endif
-	}
-	restore_flags(flags);
-}
-
 /* Using this method to free up mmu entries eliminates a lot of
  * potential races since we have a kernel that incurs tlb
  * replacement faults.  There may be performance penalties.
+ *
+ * NOTE: Must be called with interrupts disabled.
  */
-static inline struct sun4c_mmu_entry *sun4c_user_strategy(void)
+static struct sun4c_mmu_entry *sun4c_user_strategy(void)
 {
-	struct ctx_list *next_one;
-	struct sun4c_mmu_ring *rp = 0;
+	struct sun4c_mmu_entry *entry;
 	unsigned char ctx;
-#ifdef DEBUG_SUN4C_MM
-	int lim = num_contexts;
-#endif
+	int savectx;
 
 	/* If some are free, return first one. */
-	if(sun4c_ufree_ring.num_entries) {
-#ifdef DEBUG_SUN4C_MM
-		if(sun4c_ufree_ring.ringhd.next == &sun4c_ufree_ring.ringhd)
-			panic("sun4c_user_strategy: num_entries!=0 but ring empty.");
-#endif
-		return sun4c_ufree_ring.ringhd.next;
+	if (sun4c_ufree_ring.num_entries) {
+		entry = sun4c_ufree_ring.ringhd.next;
+		goto unlink_out;
 	}
 
 	if (sun4c_user_taken_entries) {
-		sun4c_shrink_kernel_ring();
-#ifdef DEBUG_SUN4C_MM
-		if(sun4c_ufree_ring.ringhd.next == &sun4c_ufree_ring.ringhd)
-			panic("sun4c_user_strategy: kernel shrunk but ufree empty.");
-#endif
-		return sun4c_ufree_ring.ringhd.next;
+		entry = sun4c_kernel_strategy();
+		sun4c_user_taken_entries--;
+		goto kunlink_out;
 	}
 
-	/* Grab one from the LRU context. */
-	next_one = ctx_used.next;
-	while ((sun4c_context_ring[next_one->ctx_number].num_entries == 0)
-#ifdef DEBUG_SUN4C_MM
-	       && (--lim >= 0)
-#endif
-	       )
-		next_one = next_one->next;
+	/* Grab from the beginning of the LRU list. */
+	entry = sun4c_ulru_ring.ringhd.lru_next;
+	ctx = entry->ctx;
 
-#ifdef DEBUG_SUN4C_MM
-	if(lim < 0)
-		panic("No user segmaps!");
-#endif
+	savectx = sun4c_get_context();
+	flush_user_windows();
+	sun4c_set_context(ctx);
+	if (sun4c_vacinfo.do_hwflushes)
+		sun4c_flush_segment_hw(entry->vaddr);
+	else
+		sun4c_flush_segment_sw(entry->vaddr);
+	sun4c_user_unmap(entry);
+	remove_ring(sun4c_context_ring + ctx, entry);
+	remove_lru(entry);
+	sun4c_set_context(savectx);
 
-	ctx = next_one->ctx_number;
-	rp = &sun4c_context_ring[ctx];
+	return entry;
 
-	sun4c_demap_one(rp, ctx);
-#ifdef DEBUG_SUN4C_MM
-	if(sun4c_ufree_ring.ringhd.next == &sun4c_ufree_ring.ringhd)
-		panic("sun4c_user_strategy: demapped one but ufree empty.");
-#endif
-	return sun4c_ufree_ring.ringhd.next;
+unlink_out:
+	remove_ring(&sun4c_ufree_ring, entry);
+	return entry;
+kunlink_out:
+	remove_ring(&sun4c_kfree_ring, entry);
+	return entry;
 }
 
+/* NOTE: Must be called with interrupts disabled. */
 void sun4c_grow_kernel_ring(void)
 {
 	struct sun4c_mmu_entry *entry;
 
-#if 0
-	printk("grow: ");
-#endif
-
 	/* Prevent deadlock condition. */
-	if(sun4c_user_taken_entries >= max_user_taken_entries) {
-#if 0
-		printk("deadlock avoidance, taken= %d max= %d\n",
-		       sun4c_user_taken_entries, max_user_taken_entries);
-#endif
+	if (sun4c_user_taken_entries >= max_user_taken_entries)
 		return;
-	}
 
 	if (sun4c_ufree_ring.num_entries) {
 		entry = sun4c_ufree_ring.ringhd.next;
-#ifdef DEBUG_SUN4C_MM
-		if(entry == &sun4c_ufree_ring.ringhd)
-			panic("\nsun4c_grow_kernel_ring: num_entries!=0, ring empty.");
-#endif
         	remove_ring(&sun4c_ufree_ring, entry);
 		add_ring(&sun4c_kfree_ring, entry);
-#ifdef DEBUG_SUN4C_MM
-		if(sun4c_user_taken_entries < 0)
-			panic("\nsun4c_grow_kernel_ring: taken < 0.");
-#endif
 		sun4c_user_taken_entries++;
-#if 0
-		printk("ufree= %d, kfree= %d, kernel= %d\n",
-			sun4c_ufree_ring.num_entries,
-			sun4c_kfree_ring.num_entries,
-			sun4c_kernel_ring.num_entries);
-#endif
 	}
 }
 
-static inline void alloc_user_segment(unsigned long address, unsigned char ctx)
-{
-	struct sun4c_mmu_entry *entry;
-	unsigned long flags;
-
-	save_and_cli(flags);
-	entry = sun4c_user_strategy();
-	entry->vaddr = (address & SUN4C_REAL_PGDIR_MASK);
-	assign_user_entry(ctx, entry);
-	sun4c_user_map(entry);
-	restore_flags(flags);
-}
-
 /* This is now a fast in-window trap handler to avoid any and all races. */
 static void sun4c_quick_kernel_fault(unsigned long address)
 {
@@ -1234,7 +1046,7 @@
 #define BUCKET_PTE_PAGE(pte)   \
         (PAGE_OFFSET + (((pte) & SUN4C_PFN_MASK) << PAGE_SHIFT))
 
-static inline void get_locked_segment(unsigned long addr)
+static void get_locked_segment(unsigned long addr)
 {
 	struct sun4c_mmu_entry *stolen;
 	unsigned long flags;
@@ -1242,19 +1054,14 @@
 	save_and_cli(flags);
 	addr &= SUN4C_REAL_PGDIR_MASK;
 	stolen = sun4c_user_strategy();
-	remove_ring(&sun4c_ufree_ring, stolen);
 	max_user_taken_entries--;
-#ifdef DEBUG_SUN4C_MM
-	if(max_user_taken_entries < 0)
-		panic("get_locked_segment: max_user_taken < 0.");
-#endif
 	stolen->vaddr = addr;
-	FUW_INLINE
+	flush_user_windows();
 	sun4c_kernel_map(stolen);
 	restore_flags(flags);
 }
 
-static inline void free_locked_segment(unsigned long addr)
+static void free_locked_segment(unsigned long addr)
 {
 	struct sun4c_mmu_entry *entry;
 	unsigned long flags;
@@ -1265,14 +1072,13 @@
 	pseg = sun4c_get_segmap(addr);
 	entry = &mmu_entry_pool[pseg];
 
-	FUW_INLINE
-	sun4c_flush_segment(addr);
+	flush_user_windows();
+	if (sun4c_vacinfo.do_hwflushes)
+		sun4c_flush_segment_hw(addr);
+	else
+		sun4c_flush_segment_sw(addr);
 	sun4c_kernel_unmap(entry);
 	add_ring(&sun4c_ufree_ring, entry);
-#ifdef DEBUG_SUN4C_MM
-	if(max_user_taken_entries < 0)
-		panic("free_locked_segment: max_user_taken < 0.");
-#endif
 	max_user_taken_entries++;
 	restore_flags(flags);
 }
@@ -1284,8 +1090,8 @@
 	/* 32 buckets per segment... */
 	entry &= ~31;
 	start = entry;
-	for(end = (start + 32); start < end; start++)
-		if(sun4c_bucket[start] != BUCKET_EMPTY)
+	for (end = (start + 32); start < end; start++)
+		if (sun4c_bucket[start] != BUCKET_EMPTY)
 			return;
 
 	/* Entire segment empty, release it. */
@@ -1304,23 +1110,39 @@
 	int entry;
 
 	pages = __get_free_pages(GFP_KERNEL, TASK_STRUCT_ORDER);
-	if(!pages)
+	if (!pages)
 		return (struct task_struct *) 0;
 
-	for(entry = sun4c_lowbucket_avail; entry < NR_TASK_BUCKETS; entry++)
-		if(sun4c_bucket[entry] == BUCKET_EMPTY)
+	for (entry = sun4c_lowbucket_avail; entry < NR_TASK_BUCKETS; entry++)
+		if (sun4c_bucket[entry] == BUCKET_EMPTY)
 			break;
-	if(entry == NR_TASK_BUCKETS) {
+	if (entry == NR_TASK_BUCKETS) {
 		free_pages(pages, TASK_STRUCT_ORDER);
 		return (struct task_struct *) 0;
 	}
-	if(entry >= sun4c_lowbucket_avail)
+	if (entry >= sun4c_lowbucket_avail)
 		sun4c_lowbucket_avail = entry + 1;
 
 	addr = BUCKET_ADDR(entry);
 	sun4c_bucket[entry] = (union task_union *) addr;
-	if(sun4c_get_segmap(addr) == invalid_segment)
+	if (sun4c_get_segmap(addr) == invalid_segment)
 		get_locked_segment(addr);
+
+	/* We are changing the virtual color of the page(s)
+	 * so we must flush the cache to guarentee consistancy.
+	 */
+	if (sun4c_vacinfo.do_hwflushes) {
+		sun4c_flush_page_hw(pages);
+#ifndef CONFIG_SUN4	
+		sun4c_flush_page_hw(pages + PAGE_SIZE);
+#endif
+	} else {
+		sun4c_flush_page_sw(pages);
+#ifndef CONFIG_SUN4	
+		sun4c_flush_page_sw(pages + PAGE_SIZE);
+#endif
+	}
+
 	sun4c_put_pte(addr, BUCKET_PTE(pages));
 #ifndef CONFIG_SUN4	
 	sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE));
@@ -1344,7 +1166,7 @@
 	sun4c_put_pte(tsaddr + PAGE_SIZE, 0);
 #endif
 	sun4c_bucket[entry] = BUCKET_EMPTY;
-	if(entry < sun4c_lowbucket_avail)
+	if (entry < sun4c_lowbucket_avail)
 		sun4c_lowbucket_avail = entry;
 
 	free_pages(pages, TASK_STRUCT_ORDER);
@@ -1367,7 +1189,7 @@
 	sun4c_put_pte(tsaddr + PAGE_SIZE, 0);
 #endif
 	sun4c_bucket[entry] = BUCKET_EMPTY;
-	if(entry < sun4c_lowbucket_avail)
+	if (entry < sun4c_lowbucket_avail)
 		sun4c_lowbucket_avail = entry;
 
 	free_pages(pages, TASK_STRUCT_ORDER);
@@ -1378,10 +1200,10 @@
 {
 	int entry;
 
-	if(sizeof(union task_union) != (PAGE_SIZE << TASK_STRUCT_ORDER)) {
+	if (sizeof(union task_union) != (PAGE_SIZE << TASK_STRUCT_ORDER)) {
 		prom_printf("task union not %d page(s)!\n", 1 << TASK_STRUCT_ORDER);
 	}
-	for(entry = 0; entry < NR_TASK_BUCKETS; entry++)
+	for (entry = 0; entry < NR_TASK_BUCKETS; entry++)
 		sun4c_bucket[entry] = BUCKET_EMPTY;
 	sun4c_lowbucket_avail = 0;
 }
@@ -1501,7 +1323,7 @@
 	unsigned long page;
 
 	page = ((unsigned long)bufptr) & PAGE_MASK;
-	if(MAP_NR(page) > max_mapnr) {
+	if (MAP_NR(page) > max_mapnr) {
 		sun4c_flush_page(page);
 		return (__u32)bufptr; /* already locked */
 	}
@@ -1510,7 +1332,7 @@
 
 static void sun4c_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
-	while(sz >= 0) {
+	while (sz >= 0) {
 		sg[sz].dvma_addr = (__u32)sun4c_lockarea(sg[sz].addr, sg[sz].len);
 		sz--;
 	}
@@ -1518,14 +1340,14 @@
 
 static void sun4c_release_scsi_one(__u32 bufptr, unsigned long len, struct linux_sbus *sbus)
 {
-	if(bufptr < sun4c_iobuffer_start)
+	if (bufptr < sun4c_iobuffer_start)
 		return; /* On kernel stack or similar, see above */
 	sun4c_unlockarea((char *)bufptr, len);
 }
 
 static void sun4c_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
-	while(sz >= 0) {
+	while (sz >= 0) {
 		sun4c_unlockarea((char *)sg[sz].dvma_addr, sg[sz].len);
 		sz--;
 	}
@@ -1546,7 +1368,7 @@
 	sun4c_taskstack_start = SUN4C_LOCK_VADDR;
 	sun4c_taskstack_end = (sun4c_taskstack_start +
 			       (TASK_ENTRY_SIZE * NR_TASK_BUCKETS));
-	if(sun4c_taskstack_end >= SUN4C_LOCK_END) {
+	if (sun4c_taskstack_end >= SUN4C_LOCK_END) {
 		prom_printf("Too many tasks, decrease NR_TASK_BUCKETS please.\n");
 		prom_halt();
 	}
@@ -1576,12 +1398,12 @@
 {
 	unsigned long begin, end;
 
-	FUW_INLINE
+	flush_user_windows();
 	begin = (KERNBASE + SUN4C_REAL_PGDIR_SIZE);
 	end = (begin + SUN4C_VAC_SIZE);
 
-	if(sun4c_vacinfo.linesize == 32) {
-		while(begin < end) {
+	if (sun4c_vacinfo.linesize == 32) {
+		while (begin < end) {
 			__asm__ __volatile__("
 			ld	[%0 + 0x00], %%g0
 			ld	[%0 + 0x20], %%g0
@@ -1603,7 +1425,7 @@
 			begin += 512;
 		}
 	} else {
-		while(begin < end) {
+		while (begin < end) {
 			__asm__ __volatile__("
 			ld	[%0 + 0x00], %%g0
 			ld	[%0 + 0x10], %%g0
@@ -1631,29 +1453,31 @@
 {
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT && sun4c_context_ring[new_ctx].num_entries) {
-		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
-		unsigned long flags;
+	if (new_ctx != NO_CONTEXT) {
+		flush_user_windows();
+		if (sun4c_context_ring[new_ctx].num_entries) {
+			struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
+			unsigned long flags;
+
+			save_and_cli(flags);
+			if (head->next != head) {
+				struct sun4c_mmu_entry *entry = head->next;
+				int savectx = sun4c_get_context();
+
+				sun4c_set_context(new_ctx);
+				sun4c_flush_context_hw();
+				do {
+					struct sun4c_mmu_entry *next = entry->next;
 
-		save_and_cli(flags);
-		if(head->next != head) {
-			struct sun4c_mmu_entry *entry = head->next;
-			int savectx = sun4c_get_context();
-
-			FUW_INLINE
-			sun4c_set_context(new_ctx);
-			sun4c_flush_context_hw();
-			do {
-				struct sun4c_mmu_entry *next = entry->next;
-
-				sun4c_user_unmap(entry);
-				free_user_entry(new_ctx, entry);
+					sun4c_user_unmap(entry);
+					free_user_entry(new_ctx, entry);
 
-				entry = next;
-			} while(entry != head);
-			sun4c_set_context(savectx);
+					entry = next;
+				} while (entry != head);
+				sun4c_set_context(savectx);
+			}
+			restore_flags(flags);
 		}
-		restore_flags(flags);
 	}
 }
 
@@ -1661,29 +1485,28 @@
 {
 	int new_ctx = mm->context;
 	
-#if KGPROF_PROFILING
-	kgprof_profile();
-#endif
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
 		struct sun4c_mmu_entry *entry;
 		unsigned long flags;
 
-		FUW_INLINE
+		flush_user_windows();
+
 		save_and_cli(flags);
 
 		/* All user segmap chains are ordered on entry->vaddr. */
-		for(entry = head->next;
-		    (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
-		    entry = entry->next)
+		for (entry = head->next;
+		     (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
+		     entry = entry->next)
 			;
 
 		/* Tracing various job mixtures showed that this conditional
 		 * only passes ~35% of the time for most worse case situations,
 		 * therefore we avoid all of this gross overhead ~65% of the time.
 		 */
-		if((entry != head) && (entry->vaddr < end)) {
+		if ((entry != head) && (entry->vaddr < end)) {
 			int octx = sun4c_get_context();
+
 			sun4c_set_context(new_ctx);
 
 			/* At this point, always, (start >= entry->vaddr) and
@@ -1698,11 +1521,11 @@
 
 				/* "realstart" is always >= entry->vaddr */
 				realend = entry->vaddr + SUN4C_REAL_PGDIR_SIZE;
-				if(end < realend)
+				if (end < realend)
 					realend = end;
-				if((realend - entry->vaddr) <= (PAGE_SIZE << 3)) {
+				if ((realend - entry->vaddr) <= (PAGE_SIZE << 3)) {
 					unsigned long page = entry->vaddr;
-					while(page < realend) {
+					while (page < realend) {
 						sun4c_flush_page_hw(page);
 						page += PAGE_SIZE;
 					}
@@ -1712,14 +1535,13 @@
 					free_user_entry(new_ctx, entry);
 				}
 				entry = next;
-			} while((entry != head) && (entry->vaddr < end));
+			} while ((entry != head) && (entry->vaddr < end));
 			sun4c_set_context(octx);
 		}
 		restore_flags(flags);
 	}
 }
 
-/* XXX no save_and_cli/restore_flags needed, but put here if darkside still crashes */
 static void sun4c_flush_cache_page_hw(struct vm_area_struct *vma, unsigned long page)
 {
 	struct mm_struct *mm = vma->vm_mm;
@@ -1728,76 +1550,84 @@
 	/* Sun4c has no separate I/D caches so cannot optimize for non
 	 * text page flushes.
 	 */
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		int octx = sun4c_get_context();
+		unsigned long flags;
 
-		FUW_INLINE
+		flush_user_windows();
+		save_and_cli(flags);
 		sun4c_set_context(new_ctx);
 		sun4c_flush_page_hw(page);
 		sun4c_set_context(octx);
+		restore_flags(flags);
 	}
 }
 
 static void sun4c_flush_page_to_ram_hw(unsigned long page)
 {
+	unsigned long flags;
+
+	save_and_cli(flags);
 	sun4c_flush_page_hw(page);
+	restore_flags(flags);
 }
 
 static void sun4c_flush_cache_mm_sw(struct mm_struct *mm)
 {
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT && sun4c_context_ring[new_ctx].num_entries) {
-		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
-		unsigned long flags;
+	if (new_ctx != NO_CONTEXT) {
+		flush_user_windows();
 
-		save_and_cli(flags);
-		if(head->next != head) {
-			struct sun4c_mmu_entry *entry = head->next;
-			int savectx = sun4c_get_context();
-
-			FUW_INLINE
-			sun4c_set_context(new_ctx);
-			sun4c_flush_context_sw();
-			do {
-				struct sun4c_mmu_entry *next = entry->next;
+		if (sun4c_context_ring[new_ctx].num_entries) {
+			struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
+			unsigned long flags;
+
+			save_and_cli(flags);
+			if (head->next != head) {
+				struct sun4c_mmu_entry *entry = head->next;
+				int savectx = sun4c_get_context();
+
+				sun4c_set_context(new_ctx);
+				sun4c_flush_context_sw();
+				do {
+					struct sun4c_mmu_entry *next = entry->next;
 
-				sun4c_user_unmap(entry);
-				free_user_entry(new_ctx, entry);
+					sun4c_user_unmap(entry);
+					free_user_entry(new_ctx, entry);
 
-				entry = next;
-			} while(entry != head);
-			sun4c_set_context(savectx);
+					entry = next;
+				} while (entry != head);
+				sun4c_set_context(savectx);
+			}
+			restore_flags(flags);
 		}
-		restore_flags(flags);
 	}
 }
 
 static void sun4c_flush_cache_range_sw(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
 	int new_ctx = mm->context;
-	
-#if KGPROF_PROFILING
-	kgprof_profile();
-#endif
-	if(new_ctx != NO_CONTEXT) {
+
+	if (new_ctx != NO_CONTEXT) {
 		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
 		struct sun4c_mmu_entry *entry;
 		unsigned long flags;
 
-		FUW_INLINE
+		flush_user_windows();
+
 		save_and_cli(flags);
 		/* All user segmap chains are ordered on entry->vaddr. */
-		for(entry = head->next;
-		    (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
-		    entry = entry->next)
+		for (entry = head->next;
+		     (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
+		     entry = entry->next)
 			;
 
 		/* Tracing various job mixtures showed that this conditional
 		 * only passes ~35% of the time for most worse case situations,
 		 * therefore we avoid all of this gross overhead ~65% of the time.
 		 */
-		if((entry != head) && (entry->vaddr < end)) {
+		if ((entry != head) && (entry->vaddr < end)) {
 			int octx = sun4c_get_context();
 			sun4c_set_context(new_ctx);
 
@@ -1813,11 +1643,11 @@
 
 				/* "realstart" is always >= entry->vaddr */
 				realend = entry->vaddr + SUN4C_REAL_PGDIR_SIZE;
-				if(end < realend)
+				if (end < realend)
 					realend = end;
-				if((realend - entry->vaddr) <= (PAGE_SIZE << 3)) {
+				if ((realend - entry->vaddr) <= (PAGE_SIZE << 3)) {
 					unsigned long page = entry->vaddr;
-					while(page < realend) {
+					while (page < realend) {
 						sun4c_flush_page_sw(page);
 						page += PAGE_SIZE;
 					}
@@ -1827,7 +1657,7 @@
 					free_user_entry(new_ctx, entry);
 				}
 				entry = next;
-			} while((entry != head) && (entry->vaddr < end));
+			} while ((entry != head) && (entry->vaddr < end));
 			sun4c_set_context(octx);
 		}
 		restore_flags(flags);
@@ -1842,19 +1672,26 @@
 	/* Sun4c has no separate I/D caches so cannot optimize for non
 	 * text page flushes.
 	 */
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		int octx = sun4c_get_context();
+		unsigned long flags;
 
-		FUW_INLINE
+		flush_user_windows();
+		save_and_cli(flags);
 		sun4c_set_context(new_ctx);
 		sun4c_flush_page_sw(page);
 		sun4c_set_context(octx);
+		restore_flags(flags);
 	}
 }
 
 static void sun4c_flush_page_to_ram_sw(unsigned long page)
 {
+	unsigned long flags;
+
+	save_and_cli(flags);
 	sun4c_flush_page_sw(page);
+	restore_flags(flags);
 }
 
 /* Sun4c cache is unified, both instructions and data live there, so
@@ -1881,8 +1718,11 @@
 	flush_user_windows();
 	while (sun4c_kernel_ring.num_entries) {
 		next_entry = this_entry->next;
-		sun4c_flush_segment(this_entry->vaddr);
-		for(ctx = 0; ctx < num_contexts; ctx++) {
+		if (sun4c_vacinfo.do_hwflushes)
+			sun4c_flush_segment_hw(this_entry->vaddr);
+		else
+			sun4c_flush_segment_sw(this_entry->vaddr);
+		for (ctx = 0; ctx < num_contexts; ctx++) {
 			sun4c_set_context(ctx);
 			sun4c_put_segmap(this_entry->vaddr, invalid_segment);
 		}
@@ -1897,16 +1737,15 @@
 {
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
 		unsigned long flags;
 
 		save_and_cli(flags);
-		if(head->next != head) {
+		if (head->next != head) {
 			struct sun4c_mmu_entry *entry = head->next;
 			int savectx = sun4c_get_context();
 
-			FUW_INLINE
 			sun4c_set_context(new_ctx);
 			sun4c_flush_context_hw();
 			do {
@@ -1916,7 +1755,7 @@
 				free_user_entry(new_ctx, entry);
 
 				entry = next;
-			} while(entry != head);
+			} while (entry != head);
 			sun4c_set_context(savectx);
 		}
 		restore_flags(flags);
@@ -1927,26 +1766,21 @@
 {
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
 		struct sun4c_mmu_entry *entry;
 		unsigned long flags;
-#if KGPROF_PROFILING
-		kgprof_profile();
-#endif
 
 		save_and_cli(flags);
 		/* See commentary in sun4c_flush_cache_range_*(). */
-		for(entry = head->next;
-		    (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
-		    entry = entry->next)
+		for (entry = head->next;
+		     (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
+		     entry = entry->next)
 			;
 
-		if((entry != head) && (entry->vaddr < end)) {
+		if ((entry != head) && (entry->vaddr < end)) {
 			int octx = sun4c_get_context();
 
-			/* This window flush is paranoid I think... -DaveM */
-			FUW_INLINE
 			sun4c_set_context(new_ctx);
 			do {
 				struct sun4c_mmu_entry *next = entry->next;
@@ -1956,7 +1790,7 @@
 				free_user_entry(new_ctx, entry);
 
 				entry = next;
-			} while((entry != head) && (entry->vaddr < end));
+			} while ((entry != head) && (entry->vaddr < end));
 			sun4c_set_context(octx);
 		}
 		restore_flags(flags);
@@ -1968,15 +1802,17 @@
 	struct mm_struct *mm = vma->vm_mm;
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		int savectx = sun4c_get_context();
+		unsigned long flags;
 
-		FUW_INLINE
+		save_and_cli(flags);
 		sun4c_set_context(new_ctx);
 		page &= PAGE_MASK;
 		sun4c_flush_page_hw(page);
 		sun4c_put_pte(page, 0);
 		sun4c_set_context(savectx);
+		restore_flags(flags);
 	}
 }
 
@@ -1984,16 +1820,15 @@
 {
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
 		unsigned long flags;
 
 		save_and_cli(flags);
-		if(head->next != head) {
+		if (head->next != head) {
 			struct sun4c_mmu_entry *entry = head->next;
 			int savectx = sun4c_get_context();
 
-			FUW_INLINE
 			sun4c_set_context(new_ctx);
 			sun4c_flush_context_sw();
 			do {
@@ -2003,7 +1838,7 @@
 				free_user_entry(new_ctx, entry);
 
 				entry = next;
-			} while(entry != head);
+			} while (entry != head);
 			sun4c_set_context(savectx);
 		}
 		restore_flags(flags);
@@ -2014,27 +1849,21 @@
 {
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
 		struct sun4c_mmu_entry *entry;
 		unsigned long flags;
 
-#if KGPROF_PROFILING
-		kgprof_profile();
-#endif
-
 		save_and_cli(flags);
 		/* See commentary in sun4c_flush_cache_range_*(). */
-		for(entry = head->next;
-		    (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
-		    entry = entry->next)
+		for (entry = head->next;
+		     (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
+		     entry = entry->next)
 			;
 
-		if((entry != head) && (entry->vaddr < end)) {
+		if ((entry != head) && (entry->vaddr < end)) {
 			int octx = sun4c_get_context();
 
-			/* This window flush is paranoid I think... -DaveM */
-			FUW_INLINE
 			sun4c_set_context(new_ctx);
 			do {
 				struct sun4c_mmu_entry *next = entry->next;
@@ -2044,7 +1873,7 @@
 				free_user_entry(new_ctx, entry);
 
 				entry = next;
-			} while((entry != head) && (entry->vaddr < end));
+			} while ((entry != head) && (entry->vaddr < end));
 			sun4c_set_context(octx);
 		}
 		restore_flags(flags);
@@ -2056,15 +1885,17 @@
 	struct mm_struct *mm = vma->vm_mm;
 	int new_ctx = mm->context;
 
-	if(new_ctx != NO_CONTEXT) {
+	if (new_ctx != NO_CONTEXT) {
 		int savectx = sun4c_get_context();
+		unsigned long flags;
 
-		FUW_INLINE
+		save_and_cli(flags);
 		sun4c_set_context(new_ctx);
 		page &= PAGE_MASK;
 		sun4c_flush_page_sw(page);
 		sun4c_put_pte(page, 0);
 		sun4c_set_context(savectx);
+		restore_flags(flags);
 	}
 }
 
@@ -2077,7 +1908,6 @@
 {
 }
 
-
 void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
 		     int bus_type, int rdonly)
 {
@@ -2085,7 +1915,7 @@
 
 	page_entry = ((physaddr >> PAGE_SHIFT) & SUN4C_PFN_MASK);
 	page_entry |= ((pg_iobits | _SUN4C_PAGE_PRIV) & ~(_SUN4C_PAGE_PRESENT));
-	if(rdonly)
+	if (rdonly)
 		page_entry &= ~_SUN4C_WRITEABLE;
 	sun4c_put_pte(virt_addr, page_entry);
 }
@@ -2100,7 +1930,7 @@
 	struct ctx_list *ctxp;
 
 	ctxp = ctx_free.next;
-	if(ctxp != &ctx_free) {
+	if (ctxp != &ctx_free) {
 		remove_from_ctx_list(ctxp);
 		add_to_used_ctxlist(ctxp);
 		mm->context = ctxp->ctx_number;
@@ -2108,26 +1938,22 @@
 		return;
 	}
 	ctxp = ctx_used.next;
-	if(ctxp->ctx_mm == current->mm)
+	if (ctxp->ctx_mm == current->mm)
 		ctxp = ctxp->next;
-#ifdef DEBUG_SUN4C_MM
-	if(ctxp == &ctx_used)
-		panic("out of mmu contexts");
-#endif
 	remove_from_ctx_list(ctxp);
 	add_to_used_ctxlist(ctxp);
 	ctxp->ctx_mm->context = NO_CONTEXT;
 	ctxp->ctx_mm = mm;
 	mm->context = ctxp->ctx_number;
 	sun4c_demap_context_hw(&sun4c_context_ring[ctxp->ctx_number],
-			    ctxp->ctx_number);
+			       ctxp->ctx_number);
 }
 
 static void sun4c_switch_to_context_hw(struct task_struct *tsk)
 {
 	struct ctx_list *ctx;
 
-	if(tsk->mm->context == NO_CONTEXT) {
+	if (tsk->mm->context == NO_CONTEXT) {
 		sun4c_alloc_context_hw(tsk->mm);
 	} else {
 		/* Update the LRU ring of contexts. */
@@ -2141,7 +1967,7 @@
 static void sun4c_init_new_context_hw(struct mm_struct *mm)
 {
 	sun4c_alloc_context_hw(mm);
-	if(mm == current->mm)
+	if (mm == current->mm)
 		sun4c_set_context(mm->context);
 }
 
@@ -2149,7 +1975,7 @@
 {
 	struct ctx_list *ctx_old;
 
-	if(mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) {
+	if (mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) {
 		sun4c_demap_context_hw(&sun4c_context_ring[mm->context], mm->context);
 		ctx_old = ctx_list_pool + mm->context;
 		remove_from_ctx_list(ctx_old);
@@ -2163,7 +1989,7 @@
 	struct ctx_list *ctxp;
 
 	ctxp = ctx_free.next;
-	if(ctxp != &ctx_free) {
+	if (ctxp != &ctx_free) {
 		remove_from_ctx_list(ctxp);
 		add_to_used_ctxlist(ctxp);
 		mm->context = ctxp->ctx_number;
@@ -2171,26 +1997,22 @@
 		return;
 	}
 	ctxp = ctx_used.next;
-	if(ctxp->ctx_mm == current->mm)
+	if (ctxp->ctx_mm == current->mm)
 		ctxp = ctxp->next;
-#ifdef DEBUG_SUN4C_MM
-	if(ctxp == &ctx_used)
-		panic("out of mmu contexts");
-#endif
 	remove_from_ctx_list(ctxp);
 	add_to_used_ctxlist(ctxp);
 	ctxp->ctx_mm->context = NO_CONTEXT;
 	ctxp->ctx_mm = mm;
 	mm->context = ctxp->ctx_number;
 	sun4c_demap_context_sw(&sun4c_context_ring[ctxp->ctx_number],
-			    ctxp->ctx_number);
+			       ctxp->ctx_number);
 }
 
 static void sun4c_switch_to_context_sw(struct task_struct *tsk)
 {
 	struct ctx_list *ctx;
 
-	if(tsk->mm->context == NO_CONTEXT) {
+	if (tsk->mm->context == NO_CONTEXT) {
 		sun4c_alloc_context_sw(tsk->mm);
 	} else {
 		/* Update the LRU ring of contexts. */
@@ -2204,7 +2026,7 @@
 static void sun4c_init_new_context_sw(struct mm_struct *mm)
 {
 	sun4c_alloc_context_sw(mm);
-	if(mm == current->mm)
+	if (mm == current->mm)
 		sun4c_set_context(mm->context);
 }
 
@@ -2212,7 +2034,7 @@
 {
 	struct ctx_list *ctx_old;
 
-	if(mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) {
+	if (mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) {
 		sun4c_demap_context_sw(&sun4c_context_ring[mm->context], mm->context);
 		ctx_old = ctx_list_pool + mm->context;
 		remove_from_ctx_list(ctx_old);
@@ -2227,7 +2049,7 @@
 	int len;
 
 	used_user_entries = 0;
-	for(i=0; i < num_contexts; i++)
+	for (i = 0; i < num_contexts; i++)
 		used_user_entries += sun4c_context_ring[i].num_entries;
 
 	len = sprintf(buf, 
@@ -2241,10 +2063,7 @@
 		"usedpsegs\t: %d\n"
 		"ufreepsegs\t: %d\n"
 		"user_taken\t: %d\n"
-		"max_taken\t: %d\n"
-		"context\t\t: %d flushes\n"
-		"segment\t\t: %d flushes\n"
-		"page\t\t: %d flushes\n",
+		"max_taken\t: %d\n",
 		sun4c_vacinfo.num_bytes,
 		(sun4c_vacinfo.do_hwflushes ? "yes" : "no"),
 		sun4c_vacinfo.linesize,
@@ -2255,22 +2074,7 @@
 		used_user_entries,
 		sun4c_ufree_ring.num_entries,
 		sun4c_user_taken_entries,
-		max_user_taken_entries,
-		ctxflushes, segflushes, pageflushes);
-
-#if KGPROF_PROFILING
-	{
-		int i,j;
-		len += sprintf(buf + len,"kgprof profiling:\n");
-		for (i=0;i<KGPROF_SIZE && kgprof_counters[i].addr[0];i++) {
-			len += sprintf(buf + len,"%5d  ",kgprof_counters[i].count);
-			for (j=0;j<KGPROF_DEPTH;j++) {
-				len += sprintf(buf + len,"%08x ",kgprof_counters[i].addr[j]);
-			}
-			len += sprintf(buf + len,"\n");
-		}
-	}
-#endif
+		max_user_taken_entries);
 
 	return len;
 }
@@ -2279,13 +2083,6 @@
  * data structures.
  */
 
-#if 0 /* Not used due to BTFIXUPs */
-static unsigned int sun4c_pmd_align(unsigned int addr) { return SUN4C_PMD_ALIGN(addr); }
-#endif
-#if 0 /* Not used due to BTFIXUPs */
-static unsigned int sun4c_pgdir_align(unsigned int addr) { return SUN4C_PGDIR_ALIGN(addr); }
-#endif
-
 /* First the functions which the mid-level code uses to directly
  * manipulate the software page tables.  Some defines since we are
  * emulating the i386 page directory layout.
@@ -2297,17 +2094,6 @@
 #define PGD_DIRTY    0x040
 #define PGD_TABLE    (PGD_PRESENT | PGD_RW | PGD_USER | PGD_ACCESSED | PGD_DIRTY)
 
-#if 0 /* Not used due to BTFIXUPs */
-static unsigned long sun4c_vmalloc_start(void)
-{
-	return SUN4C_VMALLOC_START;
-}
-#endif
-
-#if 0 /* Not used due to BTFIXUPs */
-static int sun4c_pte_none(pte_t pte)		{ return !pte_val(pte); }
-#endif
-
 static int sun4c_pte_present(pte_t pte)
 {
 	return ((pte_val(pte) & (_SUN4C_PAGE_PRESENT | _SUN4C_PAGE_PRIV)) != 0);
@@ -2336,48 +2122,6 @@
  * The following only work if pte_present() is true.
  * Undefined behaviour if not..
  */
-#if 0 /* Not used due to BTFIXUPs */
-static int sun4c_pte_write(pte_t pte)
-{
-	return pte_val(pte) & _SUN4C_PAGE_WRITE;
-}
-#endif
-
-#if 0 /* Not used due to BTFIXUPs */
-static int sun4c_pte_dirty(pte_t pte)
-{
-	return pte_val(pte) & _SUN4C_PAGE_MODIFIED;
-}
-#endif
-
-#if 0 /* Not used due to BTFIXUPs */
-static int sun4c_pte_young(pte_t pte)
-{
-	return pte_val(pte) & _SUN4C_PAGE_ACCESSED;
-}
-#endif
-
-#if 0 /* Not used due to BTFIXUPs */
-static pte_t sun4c_pte_wrprotect(pte_t pte)
-{
-	return __pte(pte_val(pte) & ~(_SUN4C_PAGE_WRITE | _SUN4C_PAGE_SILENT_WRITE));
-}
-#endif
-
-#if 0 /* Not used due to BTFIXUPs */
-static pte_t sun4c_pte_mkclean(pte_t pte)
-{
-	return __pte(pte_val(pte) & ~(_SUN4C_PAGE_MODIFIED | _SUN4C_PAGE_SILENT_WRITE));
-}
-#endif
-
-#if 0 /* Not used due to BTFIXUPs */
-static pte_t sun4c_pte_mkold(pte_t pte)
-{
-	return __pte(pte_val(pte) & ~(_SUN4C_PAGE_ACCESSED | _SUN4C_PAGE_SILENT_READ));
-}
-#endif
-
 static pte_t sun4c_pte_mkwrite(pte_t pte)
 {
 	pte = __pte(pte_val(pte) | _SUN4C_PAGE_WRITE);
@@ -2421,14 +2165,6 @@
 	return __pte(((page - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(pgprot));
 }
 
-#if 0 /* Not used due to BTFIXUPs */
-static pte_t sun4c_pte_modify(pte_t pte, pgprot_t newprot)
-{
-	return __pte((pte_val(pte) & _SUN4C_PAGE_CHG_MASK) |
-		     pgprot_val(newprot));
-}
-#endif
-
 static unsigned long sun4c_pte_page(pte_t pte)
 {
 	return (PAGE_OFFSET + ((pte_val(pte) & SUN4C_PFN_MASK) << (PAGE_SHIFT)));
@@ -2489,7 +2225,7 @@
 
 static pte_t *sun4c_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
 {
-	if(address >= SUN4C_LOCK_VADDR)
+	if (address >= SUN4C_LOCK_VADDR)
 		return NULL;
 	address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1);
 	if (sun4c_pmd_none(*pmd))
@@ -2529,7 +2265,7 @@
 {
 	unsigned long *ret;
 
-	if((ret = pgd_quicklist) != NULL) {
+	if ((ret = pgd_quicklist) != NULL) {
 		pgd_quicklist = (unsigned long *)(*ret);
 		ret[0] = ret[1];
 		pgtable_cache_size--;
@@ -2548,15 +2284,15 @@
 static int sun4c_check_pgt_cache(int low, int high)
 {
 	int freed = 0;
-	if(pgtable_cache_size > high) {
+	if (pgtable_cache_size > high) {
 		do {
-			if(pgd_quicklist)
+			if (pgd_quicklist)
 				free_pgd_slow(get_pgd_fast()), freed++;
-			if(pmd_quicklist)
+			if (pmd_quicklist)
 				free_pmd_slow(get_pmd_fast()), freed++;
-			if(pte_quicklist)
+			if (pte_quicklist)
 				free_pte_slow(get_pte_fast()), freed++;
-		} while(pgtable_cache_size > low);
+		} while (pgtable_cache_size > low);
 	}
 	return freed;
 }
@@ -2577,7 +2313,7 @@
 {
 	unsigned long *ret;
 
-	if((ret = (unsigned long *)pte_quicklist) != NULL) {
+	if ((ret = (unsigned long *)pte_quicklist) != NULL) {
 		pte_quicklist = (unsigned long *)(*ret);
 		ret[0] = ret[1];
 		pgtable_cache_size--;
@@ -2680,9 +2416,9 @@
 
 	if (vma->vm_file)
 		dentry = vma->vm_file->f_dentry;
-	if(dentry)
+	if (dentry)
 		inode = dentry->d_inode;
-	if(inode) {
+	if (inode) {
 		unsigned long offset = (address & PAGE_MASK) - vma->vm_start;
 		struct vm_area_struct *vmaring = inode->i_mmap; 
 		int alias_found = 0;
@@ -2691,19 +2427,21 @@
 			unsigned long start;
 
 			/* Do not mistake ourselves as another mapping. */
-			if(vmaring == vma)
+			if (vmaring == vma)
 				continue;
 
 			if (S4CVAC_BADALIAS(vaddr, address)) {
 				alias_found++;
 				start = vmaring->vm_start;
-				while(start < vmaring->vm_end) {
+				while (start < vmaring->vm_end) {
 					pgdp = sun4c_pgd_offset(vmaring->vm_mm, start);
-					if(!pgdp) goto next;
+					if (!pgdp)
+						goto next;
 					ptep = sun4c_pte_offset((pmd_t *) pgdp, start);
-					if(!ptep) goto next;
+					if (!ptep)
+						goto next;
 
-					if(pte_val(*ptep) & _SUN4C_PAGE_PRESENT) {
+					if (pte_val(*ptep) & _SUN4C_PAGE_PRESENT) {
 						flush_cache_page(vmaring, start);
 						*ptep = __pte(pte_val(*ptep) |
 							      _SUN4C_PAGE_NOCACHE);
@@ -2715,7 +2453,7 @@
 			}
 		} while ((vmaring = vmaring->vm_next_share) != NULL);
 
-		if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) {
+		if (alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) {
 			pgdp = sun4c_pgd_offset(vma->vm_mm, address);
 			ptep = sun4c_pte_offset((pmd_t *) pgdp, address);
 			*ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE);
@@ -2724,16 +2462,62 @@
 	}
 }
 
+/* An experiment, turn off by default for now... -DaveM */
+#define SUN4C_PRELOAD_PSEG
+
 void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
 {
 	unsigned long flags;
+	int pseg;
 
 	save_and_cli(flags);
 	address &= PAGE_MASK;
-	if(sun4c_get_segmap(address) == invalid_segment)
-		alloc_user_segment(address, sun4c_get_context());
+	if ((pseg = sun4c_get_segmap(address)) == invalid_segment) {
+		struct sun4c_mmu_entry *entry = sun4c_user_strategy();
+		struct mm_struct *mm = vma->vm_mm;
+		unsigned long start, end;
+
+		entry->vaddr = start = (address & SUN4C_REAL_PGDIR_MASK);
+		entry->ctx = mm->context;
+		add_ring_ordered(sun4c_context_ring + mm->context, entry);
+		sun4c_put_segmap(entry->vaddr, entry->pseg);
+		end = start + SUN4C_REAL_PGDIR_SIZE;
+		while (start < end) {
+#ifdef SUN4C_PRELOAD_PSEG
+			pgd_t *pgdp = sun4c_pgd_offset(mm, start);
+			pte_t *ptep;
+
+			if (!pgdp)
+				goto no_mapping;
+			ptep = sun4c_pte_offset((pmd_t *) pgdp, start);
+			if (!ptep || !(pte_val(*ptep) & _SUN4C_PAGE_PRESENT))
+				goto no_mapping;
+			sun4c_put_pte(start, pte_val(*ptep));
+			goto next;
+
+		no_mapping:
+#endif
+			sun4c_put_pte(start, 0);
+#ifdef SUN4C_PRELOAD_PSEG
+		next:
+#endif
+			start += PAGE_SIZE;
+		}
+		if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))
+			sun4c_vac_alias_fixup(vma, address, pte);
+#ifndef SUN4C_PRELOAD_PSEG
+		sun4c_put_pte(address, pte_val(pte));
+#endif
+		restore_flags(flags);
+		return;
+	} else {
+		struct sun4c_mmu_entry *entry = &mmu_entry_pool[pseg];
+
+		remove_lru(entry);
+		add_lru(entry);
+	}
 
-	if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))
+	if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))
 		sun4c_vac_alias_fixup(vma, address, pte);
 
 	sun4c_put_pte(address, pte_val(pte));
@@ -2786,8 +2570,8 @@
 	start_mem = sparc_context_init(start_mem, num_contexts);
 	start_mem = free_area_init(start_mem, end_mem);
 	cnt = 0;
-	for(i = 0; i < num_segmaps; i++)
-		if(mmu_entry_pool[i].locked)
+	for (i = 0; i < num_segmaps; i++)
+		if (mmu_entry_pool[i].locked)
 			cnt++;
 
 	max_user_taken_entries = num_segmaps - cnt - 40 - 1;
@@ -2839,7 +2623,7 @@
 
 	BTFIXUPSET_CALL(flush_cache_all, sun4c_flush_cache_all, BTFIXUPCALL_NORM);
 
-	if(sun4c_vacinfo.do_hwflushes) {
+	if (sun4c_vacinfo.do_hwflushes) {
 		BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_hw, BTFIXUPCALL_NORM);
 		BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_hw, BTFIXUPCALL_NORM);
 		BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_hw, BTFIXUPCALL_NORM);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/swift.S linux.14p6/arch/sparc/mm/swift.S
--- linux.vanilla/arch/sparc/mm/swift.S	Thu Jan  1 01:00:00 1970
+++ linux.14p6/arch/sparc/mm/swift.S	Tue Nov 16 15:13:27 1999
@@ -0,0 +1,274 @@
+/* $Id: swift.S,v 1.1.2.3 1999/10/14 01:00:17 davem Exp $
+ * swift.S: MicroSparc-II mmu/cache operations.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+#include <asm/asm_offsets.h>
+
+#define WINDOW_FLUSH(tmp1, tmp2)					\
+	mov	0, tmp1;						\
+98:	ld	[%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2;	\
+	orcc	%g0, tmp2, %g0;						\
+	add	tmp1, 1, tmp1;						\
+	bne	98b;							\
+	 save	%sp, -64, %sp;						\
+99:	subcc	tmp1, 1, tmp1;						\
+	bne	99b;							\
+	 restore %g0, %g0, %g0;
+
+	.text
+	.align	4
+
+#if 1	/* XXX screw this, I can't get the VAC flushes working
+	 * XXX reliably... -DaveM
+	 */
+	.globl	swift_flush_cache_all, swift_flush_cache_mm
+	.globl	swift_flush_cache_range, swift_flush_cache_page
+	.globl	swift_flush_page_for_dma, swift_flush_chunk
+	.globl	swift_flush_page_to_ram
+
+swift_flush_cache_all:
+swift_flush_cache_mm:
+swift_flush_cache_range:
+swift_flush_cache_page:
+swift_flush_page_for_dma:
+swift_flush_chunk:
+swift_flush_page_to_ram:
+	sethi	%hi(0x2000), %o0
+1:	subcc	%o0, 0x10, %o0
+	sta	%g0, [%o0] ASI_M_TXTC_TAG
+	sta	%g0, [%o0] ASI_M_DATAC_TAG
+	bne	1b
+	 nop
+	retl
+	 nop
+#else
+
+	.globl	swift_flush_cache_all
+swift_flush_cache_all:
+	WINDOW_FLUSH(%g4, %g5)
+
+	/* Just clear out all the tags. */
+	sethi	%hi(16 * 1024), %o0
+1:	subcc	%o0, 16, %o0
+	sta	%g0, [%o0] ASI_M_TXTC_TAG
+	bne	1b
+	 sta	%g0, [%o0] ASI_M_DATAC_TAG
+	retl
+	 nop
+
+	.globl	swift_flush_cache_mm
+swift_flush_cache_mm:
+#ifndef __SMP__
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	swift_flush_cache_mm_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+	rd	%psr, %g1
+	andn	%g1, PSR_ET, %g3
+	wr	%g3, 0x0, %psr
+	nop
+	nop
+	mov	SRMMU_CTX_REG, %g7
+	lda	[%g7] ASI_M_MMUREGS, %g5
+	sta	%g2, [%g7] ASI_M_MMUREGS
+
+#if 1
+	sethi	%hi(0x2000), %o0
+1:	subcc	%o0, 0x10, %o0
+	sta	%g0, [%o0] ASI_M_FLUSH_CTX
+	bne	1b
+	 nop
+#else
+	clr	%o0
+	or	%g0, 2048, %g7
+	or	%g0, 2048, %o1
+	add	%o1, 2048, %o2
+	add	%o2, 2048, %o3
+	mov	16, %o4
+	add	%o4, 2048, %o5
+	add	%o5, 2048, %g2
+	add	%g2, 2048, %g3
+1:	sta	%g0, [%o0      ] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o1] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o2] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o3] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o4] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o5] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %g2] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %g3] ASI_M_FLUSH_CTX
+	subcc	%g7, 32, %g7
+	bne	1b
+	 add	%o0, 32, %o0
+#endif
+
+	mov	SRMMU_CTX_REG, %g7
+	sta	%g5, [%g7] ASI_M_MMUREGS
+	wr	%g1, 0x0, %psr
+	nop
+	nop
+swift_flush_cache_mm_out:
+	retl
+	 nop
+
+	.globl	swift_flush_cache_range
+swift_flush_cache_range:
+	sub	%o2, %o1, %o2
+	sethi	%hi(4096), %o3
+	cmp	%o2, %o3
+	bgu	swift_flush_cache_mm
+	 nop
+	b	70f
+	 nop
+
+	.globl	swift_flush_cache_page
+swift_flush_cache_page:
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+70:
+#ifndef __SMP__
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	swift_flush_cache_page_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+	rd	%psr, %g1
+	andn	%g1, PSR_ET, %g3
+	wr	%g3, 0x0, %psr
+	nop
+	nop
+	mov	SRMMU_CTX_REG, %g7
+	lda	[%g7] ASI_M_MMUREGS, %g5
+	sta	%g2, [%g7] ASI_M_MMUREGS
+
+	andn	%o1, (PAGE_SIZE - 1), %o1
+#if 1
+	sethi	%hi(0x1000), %o0
+1:	subcc	%o0, 0x10, %o0
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	bne	1b
+	 nop
+#else
+	or	%g0, 512, %g7
+	or	%g0, 512, %o0
+	add	%o0, 512, %o2
+	add	%o2, 512, %o3
+	add	%o3, 512, %o4
+	add	%o4, 512, %o5
+	add	%o5, 512, %g3
+	add	%g3, 512, %g4
+1:	sta	%g0, [%o1      ] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g4] ASI_M_FLUSH_PAGE
+	subcc	%g7, 16, %g7
+	bne	1b
+	 add	%o1, 16, %o1
+#endif
+
+	mov	SRMMU_CTX_REG, %g7
+	sta	%g5, [%g7] ASI_M_MMUREGS
+	wr	%g1, 0x0, %psr
+	nop
+	nop
+swift_flush_cache_page_out:
+	retl
+	 nop
+
+	/* Swift is write-thru, however it is not
+	 * I/O nor TLB-walk coherent.  Also it has
+	 * caches which are virtually indexed and tagged.
+	 */
+	.globl	swift_flush_page_for_dma
+	.globl	swift_flush_chunk
+	.globl	swift_flush_page_to_ram
+swift_flush_page_for_dma:
+swift_flush_chunk:
+swift_flush_page_to_ram:
+	andn	%o0, (PAGE_SIZE - 1), %o1
+#if 1
+	sethi	%hi(0x1000), %o0
+1:	subcc	%o0, 0x10, %o0
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	bne	1b
+	 nop
+#else
+	or	%g0, 512, %g7
+	or	%g0, 512, %o0
+	add	%o0, 512, %o2
+	add	%o2, 512, %o3
+	add	%o3, 512, %o4
+	add	%o4, 512, %o5
+	add	%o5, 512, %g3
+	add	%g3, 512, %g4
+1:	sta	%g0, [%o1      ] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g4] ASI_M_FLUSH_PAGE
+	subcc	%g7, 16, %g7
+	bne	1b
+	 add	%o1, 16, %o1
+#endif
+	retl
+	 nop
+#endif
+
+	.globl	swift_flush_sig_insns
+swift_flush_sig_insns:
+	flush	%o1
+	retl
+	 flush	%o1 + 4
+
+	.globl	swift_flush_tlb_mm
+	.globl	swift_flush_tlb_range
+	.globl	swift_flush_tlb_all
+swift_flush_tlb_mm:
+swift_flush_tlb_range:
+#ifndef __SMP__
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	swift_flush_tlb_all_out
+#endif
+swift_flush_tlb_all:
+	mov	0x400, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+swift_flush_tlb_all_out:
+	retl
+	 nop
+
+	.globl	swift_flush_tlb_page
+swift_flush_tlb_page:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	andn	%o1, (PAGE_SIZE - 1), %o1
+#ifndef __SMP__
+	cmp	%o3, -1
+	be	swift_flush_tlb_page_out
+	 nop
+#endif
+#if 1
+	mov	0x400, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE	
+#else
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+#endif
+swift_flush_tlb_page_out:
+	retl
+	 nop
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc/mm/tsunami.S linux.14p6/arch/sparc/mm/tsunami.S
--- linux.vanilla/arch/sparc/mm/tsunami.S	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/sparc/mm/tsunami.S	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: tsunami.S,v 1.1.6.1 1999/08/09 13:00:16 davem Exp $
+/* $Id: tsunami.S,v 1.1.6.3 1999/10/06 15:36:38 davem Exp $
  * tsunami.S: High speed MicroSparc-I mmu/cache operations.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -44,11 +44,11 @@
 tsunami_flush_cache_all:
 	WINDOW_FLUSH(%g4, %g5)
 tsunami_flush_page_for_dma:
-	sta	%g0, [%g0] ASI_M_DC_FLCLEAR
 	sta	%g0, [%g0] ASI_M_IC_FLCLEAR
+tsunami_flush_chunk:
+	sta	%g0, [%g0] ASI_M_DC_FLCLEAR
 tsunami_flush_cache_out:
 tsunami_flush_page_to_ram:
-tsunami_flush_chunk:
 	retl
 	 nop
 
@@ -98,3 +98,51 @@
 tsunami_flush_tlb_page_out:
 	retl
 	 sta	%g5, [%g1] ASI_M_MMUREGS
+
+#define MIRROR_BLOCK(dst, src, offset, t0, t1, t2, t3) \
+	ldd	[src + offset + 0x18], t0; \
+	std	t0, [dst + offset + 0x18]; \
+	ldd	[src + offset + 0x10], t2; \
+	std	t2, [dst + offset + 0x10]; \
+	ldd	[src + offset + 0x08], t0; \
+	std	t0, [dst + offset + 0x08]; \
+	ldd	[src + offset + 0x00], t2; \
+	std	t2, [dst + offset + 0x00];
+
+	.globl	tsunami_copy_1page
+tsunami_copy_1page:
+/* NOTE: This routine has to be shorter than 70insns --jj */
+	or	%g0, (PAGE_SIZE >> 8), %g1
+1:
+	MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x40, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x60, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x80, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0xa0, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0xc0, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0xe0, %o2, %o3, %o4, %o5)
+	subcc	%g1, 1, %g1
+	add	%o0, 0x100, %o0
+	bne	1b
+	 add	%o1, 0x100, %o1
+
+	.globl	tsunami_setup_blockops
+tsunami_setup_blockops:
+	sethi	%hi(__copy_1page), %o0
+	or	%o0, %lo(__copy_1page), %o0
+	sethi	%hi(tsunami_copy_1page), %o1
+	or	%o1, %lo(tsunami_copy_1page), %o1
+	sethi	%hi(tsunami_setup_blockops), %o2
+	or	%o2, %lo(tsunami_setup_blockops), %o2
+	ld	[%o1], %o4
+1:	add	%o1, 4, %o1
+	st	%o4, [%o0]
+	add	%o0, 4, %o0
+	cmp	%o1, %o2
+	bne	1b
+	ld	[%o1], %o4
+	sta	%g0, [%g0] ASI_M_IC_FLCLEAR
+	sta	%g0, [%g0] ASI_M_DC_FLCLEAR
+	retl
+	 nop
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/config.in linux.14p6/arch/sparc64/config.in
--- linux.vanilla/arch/sparc64/config.in	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/config.in	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.67.2.2 1999/09/22 11:37:42 jj Exp $
+# $Id: config.in,v 1.67.2.5 1999/10/19 16:49:37 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -169,7 +169,7 @@
 	    bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
 	    int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
 	  fi
-	  dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
+	  dep_tristate 'SYM53C8XX SCSI support' CONFIG_SCSI_SYM53C8XX $CONFIG_SCSI
 	  if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then
 	    int  '  default tagged command queue depth' CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS 8
 	    int  '  maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 32
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/defconfig linux.14p6/arch/sparc64/defconfig
--- linux.vanilla/arch/sparc64/defconfig	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/defconfig	Tue Nov 16 15:13:27 1999
@@ -27,7 +27,9 @@
 CONFIG_PROM_CONSOLE=y
 CONFIG_FB=y
 CONFIG_DUMMY_CONSOLE=y
-# CONFIG_FB_PM2 is not set
+CONFIG_FB_PM2=y
+# CONFIG_FB_PM2_FIFO_DISCONNECT is not set
+CONFIG_FB_PM2_PCI=y
 # CONFIG_FB_MATROX is not set
 CONFIG_FB_ATY=y
 CONFIG_FB_SBUS=y
@@ -200,10 +202,10 @@
 CONFIG_SCSI_QLOGICPTI=m
 CONFIG_SCSI_AIC7XXX=y
 # CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT is not set
-# CONFIG_CMDS_PER_DEVICE is not set
+CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
 CONFIG_AIC7XXX_PROC_STATS=y
 CONFIG_AIC7XXX_RESET_DELAY=5
-CONFIG_SCSI_NCR53C8XX=y
+CONFIG_SCSI_SYM53C8XX=y
 CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=4
 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
 CONFIG_SCSI_NCR53C8XX_SYNC=10
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/ebus.c linux.14p6/arch/sparc64/kernel/ebus.c
--- linux.vanilla/arch/sparc64/kernel/ebus.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/kernel/ebus.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: ebus.c,v 1.36.2.3 1999/09/21 15:45:37 davem Exp $
+/* $Id: ebus.c,v 1.36.2.4 1999/11/08 23:25:45 davem Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -56,15 +56,15 @@
 	return mem;
 }
 
-__initfunc(void ebus_intmap_match(struct linux_ebus *ebus,
-				  struct linux_prom_registers *reg,
-				  int *interrupt))
+__initfunc(int ebus_intmap_match(struct linux_ebus *ebus,
+				 struct linux_prom_registers *reg,
+				 int *interrupt))
 {
 	unsigned int hi, lo, irq;
 	int i;
 
 	if (!ebus->num_ebus_intmap)
-		return;
+		return 0;
 
 	hi = reg->which_io & ebus->ebus_intmask.phys_hi;
 	lo = reg->phys_addr & ebus->ebus_intmask.phys_lo;
@@ -74,13 +74,10 @@
 		    (ebus->ebus_intmap[i].phys_lo == lo) &&
 		    (ebus->ebus_intmap[i].interrupt == irq)) {
 			*interrupt = ebus->ebus_intmap[i].cinterrupt;
-			return;
+			return 0;
 		}
 	}
-
-	prom_printf("ebus: IRQ [%08x.%08x.%08x] not found in interrupt-map\n",
-		    reg->which_io, reg->phys_addr, *interrupt);
-	prom_halt();
+	return -1;
 }
 
 __initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg,
@@ -132,9 +129,16 @@
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
 		for (i = 0; i < dev->num_irqs; i++) {
-			ebus_intmap_match(dev->bus, preg, &irqs[i]);
-			dev->irqs[i] = psycho_irq_build(dev->bus->parent,
-							dev->bus->self, irqs[i]);
+			if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) {
+				dev->irqs[i] = psycho_irq_build(dev->bus->parent,
+								dev->bus->self,
+								irqs[i]);
+			} else {
+				/* If we get a bogus interrupt property, just
+				 * record the raw value instead of punting.
+				 */
+				dev->irqs[i] = irqs[i];
+			}
 		}
 	}
 
@@ -186,9 +190,16 @@
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
 		for (i = 0; i < dev->num_irqs; i++) {
-			ebus_intmap_match(dev->bus, &regs[0], &irqs[i]);
-			dev->irqs[i] = psycho_irq_build(dev->bus->parent,
-							dev->bus->self, irqs[i]);
+			if (ebus_intmap_match(dev->bus, &regs[0], &irqs[i]) != -1) {
+				dev->irqs[i] = psycho_irq_build(dev->bus->parent,
+								dev->bus->self,
+								irqs[i]);
+			} else {
+				/* If we get a bogus interrupt property, just
+				 * record the raw value instead of punting.
+				 */
+				dev->irqs[i] = irqs[i];
+			}
 		}
 	}
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/entry.S linux.14p6/arch/sparc64/kernel/entry.S
--- linux.vanilla/arch/sparc64/kernel/entry.S	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/kernel/entry.S	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.103.2.2 1999/09/22 11:37:37 jj Exp $
+/* $Id: entry.S,v 1.103.2.4 1999/10/24 17:29:13 davem Exp $
  * arch/sparc64/kernel/entry.S:  Sparc64 trap low-level entry points.
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -214,8 +214,8 @@
 	.align		32
 	.globl		do_ivec
 do_ivec:
-	wr		%g0, ASI_UDB_INTR_R, %asi
-	ldxa		[%g0 + 0x40] %asi, %g3
+	mov		0x40, %g3
+	ldxa		[%g3 + %g0] ASI_UDB_INTR_R, %g3
 	sethi		%hi(KERNBASE), %g4
 	cmp		%g3, %g4
 	bgeu,pn		%xcc, do_ivec_xcall
@@ -234,22 +234,25 @@
 
 	sllx		%g2, %g4, %g2
 	sllx		%g4, 2, %g4
-	lduw		[%g1 + %g4], %g5	/* g5 = irq_work(cpu, pil) */
+	lduw		[%g6 + %g4], %g5	/* g5 = irq_work(cpu, pil) */
 	stw		%g5, [%g3 + 0x00]	/* bucket->irq_chain = g5 */
-	stw		%g3, [%g1 + %g4]	/* irq_work(cpu, pil) = bucket */
+	stw		%g3, [%g6 + %g4]	/* irq_work(cpu, pil) = bucket */
 	wr		%g2, 0x0, %set_softint
 	retry
 do_ivec_xcall:
-	ldxa		[%g0 + 0x50] %asi, %g6
+	mov		0x50, %g1
 
+	ldxa		[%g1 + %g0] ASI_UDB_INTR_R, %g1
 	srl		%g3, 0, %g3
-	ldxa		[%g0 + 0x60] %asi, %g7
+	mov		0x60, %g7
+	ldxa		[%g7 + %g0] ASI_UDB_INTR_R, %g7
 	stxa		%g0, [%g0] ASI_INTR_RECEIVE
 	membar		#Sync
 	jmpl		%g3, %g0
 	 nop
+
 do_ivec_spurious:
-	stw		%g3, [%g1 + 0x00]	/* irq_work(cpu, 0) = bucket */
+	stw		%g3, [%g6 + 0x00]	/* irq_work(cpu, 0) = bucket */
 	rdpr		%pstate, %g5
 
 	wrpr		%g5, PSTATE_IG | PSTATE_AG, %pstate
@@ -260,6 +263,76 @@
 	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 	ba,pt		%xcc, rtrap
 	 clr		%l6
+
+	.globl		save_alternate_globals
+save_alternate_globals: /* %o0 = save_area */
+	rdpr		%pstate, %o5
+	andn		%o5, PSTATE_IE, %o1
+	wrpr		%o1, PSTATE_AG, %pstate
+	stx		%g0, [%o0 + 0x00]
+	stx		%g1, [%o0 + 0x08]
+	stx		%g2, [%o0 + 0x10]
+	stx		%g3, [%o0 + 0x18]
+	stx		%g4, [%o0 + 0x20]
+	stx		%g5, [%o0 + 0x28]
+	stx		%g6, [%o0 + 0x30]
+	stx		%g7, [%o0 + 0x38]
+	wrpr		%o1, PSTATE_IG, %pstate
+	stx		%g0, [%o0 + 0x40]
+	stx		%g1, [%o0 + 0x48]
+	stx		%g2, [%o0 + 0x50]
+	stx		%g3, [%o0 + 0x58]
+	stx		%g4, [%o0 + 0x60]
+	stx		%g5, [%o0 + 0x68]
+	stx		%g6, [%o0 + 0x70]
+	stx		%g7, [%o0 + 0x78]
+	wrpr		%o1, PSTATE_MG, %pstate
+	stx		%g0, [%o0 + 0x80]
+	stx		%g1, [%o0 + 0x88]
+	stx		%g2, [%o0 + 0x90]
+	stx		%g3, [%o0 + 0x98]
+	stx		%g4, [%o0 + 0xa0]
+	stx		%g5, [%o0 + 0xa8]
+	stx		%g6, [%o0 + 0xb0]
+	stx		%g7, [%o0 + 0xb8]
+	wrpr		%o5, 0x0, %pstate
+	retl
+	 nop
+
+	.globl		restore_alternate_globals
+restore_alternate_globals: /* %o0 = save_area */
+	rdpr		%pstate, %o5
+	andn		%o5, PSTATE_IE, %o1
+	wrpr		%o1, PSTATE_AG, %pstate
+	ldx		[%o0 + 0x00], %g0
+	ldx		[%o0 + 0x08], %g1
+	ldx		[%o0 + 0x10], %g2
+	ldx		[%o0 + 0x18], %g3
+	ldx		[%o0 + 0x20], %g4
+	ldx		[%o0 + 0x28], %g5
+	ldx		[%o0 + 0x30], %g6
+	ldx		[%o0 + 0x38], %g7
+	wrpr		%o1, PSTATE_IG, %pstate
+	ldx		[%o0 + 0x40], %g0
+	ldx		[%o0 + 0x48], %g1
+	ldx		[%o0 + 0x50], %g2
+	ldx		[%o0 + 0x58], %g3
+	ldx		[%o0 + 0x60], %g4
+	ldx		[%o0 + 0x68], %g5
+	ldx		[%o0 + 0x70], %g6
+	ldx		[%o0 + 0x78], %g7
+	wrpr		%o1, PSTATE_MG, %pstate
+	ldx		[%o0 + 0x80], %g0
+	ldx		[%o0 + 0x88], %g1
+	ldx		[%o0 + 0x90], %g2
+	ldx		[%o0 + 0x98], %g3
+	ldx		[%o0 + 0xa0], %g4
+	ldx		[%o0 + 0xa8], %g5
+	ldx		[%o0 + 0xb0], %g6
+	ldx		[%o0 + 0xb8], %g7
+	wrpr		%o5, 0x0, %pstate
+	retl
+	 nop
 
 	.globl		getcc, setcc
 getcc:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/head.S linux.14p6/arch/sparc64/kernel/head.S
--- linux.vanilla/arch/sparc64/kernel/head.S	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/kernel/head.S	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.60.2.2 1999/08/19 01:11:12 davem Exp $
+/* $Id: head.S,v 1.60.2.4 1999/11/12 15:45:47 davem Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -52,7 +52,7 @@
 
         .ascii  "HdrS"
         .word   LINUX_VERSION_CODE
-        .half   0x0202          /* HdrS version */
+        .half   0x0203          /* HdrS version */
 root_flags:
         .half   1
 root_dev:
@@ -65,6 +65,7 @@
         .word   0
         .xword  reboot_command
 	.xword	bootstr_len
+	.word	_end
 
 	/* We must be careful, 32-bit OpenBOOT will get confused if it
 	 * tries to save away a register window to a 64-bit kernel
@@ -92,28 +93,6 @@
 	wrpr    %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate
 	wr	%g0, 0, %fprs
 
-#ifdef __SMP__
-	/* Ugly but necessary... */
-	sethi	%hi(KERNBASE), %g7
-	sethi	%hi(sparc64_cpu_startup), %g5
-	or	%g5, %lo(sparc64_cpu_startup), %g5
-	sub	%g5, %g7, %g5
-	sethi	%hi(sparc64_cpu_startup_end), %g6
-	or	%g6, %lo(sparc64_cpu_startup_end), %g6
-	sub	%g6, %g7, %g6
-	sethi	%hi(smp_trampoline), %g3
-	or	%g3, %lo(smp_trampoline), %g3
-	sub	%g3, %g7, %g3
-1:	ldx	[%g5], %g1
-	stx	%g1, [%g3]
-	membar	#StoreStore
-	flush	%g3
-	add	%g5, 8, %g5
-	cmp	%g5, %g6
-	blu,pt	%xcc, 1b
-	 add	%g3, 8, %g3
-#endif
-
 create_mappings:
 	/* %g5 holds the tlb data */
         sethi   %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
@@ -379,7 +358,7 @@
 	wrpr	%o1, (PSTATE_IG|PSTATE_IE), %pstate
 #ifndef __SMP__
 	sethi	%hi(__up_workvec), %g5
-	or	%g5, %lo(__up_workvec), %g1
+	or	%g5, %lo(__up_workvec), %g6
 #else
 	/* By definition of where we are, this is boot_cpu. */
 	sethi	%hi(cpu_data), %g5
@@ -403,7 +382,7 @@
 set_worklist:
 	sllx	%g1, 7, %g1
 	add	%g5, %g1, %g5
-	add	%g5, 64, %g1
+	add	%g5, 64, %g6
 #endif
 
 	/* Kill PROM timer */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/ioctl32.c linux.14p6/arch/sparc64/kernel/ioctl32.c
--- linux.vanilla/arch/sparc64/kernel/ioctl32.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/kernel/ioctl32.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.62.2.4 1999/09/22 17:06:56 jj Exp $
+/* $Id: ioctl32.c,v 1.62.2.7 1999/10/09 06:03:20 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
@@ -1699,9 +1699,9 @@
 	int error = -EBADF;
 
 	lock_kernel();
-	filp = fcheck(fd);
+	filp = fget(fd);
 	if(!filp)
-		goto out;
+		goto out2;
 
 	if (!filp->f_op || !filp->f_op->ioctl) {
 		error = sys_ioctl (fd, cmd, arg);
@@ -1920,6 +1920,9 @@
 	case TIOCSCTTY:
 	case TIOCGPTN:
 	case TIOCSPTLCK:
+	case TIOCGSERIAL:
+	case TIOCSSERIAL:
+	case TIOCSERGETLSR:
 	
 	/* Big F */
 	case FBIOGTYPE:
@@ -2219,6 +2222,9 @@
 	case CDROM_DRIVE_STATUS:
 	case CDROM_DISC_STATUS:
 	case CDROM_CHANGER_NSLOTS:
+	case CDROM_LOCKDOOR:
+	case CDROM_DEBUG:
+	case CDROM_GET_CAPABILITY:
 	
 	/* Big L */
 	case LOOP_SET_FD:
@@ -2385,6 +2391,10 @@
 	case AUTOFS_IOC_PROTOVER:
 	case AUTOFS_IOC_EXPIRE:
 	
+	/* Raw devices */
+	case _IO(0xac, 0): /* RAW_SETBIND */
+	case _IO(0xac, 1): /* RAW_GETBIND */
+
 		error = sys_ioctl (fd, cmd, arg);
 		goto out;
 
@@ -2397,9 +2407,11 @@
 				       (int)fd, (unsigned int)cmd, (unsigned int)arg);
 		} while(0);
 		error = -EINVAL;
-		break;
+		goto out;
 	}
 out:
+	fput(filp);
+out2:
 	unlock_kernel();
 	return error;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/psycho.c linux.14p6/arch/sparc64/kernel/psycho.c
--- linux.vanilla/arch/sparc64/kernel/psycho.c	Sat Aug 14 02:27:15 1999
+++ linux.14p6/arch/sparc64/kernel/psycho.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: psycho.c,v 1.85.2.2 1999/08/09 13:00:21 davem Exp $
+/* $Id: psycho.c,v 1.85.2.5 1999/10/28 02:28:38 davem Exp $
  * psycho.c: Ultra/AX U2P PCI controller support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
@@ -221,15 +221,12 @@
 	control |= (IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
 	switch(tsbsize) {
 	case 8:
-		pci_dvma_mask = 0x1fffffffUL;
 		control |= IOMMU_TSBSZ_8K;
 		break;
 	case 16:
-		pci_dvma_mask = 0x3fffffffUL;
 		control |= IOMMU_TSBSZ_16K;
 		break;
 	case 32:
-		pci_dvma_mask = 0x7fffffffUL;
 		control |= IOMMU_TSBSZ_32K;
 		break;
 	default:
@@ -461,8 +458,19 @@
 
 		err = prom_getproperty(node, "model", namebuf, sizeof(namebuf));
 		if ((err > 0) && !strncmp(namebuf, "SUNW,sabre", err)) {
+			/* SABRE/APB is composed of a 4GB aligned 4GB
+			 * total PCI memory space.
+			 */
+			pci_dvma_mask = 0xffffffff;
+
 			sabre_init(node);
 			goto next_pci;
+		} else {
+			/* PSYCHO has two independant 4GB, 2GB aligned,
+			 * memory spaces, and the lower 2GB is the region
+			 * for all PCI memory space device mappings.
+			 */
+			pci_dvma_mask = 0x7fffffff;
 		}
 
 		portid = prom_getintdefault(node, "upa-portid", 0xff);
@@ -755,17 +763,18 @@
 {
 	struct pci_dev *pdev;
 	unsigned short stmp;
-	unsigned int itmp;
-	unsigned char btmp;
+#if 0
+	unsigned char sabre_latency_timer = 32;
 
 	for(pdev = pci_devices; pdev; pdev = pdev->next) {
 		if(pdev->vendor == PCI_VENDOR_ID_SUN &&
 		   pdev->device == PCI_DEVICE_ID_SUN_SABRE) {
-			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+			pci_read_config_byte(pdev, PCI_LATENCY_TIMER,
+					     &sabre_latency_timer);
 			break;
 		}
 	}
-
+#endif
 	for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) {
 		if (pdev->vendor == PCI_VENDOR_ID_SUN &&
 		    pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
@@ -778,52 +787,11 @@
 			/* Status register bits are "write 1 to clear". */
 			pci_write_config_word(pdev, PCI_STATUS, 0xffff);
 			pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff);
-
-			pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &stmp);
-			stmp = PCI_BRIDGE_CTL_MASTER_ABORT |
-			       PCI_BRIDGE_CTL_SERR |
-			       PCI_BRIDGE_CTL_PARITY;
-			pci_write_config_word(pdev, PCI_BRIDGE_CONTROL, stmp);
-
-			pci_read_config_dword(pdev, APB_PCI_CONTROL_HIGH, &itmp);
-			itmp = APB_PCI_CTL_HIGH_SERR |
-			       APB_PCI_CTL_HIGH_ARBITER_EN;
-			pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp);
-
-			/* Systems with SIMBA are usually workstations, so
-			 * we configure to park to SIMBA not to the previous
-			 * bus owner.
-			 */
-			pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp);
-			itmp = APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
-			pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp);
-
-			/* Don't mess with the retry limit and PIO/DMA latency
-			 * timer settings.  But do set primary and secondary
-			 * latency timers.
-			 */
-			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
-			pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 64);
-
-			/* Here is an overview of the behavior of various
-			 * revisions of APB wrt. write buffer full conditions:
-			 *
-			 * Revision 1.0: pre-FCS, always stalls
-			 * Revision 1.1: pre-FCS, always disconnects
-			 * Revision 1.2: same behavior as rev 1.1
-			 * Revision 1.3: behavior is determined by bit 4 of
-			 *               secondary control register
-			 *		 0: stall initially, but disconnect
-			 *		    if PCI latency timer expires
-			 *		 1: always disconnect
-			 *
-			 * By setting the bit, since it is reserved in previous
-			 * revisions of APB, we get all FCS hardware to have
-			 * identical behavior when APB's write buffer fills up.
-			 */
-			pci_read_config_byte(pdev, APB_SECONDARY_CONTROL, &btmp);
-			btmp |= APB_SECONDARY_CTL_DISCON_FULL;
-			pci_write_config_byte(pdev, APB_SECONDARY_CONTROL, btmp);
+#if 0
+			/* Propagate Sabre latency timer value into APB. */
+			pci_write_config_byte(pdev, PCI_LATENCY_TIMER,
+					      sabre_latency_timer);
+#endif
 		}
 	}
 }
@@ -1958,6 +1926,12 @@
 	}
 
 	node = pcp->prom_node;
+
+	/* No need to crash if the PCI device lacks PROM
+	 * information.
+	 */
+	if (node == -1)
+		return;
 
 	err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
 	if(err == 0 || err == -1) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/ptrace.c linux.14p6/arch/sparc64/kernel/ptrace.c
--- linux.vanilla/arch/sparc64/kernel/ptrace.c	Wed Mar 24 23:10:28 1999
+++ linux.14p6/arch/sparc64/kernel/ptrace.c	Tue Nov 16 15:13:27 1999
@@ -40,6 +40,7 @@
 	pgd_t * pgdir;
 	pmd_t * pgmiddle;
 	pte_t * pgtable;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
@@ -50,8 +51,12 @@
 	current->mm->segments = (void *) (addr & PAGE_SIZE);
 
 	if (pgd_none(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, write);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, write);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %016lx\n", pgd_val(*pgdir));
@@ -60,8 +65,12 @@
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
 	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, write);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, write);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %016lx\n", pmd_val(*pgmiddle));
@@ -70,12 +79,20 @@
 	}
 	pgtable = pte_offset(pgmiddle, addr);
 	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, write);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, write);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	if (write && !pte_write(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, write);
-		goto repeat;
+		fault = handle_mm_fault(tsk, vma, addr, write);
+		if (fault > 0)
+			goto repeat;
+		if (fault < 0)
+			force_sig(SIGKILL, tsk);
+		return 0;
 	}
 	return pgtable;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/setup.c linux.14p6/arch/sparc64/kernel/setup.c
--- linux.vanilla/arch/sparc64/kernel/setup.c	Sat Aug 14 02:26:47 1999
+++ linux.14p6/arch/sparc64/kernel/setup.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.43.2.1 1999/05/28 02:18:13 davem Exp $
+/*  $Id: setup.c,v 1.43.2.2 1999/10/24 17:29:20 davem Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -434,6 +434,17 @@
 
 extern struct consw sun_serial_con;
 
+void register_prom_callbacks(void)
+{
+	prom_setcallback(prom_callback);
+	prom_feval(": linux-va>tte-data 2 \" va>tte-data\" $callback drop ; "
+		   "' linux-va>tte-data to va>tte-data");
+	prom_feval(": linux-.soft1 1 \" .soft1\" $callback 2drop ; "
+		   "' linux-.soft1 to .soft1");
+	prom_feval(": linux-.soft2 1 \" .soft2\" $callback 2drop ; "
+		   "' linux-.soft2 to .soft2");
+}
+
 __initfunc(void setup_arch(char **cmdline_p,
 	unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
@@ -478,13 +489,6 @@
 			}
 		}
 	}
-	prom_setcallback(prom_callback);
-	prom_feval(": linux-va>tte-data 2 \" va>tte-data\" $callback drop ; "
-		   "' linux-va>tte-data to va>tte-data");
-	prom_feval(": linux-.soft1 1 \" .soft1\" $callback 2drop ; "
-		   "' linux-.soft1 to .soft1");
-	prom_feval(": linux-.soft2 1 \" .soft2\" $callback 2drop ; "
-		   "' linux-.soft2 to .soft2");
 
 	/* In paging_init() we tip off this value to see if we need
 	 * to change init_mm.pgd to point to the real alias mapping.
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/smp.c linux.14p6/arch/sparc64/kernel/smp.c
--- linux.vanilla/arch/sparc64/kernel/smp.c	Sat Aug 14 02:26:15 1999
+++ linux.14p6/arch/sparc64/kernel/smp.c	Tue Nov 16 15:13:27 1999
@@ -180,7 +180,7 @@
 
 extern struct prom_cpuinfo linux_cpus[64];
 
-extern unsigned long smp_trampoline;
+extern unsigned long sparc64_cpu_startup;
 
 /* The OBP cpu startup callback truncates the 3rd arg cookie to
  * 32-bits (I think) so to be safe we have it read the pointer
@@ -206,15 +206,13 @@
 			continue;
 
 		if(cpu_present_map & (1UL << i)) {
-			unsigned long entry = (unsigned long)(&smp_trampoline);
+			unsigned long entry = (unsigned long)(&sparc64_cpu_startup);
 			unsigned long cookie = (unsigned long)(&cpu_new_task);
 			struct task_struct *p;
 			int timeout;
 			int no;
-			extern unsigned long phys_base;
 
-			entry += phys_base - KERNBASE;
-			cookie += phys_base - KERNBASE;
+			prom_printf("Starting CPU %d... ", i);
 			kernel_thread(start_secondary, NULL, CLONE_PID);
 			p = task[++cpucount];
 			p->processor = i;
@@ -235,9 +233,11 @@
 				cpu_number_map[i] = cpucount;
 				__cpu_logical_map[cpucount] = i;
 				prom_cpu_nodes[i] = linux_cpus[no].prom_node;
+				prom_printf("OK\n");
 			} else {
 				cpucount--;
 				printk("Processor %d is stuck.\n", i);
+				prom_printf("FAILED\n");
 			}
 		}
 		if(!callin_flag) {
@@ -536,14 +536,31 @@
 /* Imprisoned penguins run with %pil == 15, but PSTATE_IE set, so they
  * can service tlb flush xcalls...
  */
+extern void prom_world(int);
+extern void save_alternate_globals(unsigned long *);
+extern void restore_alternate_globals(unsigned long *);
 void smp_penguin_jailcell(void)
 {
-	flushw_user();
+	unsigned long global_save[24];
+
+	__asm__ __volatile__("flushw");
+	save_alternate_globals(global_save);
+	prom_world(1);
 	atomic_inc(&smp_capture_registry);
 	membar("#StoreLoad | #StoreStore");
 	while(penguins_are_doing_time)
 		membar("#LoadLoad");
+	restore_alternate_globals(global_save);
 	atomic_dec(&smp_capture_registry);
+	prom_world(0);
+}
+
+extern unsigned long xcall_promstop;
+
+void smp_promstop_others(void)
+{
+	if (smp_processors_ready)
+		smp_cross_call(&xcall_promstop, 0, 0, 0);
 }
 
 static inline void sparc64_do_profile(unsigned long pc)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/sys_sparc32.c linux.14p6/arch/sparc64/kernel/sys_sparc32.c
--- linux.vanilla/arch/sparc64/kernel/sys_sparc32.c	Sat Aug 14 02:26:47 1999
+++ linux.14p6/arch/sparc64/kernel/sys_sparc32.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.107.2.1 1999/05/16 10:48:44 davem Exp $
+/* $Id: sys_sparc32.c,v 1.107.2.5 1999/11/12 11:17:47 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -44,6 +44,7 @@
 #include <linux/personality.h>
 #include <linux/stat.h>
 #include <linux/timex.h>
+#include <linux/filter.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -1136,8 +1137,10 @@
 	}
 
 	ret = -EINVAL;
-	if (n < 0 || n > KFDS_NR)
+	if (n < 0)
 		goto out_nofds;
+	if (n > current->files->max_fdset)
+		n = current->files->max_fdset;
 
 	/*
 	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
@@ -1197,84 +1200,157 @@
 	return ret;
 }
 
-static inline int putstat(struct stat32 *ubuf, struct stat *kbuf)
+static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf)
 {
+	unsigned long ino, blksize, blocks;
+	kdev_t dev, rdev;
+	umode_t mode;
+	nlink_t nlink;
+	uid_t uid;
+	gid_t gid;
+	off_t size;
+	time_t atime, mtime, ctime;
 	int err;
-	
-	err = put_user (kbuf->st_dev, &ubuf->st_dev);
-	err |= __put_user (kbuf->st_ino, &ubuf->st_ino);
-	err |= __put_user (kbuf->st_mode, &ubuf->st_mode);
-	err |= __put_user (kbuf->st_nlink, &ubuf->st_nlink);
-	err |= __put_user (kbuf->st_uid, &ubuf->st_uid);
-	err |= __put_user (kbuf->st_gid, &ubuf->st_gid);
-	err |= __put_user (kbuf->st_rdev, &ubuf->st_rdev);
-	err |= __put_user (kbuf->st_size, &ubuf->st_size);
-	err |= __put_user (kbuf->st_atime, &ubuf->st_atime);
-	err |= __put_user (kbuf->st_mtime, &ubuf->st_mtime);
-	err |= __put_user (kbuf->st_ctime, &ubuf->st_ctime);
-	err |= __put_user (kbuf->st_blksize, &ubuf->st_blksize);
-	err |= __put_user (kbuf->st_blocks, &ubuf->st_blocks);
+
+	/* Stream the loads of inode data into the load buffer,
+	 * then we push it all into the store buffer below.  This
+	 * should give optimal cache performance.
+	 */
+	ino = inode->i_ino;
+	dev = inode->i_dev;
+	mode = inode->i_mode;
+	nlink = inode->i_nlink;
+	uid = inode->i_uid;
+	gid = inode->i_gid;
+	rdev = inode->i_rdev;
+	size = inode->i_size;
+	atime = inode->i_atime;
+	mtime = inode->i_mtime;
+	ctime = inode->i_ctime;
+	blksize = inode->i_blksize;
+	blocks = inode->i_blocks;
+
+	err  = put_user(kdev_t_to_nr(dev), &statbuf->st_dev);
+	err |= put_user(ino, &statbuf->st_ino);
+	err |= put_user(mode, &statbuf->st_mode);
+	err |= put_user(nlink, &statbuf->st_nlink);
+	err |= put_user(uid, &statbuf->st_uid);
+	err |= put_user(gid, &statbuf->st_gid);
+	err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev);
+	err |= put_user(size, &statbuf->st_size);
+	err |= put_user(atime, &statbuf->st_atime);
+	err |= put_user(0, &statbuf->__unused1);
+	err |= put_user(mtime, &statbuf->st_mtime);
+	err |= put_user(0, &statbuf->__unused2);
+	err |= put_user(ctime, &statbuf->st_ctime);
+	err |= put_user(0, &statbuf->__unused3);
+	if (blksize) {
+		err |= put_user(blksize, &statbuf->st_blksize);
+		err |= put_user(blocks, &statbuf->st_blocks);
+	} else {
+		unsigned int tmp_blocks;
+
+#define D_B   7
+#define I_B   (BLOCK_SIZE / sizeof(unsigned short))
+		tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE;
+		if (tmp_blocks > D_B) {
+			unsigned int indirect;
+
+			indirect = (tmp_blocks - D_B + I_B - 1) / I_B;
+			tmp_blocks += indirect;
+			if (indirect > 1) {
+				indirect = (indirect - 1 + I_B - 1) / I_B;
+				tmp_blocks += indirect;
+				if (indirect > 1)
+					tmp_blocks++;
+			}
+		}
+		err |= put_user(BLOCK_SIZE, &statbuf->st_blksize);
+		err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks);
+#undef D_B
+#undef I_B
+	}
+	err |= put_user(0, &statbuf->__unused4[0]);
+	err |= put_user(0, &statbuf->__unused4[1]);
+
 	return err;
 }
 
-extern asmlinkage int sys_newstat(char * filename, struct stat * statbuf);
-
 asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf)
 {
-	int ret;
-	struct stat s;
-	char *filenam;
-	mm_segment_t old_fs = get_fs();
-	
-	filenam = getname32 (filename);
-	ret = PTR_ERR(filenam);
-	if (!IS_ERR(filenam)) {
-		set_fs (KERNEL_DS);
-		ret = sys_newstat(filenam, &s);
-		set_fs (old_fs);
-		putname (filenam);
-		if (putstat (statbuf, &s))
-			return -EFAULT;
+	struct dentry *dentry;
+	int error;
+
+	lock_kernel();
+	dentry = namei(filename);
+
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		struct inode *inode = dentry->d_inode;
+
+		if (inode->i_op &&
+		    inode->i_op->revalidate)
+			error = inode->i_op->revalidate(dentry);
+		else
+			error = 0;
+		if (!error)
+			error = cp_new_stat32(inode, statbuf);
+
+		dput(dentry);
 	}
-	return ret;
+	unlock_kernel();
+	return error;
 }
 
-extern asmlinkage int sys_newlstat(char * filename, struct stat * statbuf);
-
 asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf)
 {
-	int ret;
-	struct stat s;
-	char *filenam;
-	mm_segment_t old_fs = get_fs();
-	
-	filenam = getname32 (filename);
-	ret = PTR_ERR(filenam);
-	if (!IS_ERR(filenam)) {
-		set_fs (KERNEL_DS);
-		ret = sys_newlstat(filenam, &s);
-		set_fs (old_fs);
-		putname (filenam);
-		if (putstat (statbuf, &s))
-			return -EFAULT;
+	struct dentry *dentry;
+	int error;
+
+	lock_kernel();
+	dentry = lnamei(filename);
+
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		struct inode *inode = dentry->d_inode;
+
+		if (inode->i_op &&
+		    inode->i_op->revalidate)
+			error = inode->i_op->revalidate(dentry);
+		else
+			error = 0;
+		if (!error)
+			error = cp_new_stat32(inode, statbuf);
+
+		dput(dentry);
 	}
-	return ret;
+	unlock_kernel();
+	return error;
 }
 
-extern asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf);
-
 asmlinkage int sys32_newfstat(unsigned int fd, struct stat32 *statbuf)
 {
-	int ret;
-	struct stat s;
-	mm_segment_t old_fs = get_fs();
-	
-	set_fs (KERNEL_DS);
-	ret = sys_newfstat(fd, &s);
-	set_fs (old_fs);
-	if (putstat (statbuf, &s))
-		return -EFAULT;
-	return ret;
+	struct file *f;
+	int err = -EBADF;
+
+	lock_kernel();
+	f = fget(fd);
+	if (f) {
+		struct dentry *dentry = f->f_dentry;
+		struct inode *inode = dentry->d_inode;
+
+		if (inode->i_op &&
+		    inode->i_op->revalidate)
+			err = inode->i_op->revalidate(dentry);
+		else
+			err = 0;
+		if (!err)
+			err = cp_new_stat32(inode, statbuf);
+
+		fput(f);
+	}
+	unlock_kernel();
+	return err;
 }
 
 extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2);
@@ -1677,7 +1753,7 @@
 siginfo_t32 *
 siginfo64to32(siginfo_t32 *d, siginfo_t *s)
 {
-	memset (&d, 0, sizeof(siginfo_t32));
+	memset (d, 0, sizeof(siginfo_t32));
 	d->si_signo = s->si_signo;
 	d->si_errno = s->si_errno;
 	d->si_code = s->si_code;
@@ -2581,6 +2657,48 @@
 	return len;
 }
 
+extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
+				     char *optval, int optlen);
+
+asmlinkage int sys32_setsockopt(int fd, int level, int optname,
+				char *optval, int optlen)
+{
+	if (optname == SO_ATTACH_FILTER) {
+		struct sock_fprog32 {
+			__u16 len;
+			__u32 filter;
+		} *fprog32 = (struct sock_fprog32 *)optval;
+		struct sock_fprog kfprog;
+		struct sock_filter *kfilter;
+		unsigned int fsize;
+		mm_segment_t old_fs;
+		__u32 uptr;
+		int ret;
+
+		if (get_user(kfprog.len, &fprog32->len) ||
+		    __get_user(uptr, &fprog32->filter))
+			return -EFAULT;
+		kfprog.filter = (struct sock_filter *)A(uptr);
+		fsize = kfprog.len * sizeof(struct sock_filter);
+		kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL);
+		if (kfilter == NULL)
+			return -ENOMEM;
+		if (copy_from_user(kfilter, kfprog.filter, fsize)) {
+			kfree(kfilter);
+			return -EFAULT;
+		}
+		kfprog.filter = kfilter;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+		ret = sys_setsockopt(fd, level, optname,
+				     (char *)&kfprog, sizeof(kfprog));
+		set_fs(old_fs);
+		kfree(kfilter);
+		return ret;
+	}
+	return sys_setsockopt(fd, level, optname, optval, optlen);
+}
+
 /* Argument list sizes for sys_socketcall */
 #define AL(x) ((x) * sizeof(u32))
 static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
@@ -2599,8 +2717,6 @@
 extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags);
 extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size,
 				     unsigned flags, u32 addr, u32 addr_len);
-extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
-				     char *optval, int optlen);
 extern asmlinkage int sys32_getsockopt(int fd, int level, int optname,
 				       u32 optval, u32 optlen);
 
@@ -2651,7 +2767,7 @@
 		case SYS_SHUTDOWN:
 			return sys_shutdown(a0,a1);
 		case SYS_SETSOCKOPT:
-			return sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), a[4]);
+			return sys32_setsockopt(a0, a1, a[2], (char *)A(a[3]), a[4]);
 		case SYS_GETSOCKOPT:
 			return sys32_getsockopt(a0, a1, a[2], a[3], a[4]);
 		case SYS_SENDMSG:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/time.c linux.14p6/arch/sparc64/kernel/time.c
--- linux.vanilla/arch/sparc64/kernel/time.c	Tue Mar 16 00:10:43 1999
+++ linux.14p6/arch/sparc64/kernel/time.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.20 1999/03/15 22:13:40 davem Exp $
+/* $Id: time.c,v 1.20.2.1 1999/10/09 06:03:23 davem Exp $
  * time.c: UltraSparc timer and TOD clock support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -47,7 +47,7 @@
  */
 unsigned long timer_tick_offset;
 static unsigned long timer_tick_compare;
-static unsigned long timer_ticks_per_usec;
+static unsigned long timer_ticks_per_usec_quotient;
 
 static __inline__ void timer_check_rtc(void)
 {
@@ -392,7 +392,7 @@
 
 	init_timers(timer_interrupt, &clock);
 	timer_tick_offset = clock / HZ;
-	timer_ticks_per_usec = clock / 1000000;
+	timer_ticks_per_usec_quotient = ((1UL<<32) / (clock / 1000020UL));
 }
 
 static __inline__ unsigned long do_gettimeoffset(void)
@@ -408,7 +408,7 @@
 		: "r" (timer_tick_offset), "r" (timer_tick_compare)
 		: "g1", "g2");
 
-	return ticks / timer_ticks_per_usec;
+	return (ticks * timer_ticks_per_usec_quotient) >> 32UL;
 }
 
 /* This need not obtain the xtime_lock as it is coded in
@@ -431,24 +431,22 @@
 	or	%g2, %lo(xtime), %g2
 	or	%g1, %lo(timer_tick_compare), %g1
 1:	ldda	[%g2] 0x24, %o4
-	membar	#LoadLoad | #MemIssue
 	rd	%tick, %o1
 	ldx	[%g1], %g7
-	membar	#LoadLoad | #MemIssue
 	ldda	[%g2] 0x24, %o2
-	membar	#LoadLoad
 	xor	%o4, %o2, %o2
 	xor	%o5, %o3, %o3
 	orcc	%o2, %o3, %g0
 	bne,pn	%xcc, 1b
 	 sethi	%hi(lost_ticks), %o2
-	sethi	%hi(timer_ticks_per_usec), %o3
+	sethi	%hi(timer_ticks_per_usec_quotient), %o3
 	ldx	[%o2 + %lo(lost_ticks)], %o2
 	add	%g3, %o1, %o1
-	ldx	[%o3 + %lo(timer_ticks_per_usec)], %o3
+	ldx	[%o3 + %lo(timer_ticks_per_usec_quotient)], %o3
 	sub	%o1, %g7, %o1
+	mulx	%o3, %o1, %o1
 	brz,pt	%o2, 1f
-	 udivx	%o1, %o3, %o1
+	 srlx	%o1, 32, %o1
 	sethi	%hi(10000), %g2
 	or	%g2, %lo(10000), %g2
 	add	%o1, %g2, %o1
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/kernel/trampoline.S linux.14p6/arch/sparc64/kernel/trampoline.S
--- linux.vanilla/arch/sparc64/kernel/trampoline.S	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/kernel/trampoline.S	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: trampoline.S,v 1.8.2.2 1999/08/19 01:11:14 davem Exp $
+/* $Id: trampoline.S,v 1.8.2.4 1999/10/27 00:22:24 davem Exp $
  * trampoline.S: Jump start slave processors on sparc64.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -14,21 +14,107 @@
 #include <asm/asm_offsets.h>
 
 	.data
-	.align		8
-	.globl		smp_trampoline
-smp_trampoline:		.skip	0x300
+	.align	8
+call_method:
+	.asciz	"call-method"
+	.align	8
+itlb_load:
+	.asciz	"SUNW,itlb-load"
+	.align	8
+dtlb_load:
+	.asciz	"SUNW,dtlb-load"
 
 	.text
 	.align		8
 	.globl		sparc64_cpu_startup, sparc64_cpu_startup_end
 sparc64_cpu_startup:
 	flushw
+
 	mov	(LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1
 	stxa	%g1, [%g0] ASI_LSU_CONTROL
 	membar	#Sync
-	wrpr	%g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate
-	wr	%g0, 0, %fprs
+
 	wrpr	%g0, 15, %pil
+	wr	%g0, 0, %tick_cmpr
+
+	/* Call OBP by hand to lock KERNBASE into i/d tlbs. */
+	mov	%o0, %l0
+
+	sethi	%hi(prom_entry_lock), %g2
+1:	ldstub	[%g2 + %lo(prom_entry_lock)], %g1
+	brnz,pn	%g1, 1b
+	 membar	#StoreLoad | #StoreStore
+
+	sethi	%hi(p1275buf), %g2
+	or	%g2, %lo(p1275buf), %g2
+	ldx	[%g2 + 0x10], %l2
+	mov	%sp, %l1
+	add	%l2, -(192 + 128), %sp
+	flushw
+
+	sethi	%hi(call_method), %g2
+	or	%g2, %lo(call_method), %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x00]
+	mov	5, %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x08]
+	mov	1, %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x10]
+	sethi	%hi(itlb_load), %g2
+	or	%g2, %lo(itlb_load), %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x18]
+	sethi	%hi(mmu_ihandle_cache), %g2
+	lduw	[%g2 + %lo(mmu_ihandle_cache)], %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x20]
+	sethi	%hi(KERNBASE), %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x28]
+	sethi	%hi(kern_locked_tte_data), %g2
+	ldx	[%g2 + %lo(kern_locked_tte_data)], %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x30]
+	mov	63, %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x38]
+	sethi	%hi(p1275buf), %g2
+	or	%g2, %lo(p1275buf), %g2
+	ldx	[%g2 + 0x08], %o1
+	call	%o1
+	 add	%sp, (2047 + 128), %o0
+
+	sethi	%hi(call_method), %g2
+	or	%g2, %lo(call_method), %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x00]
+	mov	5, %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x08]
+	mov	1, %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x10]
+	sethi	%hi(dtlb_load), %g2
+	or	%g2, %lo(dtlb_load), %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x18]
+	sethi	%hi(mmu_ihandle_cache), %g2
+	lduw	[%g2 + %lo(mmu_ihandle_cache)], %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x20]
+	sethi	%hi(KERNBASE), %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x28]
+	sethi	%hi(kern_locked_tte_data), %g2
+	ldx	[%g2 + %lo(kern_locked_tte_data)], %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x30]
+	mov	63, %g2
+	stx	%g2, [%sp + 2047 + 128 + 0x38]
+	sethi	%hi(p1275buf), %g2
+	or	%g2, %lo(p1275buf), %g2
+	ldx	[%g2 + 0x08], %o1
+	call	%o1
+	 add	%sp, (2047 + 128), %o0
+
+	sethi	%hi(prom_entry_lock), %g2
+	stb	%g0, [%g2 + %lo(prom_entry_lock)]
+	membar	#StoreStore | #StoreLoad
+
+	mov	%l1, %sp
+	flushw
+
+	mov	%l0, %o0
+
+	wrpr	%g0, (PSTATE_PRIV | PSTATE_PEF), %pstate
+	wr	%g0, 0, %fprs
 
 	sethi	%uhi(PAGE_OFFSET), %g4
 	sllx	%g4, 32, %g4
@@ -37,99 +123,6 @@
 	srl	%o0, 0, %o0
 	ldx	[%o0], %g6
 
-	sethi	%uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
-	sllx	%g5, 32, %g5
-	or	%g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5
-
-	sethi	%uhi(_PAGE_PADDR), %g3
-	or	%g3, %ulo(_PAGE_PADDR), %g3
-	sllx	%g3, 32, %g3
-	sethi	%hi(_PAGE_PADDR), %g7
-	or	%g7, %lo(_PAGE_PADDR), %g7
-	or	%g3, %g7, %g3
-
-	clr	%l0
-	set	0x1fff, %l2
-	rd	%pc, %l3
-	andn	%l3, %l2, %g2
-1:	ldxa	[%l0] ASI_ITLB_TAG_READ, %g1
-	nop
-	nop
-	nop
-	andn	%g1, %l2, %g1
-	cmp	%g1, %g2
-	be,a,pn	%xcc, 2f
-	 ldxa	[%l0] ASI_ITLB_DATA_ACCESS, %g1
-	cmp	%l0, (63 << 3)
-	blu,pt	%xcc, 1b
-	 add	%l0, (1 << 3), %l0
-
-2:	nop
-	nop
-	nop
-	and	%g1, %g3, %g1
-	sub	%g1, %g2, %g1
-	or	%g5, %g1, %g5
-	clr	%l0
-	sethi	%hi(KERNBASE), %g3
-	sethi	%hi(KERNBASE<<1), %g7
-	mov	TLB_TAG_ACCESS, %l7
-1:	ldxa	[%l0] ASI_ITLB_TAG_READ, %g1
-	nop
-	nop
-	nop
-	andn	%g1, %l2, %g1
-	cmp	%g1, %g3
-	blu,pn	%xcc, 2f
-	 cmp	%g1, %g7
-	bgeu,pn	%xcc, 2f
-	 nop
-	stxa	%g0, [%l7] ASI_IMMU
-	stxa	%g0, [%l0] ASI_ITLB_DATA_ACCESS
-2:	cmp	%l0, (63 << 3)
-	blu,pt	%xcc, 1b
-	 add	%l0, (1 << 3), %l0
-
-	nop
-	nop
-	nop
-	clr	%l0
-1:	ldxa	[%l0] ASI_DTLB_TAG_READ, %g1
-	nop
-	nop
-	nop
-	andn	%g1, %l2, %g1
-	cmp	%g1, %g3
-	blu,pn	%xcc, 2f
-	 cmp	%g1, %g7
-	bgeu,pn	%xcc, 2f
-	 nop
-	stxa	%g0, [%l7] ASI_DMMU
-	stxa	%g0, [%l0] ASI_DTLB_DATA_ACCESS
-2:	cmp	%l0, (63 << 3)
-	blu,pt	%xcc, 1b
-	 add	%l0, (1 << 3), %l0
-
-	nop
-	nop
-	nop
-	sethi	%hi(KERNBASE), %g3
-	mov	(63 << 3), %g7
-	stxa	%g3, [%l7] ASI_DMMU
-	stxa	%g5, [%g7] ASI_DTLB_DATA_ACCESS
-	membar	#Sync
-	stxa	%g3, [%l7] ASI_IMMU
-	stxa	%g5, [%g7] ASI_ITLB_DATA_ACCESS
-	membar	#Sync
-	flush	%g3
-	membar	#Sync
-	b,pt	%xcc, 1f
-	 nop
-1:	set	bounce, %g2
-	jmpl	%g2 + %g0, %g0
-	 nop
-
-bounce:
 	mov	PRIMARY_CONTEXT, %g7
 	stxa	%g0, [%g7] ASI_DMMU
 	membar	#Sync
@@ -137,24 +130,6 @@
 	stxa	%g0, [%g7] ASI_DMMU
 	membar	#Sync
 
-	mov	TLB_TAG_ACCESS, %g2
-	stxa	%g3, [%g2] ASI_IMMU
-	stxa	%g3, [%g2] ASI_DMMU
-
-	mov	(63 << 3), %g7
-	ldxa	[%g7] ASI_ITLB_DATA_ACCESS, %g1
-	andn	%g1, (_PAGE_G), %g1
-	stxa	%g1, [%g7] ASI_ITLB_DATA_ACCESS
-	membar	#Sync
-
-	ldxa	[%g7] ASI_DTLB_DATA_ACCESS, %g1
-	andn	%g1, (_PAGE_G), %g1
-	stxa	%g1, [%g7] ASI_DTLB_DATA_ACCESS
-	membar	#Sync
-
-	flush	%g3
-	membar	#Sync
-
 	mov	1, %g5
 	sllx	%g5, (PAGE_SHIFT + 1), %g5
 	sub	%g5, (REGWIN_SZ + STACK_BIAS), %g5
@@ -167,12 +142,12 @@
 	/* Setup the trap globals, then we can resurface. */
 	rdpr	%pstate, %o1
 	mov	%g6, %o2
-	wrpr	%o1, (PSTATE_AG | PSTATE_IE), %pstate
+	wrpr	%o1, PSTATE_AG, %pstate
 	sethi	%hi(sparc64_ttable_tl0), %g5
 	wrpr	%g5, %tba
 	mov	%o2, %g6
 
-	wrpr	%o1, (PSTATE_MG | PSTATE_IE), %pstate
+	wrpr	%o1, PSTATE_MG, %pstate
 #define KERN_HIGHBITS		((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
 #define KERN_LOWBITS		(_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
 #ifdef THIS_IS_CHEETAH
@@ -198,7 +173,7 @@
 #undef VPTE_BASE
 
 	/* Setup interrupt globals, we are always SMP. */
-	wrpr	%o1, (PSTATE_IG | PSTATE_IE), %pstate
+	wrpr	%o1, PSTATE_IG, %pstate
 
 	/* Get our UPA MID. */
 	lduw	[%o2 + AOFF_task_processor], %g1
@@ -208,11 +183,14 @@
 	/* In theory this is: &(cpu_data[this_upamid].irq_worklists[0]) */
 	sllx	%g1, 7, %g1
 	add	%g5, %g1, %g1
-	add	%g1, 64, %g1
+	add	%g1, 64, %g6
 
 	wrpr	%g0, 0, %wstate
 	or	%o1, PSTATE_IE, %o1
 	wrpr	%o1, 0, %pstate
+
+	call	prom_set_trap_table
+	 sethi	%hi(sparc64_ttable_tl0), %o0
 
 	call	smp_callin
 	 nop
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/lib/blockops.S linux.14p6/arch/sparc64/lib/blockops.S
--- linux.vanilla/arch/sparc64/lib/blockops.S	Tue Oct 27 17:52:20 1998
+++ linux.14p6/arch/sparc64/lib/blockops.S	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: blockops.S,v 1.16 1998/10/20 03:09:04 jj Exp $
+/* $Id: blockops.S,v 1.16.2.1 1999/10/07 20:48:14 davem Exp $
  * blockops.S: UltraSparc block zero optimized routines.
  *
  * Copyright (C) 1996,1998 David S. Miller (davem@caip.rutgers.edu)
@@ -136,16 +136,18 @@
 
 	faddd		%f0, %f2, %f12			! FPA	Group
 	fmuld		%f0, %f2, %f14			! FPM
-	wr		%g0, ASI_BLK_P, %asi		! LSU	Group
 	membar		#StoreLoad | #StoreStore | #LoadStore	! LSU	Group
-1:	stda		%f0, [%o0 + 0x00] %asi		! Store	Group
-	stda		%f0, [%o0 + 0x40] %asi		! Store	Group
-	stda		%f0, [%o0 + 0x80] %asi		! Store	Group
-	stda		%f0, [%o0 + 0xc0] %asi		! Store	Group
+1:	stda		%f0, [%o0 + %g0] ASI_BLK_P	! Store	Group
+	add		%o0, 0x40, %o0			! IEU0
+	stda		%f0, [%o0 + %g0] ASI_BLK_P	! Store	Group
+	add		%o0, 0x40, %o0			! IEU0
+	stda		%f0, [%o0 + %g0] ASI_BLK_P	! Store	Group
 
+	add		%o0, 0x40, %o0			! IEU0	Group
+	stda		%f0, [%o0 + %g0] ASI_BLK_P	! Store	Group
 	subcc		%o1, 1, %o1			! IEU1
 	bne,pt		%icc, 1b			! CTI
-	 add		%o0, 0x100, %o0			! IEU0	Group
+	 add		%o0, 0x40, %o0			! IEU0	Group
 	membar		#Sync				! LSU	Group
 	VISExitHalf
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/mm/asyncd.c linux.14p6/arch/sparc64/mm/asyncd.c
--- linux.vanilla/arch/sparc64/mm/asyncd.c	Sun Oct  4 18:22:43 1998
+++ linux.14p6/arch/sparc64/mm/asyncd.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/*  $Id: asyncd.c,v 1.5 1998/09/13 04:30:33 davem Exp $
+/*  $Id: asyncd.c,v 1.5.2.1 1999/11/16 06:29:53 davem Exp $
  *  The asyncd kernel daemon. This handles paging on behalf of 
  *  processes that receive page faults due to remote (async) memory
  *  accesses. 
@@ -152,7 +152,9 @@
 	if(!pte)
 		goto no_memory;
 	if(!pte_present(*pte)) {
-		handle_mm_fault(tsk, vma, address, write);
+		int fault = handle_mm_fault(tsk, vma, address, write);
+		if (fault < 0)
+			goto no_memory;
 		goto finish_up;
 	}
 	set_pte(pte, pte_mkyoung(*pte));
@@ -164,7 +166,11 @@
 		flush_tlb_page(vma, address);
 		goto finish_up;
 	}
-	handle_mm_fault(tsk, vma, address, write);
+	{
+		int fault = handle_mm_fault(tsk, vma, address, write);
+		if (fault < 0)
+			goto no_memory;
+	}
 
 	/* Fall through for do_wp_page */
 finish_up:
@@ -173,7 +179,7 @@
 
 no_memory:
 	stats.failure++;
-	oom(tsk);
+	force_sig(SIGKILL, tsk);
 	return 1;
 	
 bad_area:	  
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/mm/fault.c linux.14p6/arch/sparc64/mm/fault.c
--- linux.vanilla/arch/sparc64/mm/fault.c	Wed Mar 17 05:52:06 1999
+++ linux.14p6/arch/sparc64/mm/fault.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.34 1999/03/16 12:12:28 jj Exp $
+/* $Id: fault.c,v 1.34.2.1 1999/11/16 06:29:56 davem Exp $
  * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -210,8 +210,15 @@
 			goto bad_area;
 	}
 	current->mm->segments = (void *) (address & PAGE_SIZE);
-	if (!handle_mm_fault(current, vma, address, write))
-		goto do_sigbus;
+survive:
+	{
+		int fault = handle_mm_fault(current, vma, address, write);
+		if (!fault)
+			goto do_sigbus;
+		if (fault < 0)
+			goto out_of_memory;
+	}
+
 	up(&mm->mmap_sem);
 	return;
 	/*
@@ -289,6 +296,17 @@
 		}
 		unhandled_fault (address, current, regs);
 	}
+	return;
+
+out_of_memory:
+	if (current->pid == 1) {
+		current->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	up(&mm->mmap_sem);
+	printk("VM: killing process %s\n", current->comm);
+	do_exit(SIGKILL);
 	return;
 
 do_sigbus:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/mm/init.c linux.14p6/arch/sparc64/mm/init.c
--- linux.vanilla/arch/sparc64/mm/init.c	Sat Aug 14 02:27:16 1999
+++ linux.14p6/arch/sparc64/mm/init.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.127.2.1 1999/06/25 10:42:10 davem Exp $
+/*  $Id: init.c,v 1.127.2.4 1999/10/24 17:29:30 davem Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
@@ -40,9 +40,6 @@
 /* Ugly, but necessary... -DaveM */
 unsigned long phys_base;
 
-/* get_new_mmu_context() uses "cache + 1".  */
-unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
-
 /* References to section boundaries */
 extern char __init_begin, __init_end, etext, __bss_start;
 
@@ -646,13 +643,23 @@
 	unsigned long data;
 };
 
-static inline void inherit_prom_mappings(void)
+extern unsigned long prom_boot_page;
+extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle);
+extern int prom_get_mmu_ihandle(void);
+extern void register_prom_callbacks(void);
+
+/* Exported for SMP bootup purposes. */
+unsigned long kern_locked_tte_data;
+
+static void inherit_prom_mappings(void)
 {
 	struct linux_prom_translation *trans;
+	unsigned long phys_page, tte_vaddr, tte_data;
+	void (*remap_func)(unsigned long, unsigned long, int);
 	pgd_t *pgdp;
 	pmd_t *pmdp;
 	pte_t *ptep;
-	int node, n, i;
+	int node, n, i, tsz;
 
 	node = prom_finddevice("/virtual-memory");
 	n = prom_getproplen(node, "translations");
@@ -660,11 +667,12 @@
 		prom_printf("Couldn't get translation property\n");
 		prom_halt();
 	}
+	n += 5 * sizeof(struct linux_prom_translation);
+	for (tsz = 1; tsz < n; tsz <<= 1)
+		/* empty */;
+	trans = sparc_init_alloc(&mempool, tsz);
 
-	for (i = 1; i < n; i <<= 1) /* empty */;
-	trans = sparc_init_alloc(&mempool, i);
-
-	if (prom_getproperty(node, "translations", (char *)trans, i) == -1) {
+	if ((n = prom_getproperty(node, "translations", (char *)trans, tsz)) == -1) {
 		prom_printf("Couldn't get translation property\n");
 		prom_halt();
 	}
@@ -696,6 +704,83 @@
 			}
 		}
 	}
+
+	/* Now fixup OBP's idea about where we really are mapped. */
+	prom_printf("Remapping the kernel... ");
+	phys_page = spitfire_get_dtlb_data(63) & _PAGE_PADDR;
+	phys_page += ((unsigned long)&prom_boot_page -
+		      (unsigned long)&empty_zero_page);
+
+	/* Lock this into i/d tlb entry 59 */
+	__asm__ __volatile__(
+		"stxa	%%g0, [%2] %3\n\t"
+		"stxa	%0, [%1] %4\n\t"
+		"membar	#Sync\n\t"
+		"flush	%%g6\n\t"
+		"stxa	%%g0, [%2] %5\n\t"
+		"stxa	%0, [%1] %6\n\t"
+		"membar	#Sync\n\t"
+		"flush	%%g6"
+		: : "r" (phys_page | _PAGE_VALID | _PAGE_SZ8K | _PAGE_CP |
+			 _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W),
+		    "r" (59 << 3), "r" (TLB_TAG_ACCESS),
+		    "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS),
+		    "i" (ASI_IMMU), "i" (ASI_ITLB_DATA_ACCESS)
+		: "memory");
+
+	tte_vaddr = (unsigned long) &empty_zero_page;
+	kern_locked_tte_data = tte_data = spitfire_get_dtlb_data(63);
+
+	remap_func = (void *)  ((unsigned long) &prom_remap -
+				(unsigned long) &prom_boot_page);
+
+	remap_func(spitfire_get_dtlb_data(63) & _PAGE_PADDR,
+		   (unsigned long) &empty_zero_page,
+		   prom_get_mmu_ihandle());
+
+	/* Flush out that temporary mapping. */
+	spitfire_flush_dtlb_nucleus_page(0x0);
+	spitfire_flush_itlb_nucleus_page(0x0);
+
+	/* Now lock us back into the TLBs via OBP. */
+	prom_dtlb_load(63, tte_data, tte_vaddr);
+	prom_itlb_load(63, tte_data, tte_vaddr);
+
+	/* Re-read translations property. */
+	if ((n = prom_getproperty(node, "translations", (char *)trans, tsz)) == -1) {
+		prom_printf("Couldn't get translation property\n");
+		prom_halt();
+	}
+	n = n / sizeof(*trans);
+
+	for (i = 0; i < n; i++) {
+		unsigned long vaddr = trans[i].virt;
+		unsigned long size = trans[i].size;
+
+		if (vaddr < 0xf0000000UL) {
+			unsigned long avoid_start = (unsigned long) &empty_zero_page;
+			unsigned long avoid_end = avoid_start + (4 * 1024 * 1024);
+
+			if (vaddr < avoid_start) {
+				unsigned long top = vaddr + size;
+
+				if (top > avoid_start)
+					top = avoid_start;
+				prom_unmap(top - vaddr, vaddr);
+			}
+			if ((vaddr + size) > avoid_end) {
+				unsigned long bottom = vaddr;
+
+				if (bottom < avoid_end)
+					bottom = avoid_end;
+				prom_unmap((vaddr + size) - bottom, bottom);
+			}
+		}
+	}
+
+	prom_printf("done.\n");
+
+	register_prom_callbacks();
 }
 
 /* The OBP specifications for sun4u mark 0xfffffffc00000000 and
@@ -956,6 +1041,8 @@
 
 #define CTX_BMAP_SLOTS (1UL << (CTX_VERSION_SHIFT - 6))
 unsigned long mmu_context_bmap[CTX_BMAP_SLOTS];
+spinlock_t ctx_alloc_lock = SPIN_LOCK_UNLOCKED;
+unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
 
 /* Caller does TLB context flushing on local CPU if necessary.
  *
@@ -966,14 +1053,17 @@
  */
 void get_new_mmu_context(struct mm_struct *mm)
 {
-	unsigned long ctx = (tlb_context_cache + 1) & ~(CTX_VERSION_MASK);
-	unsigned long new_ctx;
+	unsigned long ctx, new_ctx;
 	
+	spin_lock(&ctx_alloc_lock);
+	ctx = (tlb_context_cache + 1) & ~(CTX_VERSION_MASK);
 	if (ctx == 0)
 		ctx = 1;
 	if ((mm->context != NO_CONTEXT) &&
-	    !((mm->context ^ tlb_context_cache) & CTX_VERSION_MASK))
-		clear_bit(mm->context & ~(CTX_VERSION_MASK), mmu_context_bmap);
+	    !((mm->context ^ tlb_context_cache) & CTX_VERSION_MASK)) {
+		unsigned long nr = mm->context & ~(CTX_VERSION_MASK);
+		mmu_context_bmap[nr >> 6] &= ~(1UL << (nr & 63));
+	}
 	new_ctx = find_next_zero_bit(mmu_context_bmap, 1UL << CTX_VERSION_SHIFT, ctx);
 	if (new_ctx >= (1UL << CTX_VERSION_SHIFT)) {
 		new_ctx = find_next_zero_bit(mmu_context_bmap, ctx, 1);
@@ -1000,10 +1090,12 @@
 			goto out;
 		}
 	}
-	set_bit(new_ctx, mmu_context_bmap);
+	mmu_context_bmap[new_ctx >> 6] |= (1UL << (new_ctx & 63));
 	new_ctx |= (tlb_context_cache & CTX_VERSION_MASK);
 out:
 	tlb_context_cache = new_ctx;
+	spin_unlock(&ctx_alloc_lock);
+
 	mm->context = new_ctx;
 	mm->cpu_vm_mask = 0;
 }
@@ -1214,7 +1306,7 @@
 	/* Allocate 64M for dynamic DVMA mapping area. */
 	allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000);
 	inherit_prom_mappings();
-	
+
 	/* Ok, we can use our TLB miss and window trap handlers safely.
 	 * We need to do a quick peek here to see if we are on StarFire
 	 * or not, so setup_tba can setup the IRQ globals correctly (it
@@ -1230,24 +1322,12 @@
 		setup_tba(is_starfire);
 	}
 
-	/* Really paranoid. */
-	flushi((long)&empty_zero_page);
-	membar("#Sync");
-
-	/* Cleanup the extra locked TLB entry we created since we have the
-	 * nice TLB miss handlers of ours installed now.
-	 */
+	inherit_locked_prom_mappings(1);
+	
 	/* We only created DTLB mapping of this stuff. */
 	spitfire_flush_dtlb_nucleus_page(alias_base);
 	if (second_alias_page)
 		spitfire_flush_dtlb_nucleus_page(second_alias_page);
-	membar("#Sync");
-
-	/* Paranoid */
-	flushi((long)&empty_zero_page);
-	membar("#Sync");
-
-	inherit_locked_prom_mappings(1);
 
 	flush_tlb_all();
 
@@ -1256,11 +1336,97 @@
 	return device_scan (PAGE_ALIGN (start_mem));
 }
 
+/* Ok, it seems that the prom can allocate some more memory chunks
+ * as a side effect of some prom calls we perform during the
+ * boot sequence.  My most likely theory is that it is from the
+ * prom_set_traptable() call, and OBP is allocating a scratchpad
+ * for saving client program register state etc.
+ */
+__initfunc(static void sort_memlist(struct linux_mlist_p1275 *thislist))
+{
+	int swapi = 0;
+	int i, mitr;
+	unsigned long tmpaddr, tmpsize;
+	unsigned long lowest;
+
+	for(i=0; thislist[i].theres_more != 0; i++) {
+		lowest = thislist[i].start_adr;
+		for(mitr = i+1; thislist[mitr-1].theres_more != 0; mitr++)
+			if(thislist[mitr].start_adr < lowest) {
+				lowest = thislist[mitr].start_adr;
+				swapi = mitr;
+			}
+		if(lowest == thislist[i].start_adr) continue;
+		tmpaddr = thislist[swapi].start_adr;
+		tmpsize = thislist[swapi].num_bytes;
+		for(mitr = swapi; mitr > i; mitr--) {
+			thislist[mitr].start_adr = thislist[mitr-1].start_adr;
+			thislist[mitr].num_bytes = thislist[mitr-1].num_bytes;
+		}
+		thislist[i].start_adr = tmpaddr;
+		thislist[i].num_bytes = tmpsize;
+	}
+}
+
+__initfunc(static void rescan_sp_banks(void))
+{
+	struct linux_prom64_registers memlist[64];
+	struct linux_mlist_p1275 avail[64], *mlist;
+	unsigned long bytes, base_paddr;
+	int num_regs, node = prom_finddevice("/memory");
+	int i;
+
+	num_regs = prom_getproperty(node, "available",
+				    (char *) memlist, sizeof(memlist));
+	num_regs = (num_regs / sizeof(struct linux_prom64_registers));
+	for (i = 0; i < num_regs; i++) {
+		avail[i].start_adr = memlist[i].phys_addr;
+		avail[i].num_bytes = memlist[i].reg_size;
+		avail[i].theres_more = &avail[i + 1];
+	}
+	avail[i - 1].theres_more = NULL;
+	sort_memlist(avail);
+
+	mlist = &avail[0];
+	i = 0;
+	bytes = mlist->num_bytes;
+	base_paddr = mlist->start_adr;
+  
+	sp_banks[0].base_addr = base_paddr;
+	sp_banks[0].num_bytes = bytes;
+
+	while (mlist->theres_more != NULL){
+		i++;
+		mlist = mlist->theres_more;
+		bytes = mlist->num_bytes;
+		if (i >= SPARC_PHYS_BANKS-1) {
+			printk ("The machine has more banks than "
+				"this kernel can support\n"
+				"Increase the SPARC_PHYS_BANKS "
+				"setting (currently %d)\n",
+				SPARC_PHYS_BANKS);
+			i = SPARC_PHYS_BANKS-1;
+			break;
+		}
+    
+		sp_banks[i].base_addr = mlist->start_adr;
+		sp_banks[i].num_bytes = mlist->num_bytes;
+	}
+
+	i++;
+	sp_banks[i].base_addr = 0xdeadbeefbeefdeadUL;
+	sp_banks[i].num_bytes = 0;
+
+	for (i = 0; sp_banks[i].num_bytes != 0; i++)
+		sp_banks[i].num_bytes &= PAGE_MASK;
+}
+
 __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
 {
 	unsigned long tmp = 0, paddr, endaddr;
 	unsigned long end = __pa(end_mem);
 
+	rescan_sp_banks();
 	dvmaio_init();
 	for (paddr = __pa(start_mem); paddr < end; ) {
 		for (; sp_banks[tmp].num_bytes != 0; tmp++)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/mm/ultra.S linux.14p6/arch/sparc64/mm/ultra.S
--- linux.vanilla/arch/sparc64/mm/ultra.S	Sun Mar 28 18:07:47 1999
+++ linux.14p6/arch/sparc64/mm/ultra.S	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: ultra.S,v 1.32 1999/03/28 08:39:34 davem Exp $
+/* $Id: ultra.S,v 1.32.2.1 1999/10/24 17:29:34 davem Exp $
  * ultra.S: Don't expand these all over the place...
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -173,10 +173,10 @@
 	 *
 	 * Register usage:
 	 *   %g5	mm->context	(all tlb flushes)
-	 *   %g6	address arg 1	(tlb page and range flushes)
+	 *   %g1	address arg 1	(tlb page and range flushes)
 	 *   %g7	address arg 2	(tlb range flush only)
 	 *
-	 *   %g1	ivector table, don't touch
+	 *   %g6	ivector table, don't touch
 	 *   %g2	scratch 1
 	 *   %g3	scratch 2
 	 *   %g4	scratch 3
@@ -187,7 +187,7 @@
 	.globl		xcall_flush_tlb_page, xcall_flush_tlb_mm, xcall_flush_tlb_range
 xcall_flush_tlb_page:
 	mov		SECONDARY_CONTEXT, %g2
-	or		%g6, 0x10, %g4
+	or		%g1, 0x10, %g4
 	ldxa		[%g2] ASI_DMMU, %g3
 	stxa		%g5, [%g2] ASI_DMMU
 	stxa		%g0, [%g4] ASI_DMMU_DEMAP
@@ -208,11 +208,11 @@
 xcall_flush_tlb_range:
 	sethi		%hi(8192 - 1), %g2
 	or		%g2, %lo(8192 - 1), %g2
-	andn		%g6, %g2, %g6
+	andn		%g1, %g2, %g1
 	andn		%g7, %g2, %g7
-	sub		%g7, %g6, %g3
+	sub		%g7, %g1, %g3
 	add		%g2, 1, %g2
-	orcc		%g6, 0x10, %g6
+	orcc		%g1, 0x10, %g1
 	srlx		%g3, 13, %g4
 
 	cmp		%g4, 96
@@ -224,8 +224,8 @@
 	nop
 	nop
 
-1:	stxa		%g0, [%g6 + %g3] ASI_DMMU_DEMAP
-	stxa		%g0, [%g6 + %g3] ASI_IMMU_DEMAP
+1:	stxa		%g0, [%g1 + %g3] ASI_DMMU_DEMAP
+	stxa		%g0, [%g1 + %g3] ASI_IMMU_DEMAP
 	brnz,pt		%g3, 1b
 	 sub		%g3, %g2, %g3
 	stxa		%g7, [%g4] ASI_DMMU
@@ -261,6 +261,22 @@
 	b,pt		%xcc, rtrap
 	 clr		%l6
 
+	.globl		xcall_promstop
+xcall_promstop:
+	rdpr		%pstate, %g2
+	wrpr		%g2, PSTATE_IG | PSTATE_AG, %pstate
+	rdpr		%pil, %g2
+	wrpr		%g0, 15, %pil
+	sethi		%hi(109f), %g7
+	b,pt		%xcc, etrap_irq
+109:	 or		%g7, %lo(109b), %g7
+	flushw
+	call		prom_stopself
+	 nop
+	/* We should not return, just spin if we do... */
+1:	b,a,pt		%xcc, 1b
+	nop
+
 	.globl		xcall_receive_signal
 xcall_receive_signal:
 	rdpr		%pstate, %g2
@@ -302,7 +318,7 @@
 	cmp		%g2, 63
 	ble,pt		%icc, 1b
 	 sll		%g2, 3, %g3
-	flush		%g1
+	flush		%g6
 	retry
 
 	.globl		xcall_flush_cache_all
@@ -315,6 +331,6 @@
 	cmp		%g3, %g2
 	bleu,pt		%xcc, 1b
 	 nop
-	flush		%g1
+	flush		%g6
 	retry
 #endif /* __SMP__ */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/prom/Makefile linux.14p6/arch/sparc64/prom/Makefile
--- linux.vanilla/arch/sparc64/prom/Makefile	Mon Mar 17 22:54:23 1997
+++ linux.14p6/arch/sparc64/prom/Makefile	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.2 1997/02/25 12:40:25 jj Exp $
+# $Id: Makefile,v 1.2.6.1 1999/10/24 17:29:02 davem Exp $
 # Makefile for the Sun Boot PROM interface library under
 # Linux.
 #
@@ -9,13 +9,19 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 OBJS  = bootstr.o devops.o init.o memory.o misc.o \
-        ranges.o tree.o console.o printf.o p1275.o
+        ranges.o tree.o console.o printf.o p1275.o map.o
 
 all: promlib.a
 
 promlib.a: $(OBJS)
 	$(AR) rcs promlib.a $(OBJS)
 	sync
+
+.S.s:
+	$(CPP) -D__ASSEMBLY__ $(AFLAGS) -ansi $< -o $*.s
+
+.S.o:
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o
 
 dep:
 	$(CPP) -M *.c > .depend
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/prom/map.S linux.14p6/arch/sparc64/prom/map.S
--- linux.vanilla/arch/sparc64/prom/map.S	Thu Jan  1 01:00:00 1970
+++ linux.14p6/arch/sparc64/prom/map.S	Tue Nov 16 15:13:27 1999
@@ -0,0 +1,70 @@
+/* $Id: map.S,v 1.1.2.1 1999/10/24 17:28:59 davem Exp $
+ * map.S: Tricky coding required to fixup the kernel OBP maps
+ *	  properly.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+	.text
+	.align	8192
+	.globl	prom_boot_page
+prom_boot_page:
+call_method:
+	.asciz	"call-method"
+	.align	8
+map:
+	.asciz	"map"
+	.align	8
+
+	/* When we are invoked, our caller has remapped us to
+	 * page zero, therefore we must use PC relative addressing
+	 * for everything after we begin performing the unmap/map
+	 * calls.
+	 */
+	.globl	prom_remap
+prom_remap:	/* %o0 = physpage, %o1 = virtpage, %o2 = mmu_ihandle */
+	rd	%pc, %g1
+	srl	%o2, 0, %o2			! kill sign extension
+	sethi	%hi(p1275buf), %g2
+	or	%g2, %lo(p1275buf), %g2
+	ldx	[%g2 + 0x10], %g3		! prom_cif_stack
+	save	%g3, -(192 + 128), %sp
+	ldx	[%g2 + 0x08], %l0		! prom_cif_handler
+	mov	%g6, %i3
+	mov	%g4, %i4
+	flushw
+
+	sethi	%hi(prom_remap - call_method), %g7
+	or	%g7, %lo(prom_remap - call_method), %g7
+	sub	%g1, %g7, %l2			! call-method string
+	sethi	%hi(prom_remap - map), %g7
+	or	%g7, %lo(prom_remap - map), %g7
+	sub	%g1, %g7, %l4			! map string
+
+	/* OK, map the 4MB region we really live at. */
+	stx	%l2, [%sp + 2047 + 128 + 0x00]	! call-method
+	mov	7, %l5
+	stx	%l5, [%sp + 2047 + 128 + 0x08]	! num_args
+	mov	1, %l5
+	stx	%l5, [%sp + 2047 + 128 + 0x10]	! num_rets
+	stx	%l4, [%sp + 2047 + 128 + 0x18]	! map
+	stx	%i2, [%sp + 2047 + 128 + 0x20]	! mmu_ihandle
+	mov	-1, %l5
+	stx	%l5, [%sp + 2047 + 128 + 0x28]	! mode == default
+	sethi	%hi(4 * 1024 * 1024), %l5
+	stx	%l5, [%sp + 2047 + 128 + 0x30]	! size
+	stx	%i1, [%sp + 2047 + 128 + 0x38]	! vaddr
+	stx	%g0, [%sp + 2047 + 128 + 0x40]	! filler
+	stx	%i0, [%sp + 2047 + 128 + 0x48]	! paddr
+	call	%l0
+	 add	%sp, (2047 + 128), %o0		! argument array
+
+	/* Restore hard-coded globals. */
+	mov	%i3, %g6
+	mov	%i4, %g4
+
+	/* Wheee.... we are done. */
+	ret
+	restore
+
+	.align	8192
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/prom/misc.c linux.14p6/arch/sparc64/prom/misc.c
--- linux.vanilla/arch/sparc64/prom/misc.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/prom/misc.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.14.2.1 1999/08/19 01:11:18 davem Exp $
+/* $Id: misc.c,v 1.14.2.3 1999/10/27 00:22:26 davem Exp $
  * misc.c:  Miscellaneous prom functions that don't belong
  *          anywhere else.
  *
@@ -10,6 +10,8 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
@@ -37,6 +39,11 @@
 extern int serial_console;
 #endif
 
+#ifdef __SMP__
+extern void smp_capture(void);
+extern void smp_release(void);
+#endif
+
 /* Drop into the prom, with the chance to continue with the 'go'
  * prom command.
  */
@@ -44,26 +51,57 @@
 prom_cmdline(void)
 {
 	unsigned long flags;
-    
+
+	__save_and_cli(flags);
+
 #ifdef CONFIG_SUN_CONSOLE
 	if(!serial_console && prom_palette)
 		prom_palette (1);
 #endif
-	__save_and_cli(flags);
+
+	/* We always arrive here via a serial interrupt.
+	 * So in order for everything to work reliably, even
+	 * on SMP, we need to drop the IRQ locks we hold.
+	 */
+#ifdef __SMP__
+	hardirq_exit(smp_processor_id());
+	smp_capture();
+#else
+	local_irq_count--;
+#endif
+
 	p1275_cmd ("enter", P1275_INOUT(0,0));
-	__restore_flags(flags);
+
+#ifdef __SMP__
+	smp_release();
+	hardirq_enter(smp_processor_id());
+	spin_unlock_wait(&global_irq_lock);
+#else
+	local_irq_count++;
+#endif
+
 #ifdef CONFIG_SUN_CONSOLE
 	if(!serial_console && prom_palette)
 		prom_palette (0);
 #endif
+
+	__restore_flags(flags);
 }
 
+#ifdef __SMP__
+extern void smp_promstop_others(void);
+#endif
+
 /* Drop into the prom, but completely terminate the program.
  * No chance of continuing.
  */
 void
 prom_halt(void)
 {
+#ifdef __SMP__
+	smp_promstop_others();
+	udelay(8000);
+#endif
 again:
 	p1275_cmd ("exit", P1275_INOUT(0,0));
 	goto again; /* PROM is out to get me -DaveM */
@@ -122,10 +160,10 @@
 	p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba);
 }
 
-/* This is only used internally below. */
-static int prom_get_mmu_ihandle(void)
+int mmu_ihandle_cache = 0;
+
+int prom_get_mmu_ihandle(void)
 {
-	static int mmu_ihandle_cache = 0;
 	int node, ret;
 
 	if (mmu_ihandle_cache != 0)
@@ -165,7 +203,10 @@
 		    unsigned long vaddr)
 {
 	return p1275_cmd("call-method",
-			 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)),
+			 (P1275_ARG(0, P1275_ARG_IN_STRING) |
+			  P1275_ARG(2, P1275_ARG_IN_64B) |
+			  P1275_ARG(3, P1275_ARG_IN_64B) |
+			  P1275_INOUT(5, 1)),
 			 "SUNW,itlb-load",
 			 prom_get_mmu_ihandle(),
 			 /* And then our actual args are pushed backwards. */
@@ -179,7 +220,10 @@
 		    unsigned long vaddr)
 {
 	return p1275_cmd("call-method",
-			 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)),
+			 (P1275_ARG(0, P1275_ARG_IN_STRING) |
+			  P1275_ARG(2, P1275_ARG_IN_64B) |
+			  P1275_ARG(3, P1275_ARG_IN_64B) |
+			  P1275_INOUT(5, 1)),
 			 "SUNW,dtlb-load",
 			 prom_get_mmu_ihandle(),
 			 /* And then our actual args are pushed backwards. */
@@ -188,6 +232,41 @@
 			 index);
 }
 
+int prom_map(int mode, unsigned long size,
+	     unsigned long vaddr, unsigned long paddr)
+{
+	int ret = p1275_cmd("call-method",
+			    (P1275_ARG(0, P1275_ARG_IN_STRING) |
+			     P1275_ARG(3, P1275_ARG_IN_64B) |
+			     P1275_ARG(4, P1275_ARG_IN_64B) |
+			     P1275_ARG(6, P1275_ARG_IN_64B) |
+			     P1275_INOUT(7, 1)),
+			    "map",
+			    prom_get_mmu_ihandle(),
+			    mode,
+			    size,
+			    vaddr,
+			    0,
+			    paddr);
+
+	if (ret == 0)
+		ret = -1;
+	return ret;
+}
+
+void prom_unmap(unsigned long size, unsigned long vaddr)
+{
+	p1275_cmd("call-method",
+		  (P1275_ARG(0, P1275_ARG_IN_STRING) |
+		   P1275_ARG(2, P1275_ARG_IN_64B) |
+		   P1275_ARG(3, P1275_ARG_IN_64B) |
+		   P1275_INOUT(4, 0)),
+		  "unmap",
+		  prom_get_mmu_ihandle(),
+		  size,
+		  vaddr);
+}
+
 /* Set aside physical memory which is not touched or modified
  * across soft resets.
  */
@@ -226,7 +305,7 @@
 	return p1275_cmd("call-method",
 			 (P1275_ARG(0, P1275_ARG_IN_STRING)	|
 			  P1275_ARG(3, P1275_ARG_OUT_BUF)	|
-			  P1275_ARG(5, P1275_ARG_IN_64B)	|
+			  P1275_ARG(6, P1275_ARG_IN_64B)	|
 			  P1275_INOUT(8, 2)),
 			 "SUNW,get-unumber", prom_get_memory_ihandle(),
 			 buflen, buf, P1275_SIZE(buflen),
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/sparc64/prom/p1275.c linux.14p6/arch/sparc64/prom/p1275.c
--- linux.vanilla/arch/sparc64/prom/p1275.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/arch/sparc64/prom/p1275.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: p1275.c,v 1.15.2.1 1999/08/19 01:11:19 davem Exp $
+/* $Id: p1275.c,v 1.15.2.3 1999/10/27 00:22:27 davem Exp $
  * p1275.c: Sun IEEE 1275 PROM low level interface routines
  *
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -253,9 +253,7 @@
  */
 static int prom_entry_depth = 0;
 #ifdef __SMP__
-static spinlock_t prom_entry_lock = SPIN_LOCK_UNLOCKED;
-extern void smp_capture(void);
-extern void smp_release(void);
+spinlock_t prom_entry_lock = SPIN_LOCK_UNLOCKED;
 #endif
 
 static __inline__ unsigned long prom_get_lock(void)
@@ -270,9 +268,6 @@
 		if (prom_entry_depth != 0)
 			panic("prom_get_lock");
 #endif
-#ifdef __SMP__
-		smp_capture();
-#endif
 	}
 	prom_entry_depth++;
 
@@ -281,12 +276,9 @@
 
 static __inline__ void prom_release_lock(unsigned long flags)
 {
-	if (--prom_entry_depth == 0) {
-#ifdef __SMP__
-		smp_release();
-#endif
+	if (--prom_entry_depth == 0)
 		spin_unlock(&prom_entry_lock);
-	}
+
 	__restore_flags(flags);
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/Config.in linux.14p6/drivers/block/Config.in
--- linux.vanilla/drivers/block/Config.in	Sat Aug 14 02:27:16 1999
+++ linux.14p6/drivers/block/Config.in	Fri Oct 22 22:18:35 1999
@@ -131,7 +131,9 @@
 if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
   source drivers/block/paride/Config.in
 fi
-tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
+if [ "$CONFIG_PCI" = "y" ]; then
+  tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
+fi
 
 if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
   define_bool CONFIG_BLK_DEV_HD y
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/genhd.c linux.14p6/drivers/block/genhd.c
--- linux.vanilla/drivers/block/genhd.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/drivers/block/genhd.c	Fri Oct 22 22:15:20 1999
@@ -784,8 +784,11 @@
 	label = (struct sun_disklabel *) bh->b_data;
 	p = label->partitions;
 	if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) {
+#if 0
+		/* There is no error here - it is just not a sunlabel. */
 		printk("Dev %s Sun disklabel: bad magic %04x\n",
 		       kdevname(dev), be16_to_cpu(label->magic));
+#endif
 		brelse(bh);
 		return 0;
 	}
@@ -856,8 +859,11 @@
 	p = &label->partitions[0];
 	magic = label->magic_mushroom;
 	if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) {
+#if 0
+		/* There is no error here - it is just not an sgilabel. */
 		printk("Dev %s SGI disklabel: bad magic %08x\n",
 		       kdevname(dev), magic);
+#endif
 		brelse(bh);
 		return 0;
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-cd.c linux.14p6/drivers/block/ide-cd.c
--- linux.vanilla/drivers/block/ide-cd.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/drivers/block/ide-cd.c	Thu Nov  4 23:38:28 1999
@@ -519,7 +519,7 @@
 
 /* Returns 0 if the request should be continued.
    Returns 1 if the request was ended. */
-static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
+static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, int good_stat,
 				int *stat_ret)
 {
 	struct request *rq = HWGROUP(drive)->rq;
@@ -551,7 +551,7 @@
 				                      rq->buffer;
 			pc->stat = 1;
 			cdrom_end_request (1, drive);
-			ide_error (drive, "request sense failure", stat);
+			*startstop = ide_error (drive, "request sense failure", stat);
 			return 1;
 
 		} else if (cmd == PACKET_COMMAND) {
@@ -633,7 +633,7 @@
 			} else if ((err & ~ABRT_ERR) != 0) {
 				/* Go to the default handler
 				   for other errors. */
-				ide_error (drive, "cdrom_decode_status", stat);
+				*startstop = ide_error (drive, "cdrom_decode_status", stat);
 				return 1;
 			} else if ((++rq->errors > ERROR_MAX)) {
 				/* We've racked up too many retries.  Abort. */
@@ -649,6 +649,7 @@
 	}
 
 	/* Retry, or handle the next request. */
+	*startstop = ide_stopped;
 	return 1;
 }
 
@@ -660,13 +661,15 @@
    called when the interrupt from the drive arrives.  Otherwise, HANDLER
    will be called immediately after the drive is prepared for the transfer. */
 
-static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
+static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
 				       ide_handler_t *handler)
 {
+	ide_startstop_t startstop;
 	struct cdrom_info *info = drive->driver_data;
 
 	/* Wait for the controller to be idle. */
-	if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1;
+	if (ide_wait_stat (&startstop, drive, 0, BUSY_STAT, WAIT_READY))
+		return startstop;
 
 	if (info->dma)
 		info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive);
@@ -686,12 +689,11 @@
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
 		ide_set_handler (drive, handler, WAIT_CMD);
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+		return ide_started;
 	} else {
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
-		(*handler) (drive);
+		return (*handler) (drive);
 	}
-
-	return 0;
 }
 
 
@@ -700,7 +702,7 @@
    by cdrom_start_packet_command.
    HANDLER is the interrupt handler to call when the command completes
    or there's data ready. */
-static int cdrom_transfer_packet_command (ide_drive_t *drive,
+static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
                                           char *cmd_buf, int cmd_len,
 					  ide_handler_t *handler)
 {
@@ -708,14 +710,16 @@
 		/* Here we should have been called after receiving an interrupt
 		   from the device.  DRQ should how be set. */
 		int stat_dum;
+		ide_startstop_t startstop;
 
 		/* Check for errors. */
-		if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum))
-			return 1;
+		if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum))
+			return startstop;
 	} else {
+		ide_startstop_t startstop;
 		/* Otherwise, we must wait for DRQ to get set. */
-		if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
-			return 1;
+		if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
+			return startstop;
 	}
 
 	/* Arm the interrupt handler. */
@@ -724,7 +728,7 @@
 	/* Send the command to the device. */
 	atapi_output_bytes (drive, cmd_buf, cmd_len);
 
-	return 0;
+	return ide_started;
 }
 
 
@@ -826,12 +830,13 @@
 /*
  * Interrupt routine.  Called when a read request has completed.
  */
-static void cdrom_read_intr (ide_drive_t *drive)
+static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
 {
 	int stat;
 	int ireason, len, sectors_to_transfer, nskip;
 	struct cdrom_info *info = drive->driver_data;
 	int i, dma = info->dma, dma_error = 0;
+	ide_startstop_t startstop;
 
 	struct request *rq = HWGROUP(drive)->rq;
 
@@ -842,8 +847,8 @@
 			HWIF(drive)->dmaproc(ide_dma_off, drive);
 	}
 
-	if (cdrom_decode_status (drive, 0, &stat))
-		return;
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
  
 	if (dma) {
 		if (!dma_error) {
@@ -851,9 +856,9 @@
 				i -= rq->current_nr_sectors;
 				ide_end_request(1, HWGROUP(drive));
 			}
+			return ide_stopped;
 		} else
-			ide_error (drive, "dma error", stat);
-		return;
+			return ide_error (drive, "dma error", stat);
 	}
 
 	/* Read the interrupt reason and the transfer length. */
@@ -870,11 +875,12 @@
 			cdrom_end_request (0, drive);
 		} else
 			cdrom_end_request (1, drive);
-		return;
+		return ide_stopped;
 	}
 
 	/* Check that the drive is expecting to do the same thing we are. */
-	if (cdrom_read_check_ireason (drive, len, ireason)) return;
+	if (cdrom_read_check_ireason (drive, len, ireason))
+		return ide_stopped;
 
 	/* Assume that the drive will always provide data in multiples
 	   of at least SECTOR_SIZE, as it gets hairy to keep track
@@ -889,7 +895,7 @@
 			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
 		}
 		cdrom_end_request (0, drive);
-		return;
+		return ide_stopped;
 	}
 
 	/* The number of sectors we need to read from the drive. */
@@ -952,6 +958,7 @@
 	/* Done moving data!
 	   Wait for another interrupt. */
 	ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
+	return ide_started;
 }
 
 
@@ -1019,7 +1026,7 @@
  * However, for drq_interrupt devices, it is called from an interrupt
  * when the drive is ready to accept the command.
  */
-static void cdrom_start_read_continuation (ide_drive_t *drive)
+static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive)
 {
 	struct packet_command pc;
 	struct request *rq = HWGROUP(drive)->rq;
@@ -1046,7 +1053,7 @@
 			printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
 				drive->name, rq->current_nr_sectors);
 			cdrom_end_request (0, drive);
-			return;
+			return ide_stopped;
 		}
 
 		sector -= nskip;
@@ -1072,22 +1079,22 @@
 	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
 
 	/* Send the command to the drive and return. */
-	(void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
-					      &cdrom_read_intr);
+	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_read_intr);
 }
 
 #define IDECD_SEEK_THRESHOLD	(1000)			/* 1000 blocks */
 #define IDECD_SEEK_TIMER	(2 * WAIT_MIN_SLEEP)	/* 40 ms */
 #define IDECD_SEEK_TIMEOUT     WAIT_CMD                /* 10 sec */
 
-static void cdrom_seek_intr (ide_drive_t *drive)
+static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
 	int stat;
 	static int retry = 10;
+	ide_startstop_t startstop;
 
-	if (cdrom_decode_status (drive, 0, &stat))
-		return;
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
 	CDROM_CONFIG_FLAGS(drive)->seeking = 1;
 
 	if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) {
@@ -1096,9 +1103,10 @@
 			drive->dsc_overlap = 0;
 		}
 	}
+	return ide_stopped;
 }
 
-static void cdrom_start_seek_continuation (ide_drive_t *drive)
+static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
 {
 	struct packet_command pc;
 	struct request *rq = HWGROUP(drive)->rq;
@@ -1113,22 +1121,22 @@
 	memset (&pc.c, 0, sizeof (pc.c));
 	pc.c[0] = SEEK;
 	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
-	(void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr);
+	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr);
 }
 
-static void cdrom_start_seek (ide_drive_t *drive, unsigned int block)
+static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block)
 {
 	struct cdrom_info *info = drive->driver_data;
 
 	info->dma = 0;
 	info->start_seek = jiffies;
-	cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation);
+	return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation);
 }
 
 /*
  * Start a read request from the CD-ROM.
  */
-static void cdrom_start_read (ide_drive_t *drive, unsigned int block)
+static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block)
 {
 	struct cdrom_info *info = drive->driver_data;
 	struct request *rq = HWGROUP(drive)->rq;
@@ -1148,7 +1156,7 @@
 
 	/* Satisfy whatever we can of this request from our cached sector. */
 	if (cdrom_read_from_buffer (drive))
-		return;
+		return ide_stopped;
 
 	/* Clear the local sector buffer. */
 	info->nsectors_buffered = 0;
@@ -1159,8 +1167,7 @@
 		info->dma = 0;
 
 	/* Start sending the read request to the drive. */
-	cdrom_start_packet_command (drive, 32768,
-				    cdrom_start_read_continuation);
+	return cdrom_start_packet_command (drive, 32768, cdrom_start_read_continuation);
 }
 
 
@@ -1178,15 +1185,16 @@
 
 
 /* Interrupt routine for packet command completion. */
-static void cdrom_pc_intr (ide_drive_t *drive)
+static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
 {
 	int ireason, len, stat, thislen;
 	struct request *rq = HWGROUP(drive)->rq;
 	struct packet_command *pc = (struct packet_command *)rq->buffer;
+	ide_startstop_t startstop;
 
 	/* Check for errors. */
-	if (cdrom_decode_status (drive, 0, &stat))
-		return;
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
 
 	/* Read the interrupt reason and the transfer length. */
 	ireason = IN_BYTE (IDE_NSECTOR_REG);
@@ -1219,7 +1227,7 @@
 			pc->stat = 1;
 			cdrom_end_request (1, drive);
 		}
-		return;
+		return ide_stopped;
 	}
 
 	/* Figure out how much data to transfer. */
@@ -1288,21 +1296,21 @@
 
 	/* Now we wait for another interrupt. */
 	ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
+	return ide_started;
 }
 
 
-static void cdrom_do_pc_continuation (ide_drive_t *drive)
+static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	struct packet_command *pc = (struct packet_command *)rq->buffer;
 
 	/* Send the command to the drive and return. */
-	cdrom_transfer_packet_command (drive, pc->c,
-				       sizeof (pc->c), &cdrom_pc_intr);
+	return cdrom_transfer_packet_command (drive, pc->c, sizeof (pc->c), &cdrom_pc_intr);
 }
 
 
-static void cdrom_do_packet_command (ide_drive_t *drive)
+static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
 {
 	int len;
 	struct request *rq = HWGROUP(drive)->rq;
@@ -1317,7 +1325,7 @@
 	pc->stat = 0;
 
 	/* Start sending the command to the drive. */
-	cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
+	return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
 }
 
 
@@ -1401,19 +1409,20 @@
 /****************************************************************************
  * cdrom driver request routine.
  */
-static
-void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t
+ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
 	if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
-		cdrom_do_packet_command (drive);
+		return cdrom_do_packet_command (drive);
 	else if (rq -> cmd == RESET_DRIVE_COMMAND) {
 		cdrom_end_request (1, drive);
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	} else if (rq -> cmd != READ) {
 		printk ("ide-cd: bad cmd %d\n", rq -> cmd);
 		cdrom_end_request (0, drive);
+		return ide_stopped;
 	} else {
+		ide_startstop_t action;
 		struct cdrom_info *info = drive->driver_data;
 
 		if (CDROM_CONFIG_FLAGS(drive)->seeking) {
@@ -1423,17 +1432,18 @@
 			if ((stat & SEEK_STAT) != SEEK_STAT) {
 				if (elpased < IDECD_SEEK_TIMEOUT) {
 					ide_stall_queue (drive, IDECD_SEEK_TIMER);
-					return;
+					return ide_stopped;
 				}
 				printk ("%s: DSC timeout\n", drive->name);
 			}
 			CDROM_CONFIG_FLAGS(drive)->seeking = 0;
 		}
 		if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
-			cdrom_start_seek (drive, block);
+			action = cdrom_start_seek (drive, block);
 		else
-			cdrom_start_read (drive, block);
+			action = cdrom_start_read (drive, block);
 		info->last_block = block;
+		return action;
 	}
 }
 
@@ -2890,8 +2900,11 @@
 		struct atapi_capabilities_page cap;
 	} buf;
 
-	if (CDROM_CONFIG_FLAGS (drive)->nec260)
+	if (CDROM_CONFIG_FLAGS (drive)->nec260) {
+		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
 		return nslots;
+	}
 
 	do {	/* we seem to get stat=0x01,err=0x00 the first time (??) */
 		if (attempts-- <= 0)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-disk.c linux.14p6/drivers/block/ide-disk.c
--- linux.vanilla/drivers/block/ide-disk.c	Thu Mar 11 01:49:43 1999
+++ linux.14p6/drivers/block/ide-disk.c	Wed Oct 27 18:56:40 1999
@@ -88,36 +88,41 @@
  */
 static int lba_capacity_is_ok (struct hd_driveid *id)
 {
-	unsigned long lba_sects   = id->lba_capacity;
-	unsigned long chs_sects   = id->cyls * id->heads * id->sectors;
-	unsigned long _10_percent = chs_sects / 10;
+	unsigned long lba_sects, chs_sects, head, tail;
 
 	/*
-	 * very large drives (8GB+) may lie about the number of cylinders
-	 * This is a split test for drives 8 Gig and Bigger only.
+	 * The ATA spec tells large drives to return
+	 * C/H/S = 16383/16/63 independent of their size.
+	 * Some drives can be jumpered to use 15 heads instead of 16.
 	 */
-	if ((id->lba_capacity >= 16514064) && (id->cyls == 0x3fff) &&
-	    (id->heads == 16) && (id->sectors == 63)) {
-		id->cyls = lba_sects / (16 * 63); /* correct cyls */
-		return 1;	/* lba_capacity is our only option */
-	}
+	if (id->cyls == 16383 && id->sectors == 63 &&
+	    (id->heads == 15 || id->heads == 16) &&
+	    id->lba_capacity >= 16383*63*id->heads)
+		return 1;
+
+	lba_sects   = id->lba_capacity;
+	chs_sects   = id->cyls * id->heads * id->sectors;
+
 	/* perform a rough sanity check on lba_sects:  within 10% is "okay" */
-	if ((lba_sects - chs_sects) < _10_percent) {
-		return 1;	/* lba_capacity is good */
-	}
+	if ((lba_sects - chs_sects) < chs_sects/10)
+		return 1;
+
 	/* some drives have the word order reversed */
-	lba_sects = (lba_sects << 16) | (lba_sects >> 16);
-	if ((lba_sects - chs_sects) < _10_percent) {
-		id->lba_capacity = lba_sects;	/* fix it */
+	head = ((lba_sects >> 16) & 0xffff);
+	tail = (lba_sects & 0xffff);
+	lba_sects = (head | (tail << 16));
+	if ((lba_sects - chs_sects) < chs_sects/10) {
+		id->lba_capacity = lba_sects;
 		return 1;	/* lba_capacity is (now) good */
 	}
-	return 0;	/* lba_capacity value is bad */
+
+	return 0;	/* lba_capacity value may be bad */
 }
 
 /*
  * read_intr() is the handler for disk read/multread interrupts
  */
-static void read_intr (ide_drive_t *drive)
+static ide_startstop_t read_intr (ide_drive_t *drive)
 {
 	byte stat;
 	int i;
@@ -125,8 +130,7 @@
 	struct request *rq;
 
 	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		ide_error(drive, "read_intr", stat);
-		return;
+		return ide_error(drive, "read_intr", stat);
 	}
 	msect = drive->mult_count;
 	
@@ -138,12 +142,6 @@
 		msect -= nsect;
 	} else
 		nsect = 1;
-	/*
-	 * PIO input can take longish times, so we drop the spinlock.
-	 * On SMP, bad things might happen if syscall level code adds
-	 * a new request while we do this PIO, so we just freeze all
-	 * request queue handling while doing the PIO. FIXME
-	 */
 	idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
 #ifdef DEBUG
 	printk("%s:  read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
@@ -160,21 +158,24 @@
 		if (msect)
 			goto read_next;
 		ide_set_handler (drive, &read_intr, WAIT_CMD);
+		return ide_started;
 	}
+	return ide_stopped;
 }
 
 /*
  * write_intr() is the handler for disk write interrupts
  */
-static void write_intr (ide_drive_t *drive)
+static ide_startstop_t write_intr (ide_drive_t *drive)
 {
 	byte stat;
 	int i;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	struct request *rq = hwgroup->rq;
-	int error = 0;
 
-	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+	if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+		printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat);
+	} else {
 #ifdef DEBUG
 		printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
 			drive->name, rq->sector, (unsigned long) rq->buffer,
@@ -191,25 +192,28 @@
 			if (i > 0) {
 				idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
 				ide_set_handler (drive, &write_intr, WAIT_CMD);
+				return ide_started;
 			}
-			goto out;
+			return ide_stopped;
 		}
-	} else
-		error = 1;
-out:
-	if (error)
-		ide_error(drive, "write_intr", stat);
+		printk("%s: write_intr error2: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat);
+	}
+	return ide_error(drive, "write_intr", stat);
 }
 
 /*
  * ide_multwrite() transfers a block of up to mcount sectors of data
  * to a drive as part of a disk multiple-sector write operation.
+ *
+ * Returns 0 if successful;  returns 1 if request had to be aborted due to corrupted buffer list.
  */
-void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
+int ide_multwrite (ide_drive_t *drive, unsigned int mcount)
 {
-	struct request *rq = &HWGROUP(drive)->wrq;
+	ide_hwgroup_t	*hwgroup= HWGROUP(drive);
+	struct request *rq = &hwgroup->wrq;
 
 	do {
+		unsigned long flags;
 		unsigned int nsect = rq->current_nr_sectors;
 		if (nsect > mcount)
 			nsect = mcount;
@@ -221,6 +225,7 @@
 			drive->name, rq->sector, (unsigned long) rq->buffer,
 			nsect, rq->nr_sectors - nsect);
 #endif
+		spin_lock_irqsave(&io_request_lock, flags);	/* Is this really necessary? */
 		if ((rq->nr_sectors -= nsect) <= 0)
 			break;
 		if ((rq->current_nr_sectors -= nsect) == 0) {
@@ -228,32 +233,36 @@
 				rq->current_nr_sectors = rq->bh->b_size>>9;
 				rq->buffer             = rq->bh->b_data;
 			} else {
-				panic("%s: buffer list corrupted\n", drive->name);
-				break;
+				spin_unlock_irqrestore(&io_request_lock, flags);
+				printk("%s: buffer list corrupted\n", drive->name);
+				ide_end_request(0, hwgroup);
+				return 1;
 			}
 		} else {
 			rq->buffer += nsect << 9;
 		}
+		spin_unlock_irqrestore(&io_request_lock, flags);
 	} while (mcount);
+	return 0;
 }
 
 /*
  * multwrite_intr() is the handler for disk multwrite interrupts
  */
-static void multwrite_intr (ide_drive_t *drive)
+static ide_startstop_t multwrite_intr (ide_drive_t *drive)
 {
 	byte stat;
 	int i;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	struct request *rq = &hwgroup->wrq;
-	int error = 0;
 
 	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 		if (stat & DRQ_STAT) {
 			if (rq->nr_sectors) {
-				ide_multwrite(drive, drive->mult_count);
+				if (ide_multwrite(drive, drive->mult_count))
+					return ide_stopped;
 				ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
-				goto out;
+				return ide_started;
 			}
 		} else {
 			if (!rq->nr_sectors) {	/* all done? */
@@ -262,20 +271,17 @@
 					i -= rq->current_nr_sectors;
 					ide_end_request(1, hwgroup);
 				}
-				goto out;
+				return ide_stopped;
 			}
 		}
-	} else
-		error = 1;
-out:
-	if (error)
-		ide_error(drive, "multwrite_intr", stat);
+	}
+	return ide_error(drive, "multwrite_intr", stat);
 }
 
 /*
  * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
  */
-static void set_multmode_intr (ide_drive_t *drive)
+static ide_startstop_t set_multmode_intr (ide_drive_t *drive)
 {
 	byte stat = GET_STAT();
 
@@ -286,28 +292,31 @@
 		drive->special.b.recalibrate = 1;
 		(void) ide_dump_status(drive, "set_multmode", stat);
 	}
+	return ide_stopped;
 }
 
 /*
  * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
  */
-static void set_geometry_intr (ide_drive_t *drive)
+static ide_startstop_t set_geometry_intr (ide_drive_t *drive)
 {
 	byte stat = GET_STAT();
 
 	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-		ide_error(drive, "set_geometry_intr", stat);
+		return ide_error(drive, "set_geometry_intr", stat);
+	return ide_stopped;
 }
 
 /*
  * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
  */
-static void recal_intr (ide_drive_t *drive)
+static ide_startstop_t recal_intr (ide_drive_t *drive)
 {
 	byte stat = GET_STAT();
 
 	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-		ide_error(drive, "recal_intr", stat);
+		return ide_error(drive, "recal_intr", stat);
+	return ide_stopped;
 }
 
 /*
@@ -315,7 +324,7 @@
  * using LBA if supported, or CHS otherwise, to address sectors.
  * It also takes care of issuing special DRIVE_CMDs.
  */
-static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
 #ifdef CONFIG_BLK_DEV_PDC4030
 	ide_hwif_t *hwif = HWIF(drive);
@@ -361,45 +370,61 @@
 	}
 #ifdef CONFIG_BLK_DEV_PDC4030
 	if (use_pdc4030_io) {
-		extern void do_pdc4030_io(ide_drive_t *, struct request *);
-		do_pdc4030_io (drive, rq);
-		return;
+		extern ide_startstop_t do_pdc4030_io(ide_drive_t *, struct request *);
+		return do_pdc4030_io (drive, rq);
 	}
 #endif /* CONFIG_BLK_DEV_PDC4030 */
 	if (rq->cmd == READ) {
 #ifdef CONFIG_BLK_DEV_IDEDMA
 		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
-			return;
+			return ide_started;
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 		ide_set_handler(drive, &read_intr, WAIT_CMD);
 		OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG);
-		return;
+		return ide_started;
 	}
 	if (rq->cmd == WRITE) {
+		ide_startstop_t startstop;
 #ifdef CONFIG_BLK_DEV_IDEDMA
 		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
-			return;
+			return ide_started;
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 		OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG);
-		if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+		if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
 			printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name,
 				drive->mult_count ? "MULTWRITE" : "WRITE");
-			return;
+			return startstop;
 		}
 		if (!drive->unmask)
 			__cli();	/* local CPU only */
 		if (drive->mult_count) {
-			HWGROUP(drive)->wrq = *rq; /* scratchpad */
+			ide_hwgroup_t *hwgroup = HWGROUP(drive);
+			/*
+			 * Ugh.. this part looks ugly because we MUST set up
+			 * the interrupt handler before outputting the first block
+			 * of data to be written.  If we hit an error (corrupted buffer list)
+			 * in ide_multwrite(), then we need to remove the handler/timer
+			 * before returning.  Fortunately, this NEVER happens (right?).
+			 */
+			hwgroup->wrq = *rq; /* scratchpad */
 			ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
-			ide_multwrite(drive, drive->mult_count);
+			if (ide_multwrite(drive, drive->mult_count)) {
+				unsigned long flags;
+				spin_lock_irqsave(&io_request_lock, flags);
+				hwgroup->handler = NULL;
+				del_timer(&hwgroup->timer);
+				spin_unlock_irqrestore(&io_request_lock, flags);
+				return ide_stopped;
+			}
 		} else {
 			ide_set_handler (drive, &write_intr, WAIT_CMD);
 			idedisk_output_data(drive, rq->buffer, SECTOR_WORDS);
 		}
-		return;
+		return ide_started;
 	}
 	printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd);
 	ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
 }
 
 static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
@@ -446,7 +471,6 @@
 	/* Determine capacity, and use LBA if the drive properly supports it */
 	if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
 		if (id->lba_capacity >= capacity) {
-			drive->cyl = id->lba_capacity / (drive->head * drive->sect);
 			capacity = id->lba_capacity;
 			drive->select.b.lba = 1;
 		}
@@ -454,7 +478,7 @@
 	return (capacity - drive->sect0);
 }
 
-static void idedisk_special (ide_drive_t *drive)
+static ide_startstop_t idedisk_special (ide_drive_t *drive)
 {
 	special_t *s = &drive->special;
 
@@ -480,7 +504,9 @@
 		int special = s->all;
 		s->all = 0;
 		printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
+		return ide_stopped;
 	}
+	return IS_PDC4030_DRIVE ? ide_stopped : ide_started;
 }
 
 static void idedisk_pre_reset (ide_drive_t *drive)
@@ -602,7 +628,7 @@
 		return -EBUSY;
 	drive->nowerr = arg;
 	drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
-	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 	return 0;
 }
 
@@ -721,9 +747,9 @@
 		if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) {
 			id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors)));
 		}
-		drive->cyl  = id->cur_cyls    = id->cyls;
-		drive->head = id->cur_heads   = id->heads;
-		drive->sect = id->cur_sectors = id->sectors;
+		drive->cyl  = id->cyls;
+		drive->head = id->heads;
+		drive->sect = id->sectors;
 	}
 
 	/* calculate drive capacity, and select LBA if possible */
@@ -733,21 +759,19 @@
 	 * if possible, give fdisk access to more of the drive,
 	 * by correcting bios_cyls:
 	 */
-	if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
-	    (!drive->forced_geom) && drive->bios_sect && drive->bios_head) {
-		drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
-#ifdef DEBUG
-		printk("Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n",
-			drive->id->cur_cyls,
-			drive->id->cur_heads,
-			drive->id->cur_sectors,
-			drive->bios_cyl,
-			drive->bios_head,
-			drive->bios_sect);
-#endif
-		drive->id->cur_cyls    = drive->bios_cyl;
-		drive->id->cur_heads   = drive->bios_head;
-		drive->id->cur_sectors = drive->bios_sect;
+	if (!drive->forced_geom &&
+	    capacity > drive->bios_cyl * drive->bios_sect * drive->bios_head) {
+		unsigned long cylsize;
+		cylsize = drive->bios_sect * drive->bios_head;
+		if (cylsize == 0 || capacity/cylsize > 65535) {
+			drive->bios_sect = 63;
+			drive->bios_head = 255;
+			cylsize = 63*255;
+		}
+		if (capacity/cylsize > 65535)
+			drive->bios_cyl = 65535;
+		else
+			drive->bios_cyl = capacity/cylsize;
 	}
 
 #if 0	/* done instead for entire identify block in arch/ide.h stuff */
@@ -762,7 +786,7 @@
 	if (drive->using_dma) {
 		if ((id->field_valid & 4) &&
 		    (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
-			printk(", UDMA");	/* UDMA BIOS-enabled! */
+			printk(", UDMA");       /* UDMA BIOS-enabled! */
 		} else if (id->field_valid & 4) {
 			printk(", (U)DMA");	/* Can be BIOS-enabled! */
 		} else {
@@ -770,19 +794,6 @@
 		}
 	}
 	printk("\n");
-
-	if (drive->select.b.lba) {
-		if (*(int *)&id->cur_capacity0 < id->lba_capacity) {
-#ifdef DEBUG
-			printk("     CurSects=%d, LBASects=%d, ",
-				*(int *)&id->cur_capacity0, id->lba_capacity);
-#endif
-			*(int *)&id->cur_capacity0 = id->lba_capacity;
-#ifdef DEBUG
-			printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0);
-#endif
-		}
-	}
 
 	drive->mult_count = 0;
 	if (id->max_multsect) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-dma.c linux.14p6/drivers/block/ide-dma.c
--- linux.vanilla/drivers/block/ide-dma.c	Sat Aug 14 02:26:27 1999
+++ linux.14p6/drivers/block/ide-dma.c	Wed Oct 27 18:56:40 1999
@@ -136,7 +136,7 @@
 /*
  * dma_intr() is the handler for disk read/write DMA interrupts
  */
-void ide_dma_intr (ide_drive_t *drive)
+ide_startstop_t ide_dma_intr (ide_drive_t *drive)
 {
 	int i;
 	byte stat, dma_stat;
@@ -151,12 +151,11 @@
 				i -= rq->current_nr_sectors;
 				ide_end_request(1, HWGROUP(drive));
 			}
-			return;
+			return ide_stopped;
 		}
 		printk("%s: dma_intr: bad DMA status\n", drive->name);
 	}
-	ide__sti();	/* local CPU only */
-	ide_error(drive, "dma_intr", stat);
+	return ide_error(drive, "dma_intr", stat);
 }
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-floppy.c linux.14p6/drivers/block/ide-floppy.c
--- linux.vanilla/drivers/block/ide-floppy.c	Sat Aug 14 02:27:16 1999
+++ linux.14p6/drivers/block/ide-floppy.c	Wed Oct 27 18:56:40 1999
@@ -834,7 +834,7 @@
  *	idefloppy_pc_intr is the usual interrupt handler which will be called
  *	during a packet command.
  */
-static void idefloppy_pc_intr (ide_drive_t *drive)
+static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
 {
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	idefloppy_status_reg_t status;
@@ -879,24 +879,22 @@
 			rq->errors++;
 			if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
 				printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
-				ide_do_reset (drive);
-				return;
+				return ide_do_reset (drive);
 			}
 			idefloppy_retry_pc (drive);				/* Retry operation */
-			return;
+			return ide_stopped; /* queued, but not started */
 		}
 		pc->error = 0;
 		if (floppy->failed_pc == pc)
 			floppy->failed_pc=NULL;
 		pc->callback(drive);			/* Command finished - Call the callback function */
-		return;
+		return ide_stopped;
 	}
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
 		printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
 		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
@@ -905,14 +903,12 @@
 
 	if (ireason.b.cod) {
 		printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
 		printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
 		printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
 		temp = pc->actually_transferred + bcount.all;
@@ -921,7 +917,7 @@
 				printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
 				idefloppy_discard_data (drive,bcount.all);
 				ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);
-				return;
+				return ide_started;
 			}
 #if IDEFLOPPY_DEBUG_LOG
 			printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
@@ -943,31 +939,33 @@
 	pc->current_position+=bcount.all;
 
 	ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);		/* And set the interrupt handler again */
+	return ide_started;
 }
 
-static void idefloppy_transfer_pc (ide_drive_t *drive)
+static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive)
 {
+	ide_startstop_t startstop;
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	idefloppy_ireason_reg_t ireason;
 
-	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+	if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
 		printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
-		return;
+		return startstop;
 	}
 	ireason.all=IN_BYTE (IDE_IREASON_REG);
 	if (!ireason.b.cod || ireason.b.io) {
 		printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD);	/* Set the interrupt routine */
 	atapi_output_bytes (drive, floppy->pc->c, 12);		/* Send the actual packet */
+	return ide_started;
 }
 
 /*
  *	Issue a packet command
  */
-static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
+static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
 {
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	idefloppy_bcount_reg_t bcount;
@@ -995,7 +993,7 @@
 		}
 		floppy->failed_pc=NULL;
 		pc->callback(drive);
-		return;
+		return ide_stopped;
 	}
 #if IDEFLOPPY_DEBUG_LOG
 	printk (KERN_INFO "Retry number - %d\n",pc->retries);
@@ -1030,9 +1028,10 @@
 	if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
 		ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD);
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+		return ide_started;
 	} else {
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
-		idefloppy_transfer_pc (drive);
+		return idefloppy_transfer_pc (drive);
 	}
 }
 
@@ -1138,7 +1137,7 @@
 /*
  *	idefloppy_do_request is our request handling function.	
  */
-static void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	idefloppy_pc_t *pc;
@@ -1155,7 +1154,7 @@
 		else
 			printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
 		idefloppy_end_request (0, HWGROUP(drive));
-		return;
+		return ide_stopped;
 	}
 	switch (rq->cmd) {
 		case READ:
@@ -1163,7 +1162,7 @@
 			if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
 				printk ("%s: unsupported r/w request size\n", drive->name);
 				idefloppy_end_request (0, HWGROUP(drive));
-				return;
+				return ide_stopped;
 			}
 			pc = idefloppy_next_pc_storage (drive);
 			idefloppy_create_rw_cmd (floppy, pc, rq, block);
@@ -1174,10 +1173,10 @@
 		default:
 			printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
 			idefloppy_end_request (0,HWGROUP (drive));
-			return;
+			return ide_stopped;
 	}
 	pc->rq = rq;
-	idefloppy_issue_pc (drive, pc);
+	return idefloppy_issue_pc (drive, pc);
 }
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-probe.c linux.14p6/drivers/block/ide-probe.c
--- linux.vanilla/drivers/block/ide-probe.c	Mon Mar 22 20:44:18 1999
+++ linux.14p6/drivers/block/ide-probe.c	Wed Oct 27 18:56:40 1999
@@ -560,10 +560,6 @@
 		hwgroup->handler  = NULL;
 		hwgroup->drive    = NULL;
 		hwgroup->busy     = 0;
-		hwgroup->spinlock = (spinlock_t)SPIN_LOCK_UNLOCKED;
-#if (DEBUG_SPINLOCK > 0)
-		printk("hwgroup(%s) spinlock is %p\n", hwif->name,  &hwgroup->spinlock);	/* FIXME */
-#endif
 		init_timer(&hwgroup->timer);
 		hwgroup->timer.function = &ide_timer_expiry;
 		hwgroup->timer.data = (unsigned long) hwgroup;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-proc.c linux.14p6/drivers/block/ide-proc.c
--- linux.vanilla/drivers/block/ide-proc.c	Wed May  6 22:42:53 1998
+++ linux.14p6/drivers/block/ide-proc.c	Fri Oct 22 22:15:20 1999
@@ -516,8 +516,10 @@
 	char		*out = page;
 	int		len;
 
-	out += sprintf(out,"physical     %hi/%hi/%hi\n", drive->cyl, drive->head, drive->sect);
-	out += sprintf(out,"logical      %hi/%hi/%hi\n", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+	out += sprintf(out,"physical     %d/%d/%d\n",
+		       drive->cyl, drive->head, drive->sect);
+	out += sprintf(out,"logical      %d/%d/%d\n",
+		       drive->bios_cyl, drive->bios_head, drive->bios_sect);
 	len = out - page;
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-tape.c linux.14p6/drivers/block/ide-tape.c
--- linux.vanilla/drivers/block/ide-tape.c	Sat Aug 14 02:27:16 1999
+++ linux.14p6/drivers/block/ide-tape.c	Wed Oct 27 18:56:40 1999
@@ -522,7 +522,7 @@
 	int b_count;
 	byte *buffer;				/* Data buffer */
 	byte *current_position;			/* Pointer into the above buffer */
-	void (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
+	ide_startstop_t (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
 	byte pc_buffer[IDETAPE_PC_BUFFER_SIZE];	/* Temporary buffer */
 	unsigned int flags;			/* Status/Action bit flags */
 } idetape_pc_t;
@@ -1656,7 +1656,7 @@
 	}
 }
 
-static void idetape_request_sense_callback (ide_drive_t *drive)
+static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 
@@ -1670,6 +1670,7 @@
 		printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
 		idetape_end_request (0,HWGROUP (drive));
 	}
+	return ide_stopped;
 }
 
 /*
@@ -1701,7 +1702,7 @@
  *	last packet command. We queue a request sense packet command in
  *	the head of the request list.
  */
-static void idetape_retry_pc (ide_drive_t *drive)
+static ide_startstop_t idetape_retry_pc (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_pc_t *pc;
@@ -1714,6 +1715,7 @@
 	idetape_create_request_sense_cmd (pc);
 	set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
 	idetape_queue_pc_head (drive, pc, rq);
+	return ide_stopped;
 }
 
 /*
@@ -1724,7 +1726,7 @@
  *	algorithm described before idetape_issue_packet_command.
  *
  */
-static void idetape_pc_intr (ide_drive_t *drive)
+static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_status_reg_t status;
@@ -1780,11 +1782,9 @@
 #endif /* IDETAPE_DEBUG_LOG */
 			if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
 				printk (KERN_ERR "ide-tape: I/O error in request sense command\n");
-				ide_do_reset (drive);
-				return;
+				return ide_do_reset (drive);
 			}
-			idetape_retry_pc (drive);				/* Retry operation */
-			return;
+			return idetape_retry_pc (drive);		/* Retry operation */
 		}
 		pc->error = 0;
 		if (test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) {	/* Media access command */
@@ -1792,20 +1792,18 @@
 			tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST;
 			tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT;
 			idetape_postpone_request (drive);		/* Allow ide.c to handle other requests */
-			return;
+			return ide_stopped;
 		}
 		if (tape->failed_pc == pc)
 			tape->failed_pc=NULL;
-		pc->callback(drive);			/* Command finished - Call the callback function */
-		return;
+		return pc->callback(drive);			/* Command finished - Call the callback function */
 	}
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
 		printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
 		printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
 		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
@@ -1814,14 +1812,12 @@
 
 	if (ireason.b.cod) {
 		printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
 		printk (KERN_ERR "ide-tape: We wanted to %s, ", ireason.b.io ? "Write":"Read");
 		printk (KERN_ERR "but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
 		temp = pc->actually_transferred + bcount.all;
@@ -1830,7 +1826,7 @@
 				printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n");
 				idetape_discard_data (drive,bcount.all);
 				ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD);
-				return;
+				return ide_started;
 			}
 #if IDETAPE_DEBUG_LOG
 			printk (KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n");
@@ -1852,6 +1848,7 @@
 	pc->current_position+=bcount.all;
 
 	ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD);		/* And set the interrupt handler again */
+	return ide_started;
 }
 
 /*
@@ -1897,16 +1894,17 @@
  *
  */
 
-static void idetape_transfer_pc(ide_drive_t *drive)
+static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_pc_t *pc = tape->pc;
 	idetape_ireason_reg_t ireason;
 	int retries = 100;
+	ide_startstop_t startstop;
 
-	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+	if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
 		printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
-		return;
+		return startstop;
 	}
 	ireason.all=IN_BYTE (IDE_IREASON_REG);
 	while (retries-- && (!ireason.b.cod || ireason.b.io)) {
@@ -1921,14 +1919,14 @@
 	}
 	if (!ireason.b.cod || ireason.b.io) {
 		printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD);	/* Set the interrupt routine */
 	atapi_output_bytes (drive,pc->c,12);			/* Send the actual packet */
+	return ide_started;
 }
 
-static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
+static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_bcount_reg_t bcount;
@@ -1957,8 +1955,7 @@
 			pc->error = IDETAPE_ERROR_GENERAL;		/* Giving up */
 		}
 		tape->failed_pc=NULL;
-		pc->callback(drive);
-		return;
+		return pc->callback(drive);
 	}
 #if IDETAPE_DEBUG_LOG
 	printk (KERN_INFO "Retry number - %d\n",pc->retries);
@@ -1992,13 +1989,14 @@
 	if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
 		ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD);
 		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
+		return ide_started;
 	} else {
 		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
-		idetape_transfer_pc(drive);
+		return idetape_transfer_pc(drive);
 	}
 }
 
-static void idetape_media_access_finished (ide_drive_t *drive)
+static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_pc_t *pc = tape->pc;
@@ -2008,8 +2006,7 @@
 	if (status.b.dsc) {
 		if (status.b.check) {					/* Error detected */
 			printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name);
-			idetape_retry_pc (drive);			/* Retry operation */
-			return;
+			return idetape_retry_pc (drive);		/* Retry operation */
 		}
 		pc->error = 0;
 		if (tape->failed_pc == pc)
@@ -2018,13 +2015,13 @@
 		pc->error = IDETAPE_ERROR_GENERAL;
 		tape->failed_pc = NULL;
 	}
-	pc->callback (drive);
+	return pc->callback (drive);
 }
 
 /*
  *	General packet command callback function.
  */
-static void idetape_pc_callback (ide_drive_t *drive)
+static ide_startstop_t idetape_pc_callback (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	
@@ -2033,9 +2030,10 @@
 #endif /* IDETAPE_DEBUG_LOG */
 
 	idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
+	return ide_stopped;
 }
 
-static void idetape_rw_callback (ide_drive_t *drive)
+static ide_startstop_t idetape_rw_callback (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	struct request *rq = HWGROUP(drive)->rq;
@@ -2052,6 +2050,7 @@
 		idetape_end_request (1, HWGROUP (drive));
 	else
 		idetape_end_request (tape->pc->error, HWGROUP (drive));
+	return ide_stopped;
 }
 
 static void idetape_create_locate_cmd (idetape_pc_t *pc, unsigned int block, byte partition)
@@ -2170,7 +2169,7 @@
 		set_bit (PC_DMA_RECOMMENDED, &pc->flags);
 }
 
-static void idetape_read_position_callback (ide_drive_t *drive)
+static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_read_position_result_t *result;
@@ -2200,6 +2199,7 @@
 		}
 	} else
 		idetape_end_request (0,HWGROUP (drive));
+	return ide_stopped;
 }
 
 static void idetape_create_read_position_cmd (idetape_pc_t *pc)
@@ -2213,7 +2213,7 @@
 /*
  *	idetape_do_request is our request handling function.	
  */
-static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_pc_t *pc;
@@ -2229,24 +2229,23 @@
 		/*
 		 *	We do not support buffer cache originated requests.
 		 */
-		printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue\n", drive->name);
+		printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%d)\n", drive->name, rq->cmd);
 		ide_end_request (0,HWGROUP (drive));			/* Let the common code handle it */
-		return;
+		return ide_stopped;
 	}
 
 	/*
 	 *	Retry a failed packet command
 	 */
 	if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
-		idetape_issue_packet_command (drive, tape->failed_pc);
-		return;
+		return idetape_issue_packet_command (drive, tape->failed_pc);
 	}
 #if IDETAPE_DEBUG_BUGS
 	if (postponed_rq != NULL)
 		if (rq != postponed_rq) {
 			printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
 			idetape_end_request (0,HWGROUP (drive));
-			return;
+			return ide_stopped;
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
 
@@ -2266,15 +2265,16 @@
 			tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT;
 		} else if ((signed long) (jiffies - tape->dsc_timeout) > 0) {
 			printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name);
-			if (rq->cmd == IDETAPE_PC_RQ2)
+			if (rq->cmd == IDETAPE_PC_RQ2) {
 				idetape_media_access_finished (drive);
-			else
-				ide_do_reset (drive);
-			return;
+				return ide_stopped;
+			} else {
+				return ide_do_reset (drive);
+			}
 		} else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD)
 			tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW;
 		idetape_postpone_request (drive);
-		return;
+		return ide_stopped;
 	}
 	switch (rq->cmd) {
 		case IDETAPE_READ_RQ:
@@ -2289,20 +2289,20 @@
 			rq->cmd = IDETAPE_WRITE_RQ;
 			rq->errors = IDETAPE_ERROR_EOD;
 			idetape_end_request (1, HWGROUP(drive));
-			return;
+			return ide_stopped;
 		case IDETAPE_PC_RQ1:
 			pc=(idetape_pc_t *) rq->buffer;
 			rq->cmd = IDETAPE_PC_RQ2;
 			break;
 		case IDETAPE_PC_RQ2:
 			idetape_media_access_finished (drive);
-			return;
+			return ide_stopped;
 		default:
 			printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n");
 			idetape_end_request (0,HWGROUP (drive));
-			return;
+			return ide_stopped;
 	}
-	idetape_issue_packet_command (drive, pc);
+	return idetape_issue_packet_command (drive, pc);
 }
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide.c linux.14p6/drivers/block/ide.c
--- linux.vanilla/drivers/block/ide.c	Wed Oct 20 01:12:34 1999
+++ linux.14p6/drivers/block/ide.c	Tue Nov 16 15:34:42 1999
@@ -433,7 +433,7 @@
 #if 0
 	udelay(1);	/* need to guarantee 400ns since last command was issued */
 #endif
-	if (GET_STAT() & BUSY_STAT)
+	if (GET_STAT() & BUSY_STAT)	/* Note: this may clear a pending IRQ!! */
 		return 0;	/* drive busy:  definitely not interrupting */
 	return 1;		/* drive ready: *might* be interrupting */
 }
@@ -471,17 +471,15 @@
 	unsigned long flags;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-#ifdef DEBUG
+	spin_lock_irqsave(&io_request_lock, flags);
 	if (hwgroup->handler != NULL) {
 		printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
 			drive->name, hwgroup->handler, handler);
 	}
-#endif
 	hwgroup->handler       = handler;
 	hwgroup->timer.expires = jiffies + timeout;
-	add_timer(&(hwgroup->timer));
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	add_timer(&hwgroup->timer);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 /*
@@ -516,7 +514,7 @@
 	}
 }
 
-static void do_reset1 (ide_drive_t *, int);		/* needed below */
+static ide_startstop_t do_reset1 (ide_drive_t *, int);		/* needed below */
 
 /*
  * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
@@ -524,7 +522,7 @@
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
  */
-static void atapi_reset_pollfunc (ide_drive_t *drive)
+static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive)
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	byte stat;
@@ -537,14 +535,14 @@
 	} else {
 		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
 			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
-			return;	/* continue polling */
+			return ide_started;	/* continue polling */
 		}
 		hwgroup->poll_timeout = 0;	/* end of polling */
 		printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
-		do_reset1 (drive, 1);	/* do it the old fashioned way */
-		return;
+		return do_reset1 (drive, 1);	/* do it the old fashioned way */
 	}
 	hwgroup->poll_timeout = 0;	/* done polling */
+	return ide_stopped;
 }
 
 /*
@@ -553,7 +551,7 @@
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
  */
-static void reset_pollfunc (ide_drive_t *drive)
+static ide_startstop_t reset_pollfunc (ide_drive_t *drive)
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	ide_hwif_t *hwif = HWIF(drive);
@@ -562,7 +560,7 @@
 	if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
 		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
 			ide_set_handler (drive, &reset_pollfunc, HZ/20);
-			return;	/* continue polling */
+			return ide_started;	/* continue polling */
 		}
 		printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
 	} else  {
@@ -594,6 +592,7 @@
 		}
 	}
 	hwgroup->poll_timeout = 0;	/* done polling */
+	return ide_stopped;
 }
 
 static void pre_reset (ide_drive_t *drive)
@@ -623,7 +622,7 @@
  * (up to 30 seconds worstcase).  So, instead of busy-waiting here for it,
  * we set a timer to poll at 50ms intervals.
  */
-static void do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
+static ide_startstop_t do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
 {
 	unsigned int unit;
 	unsigned long flags;
@@ -642,7 +641,7 @@
 		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
 		ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
 		__restore_flags (flags);	/* local CPU only */
-		return;
+		return ide_started;
 	}
 
 	/*
@@ -670,14 +669,15 @@
 #endif	/* OK_TO_RESET_CONTROLLER */
 
 	__restore_flags (flags);	/* local CPU only */
+	return ide_started;
 }
 
 /*
  * ide_do_reset() is the entry point to the drive/interface reset code.
  */
-void ide_do_reset (ide_drive_t *drive)
+ide_startstop_t ide_do_reset (ide_drive_t *drive)
 {
-	do_reset1 (drive, 0);
+	return do_reset1 (drive, 0);
 }
 
 /*
@@ -703,11 +703,8 @@
 	HWGROUP(drive)->rq = NULL;
 	rq->rq_status = RQ_INACTIVE;
 	spin_unlock_irqrestore(&io_request_lock, flags);
-	save_flags(flags);	/* all CPUs; overkill? */
-	cli();			/* all CPUs; overkill? */
 	if (rq->sem != NULL)
 		up(rq->sem);	/* inform originator that rq has been serviced */
-	restore_flags(flags);	/* all CPUs; overkill? */
 }
 
 /*
@@ -800,19 +797,19 @@
 /*
  * ide_error() takes action based on the error returned by the drive.
  */
-void ide_error (ide_drive_t *drive, const char *msg, byte stat)
+ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat)
 {
 	struct request *rq;
 	byte err;
 
 	err = ide_dump_status(drive, msg, stat);
 	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
-		return;
+		return ide_stopped;
 	/* retry only "normal" I/O: */
 	if (rq->cmd == IDE_DRIVE_CMD) {
 		rq->errors = 1;
 		ide_end_drive_cmd(drive, stat, err);
-		return;
+		return ide_stopped;
 	}
 	if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */
 		rq->errors |= ERROR_RESET;
@@ -821,7 +818,7 @@
 			/* err has different meaning on cdrom and tape */
 			if (err == ABRT_ERR) {
 				if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)
-					return;	/* some newer drives don't support WIN_SPECIFY */
+					return ide_stopped; /* some newer drives don't support WIN_SPECIFY */
 			} else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR))
 				; /* UDMA crc error -- just retry the operation */
 			else if (err & (BBD_ERR | ECC_ERR))	/* retries won't help these */
@@ -843,12 +840,13 @@
 	} else {
 		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
 			++rq->errors;
-			ide_do_reset(drive);
-			return;
-		} else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+			return ide_do_reset(drive);
+		}
+		if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
 			drive->special.b.recalibrate = 1;
 		++rq->errors;
 	}
+	return ide_stopped;
 }
 
 /*
@@ -866,7 +864,7 @@
 /*
  * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
  */
-static void drive_cmd_intr (ide_drive_t *drive)
+static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	byte *args = (byte *) rq->buffer;
@@ -883,17 +881,17 @@
 			udelay(100);
 	}
 
-	if (OK_STAT(stat, READY_STAT, BAD_STAT))
-		ide_end_drive_cmd (drive, stat, GET_ERR());
-	else
-		ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
+		return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+	ide_end_drive_cmd (drive, stat, GET_ERR());
+	return ide_stopped;
 }
 
 /*
  * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
  * commands to a drive.  It used to do much more, but has been scaled back.
  */
-static inline void do_special (ide_drive_t *drive)
+static ide_startstop_t do_special (ide_drive_t *drive)
 {
 	special_t *s = &drive->special;
 
@@ -906,11 +904,12 @@
 		if (tuneproc != NULL)
 			tuneproc(drive, drive->tune_req);
 	} else if (drive->driver != NULL) {
-		DRIVER(drive)->special(drive);
+		return DRIVER(drive)->special(drive);
 	} else if (s->all) {
 		printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
 		s->all = 0;
 	}
+	return ide_stopped;
 }
 
 /*
@@ -924,12 +923,12 @@
  * setting a timer to wake up at half second intervals thereafter,
  * until timeout is achieved, before timing out.
  */
-int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
+int ide_wait_stat (ide_startstop_t *startstop, 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" */
 	if ((stat = GET_STAT()) & BUSY_STAT) {
 		__save_flags(flags);	/* local CPU only */
@@ -938,7 +937,7 @@
 		while ((stat = GET_STAT()) & BUSY_STAT) {
 			if (0 < (signed long)(jiffies - timeout)) {
 				__restore_flags(flags);	/* local CPU only */
-				ide_error(drive, "status timeout", stat);
+				*startstop = ide_error(drive, "status timeout", stat);
 				return 1;
 			}
 		}
@@ -956,7 +955,7 @@
 		if (OK_STAT((stat = GET_STAT()), good, bad))
 			return 0;
 	}
-	ide_error(drive, "status error", stat);
+	*startstop = ide_error(drive, "status error", stat);
 	return 1;
 }
 
@@ -964,7 +963,7 @@
  * execute_drive_cmd() issues a special drive command,
  * usually initiated by ioctl() from the external hdparm program.
  */
-static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
+static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq)
 {
 	byte *args = rq->buffer;
 	if (args) {
@@ -978,7 +977,7 @@
 		}
 		OUT_BYTE(args[2],IDE_FEATURE_REG);
 		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
-		return;
+		return ide_started;
 	} else {
 		/*
 		 * NULL is actually a valid way of waiting for
@@ -988,21 +987,21 @@
 		printk("%s: DRIVE_CMD (null)\n", drive->name);
 #endif
 		ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
-		return;
+		return ide_stopped;
 	}
 }
 
 /*
  * start_request() initiates handling of a new I/O request
  */
-static inline void start_request (ide_drive_t *drive)
+static ide_startstop_t start_request (ide_drive_t *drive)
 {
+	ide_startstop_t startstop;
 	unsigned long block, blockend;
 	struct request *rq = drive->queue;
 	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
 	ide_hwif_t *hwif = HWIF(drive);
 
-	ide__sti();	/* local CPU only */
 #ifdef DEBUG
 	printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
@@ -1032,29 +1031,27 @@
 	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
 #endif
 	SELECT_DRIVE(hwif, drive);
-	if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+	if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 		printk("%s: drive not ready for command\n", drive->name);
-		return;
+		return startstop;
 	}
 	if (!drive->special.all) {
 		if (rq->cmd == IDE_DRIVE_CMD) {
-			execute_drive_cmd(drive, rq);
-			return;
+			return execute_drive_cmd(drive, rq);
 		}
 		if (drive->driver != NULL) {
-			DRIVER(drive)->do_request(drive, rq, block);
-			return;
+			return (DRIVER(drive)->do_request(drive, rq, block));
 		}
 		printk("%s: media type %d not supported\n", drive->name, drive->media);
 		goto kill_rq;
 	}
-	do_special(drive);
-	return;
+	return do_special(drive);
 kill_rq:
 	if (drive->driver != NULL)
 		DRIVER(drive)->end_request(0, HWGROUP(drive));
 	else
 		ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
 }
 
 /*
@@ -1115,22 +1112,54 @@
 }
 
 /*
- * Caller must have already acquired spinlock using *spinflags 
+ * Issue a new request to a drive from hwgroup
+ * Caller must have already done spin_lock_irqsave(&io_request_lock, ..);
+ *
+ * A hwgroup is a serialized group of IDE interfaces.  Usually there is
+ * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
+ * may have both interfaces in a single hwgroup to "serialize" access.
+ * Or possibly multiple ISA interfaces can share a common IRQ by being grouped
+ * together into one hwgroup for serialized access.
+ *
+ * Note also that several hwgroups can end up sharing a single IRQ,
+ * possibly along with many other devices.  This is especially common in
+ * PCI-based systems with off-board IDE controller cards.
+ *
+ * The IDE driver uses the single global io_request_lock spinlock to protect
+ * access to the request queues, and to protect the hwgroup->busy flag.
+ *
+ * The first thread into the driver for a particular hwgroup sets the
+ * hwgroup->busy flag to indicate that this hwgroup is now active,
+ * and then initiates processing of the top request from the request queue.
+ *
+ * Other threads attempting entry notice the busy setting, and will simply
+ * queue their new requests and exit immediately.  Note that hwgroup->busy
+ * remains set even when the driver is merely awaiting the next interrupt.
+ * Thus, the meaning is "this hwgroup is busy processing a request".
+ *
+ * When processing of a request completes, the completing thread or IRQ-handler
+ * will start the next request from the queue.  If no more work remains,
+ * the driver will clear the hwgroup->busy flag and exit.
+ *
+ * The io_request_lock (spinlock) is used to protect all access to the
+ * hwgroup->busy flag, but is otherwise not needed for most processing in
+ * the driver.  This makes the driver much more friendlier to shared IRQs
+ * than previous designs, while remaining 100% (?) SMP safe and capable.
  */
-static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags, int masked_irq)
+static void ide_do_request (ide_hwgroup_t *hwgroup)
 {
 	struct blk_dev_struct *bdev;
 	ide_drive_t	*drive;
 	ide_hwif_t	*hwif;
-	unsigned long	io_flags;
+	ide_startstop_t	startstop;
+
+	ide_get_lock(&ide_lock, ide_intr, hwgroup);	/* for atari only: POSSIBLY BROKEN HERE(?) */
 
-	hwgroup->busy = 1;
-	while (hwgroup->handler == NULL) {
-		spin_lock_irqsave(&io_request_lock, io_flags);
+	while (!hwgroup->busy) {
+		hwgroup->busy = 1;
 		drive = choose_drive(hwgroup);
 		if (drive == NULL) {
 			unsigned long sleep = 0;
-
 			hwgroup->rq = NULL;
 			drive = hwgroup->drive;
 			do {
@@ -1140,24 +1169,31 @@
 				if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
 					sleep = drive->sleep;
 			} while ((drive = drive->next) != hwgroup->drive);
-			spin_unlock_irqrestore(&io_request_lock, io_flags);
 			if (sleep) {
+				/*
+				 * Take a short snooze, and then wake up this hwgroup again.
+				 * This gives other hwgroups on the same a chance to
+				 * play fairly with us, just in case there are big differences
+				 * in relative throughputs.. don't want to hog the cpu too much.
+				 */
 				if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) 
 					sleep = jiffies + WAIT_MIN_SLEEP;
-#if 1
+				hwgroup->sleeping = 1;	/* so that ide_timer_expiry knows what to do */
+#if 1	/* paranoia */
 				if (hwgroup->timer.next || hwgroup->timer.prev)
-					printk("ide_set_handler: timer already active\n");
+					printk("ide_set_handler: timer was already active\n");
 #endif
 				mod_timer(&hwgroup->timer, sleep);
+				/* we purposely leave hwgroup->busy==1 while sleeping */
 			} else {
 				/* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
 				ide_release_lock(&ide_lock);	/* for atari only */
+				hwgroup->busy = 0;
 			}
-			hwgroup->busy = 0;
-			return;
+			return;		/* no more work for this hwgroup (for now) */
 		}
 		hwif = HWIF(drive);
-		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) /* set nIEN for previous hwif */
+		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif)	/* set nIEN for previous hwif */
 			OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
 		hwgroup->hwif = hwif;
 		hwgroup->drive = drive;
@@ -1168,15 +1204,13 @@
 		if (bdev->current_request == &bdev->plug)	/* FIXME: paranoia */
 			printk("%s: Huh? nuking plugged queue\n", drive->name);
 		bdev->current_request = hwgroup->rq = drive->queue;
-		spin_unlock_irqrestore(&io_request_lock, io_flags);
-
-		if (hwif->irq != masked_irq)
-			disable_irq_nosync(hwif->irq);
-		spin_unlock_irqrestore(&hwgroup->spinlock, *hwgroup_flags);
-		start_request(drive);
-		spin_lock_irqsave(&hwgroup->spinlock, *hwgroup_flags);
-		if (hwif->irq != masked_irq)
-			enable_irq(hwif->irq);
+		spin_unlock(&io_request_lock);
+		if (!hwif->serialized)	/* play it safe with buggy hardware */
+			ide__sti();
+		startstop = start_request(drive);
+		spin_lock_irq(&io_request_lock);
+		if (startstop == ide_stopped)
+			hwgroup->busy = 0;
 	}
 }
 
@@ -1190,134 +1224,112 @@
 	return &hwif->drives[DEVICE_NR(dev) & 1].queue;
 }
 
-/*
- * do_hwgroup_request() invokes ide_do_request() after claiming hwgroup->busy.
- */
-static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-	if (hwgroup->busy) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		return;
-	}
-	del_timer(&hwgroup->timer);
-	ide_get_lock(&ide_lock, ide_intr, hwgroup);	/* for atari only */
-	ide_do_request(hwgroup, &flags, 0);
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-}
-
-/*
- * ll_rw_blk.c invokes our do_idex_request() function
- * with the io_request_spinlock already grabbed.
- * Since we need to do our own spinlock's internally,
- * on paths that don't necessarily originate through the
- * do_idex_request() path, we have to undo the spinlock on entry,
- * and restore it again on exit.
- * Fortunately, this is mostly a nop for non-SMP kernels.
- */
-static inline void unlock_do_hwgroup_request (ide_hwgroup_t *hwgroup)
-{
-	spin_unlock(&io_request_lock);
-	do_hwgroup_request (hwgroup);
-	spin_lock_irq(&io_request_lock);
-}
-
 void do_ide0_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[0].hwgroup);
+	ide_do_request (ide_hwifs[0].hwgroup);
 }
 
 #if MAX_HWIFS > 1
 void do_ide1_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[1].hwgroup);
+	ide_do_request (ide_hwifs[1].hwgroup);
 }
 #endif /* MAX_HWIFS > 1 */
 
 #if MAX_HWIFS > 2
 void do_ide2_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[2].hwgroup);
+	ide_do_request (ide_hwifs[2].hwgroup);
 }
 #endif /* MAX_HWIFS > 2 */
 
 #if MAX_HWIFS > 3
 void do_ide3_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[3].hwgroup);
+	ide_do_request (ide_hwifs[3].hwgroup);
 }
 #endif /* MAX_HWIFS > 3 */
 
 #if MAX_HWIFS > 4
 void do_ide4_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[4].hwgroup);
+	ide_do_request (ide_hwifs[4].hwgroup);
 }
 #endif /* MAX_HWIFS > 4 */
 
 #if MAX_HWIFS > 5
 void do_ide5_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[5].hwgroup);
+	ide_do_request (ide_hwifs[5].hwgroup);
 }
 #endif /* MAX_HWIFS > 5 */
 
-static void start_next_request (ide_hwgroup_t *hwgroup, int masked_irq)
-{
-	unsigned long	flags;
-	ide_drive_t	*drive;
-
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-	if (hwgroup->handler != NULL) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		return;
-	}
-	drive = hwgroup->drive;
-	set_recovery_timer(HWIF(drive));
-	drive->service_time = jiffies - drive->service_start;
-	ide_do_request(hwgroup, &flags, masked_irq);
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-}
-
+/*
+ * ide_timer_expiry() is our timeout function for all drive operations.
+ * But note that it can also be invoked as a result of a "sleep" operation
+ * triggered by the mod_timer() call in ide_do_request.
+ */
 void ide_timer_expiry (unsigned long data)
 {
-	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
-	ide_drive_t   *drive;
-	ide_handler_t *handler;
-	unsigned long flags;
+	ide_hwgroup_t	*hwgroup = (ide_hwgroup_t *) data;
+	ide_handler_t	*handler;
+	unsigned long	flags;
 
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-	drive = hwgroup->drive;
+	spin_lock_irqsave(&io_request_lock, flags);
+	del_timer(&hwgroup->timer);
 	if ((handler = hwgroup->handler) == NULL) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		do_hwgroup_request(hwgroup);
-		return;
-	}
-	hwgroup->busy = 1;	/* should already be "1" */
-	hwgroup->handler = NULL;
-	del_timer(&hwgroup->timer);	/* Is this needed?? */
-	if (hwgroup->poll_timeout != 0) {	/* polling in progress? */
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		handler(drive);
-	} else if (drive_is_ready(drive)) {
-		printk("%s: lost interrupt\n", drive->name);
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		handler(drive);
+		/*
+		 * Either a marginal timeout occured (got the interrupt just as timer expired),
+		 * or we were "sleeping" to give other devices a chance.  Either way, we don't
+		 * really want to complain about anything.
+		 */
+		if (hwgroup->sleeping) {
+			hwgroup->sleeping = 0;
+			hwgroup->busy = 0;
+		}
 	} else {
-		if (drive->waiting_for_dma) {
-			(void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
-			printk("%s: timeout waiting for DMA\n", drive->name);
-	/*
-	 *  need something here for HX PIIX3 UDMA and HPT343.......AMH
-	 *  irq timeout: status=0x58 { DriveReady SeekComplete DataRequest }
-	 */
+		ide_drive_t *drive = hwgroup->drive;
+		hwgroup->handler = NULL;
+		if (!drive) {
+			printk("ide_timer_expiry: hwgroup->drive was NULL\n");
+		} else {
+			ide_hwif_t *hwif;
+			ide_startstop_t startstop;
+			if (!hwgroup->busy) {
+				hwgroup->busy = 1;	/* paranoia */
+				printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name);
+			}
+			/*
+			 * We need to simulate a real interrupt when invoking the handler()
+			 * function, which means we need to globally mask the specific IRQ:
+			 */
+			spin_unlock(&io_request_lock);
+			hwif  = HWIF(drive);
+			disable_irq(hwif->irq);
+			__cli();	/* local CPU only, as if we were handling an interrupt */
+			if (hwgroup->poll_timeout != 0 || drive_is_ready(drive)) {
+				if (hwgroup->poll_timeout == 0) {
+					printk("%s: lost interrupt\n", drive->name);
+					(void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]);
+				}
+				startstop = handler(drive);
+			} else {
+				if (drive->waiting_for_dma) {
+					(void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
+					printk("%s: timeout waiting for DMA\n", drive->name);
+				}
+				startstop = ide_error(drive, "irq timeout", GET_STAT());
+			}
+			set_recovery_timer(hwif);
+			drive->service_time = jiffies - drive->service_start;
+			enable_irq(hwif->irq);
+			spin_lock_irq(&io_request_lock);
+			if (startstop == ide_stopped)
+				hwgroup->busy = 0;
 		}
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		ide_error(drive, "irq timeout", GET_STAT());
 	}
-	start_next_request(hwgroup, 0);
+	ide_do_request(hwgroup);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 /*
@@ -1377,9 +1389,9 @@
 	ide_hwif_t *hwif;
 	ide_drive_t *drive;
 	ide_handler_t *handler;
+	ide_startstop_t startstop;
 
-	__cli();	/* local CPU only */
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
+	spin_lock_irqsave(&io_request_lock, flags);
 	hwif = hwgroup->hwif;
 	if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
 		/*
@@ -1404,41 +1416,67 @@
 			 */
 			(void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]);
 			unexpected_intr(irq, hwgroup);
+		} 
+#ifdef CONFIG_BLK_DEV_IDEPCI		
+		else 
+		{
+			/*
+			 * Whack the status register, just in case we have a leftover pending IRQ.
+			 */
+			(void)IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
 		}
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+#endif		
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		return;
 	}
 	drive = hwgroup->drive;
-	if (!drive || !drive_is_ready(drive)) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	if (!drive) {
+		/*
+		 * This should NEVER happen, and there isn't much we could do about it here.
+		 */
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		return;
 	}
+	if (!drive_is_ready(drive)) {
+		/*
+		 * This happens regularly when we share a PCI IRQ with another device.
+		 * Unfortunately, it can also happen with some buggy drives that trigger
+		 * the IRQ before their status register is up to date.  Hopefully we have
+		 * enough advance overhead that the latter isn't a problem.
+		 */
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		return;
+	}
+	if (!hwgroup->busy) {
+		hwgroup->busy = 1;	/* paranoia */
+		printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
+	}
 	hwgroup->handler = NULL;
+	del_timer(&hwgroup->timer);
 	(void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]);
-	del_timer(&(hwgroup->timer));
-	{
-		struct request *rq;
-		unsigned long block, sectors;
-
-		if ((rq = hwgroup->rq) != NULL) {
-			block = rq->sector;
-			block += drive->part[MINOR(rq->rq_dev)&PARTN_MASK].start_sect + drive->sect0;
-			sectors = drive->using_dma ? rq->nr_sectors : drive->mult_count ? drive->mult_count : 1;
-		}
-	}
-
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	spin_unlock(&io_request_lock);
 	if (drive->unmask)
 		ide__sti();	/* local CPU only */
-	handler(drive);		/* service this interrupt, may set handler for next interrupt */
+	startstop = handler(drive);	/* service this interrupt, may set handler for next interrupt */
+	spin_lock_irq(&io_request_lock);
 	/*
 	 * Note that handler() may have set things up for another
 	 * interrupt to occur soon, but it cannot happen until
 	 * we exit from this routine, because it will be the
-	 * same irq as is currently being serviced here,
-	 * and Linux won't allow another (on any CPU) until we return.
+	 * same irq as is currently being serviced here, and Linux
+	 * won't allow another of the same (on any CPU) until we return.
 	 */
-	start_next_request(hwgroup, hwif->irq);
+	set_recovery_timer(HWIF(drive));
+	drive->service_time = jiffies - drive->service_start;
+	if (startstop == ide_stopped) {
+		if (hwgroup->handler == NULL) {	/* paranoia */
+			hwgroup->busy = 0;
+			ide_do_request(hwgroup);
+		} else {
+			printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
+		}
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 /*
@@ -1521,7 +1559,6 @@
 	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
 	if (action == ide_wait)
 		rq->sem = &sem;
-
 	spin_lock_irqsave(&io_request_lock, flags);
 	cur_rq = drive->queue;
 	if (cur_rq == NULL || action == ide_preempt) {
@@ -1537,16 +1574,13 @@
 		rq->next = cur_rq->next;
 		cur_rq->next = rq;
 	}
+	ide_do_request(hwgroup);
 	spin_unlock_irqrestore(&io_request_lock, flags);
-	do_hwgroup_request(hwgroup);
-	save_flags(flags);	/* all CPUs; overkill? */
-	cli();			/* all CPUs; overkill? */
-	if (action == ide_wait)
-	{
-		down(&sem);	/* wait for it to be serviced */
+	if (action == ide_wait) {
+		down(&sem);			/* wait for it to be serviced */
+		return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 	}
-	restore_flags(flags);	/* all CPUs; overkill? */
-	return rq->errors ? -EIO : 0;	/* return -EIO if errors */
+	return 0;
 }
 
 /*
@@ -1569,14 +1603,14 @@
 	major = MAJOR(i_rdev);
 	minor = drive->select.b.unit << PARTN_BITS;
 	hwgroup = HWGROUP(drive);
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
+	spin_lock_irqsave(&io_request_lock, flags);
 	if (drive->busy || (drive->usage > 1)) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		return -EBUSY;
 	};
 	drive->busy = 1;
 	MOD_INC_USE_COUNT;
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 
 	for (p = 0; p < (1<<PARTN_BITS); ++p) {
 		if (drive->part[p].nr_sects > 0) {
@@ -1962,7 +1996,7 @@
 	unsigned long	flags;
 
 	if ((setting->rw & SETTING_READ)) {
-		spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
+		spin_lock_irqsave(&io_request_lock, flags);
 		switch(setting->data_type) {
 			case TYPE_BYTE:
 				val = *((u8 *) setting->data);
@@ -1975,7 +2009,7 @@
 				val = *((u32 *) setting->data);
 				break;
 		}
-		spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 	}
 	return val;
 }
@@ -1985,19 +2019,28 @@
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	unsigned long timeout = jiffies + (3 * HZ);
 
-	spin_lock_irqsave(&hwgroup->spinlock, *flags);
+	spin_lock_irqsave(&io_request_lock, *flags);
 	while (hwgroup->busy) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, *flags);
-		__sti();	/* local CPU only; needed for jiffies */
+		unsigned long lflags;
+		spin_unlock_irqrestore(&io_request_lock, *flags);
+		__save_flags(lflags);	/* local CPU only */
+		__sti();		/* local CPU only; needed for jiffies */
 		if (0 < (signed long)(jiffies - timeout)) {
+			__restore_flags(lflags);	/* local CPU only */
 			printk("%s: channel busy\n", drive->name);
 			return -EBUSY;
 		}
-		spin_lock_irqsave(&hwgroup->spinlock, *flags);
+		__restore_flags(lflags);	/* local CPU only */
+		spin_lock_irqsave(&io_request_lock, *flags);
 	}
 	return 0;
 }
 
+/*
+ * FIXME:  This should be changed to enqueue a special request
+ * to the driver to change settings, and then wait on a sema for completion.
+ * The current scheme of polling is kludgey, though safe enough.
+ */
 int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
 {
 	unsigned long flags;
@@ -2030,7 +2073,7 @@
 				*p = val;
 			break;
 	}
-	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 	return 0;
 }
 
@@ -2844,9 +2887,10 @@
 	return ide_unregister_subdriver(drive);
 }
 
-static void default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
 {
 	ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
 }
  
 static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
@@ -2884,12 +2928,13 @@
 	return 0x7fffffff;	/* cdrom or tape */
 }
 
-static void default_special (ide_drive_t *drive)
+static ide_startstop_t default_special (ide_drive_t *drive)
 {
 	special_t *s = &drive->special;
 
 	s->all = 0;
 	drive->mult_req = 0;
+	return ide_stopped;
 }
 
 static void setup_driver_defaults (ide_drive_t *drive)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide.h linux.14p6/drivers/block/ide.h
--- linux.vanilla/drivers/block/ide.h	Sun Mar 28 19:03:35 1999
+++ linux.14p6/drivers/block/ide.h	Thu Nov 11 21:59:17 1999
@@ -353,14 +353,22 @@
 	} ide_hwif_t;
 
 /*
+ * Status returned from various ide_ functions
+ */
+typedef enum {
+	ide_stopped,	/* no drive operation was started */
+	ide_started	/* a drive operation was started, and a handler was set up */
+} ide_startstop_t;
+
+/*
  *  internal ide interrupt handler type
  */
-typedef void (ide_handler_t)(ide_drive_t *);
+typedef ide_startstop_t (ide_handler_t)(ide_drive_t *);
 
 typedef struct hwgroup_s {
-	spinlock_t		spinlock; /* protects "busy" and "handler" */
 	ide_handler_t		*handler;/* irq handler, if active */
-	int			busy;	/* BOOL: protects all fields below */
+	volatile int		busy;	/* BOOL: protects all fields below */
+	int			sleeping; /* BOOL: wake us up on timer expiry */
 	ide_drive_t		*drive;	/* current drive */
 	ide_hwif_t		*hwif;	/* ptr to current hwif in linked-list */
 	struct request		*rq;	/* current request */
@@ -443,13 +451,14 @@
 #define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0;
 #endif
 
+
 /*
  * Subdrivers support.
  */
 #define IDE_SUBDRIVER_VERSION	1
 
 typedef int	(ide_cleanup_proc)(ide_drive_t *);
-typedef void 	(ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long);
+typedef ide_startstop_t (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long);
 typedef void	(ide_end_request_proc)(byte, ide_hwgroup_t *);
 typedef int 	(ide_ioctl_proc)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
 typedef int	(ide_open_proc)(struct inode *, struct file *, ide_drive_t *);
@@ -457,7 +466,7 @@
 typedef int	(ide_check_media_change_proc)(ide_drive_t *);
 typedef void	(ide_pre_reset_proc)(ide_drive_t *);
 typedef unsigned long (ide_capacity_proc)(ide_drive_t *);
-typedef void	(ide_special_proc)(ide_drive_t *);
+typedef ide_startstop_t (ide_special_proc)(ide_drive_t *);
 typedef void	(ide_setting_proc)(ide_drive_t *);
 
 typedef struct ide_driver_s {
@@ -545,9 +554,9 @@
 
 /*
  * ide_error() takes action based on the error returned by the controller.
- * The calling function must return afterwards, to restart the request.
+ * The caller should return immediately after invoking this.
  */
-void ide_error (ide_drive_t *drive, const char *msg, byte stat);
+ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat);
 
 /*
  * Issue a simple drive command
@@ -567,10 +576,11 @@
  * This routine busy-waits for the drive status to be not "busy".
  * It then checks the status for all of the "good" bits and none
  * of the "bad" bits, and if all is okay it returns 0.  All other
- * cases return 1 after invoking ide_error() -- caller should return.
- *
+ * cases return 1 after doing "*startstop = ide_error()", and the
+ * caller should return the updated value of "startstop" in this case.
+ * "startstop" is unchanged when the function returns 0;
  */
-int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
+int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
 
 /*
  * This routine is called from the partition-table code in genhd.c
@@ -594,7 +604,7 @@
  * Start a reset operation for an IDE interface.
  * The caller should return immediately after invoking this.
  */
-void ide_do_reset (ide_drive_t *);
+ide_startstop_t ide_do_reset (ide_drive_t *);
 
 /*
  * This function is intended to be used prior to invoking ide_do_drive_cmd().
@@ -661,7 +671,7 @@
  * ide_multwrite() transfers a block of up to mcount sectors of data
  * to a drive as part of a disk multwrite operation.
  */
-void ide_multwrite (ide_drive_t *drive, unsigned int mcount);
+int ide_multwrite (ide_drive_t *drive, unsigned int mcount);
 
 /*
  * ide_stall_queue() can be used by a drive to give excess bandwidth back
@@ -751,7 +761,7 @@
 #define BAD_DMA_DRIVE		0
 #define GOOD_DMA_DRIVE		1
 int ide_build_dmatable (ide_drive_t *drive);
-void ide_dma_intr  (ide_drive_t *drive);
+ide_startstop_t ide_dma_intr  (ide_drive_t *drive);
 int check_drive_lists (ide_drive_t *drive, int good_bad);
 int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
 int ide_release_dma (ide_hwif_t *hwif);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ns87415.c linux.14p6/drivers/block/ns87415.c
--- linux.vanilla/drivers/block/ns87415.c	Sat Aug 14 02:26:47 1999
+++ linux.14p6/drivers/block/ns87415.c	Tue Nov 16 15:13:27 1999
@@ -97,6 +97,10 @@
 				return 0;
 			ns87415_prepare_drive(drive, 0);	/* DMA failed: select PIO xfer */
 			return 1;
+		case ide_dma_check:
+			if (drive->media != ide_disk)
+				return ide_dmaproc(ide_dma_off_quietly, drive);
+			/* Fallthrough... */
 		default:
 			return ide_dmaproc(func, drive);	/* use standard DMA stuff */
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/pdc4030.c linux.14p6/drivers/block/pdc4030.c
--- linux.vanilla/drivers/block/pdc4030.c	Tue Dec 29 19:24:57 1998
+++ linux.14p6/drivers/block/pdc4030.c	Wed Oct 27 18:56:41 1999
@@ -122,6 +122,7 @@
 */
 int init_pdc4030 (void)
 {
+	ide_startstop_t startstop;
 	ide_hwif_t *hwif = hwif_required;
         ide_drive_t *drive;
 	ide_hwif_t *second_hwif;
@@ -143,7 +144,7 @@
 	if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
 	    return 0;
 	}
-	if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+	if(ide_wait_stat(&startstop,drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
 	    printk("%s: Failed Promise read config!\n",hwif->name);
 	    return 0;
 	}
@@ -195,7 +196,7 @@
 /*
  * promise_read_intr() is the handler for disk read/multread interrupts
  */
-static void promise_read_intr (ide_drive_t *drive)
+static ide_startstop_t promise_read_intr (ide_drive_t *drive)
 {
 	byte stat;
 	int i;
@@ -203,8 +204,7 @@
 	struct request *rq;
 
 	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		ide_error(drive, "promise_read_intr", stat);
-		return;
+		return ide_error(drive, "promise_read_intr", stat);
 	}
 
 read_again:
@@ -240,17 +240,18 @@
 		    goto read_again;
 		if(stat & BUSY_STAT) {
 		    ide_set_handler (drive, &promise_read_intr, WAIT_CMD);
-		    return;
+		    return ide_started;;
 		}
 		printk("Ah! promise read intr: sectors left !DRQ !BUSY\n");
-		ide_error(drive, "promise read intr", stat);
+		return ide_error(drive, "promise read intr", stat);
 	}
+	return ide_stopped;
 }
 
 /*
  * promise_write_pollfunc() is the handler for disk write completion polling.
  */
-static void promise_write_pollfunc (ide_drive_t *drive)
+static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive)
 {
 	int i;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
@@ -259,20 +260,20 @@
         if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
             if (time_before(jiffies, hwgroup->poll_timeout)) {
                 ide_set_handler (drive, &promise_write_pollfunc, 1);
-                return; /* continue polling... */
+                return ide_started; /* continue polling... */
             }
             printk("%s: write timed-out!\n",drive->name);
-            ide_error (drive, "write timeout", GET_STAT());
-            return;
+            return ide_error (drive, "write timeout", GET_STAT());
         }
         
-	ide_multwrite(drive, 4);
+	if (ide_multwrite(drive, 4))
+		return ide_stopped;
         rq = hwgroup->rq;
         for (i = rq->nr_sectors; i > 0;) {
             i -= rq->current_nr_sectors;
             ide_end_request(1, hwgroup);
         }
-        return;
+        return ide_stopped;
 }
 
 /*
@@ -283,24 +284,27 @@
  * how it's done in the drivers for other O/Ses. There is no interrupt
  * generated on writes, which is why we have to do it like this.
  */
-static void promise_write (ide_drive_t *drive)
+static ide_startstop_t promise_write (ide_drive_t *drive)
 {
     ide_hwgroup_t *hwgroup = HWGROUP(drive);
     struct request *rq = &hwgroup->wrq;
     int i;
 
     if (rq->nr_sectors > 4) {
-        ide_multwrite(drive, rq->nr_sectors - 4);
+        if (ide_multwrite(drive, rq->nr_sectors - 4))
+		return ide_stopped;
         hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
         ide_set_handler (drive, &promise_write_pollfunc, 1);
-        return;
+        return ide_started;
     } else {
-        ide_multwrite(drive, rq->nr_sectors);
+        if (ide_multwrite(drive, rq->nr_sectors))
+		return ide_stopped;
         rq = hwgroup->rq;
         for (i = rq->nr_sectors; i > 0;) {
             i -= rq->current_nr_sectors;
             ide_end_request(1, hwgroup);
         }
+	return ide_stopped;
     }
 }
 
@@ -309,7 +313,7 @@
  * already set up. It issues a READ or WRITE command to the Promise
  * controller, assuming LBA has been used to set up the block number.
  */
-void do_pdc4030_io (ide_drive_t *drive, struct request *rq)
+ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq)
 {
 	unsigned long timeout;
 	byte stat;
@@ -330,28 +334,29 @@
                     disable_irq(HWIF(drive)->irq);
 		    ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL);
                     enable_irq(HWIF(drive)->irq);
-		    return;
+		    return ide_stopped;
 		}
 		if(IN_BYTE(IDE_SELECT_REG) & 0x01)
-		    return;
+		    return ide_started;
 		udelay(1);
 	    } while (time_before(jiffies, timeout));
 	    printk("%s: reading: No DRQ and not waiting - Odd!\n",
 		   drive->name);
-	    return;
+	    return ide_started;
 	}
 	if (rq->cmd == WRITE) {
+	    ide_startstop_t startstop;
 	    OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
-	    if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+	    if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
 		printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name);
-		return;
+		return startstop;
 	    }
 	    if (!drive->unmask)
 		__cli();	/* local CPU only */
 	    HWGROUP(drive)->wrq = *rq; /* scratchpad */
-	    promise_write(drive);
-	    return;
+	    return promise_write(drive);
 	}
 	printk("%s: bad command: %d\n", drive->name, rq->cmd);
 	ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/Config.in linux.14p6/drivers/char/Config.in
--- linux.vanilla/drivers/char/Config.in	Wed Oct 20 01:12:34 1999
+++ linux.14p6/drivers/char/Config.in	Wed Nov  3 00:18:55 1999
@@ -101,6 +101,7 @@
   tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
   tristate '   Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
   tristate '   Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
+  tristate '   Mixcom Watchdog' CONFIG_MIXCOMWD 
   endmenu
 fi
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/Makefile linux.14p6/drivers/char/Makefile
--- linux.vanilla/drivers/char/Makefile	Wed Oct 20 01:12:34 1999
+++ linux.14p6/drivers/char/Makefile	Wed Nov  3 00:18:55 1999
@@ -270,6 +270,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_MIXCOMWD),y)
+L_OBJS += mixcomwd.o
+else
+  ifeq ($(CONFIG_MIXCOMWD),m)
+  M_OBJS += mixcomwd.o
+  endif
+endif
+
 ifeq ($(CONFIG_AMIGAMOUSE),y)
 L_OBJS += amigamouse.o
 else
@@ -380,9 +388,11 @@
 
 ifeq ($(CONFIG_VIDEO_ZORAN),y)
 L_OBJS += buz.o
+L_I2C=y
 else
   ifeq ($(CONFIG_VIDEO_ZORAN),m)
     M_OBJS += buz.o
+    M_I2C=y
   endif
 endif
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/bttv.c linux.14p6/drivers/char/bttv.c
--- linux.vanilla/drivers/char/bttv.c	Wed Oct 20 01:12:35 1999
+++ linux.14p6/drivers/char/bttv.c	Fri Oct 22 22:16:43 1999
@@ -403,7 +403,7 @@
 	}
 }
 
-void attach_inform(struct i2c_bus *bus, int id)
+static void attach_inform(struct i2c_bus *bus, int id)
 {
         struct bttv *btv = (struct bttv*)bus->data;
         
@@ -422,7 +422,7 @@
 	}
 }
 
-void detach_inform(struct i2c_bus *bus, int id)
+static void detach_inform(struct i2c_bus *bus, int id)
 {
         struct bttv *btv = (struct bttv*)bus->data;
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/busmouse.c linux.14p6/drivers/char/busmouse.c
--- linux.vanilla/drivers/char/busmouse.c	Wed Dec 16 21:38:18 1998
+++ linux.14p6/drivers/char/busmouse.c	Sat Nov 13 15:41:36 1999
@@ -210,7 +210,8 @@
 	buttons = mouse.buttons;
 	mouse.dx -= dx;
 	mouse.dy -= dy;
-	mouse.ready = 0;
+	if(mouse.dx==0 && mouse.dy==0)
+		mouse.ready = 0;
 	enable_irq(mouse_irq);
 	/* restore_flags(flags); */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/buz.c linux.14p6/drivers/char/buz.c
--- linux.vanilla/drivers/char/buz.c	Wed Oct 20 01:12:35 1999
+++ linux.14p6/drivers/char/buz.c	Fri Oct 22 22:16:43 1999
@@ -374,13 +374,13 @@
 	return (btread(ZR36057_I2CBR) >> 1) & 1;
 }
 
-void attach_inform(struct i2c_bus *bus, int id)
+static void attach_inform(struct i2c_bus *bus, int id)
 {
 	DEBUG(struct zoran *zr = (struct zoran *) bus->data);
 	DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id));
 }
 
-void detach_inform(struct i2c_bus *bus, int id)
+static void detach_inform(struct i2c_bus *bus, int id)
 {
 	DEBUG(struct zoran *zr = (struct zoran *) bus->data);
 	DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id));
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/console.c linux.14p6/drivers/char/console.c
--- linux.vanilla/drivers/char/console.c	Wed Oct 20 01:12:35 1999
+++ linux.14p6/drivers/char/console.c	Sat Nov 13 17:49:37 1999
@@ -2820,6 +2820,7 @@
 EXPORT_SYMBOL(video_font_height);
 EXPORT_SYMBOL(video_scan_lines);
 EXPORT_SYMBOL(vc_resize);
+EXPORT_SYMBOL(fg_console);
 
 #ifndef VT_SINGLE_DRIVER
 EXPORT_SYMBOL(take_over_console);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/hfmodem/refclock.c linux.14p6/drivers/char/hfmodem/refclock.c
--- linux.vanilla/drivers/char/hfmodem/refclock.c	Sat Jul 18 19:47:49 1998
+++ linux.14p6/drivers/char/hfmodem/refclock.c	Fri Oct 29 23:36:36 1999
@@ -127,14 +127,13 @@
 
 #ifdef __i386__
 	if (rdtsc_ok) {
-		unsigned int tmp0, tmp1;
-		unsigned int tmp2, tmp3;
+		unsigned int tmp0, tmp1, tmp2, tmp3, tmp4;
 		
 		__asm__("rdtsc;\n\t"
 			"subl %2,%%eax\n\t"
 			"sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1) 
-			: "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx");
-		__asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax");
+			: "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi));
+		__asm__("mull %2" : "=d" (tmp2), "=a" (tmp4) : "m" (scale_rdtsc), "1" (tmp0) : "ax");
 		__asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx");
 		curtime = tmp2 + tmp3;
 		goto time_known;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/mixcomwd.c linux.14p6/drivers/char/mixcomwd.c
--- linux.vanilla/drivers/char/mixcomwd.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/char/mixcomwd.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,250 @@
+/*
+ * MixCom Watchdog: A Simple Hardware Watchdog Device
+ * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.1 (99/04/15):
+ *		- first version
+ *
+ * Version 0.2 (99/06/16):
+ *		- added kernel timer watchdog ping after close
+ *		  since the hardware does not support watchdog shutdown
+ *
+ * Version 0.3 (99/06/21):
+ *		- added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls
+ *
+ * Version 0.3.1 (99/06/22):
+ *		- allow module removal while internal timer is active,
+ *		  print warning about probable reset
+ *	
+ */
+
+#define VERSION "0.3.1" 
+  
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 };
+
+#define MIXCOM_WATCHDOG_OFFSET 0xc10
+#define MIXCOM_ID1 0x11
+#define MIXCOM_ID2 0x13
+
+static int mixcomwd_opened;
+static int mixcomwd_port;
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+static int mixcomwd_timer_alive;
+static struct timer_list mixcomwd_timer;
+#endif
+
+static void mixcomwd_ping(void)
+{
+	outb_p(55,mixcomwd_port+MIXCOM_WATCHDOG_OFFSET);
+	return;
+}
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+static void mixcomwd_timerfun(unsigned long d)
+{
+	mixcomwd_ping();
+	
+	mod_timer(&mixcomwd_timer,jiffies+ 5*HZ);
+}
+#endif
+
+/*
+ *	Allow only one person to hold it open
+ */
+ 
+static int mixcomwd_open(struct inode *inode, struct file *file)
+{
+	if(test_and_set_bit(0,&mixcomwd_opened)) {
+		return -EBUSY;
+	}
+	mixcomwd_ping();
+	
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+	if(mixcomwd_timer_alive) {
+		del_timer(&mixcomwd_timer);
+		mixcomwd_timer_alive=0;
+	} 
+#endif
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int mixcomwd_release(struct inode *inode, struct file *file)
+{
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+	if(mixcomwd_timer_alive) {
+		printk(KERN_ERR "mixcomwd: release called while internal timer alive");
+		return -EBUSY;
+	}
+	init_timer(&mixcomwd_timer);
+	mixcomwd_timer.expires=jiffies + 5 * HZ;
+	mixcomwd_timer.function=mixcomwd_timerfun;
+	mixcomwd_timer.data=0;
+	mixcomwd_timer_alive=1;
+	add_timer(&mixcomwd_timer);
+#endif
+	MOD_DEC_USE_COUNT;
+
+	clear_bit(0,&mixcomwd_opened);
+	return 0;
+}
+
+
+static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+	if (ppos != &file->f_pos) {
+		return -ESPIPE;
+	}
+
+	if(len)
+	{
+		mixcomwd_ping();
+		return 1;
+	}
+	return 0;
+}
+
+static int mixcomwd_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	int status;
+        static struct watchdog_info ident = {
+		WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog"
+	};
+                                        
+	switch(cmd)
+	{
+		case WDIOC_GETSTATUS:
+			status=mixcomwd_opened;
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+			status|=mixcomwd_timer_alive;
+#endif
+			if (copy_to_user((int *)arg, &status, sizeof(int))) {
+				return -EFAULT;
+			}
+			break;
+		case WDIOC_GETSUPPORT:
+			if (copy_to_user((struct watchdog_info *)arg, &ident, 
+			    sizeof(ident))) {
+				return -EFAULT;
+			}
+			break;
+		case WDIOC_KEEPALIVE:
+			mixcomwd_ping();
+			break;
+		default:
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations mixcomwd_fops=
+{
+	NULL,		/* Seek */
+	NULL,		/* Read */
+	mixcomwd_write,	/* Write */
+	NULL,		/* Readdir */
+	NULL,		/* Select */
+	mixcomwd_ioctl,	/* Ioctl */
+	NULL,		/* MMap */
+	mixcomwd_open,
+	NULL,		/* flush */
+	mixcomwd_release,
+	NULL,		
+	NULL		/* Fasync */
+};
+
+static struct miscdevice mixcomwd_miscdev=
+{
+	WATCHDOG_MINOR,
+	"watchdog",
+	&mixcomwd_fops
+};
+
+__initfunc(static int mixcomwd_checkcard(int port))
+{
+	int id;
+
+	if(check_region(port,1)) {
+		return 0;
+	}
+	
+	id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f;
+	if(id!=MIXCOM_ID1 && id!=MIXCOM_ID2) {
+		return 0;
+	}
+	return 1;
+}
+
+
+__initfunc(void mixcomwd_init(void))
+{
+	int i;
+	int found=0;
+
+	for (i = 0; mixcomwd_ioports[i] != 0; i++) {
+		if (mixcomwd_checkcard(mixcomwd_ioports[i])) {
+			found = 1;
+			mixcomwd_port = mixcomwd_ioports[i];
+			break;
+		}
+	}
+
+	if (!found) {
+		printk("mixcomwd: No card detected, or port not available.\n");
+		return;
+	}
+
+	request_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1,"MixCOM watchdog");
+	
+	misc_register(&mixcomwd_miscdev);
+	printk("MixCOM watchdog driver v%s, MixCOM card at 0x%3x\n",VERSION,mixcomwd_port);
+}	
+
+#ifdef MODULE
+int init_module(void)
+{
+	mixcomwd_init();
+	return 0;
+}
+
+void cleanup_module(void)
+{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+	if(mixcomwd_timer_alive) {
+		printk(KERN_WARNING "mixcomwd: I quit now, hardware will"
+			" probably reboot!\n");
+		del_timer(&mixcomwd_timer);
+		mixcomwd_timer_alive=0;
+	}
+#endif
+	release_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1);
+	misc_deregister(&mixcomwd_miscdev);
+}
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/msbusmouse.c linux.14p6/drivers/char/msbusmouse.c
--- linux.vanilla/drivers/char/msbusmouse.c	Wed Dec 16 21:39:11 1998
+++ linux.14p6/drivers/char/msbusmouse.c	Sat Nov 13 15:42:06 1999
@@ -157,7 +157,8 @@
 		put_user(0x00, buffer + i);
 	mouse.dx -= dx;
 	mouse.dy += dy;
-	mouse.ready = 0;
+	if(mouse.dx == 0 && mouse.dy == 0)
+		mouse.ready = 0;
 	return i;	
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/pc_keyb.c linux.14p6/drivers/char/pc_keyb.c
--- linux.vanilla/drivers/char/pc_keyb.c	Sat Aug 14 02:27:17 1999
+++ linux.14p6/drivers/char/pc_keyb.c	Tue Nov 16 15:20:33 1999
@@ -433,7 +433,7 @@
 				handle_scancode(scancode, !(scancode & 0x80));
 			mark_bh(KEYBOARD_BH);
 		}
-
+		mdelay(1);
 		status = kbd_read_status();
 		
 		if(!work--)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/radio-aimslab.c linux.14p6/drivers/char/radio-aimslab.c
--- linux.vanilla/drivers/char/radio-aimslab.c	Sat Aug 14 02:25:39 1999
+++ linux.14p6/drivers/char/radio-aimslab.c	Fri Oct 22 22:18:35 1999
@@ -195,7 +195,7 @@
 	return 0;
 }
 
-int rt_getsigstr(struct rt_device *dev)
+static int rt_getsigstr(struct rt_device *dev)
 {
 	if (inb(io) & 2)	/* bit set = no signal present	*/
 		return 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/radio-cadet.c linux.14p6/drivers/char/radio-cadet.c
--- linux.vanilla/drivers/char/radio-cadet.c	Sat Aug 14 02:27:17 1999
+++ linux.14p6/drivers/char/radio-cadet.c	Fri Oct 22 22:16:43 1999
@@ -39,6 +39,7 @@
 static __u8 rdsin=0,rdsout=0,rdsstat=0;
 static unsigned char rdsbuf[RDS_BUFFER];
 static int cadet_lock=0;
+static int cadet_probe(void);
 
 /*
  * Signal Strength Threshold Values
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/radio-rtrack2.c linux.14p6/drivers/char/radio-rtrack2.c
--- linux.vanilla/drivers/char/radio-rtrack2.c	Sat Aug 14 02:25:40 1999
+++ linux.14p6/drivers/char/radio-rtrack2.c	Fri Oct 22 22:18:35 1999
@@ -89,7 +89,7 @@
 	return 0;
 }
 
-int rt_getsigstr(struct rt_device *dev)
+static int rt_getsigstr(struct rt_device *dev)
 {
 	if (inb(io) & 2)	/* bit set = no signal present	*/
 		return 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/fc4/Makefile linux.14p6/drivers/fc4/Makefile
--- linux.vanilla/drivers/fc4/Makefile	Tue Mar 16 00:11:29 1999
+++ linux.14p6/drivers/fc4/Makefile	Fri Oct 22 21:24:05 1999
@@ -7,8 +7,6 @@
 M_OBJS   :=
 MOD_LIST_NAME := FC4_MODULES
 
-include ../../.config
-
 ifeq ($(CONFIG_FC4),y)
   FC4 = fc.o
   ifeq ($(CONFIG_MODULES),y)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/Config.in linux.14p6/drivers/isdn/Config.in
--- linux.vanilla/drivers/isdn/Config.in	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/Config.in	Tue Nov 16 15:10:47 1999
@@ -2,77 +2,78 @@
 # ISDN device configuration
 #
 if [ "$CONFIG_INET" != "n" ]; then
-  bool 'Support synchronous PPP' CONFIG_ISDN_PPP
-  if [ "$CONFIG_ISDN_PPP" != "n" ]; then
-    bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
-    bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
-  fi
+   bool '  Support synchronous PPP' CONFIG_ISDN_PPP
+   if [ "$CONFIG_ISDN_PPP" != "n" ]; then
+      bool '    Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
+      bool '    Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
+   fi
 fi
-bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO
+bool '  Support audio via ISDN' CONFIG_ISDN_AUDIO
 if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then
-  bool 'Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX
+   bool '    Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX
 fi
-bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION
+bool '  Support isdn diversion services' CONFIG_ISDN_DIVERSION
 if [ "$CONFIG_X25" != "n" ]; then
-  bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25
+   bool '  X.25 PLP on top of ISDN' CONFIG_ISDN_X25
 fi
-dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
-dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
-dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
-dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
+dep_tristate '  ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
+dep_tristate '  isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
+dep_tristate '  PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
+dep_tristate '  HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
-    bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
-    if [ "$CONFIG_HISAX_EURO" != "n" ]; then
-	    bool 'Support for german chargeinfo' CONFIG_DE_AOC
-	    bool 'Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
-	    bool 'Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
-    fi
-    bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
-    bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
-    bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
-    bool 'HiSax Support for Teles 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 HFC-S based cards' CONFIG_HISAX_HFCS
-    bool 'HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER
-    bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
-    bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC
-    bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET
-    bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
-    bool 'HiSax Support for Siemens I-Surf card' CONFIG_HISAX_ISURF
-    bool 'HiSax Support for HST Saphir card' CONFIG_HISAX_HSTSAPHIR
-    bool 'HiSax Support for Telekom A4T card' CONFIG_HISAX_BKM_A4T
-    bool 'HiSax Support for Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO
-    bool 'HiSax Support for Gazel cards' CONFIG_HISAX_GAZEL
-    bool 'HiSax Support for HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI
-    if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
-        bool 'HiSax Support for Winbond W6692 based cards (EXPERIMENTAL)' CONFIG_HISAX_W6692
-#	bool 'HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
-	if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
-		bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930
-	fi
-    fi
+   bool '    HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
+   if [ "$CONFIG_HISAX_EURO" != "n" ]; then
+      bool '      Support for german chargeinfo' CONFIG_DE_AOC
+      bool '      Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
+      bool '      Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
+   fi
+   bool '    HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
+   bool '    HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
+   bool '    HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
+   bool '    HiSax Support for Teles 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 HFC-S based cards' CONFIG_HISAX_HFCS
+   bool '    HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER
+   bool '    HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
+   bool '    HiSax Support for MIC card' CONFIG_HISAX_MIC
+   bool '    HiSax Support for NETjet card' CONFIG_HISAX_NETJET
+   bool '    HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
+   bool '    HiSax Support for Siemens I-Surf card' CONFIG_HISAX_ISURF
+   bool '    HiSax Support for HST Saphir card' CONFIG_HISAX_HSTSAPHIR
+   bool '    HiSax Support for Telekom A4T card' CONFIG_HISAX_BKM_A4T
+   bool '    HiSax Support for Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO
+   bool '    HiSax Support for Gazel cards' CONFIG_HISAX_GAZEL
+   bool '    HiSax Support for HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI
+   bool '    HiSax Support for Winbond W6692 based cards' CONFIG_HISAX_W6692
+   if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+#      bool '    HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
+      if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
+	 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 '  Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
+   dep_tristate '  IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
 fi
-dep_tristate 'Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN
+dep_tristate '  Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then
-    bool 'Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA
+   bool '    Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA
 fi
-dep_tristate 'AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
+dep_tristate '  AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
-    bool 'AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA
-    bool 'AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI
-    bool 'AVM T1/T1B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA
-    bool 'AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
-    bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+   bool '    AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA
+   bool '    AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI
+   bool '    AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA
+   bool '    AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
+   bool '    AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI
+   bool '    Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
 fi
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/act2000/act2000_isa.c linux.14p6/drivers/isdn/act2000/act2000_isa.c
--- linux.vanilla/drivers/isdn/act2000/act2000_isa.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/act2000/act2000_isa.c	Tue Nov 16 15:10:47 1999
@@ -1,4 +1,4 @@
-/* $Id: act2000_isa.c,v 1.9 1999/09/04 06:20:04 keil Exp $
+/* $Id: act2000_isa.c,v 1.10 1999/10/24 18:46:05 fritz Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
  *
@@ -20,6 +20,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: act2000_isa.c,v $
+ * Revision 1.10  1999/10/24 18:46:05  fritz
+ * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest
+ * kernels.
+ *
  * Revision 1.9  1999/09/04 06:20:04  keil
  * Changes from kernel set_current_state()
  *
@@ -64,14 +68,14 @@
 	0, 0, 0, 0, 0, 0, 0, 0,	0, 0, 0, 0, 0, 0, 0, 0
 };
 
-static int isa_irqs[] =
+static int act2000_isa_irqs[] =
 {
         3, 5, 7, 10, 11, 12, 15
 };
-#define ISA_NRIRQS (sizeof(isa_irqs)/sizeof(int))
+#define ISA_NRIRQS (sizeof(act2000_isa_irqs)/sizeof(int))
 
 static void
-isa_delay(long t)
+act2000_isa_delay(long t)
 {
         sti();
         current->state = TASK_INTERRUPTIBLE;
@@ -86,7 +90,7 @@
  *   0 = Signature not found.
  */
 static int
-isa_reset(unsigned short portbase)
+act2000_isa_reset(unsigned short portbase)
 {
         unsigned char reg;
         int i;
@@ -112,7 +116,7 @@
 }
 
 int
-isa_detect(unsigned short portbase)
+act2000_isa_detect(unsigned short portbase)
 {
         int ret = 0;
         unsigned long flags;
@@ -120,13 +124,13 @@
         save_flags(flags);
         cli();
         if (!check_region(portbase, ISA_REGION))
-                ret = isa_reset(portbase);
+                ret = act2000_isa_reset(portbase);
         restore_flags(flags);
         return ret;
 }
 
 static void
-isa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+act2000_isa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
         act2000_card *card = irq2card_map[irq];
         u_char istatus;
@@ -141,7 +145,7 @@
                 /* RX fifo has data */
 		istatus &= ISA_ISR_OUT_MASK;
 		outb(0, ISA_PORT_SIS);
-		isa_receive(card);
+		act2000_isa_receive(card);
 		outb(ISA_SIS_INT, ISA_PORT_SIS);
         }
         if (istatus & ISA_ISR_ERR) {
@@ -154,7 +158,7 @@
 }
 
 static void
-isa_select_irq(act2000_card * card)
+act2000_isa_select_irq(act2000_card * card)
 {
 	unsigned char reg;
 
@@ -186,9 +190,9 @@
 }
 
 static void
-isa_enable_irq(act2000_card * card)
+act2000_isa_enable_irq(act2000_card * card)
 {
-	isa_select_irq(card);
+	act2000_isa_select_irq(card);
 	/* Enable READ irq */
 	outb(ISA_SIS_INT, ISA_PORT_SIS);
 }
@@ -198,7 +202,7 @@
  * If irq is -1, choose next free irq, else irq is given explicitely.
  */
 int
-isa_config_irq(act2000_card * card, short irq)
+act2000_isa_config_irq(act2000_card * card, short irq)
 {
         int i;
         unsigned long flags;
@@ -216,8 +220,8 @@
         if (irq == -1) {
                 /* Auto select */
                 for (i = 0; i < ISA_NRIRQS; i++) {
-                        if (!request_irq(isa_irqs[i], &isa_interrupt, 0, card->regname, NULL)) {
-                                card->irq = isa_irqs[i];
+                        if (!request_irq(act2000_isa_irqs[i], &act2000_isa_interrupt, 0, card->regname, NULL)) {
+                                card->irq = act2000_isa_irqs[i];
                                 irq2card_map[card->irq] = card;
                                 card->flags |= ACT2000_FLAGS_IVALID;
                                 break;
@@ -225,7 +229,7 @@
                 }
         } else {
                 /* Fixed irq */
-                if (!request_irq(irq, &isa_interrupt, 0, card->regname, NULL)) {
+                if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, NULL)) {
                         card->irq = irq;
                         irq2card_map[card->irq] = card;
 			card->flags |= ACT2000_FLAGS_IVALID;
@@ -237,7 +241,7 @@
                        "act2000: Could not request irq\n");
                 return -EBUSY;
         } else {
-		isa_select_irq(card);
+		act2000_isa_select_irq(card);
                 /* Disable READ and WRITE irq */
                 outb(0, ISA_PORT_SIS);
                 outb(0, ISA_PORT_SOS);
@@ -246,7 +250,7 @@
 }
 
 int
-isa_config_port(act2000_card * card, unsigned short portbase)
+act2000_isa_config_port(act2000_card * card, unsigned short portbase)
 {
         if (card->flags & ACT2000_FLAGS_PVALID) {
                 release_region(card->port, ISA_REGION);
@@ -265,7 +269,7 @@
  * Release ressources, used by an adaptor.
  */
 void
-isa_release(act2000_card * card)
+act2000_isa_release(act2000_card * card)
 {
         unsigned long flags;
 
@@ -283,7 +287,7 @@
 }
 
 static int
-isa_writeb(act2000_card * card, u_char data)
+act2000_isa_writeb(act2000_card * card, u_char data)
 {
         u_char timeout = 40;
 
@@ -300,7 +304,7 @@
 }
 
 static int
-isa_readb(act2000_card * card, u_char * data)
+act2000_isa_readb(act2000_card * card, u_char * data)
 {
         u_char timeout = 40;
 
@@ -317,13 +321,13 @@
 }
 
 void
-isa_receive(act2000_card *card)
+act2000_isa_receive(act2000_card *card)
 {
 	u_char c;
 
         if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0)
 		return;
-	while (!isa_readb(card, &c)) {
+	while (!act2000_isa_readb(card, &c)) {
 		if (card->idat.isa.rcvidx < 8) {
                         card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c;
 			if (card->idat.isa.rcvidx == 8) {
@@ -335,7 +339,7 @@
 					if (card->idat.isa.rcvskb == NULL) {
 						card->idat.isa.rcvignore = 1;
 						printk(KERN_WARNING
-						       "isa_receive: no memory\n");
+						       "act2000_isa_receive: no memory\n");
 						test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
 						return;
 					}
@@ -344,12 +348,12 @@
 				} else {
 					card->idat.isa.rcvidx = 0;
 					printk(KERN_WARNING
-					       "isa_receive: Invalid CAPI msg\n");
+					       "act2000_isa_receive: Invalid CAPI msg\n");
 					{
 						int i; __u8 *p; __u8 *c; __u8 tmp[30];
 						for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++)
 							c += sprintf(c, "%02x ", *(p++));
-						printk(KERN_WARNING "isa_receive: %s\n", tmp);
+						printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp);
 					}
 				}
 			}
@@ -380,7 +384,7 @@
 }
 
 void
-isa_send(act2000_card * card)
+act2000_isa_send(act2000_card * card)
 {
 	unsigned long flags;
 	struct sk_buff *skb;
@@ -413,7 +417,7 @@
 		skb = card->sbuf;
 		l = 0;
 		while (skb->len) {
-			if (isa_writeb(card, *(skb->data))) {
+			if (act2000_isa_writeb(card, *(skb->data))) {
 				/* Fifo is full, but more data to send */
 				test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
 				/* Schedule myself */
@@ -444,7 +448,7 @@
  * Get firmware ID, check for 'ISDN' signature.
  */
 static int
-isa_getid(act2000_card * card)
+act2000_isa_getid(act2000_card * card)
 {
 
         act2000_fwid fid;
@@ -454,7 +458,7 @@
         while (1) {
                 if (count > 510)
                         return -EPROTO;
-                if (isa_readb(card, p++))
+                if (act2000_isa_readb(card, p++))
                         break;
                 count++;
         }
@@ -473,7 +477,7 @@
         printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision);
 	if (card->flags & ACT2000_FLAGS_IVALID) {
 		printk(KERN_DEBUG "Enabling Interrupts ...\n");
-		isa_enable_irq(card);
+		act2000_isa_enable_irq(card);
 	}
         return 0;
 }
@@ -482,7 +486,7 @@
  * Download microcode into card, check Firmware signature.
  */
 int
-isa_download(act2000_card * card, act2000_ddef * cb)
+act2000_isa_download(act2000_card * card, act2000_ddef * cb)
 {
         int length;
         int ret;
@@ -494,9 +498,9 @@
         u_char *buf;
         act2000_ddef cblock;
 
-        if (!isa_reset(card->port))
+        if (!act2000_isa_reset(card->port))
                 return -ENXIO;
-        isa_delay(HZ / 2);
+        act2000_isa_delay(HZ / 2);
         if ((ret = verify_area(VERIFY_READ, (void *) cb, sizeof(cblock))))
                 return ret;
         copy_from_user(&cblock, (char *) cb, sizeof(cblock));
@@ -514,7 +518,7 @@
                 b = buf;
                 copy_from_user(buf, p, l);
                 while (c < l) {
-                        if (isa_writeb(card, *b++)) {
+                        if (act2000_isa_writeb(card, *b++)) {
                                 printk(KERN_WARNING
                                        "act2000: loader timed out"
                                        " len=%d c=%d\n", length, c);
@@ -527,6 +531,6 @@
                 p += l;
         }
         kfree(buf);
-        isa_delay(HZ / 2);
-        return (isa_getid(card));
+        act2000_isa_delay(HZ / 2);
+        return (act2000_isa_getid(card));
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/act2000/act2000_isa.h linux.14p6/drivers/isdn/act2000/act2000_isa.h
--- linux.vanilla/drivers/isdn/act2000/act2000_isa.h	Sat Aug 14 02:27:18 1999
+++ linux.14p6/drivers/isdn/act2000/act2000_isa.h	Tue Nov 16 15:10:47 1999
@@ -1,4 +1,4 @@
-/* $Id: act2000_isa.h,v 1.2 1998/11/05 22:12:43 fritz Exp $
+/* $Id: act2000_isa.h,v 1.3 1999/10/24 18:46:05 fritz Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
  *
@@ -20,6 +20,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: act2000_isa.h,v $
+ * Revision 1.3  1999/10/24 18:46:05  fritz
+ * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest
+ * kernels.
+ *
  * Revision 1.2  1998/11/05 22:12:43  fritz
  * Changed mail-address.
  *
@@ -141,12 +145,12 @@
 
 /* Prototypes */
 
-extern int isa_detect(unsigned short portbase);
-extern int isa_config_irq(act2000_card * card, short irq);
-extern int isa_config_port(act2000_card * card, unsigned short portbase);
-extern int isa_download(act2000_card * card, act2000_ddef * cb);
-extern void isa_release(act2000_card * card);
-extern void isa_receive(act2000_card *card);
-extern void isa_send(act2000_card *card);
+extern int act2000_isa_detect(unsigned short portbase);
+extern int act2000_isa_config_irq(act2000_card * card, short irq);
+extern int act2000_isa_config_port(act2000_card * card, unsigned short portbase);
+extern int act2000_isa_download(act2000_card * card, act2000_ddef * cb);
+extern void act2000_isa_release(act2000_card * card);
+extern void act2000_isa_receive(act2000_card *card);
+extern void act2000_isa_send(act2000_card *card);
 
 #endif                          /* act2000_isa_h */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/act2000/module.c linux.14p6/drivers/isdn/act2000/module.c
--- linux.vanilla/drivers/isdn/act2000/module.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/act2000/module.c	Tue Nov 16 15:10:47 1999
@@ -1,4 +1,4 @@
-/* $Id: module.c,v 1.9 1999/04/12 13:13:56 fritz Exp $
+/* $Id: module.c,v 1.11 1999/10/30 09:48:04 keil Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *
@@ -20,6 +20,13 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: module.c,v $
+ * Revision 1.11  1999/10/30 09:48:04  keil
+ * miss one prefix act2000
+ *
+ * Revision 1.10  1999/10/24 18:46:05  fritz
+ * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest
+ * kernels.
+ *
  * Revision 1.9  1999/04/12 13:13:56  fritz
  * Made cards pointer static to avoid name-clash.
  *
@@ -56,12 +63,12 @@
 #include "act2000_isa.h"
 #include "capi.h"
 
-static unsigned short isa_ports[] =
+static unsigned short act2000_isa_ports[] =
 {
         0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380,
         0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60,
 };
-#define ISA_NRPORTS (sizeof(isa_ports)/sizeof(unsigned short))
+#define ISA_NRPORTS (sizeof(act2000_isa_ports)/sizeof(unsigned short))
 
 static act2000_card *cards = (act2000_card *) NULL;
 
@@ -238,7 +245,7 @@
 {
 	switch (card->bus) {
 		case ACT2000_BUS_ISA:
-			isa_send(card);
+			act2000_isa_send(card);
 			break;
 		case ACT2000_BUS_PCMCIA:
 		case ACT2000_BUS_MCA:
@@ -253,7 +260,7 @@
 {
 	switch (card->bus) {
 		case ACT2000_BUS_ISA:
-			isa_receive(card);
+			act2000_isa_receive(card);
 			break;
 		case ACT2000_BUS_PCMCIA:
 		case ACT2000_BUS_MCA:
@@ -296,7 +303,7 @@
 				case ACT2000_IOCTL_LOADBOOT:
 					switch (card->bus) {
 						case ACT2000_BUS_ISA:
-							ret = isa_download(card,
+							ret = act2000_isa_download(card,
 									   (act2000_ddef *)a);
 							if (!ret) {
 								card->flags |= ACT2000_FLAGS_LOADED;
@@ -758,7 +765,7 @@
         card->interface.statcallb(&cmd);
         switch (card->bus) {
 		case ACT2000_BUS_ISA:
-			isa_release(card);
+			act2000_isa_release(card);
 			break;
 		case ACT2000_BUS_MCA:
 		case ACT2000_BUS_PCMCIA:
@@ -792,11 +799,11 @@
 		switch (bus) {
 			case ACT2000_BUS_ISA:
 				for (i = 0; i < ISA_NRPORTS; i++)
-					if (isa_detect(isa_ports[i])) {
+					if (act2000_isa_detect(act2000_isa_ports[i])) {
 						printk(KERN_INFO
 						       "act2000: Detected ISA card at port 0x%x\n",
-						       isa_ports[i]);
-						act2000_alloccard(bus, isa_ports[i], irq, id);
+						       act2000_isa_ports[i]);
+						act2000_alloccard(bus, act2000_isa_ports[i], irq, id);
 					}
 				break;
 			case ACT2000_BUS_MCA:
@@ -819,10 +826,10 @@
 			added++;
 			switch (p->bus) {
 				case ACT2000_BUS_ISA:
-					if (isa_detect(p->port)) {
+					if (act2000_isa_detect(p->port)) {
 						if (act2000_registercard(p))
 							break;
-						if (isa_config_port(p, p->port)) {
+						if (act2000_isa_config_port(p, p->port)) {
 							printk(KERN_WARNING
 							       "act2000: Could not request port 0x%04x\n",
 							       p->port);
@@ -830,7 +837,7 @@
 							p->interface.statcallb = NULL;
 							break;
 						}
-						if (isa_config_irq(p, p->irq)) {
+						if (act2000_isa_config_irq(p, p->irq)) {
 							printk(KERN_INFO
 							       "act2000: No IRQ available, fallback to polling\n");
 							/* Fall back to polled operation */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/avmcard.h linux.14p6/drivers/isdn/avmb1/avmcard.h
--- linux.vanilla/drivers/isdn/avmb1/avmcard.h	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/avmcard.h	Tue Nov 16 15:10:47 1999
@@ -1,9 +1,16 @@
 /*
- * $Id: avmcard.h,v 1.5 1999/09/07 09:02:53 calle Exp $
+ * $Id: avmcard.h,v 1.6 1999/11/05 16:38:01 calle Exp $
  *
  * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: avmcard.h,v $
+ * Revision 1.6  1999/11/05 16:38:01  calle
+ * Cleanups before kernel 2.4:
+ * - Changed all messages to use card->name or driver->name instead of
+ *   constant string.
+ * - Moved some data from struct avmcard into new struct avmctrl_info.
+ *   Changed all lowlevel capi driver to match the new structur.
+ *
  * Revision 1.5  1999/09/07 09:02:53  calle
  * SETDATA removed. Now inside the kernel the datapart of DATA_B3_REQ and
  * DATA_B3_IND is always directly after the CAPI message. The "Data" member
@@ -78,6 +85,7 @@
 	__u8  sendbuf[128+2048];
 } avmcard_dmainfo;
 
+
 typedef struct avmcard {
 	char name[32];
 	unsigned int port;
@@ -86,13 +94,6 @@
 	enum avmcardtype cardtype;
 	int cardnr; /* for t1isa */
 
-	int versionlen;
-	char versionbuf[1024];
-	char *version[AVM_MAXVERSION];
-
-	char cardname[32];
-
-	char infobuf[128];	/* for function procinfo */
 	char msgbuf[128];	/* capimsg msg part */
 	char databuf[2048];	/* capimsg data part */
 
@@ -102,9 +103,25 @@
 	volatile __u32 csr;
 	avmcard_dmainfo *dma;
 
-	struct capi_ctr *ctrl;
+	struct avmctrl_info {
+		char cardname[32];
+
+		int versionlen;
+		char versionbuf[1024];
+		char *version[AVM_MAXVERSION];
+
+		char infobuf[128];	/* for function procinfo */
+
+		struct avmcard  *card;
+		struct capi_ctr *capi_ctrl;
+
+	} *ctrlinfo;
+
+	int nlogcontr;
 } avmcard;
 
+typedef struct avmctrl_info avmctrl_info;
+
 extern int b1_irq_table[16];
 
 /*
@@ -545,16 +562,16 @@
 }
 
 int b1_detect(unsigned int base, enum avmcardtype cardtype);
-int b1_load_t4file(unsigned int base, capiloaddatapart * t4file);
-int b1_load_config(unsigned int base, capiloaddatapart * config);
-int b1_loaded(unsigned int base);
+int b1_load_t4file(avmcard *card, capiloaddatapart * t4file);
+int b1_load_config(avmcard *card, capiloaddatapart * config);
+int b1_loaded(avmcard *card);
 int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
 void b1_reset_ctr(struct capi_ctr *ctrl);
 void b1_register_appl(struct capi_ctr *ctrl, __u16 appl,
 				capi_register_params *rp);
 void b1_release_appl(struct capi_ctr *ctrl, __u16 appl);
 void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
-void b1_parse_version(avmcard *card);
+void b1_parse_version(avmctrl_info *card);
 void b1_handle_interrupt(avmcard * card);
 
 int b1ctl_read_proc(char *page, char **start, off_t off,
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/b1.c linux.14p6/drivers/isdn/avmb1/b1.c
--- linux.vanilla/drivers/isdn/avmb1/b1.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/b1.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,21 @@
 /*
- * $Id: b1.c,v 1.10 1999/09/15 08:16:03 calle Exp $
+ * $Id: b1.c,v 1.12 1999/11/05 16:38:01 calle Exp $
  * 
  * Common module for AVM B1 cards.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1.c,v $
+ * Revision 1.12  1999/11/05 16:38:01  calle
+ * Cleanups before kernel 2.4:
+ * - Changed all messages to use card->name or driver->name instead of
+ *   constant string.
+ * - Moved some data from struct avmcard into new struct avmctrl_info.
+ *   Changed all lowlevel capi driver to match the new structur.
+ *
+ * Revision 1.11  1999/10/11 22:04:12  keil
+ * COMPAT_NEED_UACCESS (no include in isdn_compat.h)
+ *
  * Revision 1.10  1999/09/15 08:16:03  calle
  * Implementation of 64Bit extention complete.
  *
@@ -76,7 +86,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.10 $";
+static char *revision = "$Revision: 1.12 $";
 
 /* ------------------------------------------------------------- */
 
@@ -148,11 +158,12 @@
 	return 0;
 }
 
-int b1_load_t4file(unsigned int base, capiloaddatapart * t4file)
+int b1_load_t4file(avmcard *card, capiloaddatapart * t4file)
 {
 	unsigned char buf[256];
 	unsigned char *dp;
 	int i, left, retval;
+	unsigned int base = card->port;
 
 	dp = t4file->data;
 	left = t4file->len;
@@ -166,7 +177,8 @@
 		}
 		for (i = 0; i < sizeof(buf); i++)
 			if (b1_save_put_byte(base, buf[i]) < 0) {
-				printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n");
+				printk(KERN_ERR "%s: corrupted firmware file ?\n",
+						card->name);
 				return -EIO;
 			}
 		left -= sizeof(buf);
@@ -182,17 +194,19 @@
 		}
 		for (i = 0; i < left; i++)
 			if (b1_save_put_byte(base, buf[i]) < 0) {
-				printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n");
+				printk(KERN_ERR "%s: corrupted firmware file ?\n",
+						card->name);
 				return -EIO;
 			}
 	}
 	return 0;
 }
 
-int b1_load_config(unsigned int base, capiloaddatapart * config)
+int b1_load_config(avmcard *card, capiloaddatapart * config)
 {
 	unsigned char buf[256];
 	unsigned char *dp;
+	unsigned int base = card->port;
 	int i, j, left, retval;
 
 	dp = config->data;
@@ -241,8 +255,9 @@
 	return 0;
 }
 
-int b1_loaded(unsigned int base)
+int b1_loaded(avmcard *card)
 {
+	unsigned int base = card->port;
 	unsigned long stop;
 	unsigned char ans;
 	unsigned long tout = 2;
@@ -252,7 +267,8 @@
 			break;
 	}
 	if (!b1_tx_empty(base)) {
-		printk(KERN_ERR "b1_loaded: tx err, corrupted t4 file ?\n");
+		printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n",
+				card->name);
 		return 0;
 	}
 	b1_put_byte(base, SEND_POLL);
@@ -261,11 +277,12 @@
 			if ((ans = b1_get_byte(base)) == RECEIVE_POLL) {
 				return 1;
 			}
-			printk(KERN_ERR "b1_loaded: got 0x%x, firmware not running\n", ans);
+			printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n",
+					card->name, ans);
 			return 0;
 		}
 	}
-	printk(KERN_ERR "b1_loaded: firmware not running\n");
+	printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name);
 	return 0;
 }
 
@@ -273,14 +290,15 @@
 
 int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 	unsigned long flags;
 	int retval;
 
 	b1_reset(port);
 
-	if ((retval = b1_load_t4file(port, &data->firmware))) {
+	if ((retval = b1_load_t4file(card, &data->firmware))) {
 		b1_reset(port);
 		printk(KERN_ERR "%s: failed to load t4file!!\n",
 					card->name);
@@ -290,7 +308,7 @@
 	b1_disable_irq(port);
 
 	if (data->configuration.len > 0 && data->configuration.data) {
-		if ((retval = b1_load_config(port, &data->configuration))) {
+		if ((retval = b1_load_config(card, &data->configuration))) {
 			b1_reset(port);
 			printk(KERN_ERR "%s: failed to load config!!\n",
 					card->name);
@@ -298,7 +316,7 @@
 		}
 	}
 
-	if (!b1_loaded(port)) {
+	if (!b1_loaded(card)) {
 		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
 		return -EIO;
 	}
@@ -317,13 +335,14 @@
 
 void b1_reset_ctr(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 
 	b1_reset(port);
 	b1_reset(port);
 
-	memset(card->version, 0, sizeof(card->version));
+	memset(cinfo->version, 0, sizeof(cinfo->version));
 	ctrl->reseted(ctrl);
 }
 
@@ -331,7 +350,8 @@
 				__u16 appl,
 				capi_register_params *rp)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 	unsigned long flags;
 	int nconn, want = rp->level3cnt;
@@ -355,7 +375,8 @@
 
 void b1_release_appl(struct capi_ctr *ctrl, __u16 appl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 	unsigned long flags;
 
@@ -368,7 +389,8 @@
 
 void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 	unsigned long flags;
 	__u16 len = CAPIMSG_LEN(skb->data);
@@ -392,25 +414,26 @@
 
 /* ------------------------------------------------------------- */
 
-void b1_parse_version(avmcard *card)
+void b1_parse_version(avmctrl_info *cinfo)
 {
-	struct capi_ctr *ctrl = card->ctrl;
+	struct capi_ctr *ctrl = cinfo->capi_ctrl;
+	avmcard *card = cinfo->card;
 	capi_profile *profp;
 	__u8 *dversion;
 	__u8 flag;
 	int i, j;
 
 	for (j = 0; j < AVM_MAXVERSION; j++)
-		card->version[j] = "\0\0" + 1;
+		cinfo->version[j] = "\0\0" + 1;
 	for (i = 0, j = 0;
-	     j < AVM_MAXVERSION && i < card->versionlen;
-	     j++, i += card->versionbuf[i] + 1)
-		card->version[j] = &card->versionbuf[i + 1];
+	     j < AVM_MAXVERSION && i < cinfo->versionlen;
+	     j++, i += cinfo->versionbuf[i] + 1)
+		cinfo->version[j] = &cinfo->versionbuf[i + 1];
 
-	strncpy(ctrl->serial, card->version[VER_SERIAL], CAPI_SERIAL_LEN);
-	memcpy(&ctrl->profile, card->version[VER_PROFILE],sizeof(capi_profile));
+	strncpy(ctrl->serial, cinfo->version[VER_SERIAL], CAPI_SERIAL_LEN);
+	memcpy(&ctrl->profile, cinfo->version[VER_PROFILE],sizeof(capi_profile));
 	strncpy(ctrl->manu, "AVM GmbH", CAPI_MANUFACTURER_LEN);
-	dversion = card->version[VER_DRIVER];
+	dversion = cinfo->version[VER_DRIVER];
 	ctrl->version.majorversion = 2;
 	ctrl->version.minorversion = 0;
 	ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
@@ -423,23 +446,24 @@
 
 	flag = ((__u8 *)(profp->manu))[1];
 	switch (flag) {
-	case 0: if (card->version[VER_CARDTYPE])
-	           strcpy(card->cardname, card->version[VER_CARDTYPE]);
-	        else strcpy(card->cardname, "B1");
+	case 0: if (cinfo->version[VER_CARDTYPE])
+	           strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]);
+	        else strcpy(cinfo->cardname, "B1");
 		break;
-	case 3: strcpy(card->cardname,"PCMCIA B"); break;
-	case 4: strcpy(card->cardname,"PCMCIA M1"); break;
-	case 5: strcpy(card->cardname,"PCMCIA M2"); break;
-	case 6: strcpy(card->cardname,"B1 V3.0"); break;
-	case 7: strcpy(card->cardname,"B1 PCI"); break;
-	default: sprintf(card->cardname, "AVM?%u", (unsigned int)flag); break;
+	case 3: strcpy(cinfo->cardname,"PCMCIA B"); break;
+	case 4: strcpy(cinfo->cardname,"PCMCIA M1"); break;
+	case 5: strcpy(cinfo->cardname,"PCMCIA M2"); break;
+	case 6: strcpy(cinfo->cardname,"B1 V3.0"); break;
+	case 7: strcpy(cinfo->cardname,"B1 PCI"); break;
+	default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break;
         }
         printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n",
-				card->name, ctrl->cnr, card->cardname);
+				card->name, ctrl->cnr, cinfo->cardname);
 
         flag = ((__u8 *)(profp->manu))[3];
         if (flag)
-		printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n",
+		printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n",
+			card->name,
 			ctrl->cnr,
 			(flag & 0x01) ? " DSS1" : "",
 			(flag & 0x02) ? " CT1" : "",
@@ -466,7 +490,8 @@
 
 void b1_handle_interrupt(avmcard * card)
 {
-	struct capi_ctr *ctrl = card->ctrl;
+	avmctrl_info *cinfo = &card->ctrlinfo[0];
+	struct capi_ctr *ctrl = cinfo->capi_ctrl;
 	unsigned char b1cmd;
 	struct sk_buff *skb;
 
@@ -548,12 +573,12 @@
 
 	case RECEIVE_INIT:
 
-		card->versionlen = b1_get_slice(card->port, card->versionbuf);
-		b1_parse_version(card);
+		cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf);
+		b1_parse_version(cinfo);
 		printk(KERN_INFO "%s: %s-card (%s) now active\n",
 		       card->name,
-		       card->version[VER_CARDTYPE],
-		       card->version[VER_DRIVER]);
+		       cinfo->version[VER_CARDTYPE],
+		       cinfo->version[VER_DRIVER]);
 		ctrl->ready(ctrl);
 		break;
 
@@ -593,7 +618,8 @@
 int b1ctl_read_proc(char *page, char **start, off_t off,
         		int count, int *eof, struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	__u8 flag;
 	int len = 0;
 	char *s;
@@ -615,11 +641,11 @@
 	len += sprintf(page+len, "%-16s %s\n", "type", s);
 	if (card->cardtype == avm_t1isa)
 	   len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr);
-	if ((s = card->version[VER_DRIVER]) != 0)
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
 	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
-	if ((s = card->version[VER_CARDTYPE]) != 0)
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
 	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
-	if ((s = card->version[VER_SERIAL]) != 0)
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
 	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
 
 	if (card->cardtype != avm_m1) {
@@ -647,7 +673,7 @@
 			(flag & 0x04) ? " leased line with D-channel" : ""
 			);
 	}
-	len += sprintf(page+len, "%-16s %s\n", "cardname", card->cardname);
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
 
 	if (off+count >= len)
 	   *eof = 1;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/b1isa.c linux.14p6/drivers/isdn/avmb1/b1isa.c
--- linux.vanilla/drivers/isdn/avmb1/b1isa.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/b1isa.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,18 @@
 /*
- * $Id: b1isa.c,v 1.4 1999/08/22 20:26:24 calle Exp $
+ * $Id: b1isa.c,v 1.5 1999/11/05 16:38:01 calle Exp $
  * 
  * Module for AVM B1 ISA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1isa.c,v $
+ * Revision 1.5  1999/11/05 16:38:01  calle
+ * Cleanups before kernel 2.4:
+ * - Changed all messages to use card->name or driver->name instead of
+ *   constant string.
+ * - Moved some data from struct avmcard into new struct avmctrl_info.
+ *   Changed all lowlevel capi driver to match the new structur.
+ *
  * Revision 1.4  1999/08/22 20:26:24  calle
  * backported changes from kernel 2.3.14:
  * - several #include "config.h" gone, others come.
@@ -54,7 +61,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.4 $";
+static char *revision = "$Revision: 1.5 $";
 
 /* ------------------------------------------------------------- */
 
@@ -91,7 +98,8 @@
 
 static void b1isa_remove_ctr(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 
 	b1_reset(port);
@@ -100,6 +108,7 @@
 	di->detach_ctr(ctrl);
 	free_irq(card->irq, card);
 	release_region(card->port, AVMB1_PORTLEN);
+	kfree(card->ctrlinfo);
 	kfree(card);
 
 	MOD_DEC_USE_COUNT;
@@ -109,6 +118,7 @@
 
 static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p)
 {
+	avmctrl_info *cinfo;
 	avmcard *card;
 	int retval;
 
@@ -119,6 +129,15 @@
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
+        cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC);
+	if (!cinfo) {
+		printk(KERN_WARNING "b1isa: no memory.\n");
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(cinfo, 0, sizeof(avmctrl_info));
+	card->ctrlinfo = cinfo;
+	cinfo->card = card;
 	sprintf(card->name, "b1isa-%x", p->port);
 	card->port = p->port;
 	card->irq = p->irq;
@@ -128,17 +147,20 @@
 		printk(KERN_WARNING
 		       "b1isa: ports 0x%03x-0x%03x in use.\n",
 		       card->port, card->port + AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 	if (b1_irq_table[card->irq & 0xf] == 0) {
 		printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EINVAL;
 	}
 	if (   card->port != 0x150 && card->port != 0x250
 	    && card->port != 0x300 && card->port != 0x340) {
 		printk(KERN_WARNING "b1isa: illegal port 0x%x.\n", card->port);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EINVAL;
 	}
@@ -146,6 +168,7 @@
 	if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
 		printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n",
 					card->port, retval);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EIO;
 	}
@@ -157,15 +180,17 @@
 	if (retval) {
 		printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq);
 		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 
-	card->ctrl = di->attach_ctr(driver, card->name, card);
-	if (!card->ctrl) {
+	cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
+	if (!cinfo->capi_ctrl) {
 		printk(KERN_ERR "b1isa: attach controller failed.\n");
 		free_irq(card->irq, card);
 		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
@@ -176,15 +201,17 @@
 
 static char *b1isa_procinfo(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
-	if (!card)
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
 		return "";
-	sprintf(card->infobuf, "%s %s 0x%x %d",
-		card->cardname[0] ? card->cardname : "-",
-		card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-",
-		card->port, card->irq
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0
 		);
-	return card->infobuf;
+	return cinfo->infobuf;
 }
 
 /* ------------------------------------------------------------- */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/b1pci.c linux.14p6/drivers/isdn/avmb1/b1pci.c
--- linux.vanilla/drivers/isdn/avmb1/b1pci.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/b1pci.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,21 @@
 /*
- * $Id: b1pci.c,v 1.16 1999/08/11 21:01:07 keil Exp $
+ * $Id: b1pci.c,v 1.18 1999/11/05 16:38:01 calle Exp $
  * 
  * Module for AVM B1 PCI-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pci.c,v $
+ * Revision 1.18  1999/11/05 16:38:01  calle
+ * Cleanups before kernel 2.4:
+ * - Changed all messages to use card->name or driver->name instead of
+ *   constant string.
+ * - Moved some data from struct avmcard into new struct avmctrl_info.
+ *   Changed all lowlevel capi driver to match the new structur.
+ *
+ * Revision 1.17  1999/10/05 06:50:07  calle
+ * Forgot SA_SHIRQ as argument to request_irq.
+ *
  * Revision 1.16  1999/08/11 21:01:07  keil
  * new PCI codefix
  *
@@ -56,7 +66,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.16 $";
+static char *revision = "$Revision: 1.18 $";
 
 /* ------------------------------------------------------------- */
 
@@ -85,11 +95,11 @@
 	card = (avmcard *) devptr;
 
 	if (!card) {
-		printk(KERN_WARNING "b1_interrupt: wrong device\n");
+		printk(KERN_WARNING "b1pci: interrupt: wrong device\n");
 		return;
 	}
 	if (card->interrupt) {
-		printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name);
+		printk(KERN_ERR "%s: reentering interrupt hander.\n", card->name);
 		return;
 	}
 
@@ -103,7 +113,8 @@
 
 static void b1pci_remove_ctr(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 
 	b1_reset(port);
@@ -113,6 +124,7 @@
 	free_irq(card->irq, card);
 	release_region(card->port, AVMB1_PORTLEN);
 	ctrl->driverdata = 0;
+	kfree(card->ctrlinfo);
 	kfree(card);
 
 	MOD_DEC_USE_COUNT;
@@ -122,15 +134,17 @@
 
 static char *b1pci_procinfo(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
-	if (!card)
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
 		return "";
-	sprintf(card->infobuf, "%s %s 0x%x %d",
-		card->cardname[0] ? card->cardname : "-",
-		card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-",
-		card->port, card->irq
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0
 		);
-	return card->infobuf;
+	return cinfo->infobuf;
 }
 
 /* ------------------------------------------------------------- */
@@ -138,15 +152,25 @@
 static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p)
 {
 	avmcard *card;
+	avmctrl_info *cinfo;
 	int retval;
 
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
-		printk(KERN_WARNING "b1pci: no memory.\n");
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
+        cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC);
+	if (!cinfo) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(cinfo, 0, sizeof(avmctrl_info));
+	card->ctrlinfo = cinfo;
+	cinfo->card = card;
 	sprintf(card->name, "b1pci-%x", p->port);
 	card->port = p->port;
 	card->irq = p->irq;
@@ -154,15 +178,17 @@
 
 	if (check_region(card->port, AVMB1_PORTLEN)) {
 		printk(KERN_WARNING
-		       "b1pci: ports 0x%03x-0x%03x in use.\n",
-		       card->port, card->port + AVMB1_PORTLEN);
+		       "%s: ports 0x%03x-0x%03x in use.\n",
+		       driver->name, card->port, card->port + AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 	b1_reset(card->port);
 	if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
-		printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
-					card->port, retval);
+		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+					driver->name, card->port, retval);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EIO;
 	}
@@ -170,19 +196,23 @@
 
 	request_region(p->port, AVMB1_PORTLEN, card->name);
 
-	retval = request_irq(card->irq, b1pci_interrupt, 0, card->name, card);
+	retval = request_irq(card->irq, b1pci_interrupt, SA_SHIRQ, card->name, card);
 	if (retval) {
-		printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq);
+		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+				driver->name, card->irq);
 		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 
-	card->ctrl = di->attach_ctr(driver, card->name, card);
-	if (!card->ctrl) {
-		printk(KERN_ERR "b1pci: attach controller failed.\n");
+	cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
+	if (!cinfo->capi_ctrl) {
+		printk(KERN_ERR "%s: attach controller failed.\n",
+				driver->name);
 		free_irq(card->irq, card);
 		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
@@ -276,7 +306,7 @@
 	printk(KERN_ERR "%s: NO B1-PCI card detected\n", driver->name);
 	return -ESRCH;
 #else
-	printk(KERN_ERR "b1pci: kernel not compiled with PCI.\n");
+	printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name);
 	return -EIO;
 #endif
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/b1pcmcia.c linux.14p6/drivers/isdn/avmb1/b1pcmcia.c
--- linux.vanilla/drivers/isdn/avmb1/b1pcmcia.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/b1pcmcia.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,18 @@
 /*
- * $Id: b1pcmcia.c,v 1.4 1999/08/22 20:26:26 calle Exp $
+ * $Id: b1pcmcia.c,v 1.5 1999/11/05 16:38:01 calle Exp $
  * 
  * Module for AVM B1/M1/M2 PCMCIA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pcmcia.c,v $
+ * Revision 1.5  1999/11/05 16:38:01  calle
+ * Cleanups before kernel 2.4:
+ * - Changed all messages to use card->name or driver->name instead of
+ *   constant string.
+ * - Moved some data from struct avmcard into new struct avmctrl_info.
+ *   Changed all lowlevel capi driver to match the new structur.
+ *
  * Revision 1.4  1999/08/22 20:26:26  calle
  * backported changes from kernel 2.3.14:
  * - several #include "config.h" gone, others come.
@@ -55,7 +62,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.4 $";
+static char *revision = "$Revision: 1.5 $";
 
 /* ------------------------------------------------------------- */
 
@@ -74,11 +81,12 @@
 	card = (avmcard *) devptr;
 
 	if (!card) {
-		printk(KERN_WARNING "b1_interrupt: wrong device\n");
+		printk(KERN_WARNING "b1pcmcia: interrupt: wrong device\n");
 		return;
 	}
 	if (card->interrupt) {
-		printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name);
+		printk(KERN_ERR "%s: reentering interrupt hander.\n",
+			card->name);
 		return;
 	}
 
@@ -92,7 +100,8 @@
 
 static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 
 	b1_reset(port);
@@ -115,16 +124,26 @@
 				unsigned irq,
 				enum avmcardtype cardtype)
 {
+	avmctrl_info *cinfo;
 	avmcard *card;
 	int retval;
 
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
-		printk(KERN_WARNING "b1pcmcia: no memory.\n");
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
+        cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC);
+	if (!cinfo) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(cinfo, 0, sizeof(avmctrl_info));
+	card->ctrlinfo = cinfo;
+	cinfo->card = card;
 	switch (cardtype) {
 		case avm_m1: sprintf(card->name, "m1-%x", port); break;
 		case avm_m2: sprintf(card->name, "m2-%x", port); break;
@@ -136,8 +155,9 @@
 
 	b1_reset(card->port);
 	if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
-		printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n",
-					card->port, retval);
+		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+					driver->name, card->port, retval);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EIO;
 	}
@@ -145,36 +165,42 @@
 
 	retval = request_irq(card->irq, b1pcmcia_interrupt, 0, card->name, card);
 	if (retval) {
-		printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n", card->irq);
+		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+				driver->name, card->irq);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 
-	card->ctrl = di->attach_ctr(driver, card->name, card);
-	if (!card->ctrl) {
-		printk(KERN_ERR "b1pcmcia: attach controller failed.\n");
+	cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
+	if (!cinfo->capi_ctrl) {
+		printk(KERN_ERR "%s: attach controller failed.\n",
+				driver->name);
 		free_irq(card->irq, card);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 
 	MOD_INC_USE_COUNT;
-	return card->ctrl->cnr;
+	return cinfo->capi_ctrl->cnr;
 }
 
 /* ------------------------------------------------------------- */
 
 static char *b1pcmcia_procinfo(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
-	if (!card)
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
 		return "";
-	sprintf(card->infobuf, "%s %s 0x%x %d",
-		card->cardname[0] ? card->cardname : "-",
-		card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-",
-		card->port, card->irq
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0
 		);
-	return card->infobuf;
+	return cinfo->infobuf;
 }
 
 /* ------------------------------------------------------------- */
@@ -219,7 +245,7 @@
 	avmcard *card;
 
 	for (ctrl = b1pcmcia_driver.controller; ctrl; ctrl = ctrl->next) {
-		card = (avmcard *)(ctrl->driverdata);
+		card = ((avmctrl_info *)(ctrl->driverdata))->card;
 		if (card->port == port && card->irq == irq) {
 			b1pcmcia_remove_ctr(ctrl);
 			return 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/capi.c linux.14p6/drivers/isdn/avmb1/capi.c
--- linux.vanilla/drivers/isdn/avmb1/capi.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/capi.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,14 @@
 /*
- * $Id: capi.c,v 1.21 1999/09/10 17:24:18 calle Exp $
+ * $Id: capi.c,v 1.22 1999/11/13 21:27:16 keil Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capi.c,v $
+ * Revision 1.22  1999/11/13 21:27:16  keil
+ * remove KERNELVERSION
+ *
  * Revision 1.21  1999/09/10 17:24:18  calle
  * Changes for proposed standard for CAPI2.0:
  * - AK148 "Linux Exention"
@@ -282,20 +285,13 @@
 capi_poll(struct file *file, poll_table * wait)
 {
 	unsigned int mask = 0;
-#if (LINUX_VERSION_CODE >= 0x02012d)
 	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
-#else
-	unsigned int minor = MINOR(file->f_inode->i_rdev);
-#endif
 	struct capidev *cdev;
 
 	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
 		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))
@@ -523,9 +519,7 @@
 	capi_ioctl,
 	NULL,			/* capi_mmap */
 	capi_open,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118)
         NULL,                   /* capi_flush */
-#endif
 	capi_release,
 	NULL,			/* capi_fsync */
 	NULL,			/* capi_fasync */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/capidrv.c linux.14p6/drivers/isdn/avmb1/capidrv.c
--- linux.vanilla/drivers/isdn/avmb1/capidrv.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/capidrv.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,14 @@
 /*
- * $Id: capidrv.c,v 1.27 1999/09/16 15:13:04 calle Exp $
+ * $Id: capidrv.c,v 1.28 1999/11/05 16:22:37 calle Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * Revision 1.28  1999/11/05 16:22:37  calle
+ * Bugfix: Missing break in switch on ISDN_CMD_HANGUP.
+ *
  * Revision 1.27  1999/09/16 15:13:04  calle
  * forgot to change paramter type of contr for lower_callback ...
  *
@@ -165,7 +168,7 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.27 $";
+static char *revision = "$Revision: 1.28 $";
 int debugmode = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
@@ -716,8 +719,6 @@
   /* 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},
   /* 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},
@@ -1857,14 +1858,19 @@
 			);
 			ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ);
 			send_message(card, &cmdcmsg);
+			return 0;
 		} else if (bchan->plcip) {
-			bchan->disconnecting = 1;
 			if (bchan->plcip->state == ST_PLCI_INCOMING) {
-				/* just ignore, we a called from isdn_status_callback(),
-				 * which will return 0 or 2, this is handled by the
-				 * CONNECT_IND handler
+				/*
+				 * just ignore, we a called from
+				 * isdn_status_callback(),
+				 * which will return 0 or 2, this is handled
+				 * by the CONNECT_IND handler
 				 */
-			} else {
+				bchan->disconnecting = 1;
+				return 0;
+			} else if (bchan->plcip->plci) {
+				bchan->disconnecting = 1;
 				capi_fill_DISCONNECT_REQ(&cmdcmsg,
 							 global.appid,
 							 card->msgid++,
@@ -1876,8 +1882,18 @@
 				);
 				plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ);
 				send_message(card, &cmdcmsg);
+				return 0;
+			} else {
+				printk(KERN_ERR "capidrv-%d: chan %ld disconnect request while waiting for CONNECT_CONF\n",
+				       card->contrnr,
+				       c->arg);
+				return -EINVAL;
 			}
 		}
+		printk(KERN_ERR "capidrv-%d: chan %ld disconnect request on free channel\n",
+				       card->contrnr,
+				       c->arg);
+		return -EINVAL;
 /* ready */
 
 	case ISDN_CMD_SETL2:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/kcapi.c linux.14p6/drivers/isdn/avmb1/kcapi.c
--- linux.vanilla/drivers/isdn/avmb1/kcapi.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/kcapi.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,18 @@
 /*
- * $Id: kcapi.c,v 1.8 1999/09/10 17:24:18 calle Exp $
+ * $Id: kcapi.c,v 1.10 1999/10/26 15:30:32 calle Exp $
  * 
  * Kernel CAPI 2.0 Module
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: kcapi.c,v $
+ * Revision 1.10  1999/10/26 15:30:32  calle
+ * Generate error message if user want to add card, but driver module is
+ * not loaded.
+ *
+ * Revision 1.9  1999/10/11 22:04:12  keil
+ * COMPAT_NEED_UACCESS (no include in isdn_compat.h)
+ *
  * Revision 1.8  1999/09/10 17:24:18  calle
  * Changes for proposed standard for CAPI2.0:
  * - AK148 "Linux Exention"
@@ -72,7 +79,7 @@
 #include <linux/b1lli.h>
 #endif
 
-static char *revision = "$Revision: 1.8 $";
+static char *revision = "$Revision: 1.10 $";
 
 /* ------------------------------------------------------------- */
 
@@ -1226,7 +1233,12 @@
 			case AVM_CARDTYPE_T1: driver = t1isa_driver; break;
 			default: driver = 0;
 		}
-		if (!driver || !driver->add_card) {
+		if (!driver) {
+			printk(KERN_ERR "kcapi: driver not loaded.\n");
+			return -EIO;
+		}
+		if (!driver->add_card) {
+			printk(KERN_ERR "kcapi: driver has no add card function.\n");
 			return -EIO;
 		}
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/t1isa.c linux.14p6/drivers/isdn/avmb1/t1isa.c
--- linux.vanilla/drivers/isdn/avmb1/t1isa.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/avmb1/t1isa.c	Tue Nov 16 15:10:47 1999
@@ -1,11 +1,18 @@
 /*
- * $Id: t1isa.c,v 1.7 1999/09/15 08:16:03 calle Exp $
+ * $Id: t1isa.c,v 1.8 1999/11/05 16:38:01 calle Exp $
  * 
  * Module for AVM T1 HEMA-card.
  * 
  * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: t1isa.c,v $
+ * Revision 1.8  1999/11/05 16:38:01  calle
+ * Cleanups before kernel 2.4:
+ * - Changed all messages to use card->name or driver->name instead of
+ *   constant string.
+ * - Moved some data from struct avmcard into new struct avmctrl_info.
+ *   Changed all lowlevel capi driver to match the new structur.
+ *
  * Revision 1.7  1999/09/15 08:16:03  calle
  * Implementation of 64Bit extention complete.
  *
@@ -66,7 +73,7 @@
 #include "capilli.h"
 #include "avmcard.h"
 
-static char *revision = "$Revision: 1.7 $";
+static char *revision = "$Revision: 1.8 $";
 
 /* ------------------------------------------------------------- */
 
@@ -169,7 +176,8 @@
 
 static void t1_handle_interrupt(avmcard * card)
 {
-	struct capi_ctr *ctrl = card->ctrl;
+	avmctrl_info *cinfo = &card->ctrlinfo[0];
+	struct capi_ctr *ctrl = cinfo->capi_ctrl;
 	unsigned char b1cmd;
 	struct sk_buff *skb;
 
@@ -197,7 +205,8 @@
 				CAPIMSG_SETLEN(card->msgbuf, 30);
 			}
 			if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
-				printk(KERN_ERR "t1isa: incoming packet dropped\n");
+				printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
 			} else {
 				memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
 				memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
@@ -210,7 +219,8 @@
 			ApplId = (unsigned) b1_get_word(card->port);
 			MsgLen = t1_get_slice(card->port, card->msgbuf);
 			if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
-				printk(KERN_ERR "t1isa: incoming packet dropped\n");
+				printk(KERN_ERR "%s: incoming packet dropped\n",
+						card->name);
 			} else {
 				memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
 				ctrl->handle_capimsg(ctrl, ApplId, skb);
@@ -248,12 +258,12 @@
 
 		case RECEIVE_INIT:
 
-			card->versionlen = t1_get_slice(card->port, card->versionbuf);
-			b1_parse_version(card);
+			cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf);
+			b1_parse_version(cinfo);
 			printk(KERN_INFO "%s: %s-card (%s) now active\n",
 			       card->name,
-			       card->version[VER_CARDTYPE],
-			       card->version[VER_DRIVER]);
+			       cinfo->version[VER_CARDTYPE],
+			       cinfo->version[VER_DRIVER]);
 			ctrl->ready(ctrl);
 			break;
 
@@ -299,11 +309,12 @@
 	card = (avmcard *) devptr;
 
 	if (!card) {
-		printk(KERN_WARNING "t1_interrupt: wrong device\n");
+		printk(KERN_WARNING "t1isa: interrupt: wrong device\n");
 		return;
 	}
 	if (card->interrupt) {
-		printk(KERN_ERR "t1_interrupt: reentering interrupt hander (%s)\n", card->name);
+		printk(KERN_ERR "%s: reentering interrupt hander.\n",
+				 card->name);
 		return;
 	}
 
@@ -317,7 +328,8 @@
 
 static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 	unsigned long flags;
 	int retval;
@@ -325,7 +337,7 @@
 	t1_disable_irq(port);
 	b1_reset(port);
 
-	if ((retval = b1_load_t4file(port, &data->firmware))) {
+	if ((retval = b1_load_t4file(card, &data->firmware))) {
 		b1_reset(port);
 		printk(KERN_ERR "%s: failed to load t4file!!\n",
 					card->name);
@@ -333,7 +345,7 @@
 	}
 
 	if (data->configuration.len > 0 && data->configuration.data) {
-		if ((retval = b1_load_config(port, &data->configuration))) {
+		if ((retval = b1_load_config(card, &data->configuration))) {
 			b1_reset(port);
 			printk(KERN_ERR "%s: failed to load config!!\n",
 					card->name);
@@ -341,7 +353,7 @@
 		}
 	}
 
-	if (!b1_loaded(port)) {
+	if (!b1_loaded(card)) {
 		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
 		return -EIO;
 	}
@@ -360,20 +372,22 @@
 
 void t1isa_reset_ctr(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 
 	t1_disable_irq(port);
 	b1_reset(port);
 	b1_reset(port);
 
-	memset(card->version, 0, sizeof(card->version));
+	memset(cinfo->version, 0, sizeof(cinfo->version));
 	ctrl->reseted(ctrl);
 }
 
 static void t1isa_remove_ctr(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 
 	t1_disable_irq(port);
@@ -384,6 +398,7 @@
 	di->detach_ctr(ctrl);
 	free_irq(card->irq, card);
 	release_region(card->port, AVMB1_PORTLEN);
+	kfree(card->ctrlinfo);
 	kfree(card);
 
 	MOD_DEC_USE_COUNT;
@@ -394,16 +409,26 @@
 static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p)
 {
 	struct capi_ctr *ctrl;
+	avmctrl_info *cinfo;
 	avmcard *card;
 	int retval;
 
 	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
 
 	if (!card) {
-		printk(KERN_WARNING "t1isa: no memory.\n");
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		return -ENOMEM;
 	}
 	memset(card, 0, sizeof(avmcard));
+        cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC);
+	if (!cinfo) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(cinfo, 0, sizeof(avmctrl_info));
+	card->ctrlinfo = cinfo;
+	cinfo->card = card;
 	sprintf(card->name, "t1isa-%x", p->port);
 	card->port = p->port;
 	card->irq = p->irq;
@@ -411,33 +436,42 @@
 	card->cardnr = p->cardnr;
 
 	if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) {
-		printk(KERN_WARNING "t1isa: illegal port 0x%x.\n", card->port);
+		printk(KERN_WARNING "%s: illegal port 0x%x.\n",
+				driver->name, card->port);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EINVAL;
         }
 
 	if (check_region(card->port, AVMB1_PORTLEN)) {
 		printk(KERN_WARNING
-		       "t1isa: ports 0x%03x-0x%03x in use.\n",
-		       card->port, card->port + AVMB1_PORTLEN);
+		       "%s: ports 0x%03x-0x%03x in use.\n",
+		       driver->name, card->port, card->port + AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 	if (hema_irq_table[card->irq & 0xf] == 0) {
-		printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq);
+		printk(KERN_WARNING "%s: irq %d not valid.\n",
+				driver->name, card->irq);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EINVAL;
 	}
 	for (ctrl = driver->controller; ctrl; ctrl = ctrl->next) {
-		if (((avmcard *)(ctrl->driverdata))->cardnr == card->cardnr) {
-			printk(KERN_WARNING "t1isa: card with number %d already installed.\n", card->cardnr);
+	        avmcard *cardp = ((avmctrl_info *)(ctrl->driverdata))->card;
+		if (cardp->cardnr == card->cardnr) {
+			printk(KERN_WARNING "%s: card with number %d already installed at 0x%x.\n",
+					driver->name, card->cardnr, cardp->port);
+	                kfree(card->ctrlinfo);
 			kfree(card);
 			return -EBUSY;
 		}
 	}
         if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) {
-		printk(KERN_NOTICE "t1isa: NO card at 0x%x (%d)\n",
-					card->port, retval);
+		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+					driver->name, card->port, retval);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EIO;
 	}
@@ -448,17 +482,21 @@
 
 	retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card);
 	if (retval) {
-		printk(KERN_ERR "t1isa: unable to get IRQ %d.\n", card->irq);
+		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+				driver->name, card->irq);
 		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
 
-	card->ctrl = di->attach_ctr(driver, card->name, card);
-	if (!card->ctrl) {
-		printk(KERN_ERR "t1isa: attach controller failed.\n");
+	cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
+	if (!cinfo->capi_ctrl) {
+		printk(KERN_ERR "%s: attach controller failed.\n",
+				driver->name);
 		free_irq(card->irq, card);
 		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
 		kfree(card);
 		return -EBUSY;
 	}
@@ -469,7 +507,8 @@
 
 static void t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 	unsigned long flags;
 	__u16 len = CAPIMSG_LEN(skb->data);
@@ -494,15 +533,18 @@
 
 static char *t1isa_procinfo(struct capi_ctr *ctrl)
 {
-	avmcard *card = (avmcard *)(ctrl->driverdata);
-	if (!card)
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
 		return "";
-	sprintf(card->infobuf, "%s %s 0x%x %d %d",
-		card->cardname[0] ? card->cardname : "-",
-		card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-",
-		card->port, card->irq, card->cardnr
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d %d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->cardnr : 0
 		);
-	return card->infobuf;
+	return cinfo->infobuf;
 }
 
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/t1pci.c linux.14p6/drivers/isdn/avmb1/t1pci.c
--- linux.vanilla/drivers/isdn/avmb1/t1pci.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/isdn/avmb1/t1pci.c	Tue Nov 16 15:10:47 1999
@@ -0,0 +1,1166 @@
+/*
+ * $Id: t1pci.c,v 1.3 1999/11/13 21:27:16 keil Exp $
+ * 
+ * Module for AVM T1 PCI-card.
+ * 
+ * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: t1pci.c,v $
+ * Revision 1.3  1999/11/13 21:27:16  keil
+ * remove KERNELVERSION
+ *
+ * Revision 1.2  1999/11/05 16:38:02  calle
+ * Cleanups before kernel 2.4:
+ * - Changed all messages to use card->name or driver->name instead of
+ *   constant string.
+ * - Moved some data from struct avmcard into new struct avmctrl_info.
+ *   Changed all lowlevel capi driver to match the new structur.
+ *
+ * Revision 1.1  1999/10/26 15:31:42  calle
+ * Added driver for T1-PCI card.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <asm/io.h>
+#include "capicmd.h"
+#include "capiutil.h"
+#include "capilli.h"
+#include "avmcard.h"
+
+static char *revision = "$Revision: 1.3 $";
+
+#undef CONFIG_T1PCI_DEBUG
+#undef CONFIG_T1PCI_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_AVM
+#define PCI_VENDOR_ID_AVM	0x1244
+#endif
+
+#ifndef PCI_DEVICE_ID_AVM_T1
+#define PCI_DEVICE_ID_AVM_T1	0x1200
+#endif
+
+/* ------------------------------------------------------------- */
+
+int suppress_pollack = 0;
+
+MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
+
+MODULE_PARM(suppress_pollack, "0-1i");
+
+
+/* ------------------------------------------------------------- */
+
+static struct capi_driver_interface *di;
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+/* S5933 */
+
+#define	AMCC_RXPTR	0x24
+#define	AMCC_RXLEN	0x28
+#define	AMCC_TXPTR	0x2c
+#define	AMCC_TXLEN	0x30
+
+#define	AMCC_INTCSR	0x38
+#	define EN_READ_TC_INT		0x00008000L
+#	define EN_WRITE_TC_INT		0x00004000L
+#	define EN_TX_TC_INT		EN_READ_TC_INT
+#	define EN_RX_TC_INT		EN_WRITE_TC_INT
+#	define AVM_FLAG			0x30000000L
+
+#	define ANY_S5933_INT		0x00800000L
+#	define	READ_TC_INT		0x00080000L
+#	define WRITE_TC_INT		0x00040000L
+#	define	TX_TC_INT		READ_TC_INT
+#	define	RX_TC_INT		WRITE_TC_INT
+#	define MASTER_ABORT_INT		0x00100000L
+#	define TARGET_ABORT_INT		0x00200000L
+#	define BUS_MASTER_INT		0x00200000L
+#	define ALL_INT			0x000C0000L
+
+#define	AMCC_MCSR	0x3c
+#	define A2P_HI_PRIORITY		0x00000100L
+#	define EN_A2P_TRANSFERS		0x00000400L
+#	define P2A_HI_PRIORITY		0x00001000L
+#	define EN_P2A_TRANSFERS		0x00004000L
+#	define RESET_A2P_FLAGS		0x04000000L
+#	define RESET_P2A_FLAGS		0x02000000L
+
+/* ------------------------------------------------------------- */
+
+#define t1outmeml(addr, value)	writel(value, addr)
+#define t1inmeml(addr)	readl(addr)
+#define t1outmemw(addr, value)	writew(value, addr)
+#define t1inmemw(addr)	readw(addr)
+#define t1outmemb(addr, value)	writeb(value, addr)
+#define t1inmemb(addr)	readb(addr)
+
+/* ------------------------------------------------------------- */
+
+static inline int t1pci_tx_empty(unsigned int port)
+{
+	return inb(port + 0x03) & 0x1;
+}
+
+static inline int t1pci_rx_full(unsigned int port)
+{
+	return inb(port + 0x02) & 0x1;
+}
+
+static int t1pci_tolink(avmcard *card, void *buf, unsigned int len)
+{
+	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	unsigned char *s = (unsigned char *)buf;
+	while (len--) {
+		while (   !t1pci_tx_empty(card->port)
+		       && time_before(jiffies, stop));
+		if (!t1pci_tx_empty(card->port)) 
+			return -1;
+	        t1outp(card->port, 0x01, *s++);
+	}
+	return 0;
+}
+
+static int t1pci_fromlink(avmcard *card, void *buf, unsigned int len)
+{
+	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	unsigned char *s = (unsigned char *)buf;
+	while (len--) {
+		while (   !t1pci_rx_full(card->port)
+		       && time_before(jiffies, stop));
+		if (!t1pci_rx_full(card->port)) 
+			return -1;
+	        *s++ = t1inp(card->port, 0x00);
+	}
+	return 0;
+}
+
+static int WriteReg(avmcard *card, __u32 reg, __u8 val)
+{
+	__u8 cmd = 0x00;
+	if (   t1pci_tolink(card, &cmd, 1) == 0
+	    && t1pci_tolink(card, &reg, 4) == 0) {
+		__u32 tmp = val;
+		return t1pci_tolink(card, &tmp, 4);
+	}
+	return -1;
+}
+
+static __u8 ReadReg(avmcard *card, __u32 reg)
+{
+	__u8 cmd = 0x01;
+	if (   t1pci_tolink(card, &cmd, 1) == 0
+	    && t1pci_tolink(card, &reg, 4) == 0) {
+		__u32 tmp;
+		if (t1pci_fromlink(card, &tmp, 4) == 0)
+			return (__u8)tmp;
+	}
+	return 0xff;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, __u8 val)
+{
+	__u8 *s = *pp;
+	*s++ = val;
+	*pp = s;
+}
+
+static inline void _put_word(void **pp, __u32 val)
+{
+	__u8 *s = *pp;
+	*s++ = val & 0xff;
+	*s++ = (val >> 8) & 0xff;
+	*s++ = (val >> 16) & 0xff;
+	*s++ = (val >> 24) & 0xff;
+	*pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	_put_word(pp, i);
+	while (i-- > 0)
+		_put_byte(pp, *dp++);
+}
+
+static inline __u8 _get_byte(void **pp)
+{
+	__u8 *s = *pp;
+	__u8 val;
+	val = *s++;
+	*pp = s;
+	return val;
+}
+
+static inline __u32 _get_word(void **pp)
+{
+	__u8 *s = *pp;
+	__u32 val;
+	val = *s++;
+	val |= (*s++ << 8);
+	val |= (*s++ << 16);
+	val |= (*s++ << 24);
+	*pp = s;
+	return val;
+}
+
+static inline __u32 _get_slice(void **pp, unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = _get_word(pp);
+	while (i-- > 0) *dp++ = _get_byte(pp);
+	return len;
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_reset(avmcard *card)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	card->csr = 0x0;
+	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
+	t1outmeml(card->mbase+AMCC_MCSR, 0);
+	t1outmeml(card->mbase+AMCC_RXLEN, 0);
+	t1outmeml(card->mbase+AMCC_TXLEN, 0);
+
+	t1outp(card->port, T1_RESETLINK, 0x00);
+	t1outp(card->port, 0x07, 0x00);
+
+	restore_flags(flags);
+
+	t1outmeml(card->mbase+AMCC_MCSR, 0);
+	udelay(10 * 1000);
+	t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
+	udelay(10 * 1000);
+	t1outmeml(card->mbase+AMCC_MCSR, 0);
+	udelay(42 * 1000);
+
+}
+
+/* ------------------------------------------------------------- */
+
+static int t1pci_detect(avmcard *card)
+{
+	t1outmeml(card->mbase+AMCC_MCSR, 0);
+	udelay(10 * 1000);
+	t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
+	udelay(10 * 1000);
+	t1outmeml(card->mbase+AMCC_MCSR, 0);
+	udelay(42 * 1000);
+
+	t1outmeml(card->mbase+AMCC_RXLEN, 0);
+	t1outmeml(card->mbase+AMCC_TXLEN, 0);
+	card->csr = 0x0;
+	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
+
+	if (t1inmeml(card->mbase+AMCC_MCSR) != 0x000000E6)
+		return 1;
+
+	t1outmeml(card->mbase+AMCC_RXPTR, 0xffffffff);
+	t1outmeml(card->mbase+AMCC_TXPTR, 0xffffffff);
+	if (   t1inmeml(card->mbase+AMCC_RXPTR) != 0xfffffffc
+	    || t1inmeml(card->mbase+AMCC_TXPTR) != 0xfffffffc)
+		return 2;
+
+	t1outmeml(card->mbase+AMCC_RXPTR, 0x0);
+	t1outmeml(card->mbase+AMCC_TXPTR, 0x0);
+	if (   t1inmeml(card->mbase+AMCC_RXPTR) != 0x0
+	    || t1inmeml(card->mbase+AMCC_TXPTR) != 0x0)
+		return 3;
+
+	t1outp(card->port, T1_RESETLINK, 0x00);
+	t1outp(card->port, 0x07, 0x00);
+	
+	t1outp(card->port, 0x02, 0x02);
+	t1outp(card->port, 0x03, 0x02);
+
+	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x02
+	    || t1inp(card->port, 0x3) != 0x03)
+		return 4;
+
+	t1outp(card->port, 0x02, 0x00);
+	t1outp(card->port, 0x03, 0x00);
+
+	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x00
+	    || t1inp(card->port, 0x3) != 0x01)
+		return 5;
+
+	/* Transputer test */
+	
+	if (   WriteReg(card, 0x80001000, 0x11) != 0
+	    || WriteReg(card, 0x80101000, 0x22) != 0
+	    || WriteReg(card, 0x80201000, 0x33) != 0
+	    || WriteReg(card, 0x80301000, 0x44) != 0)
+		return 6;
+
+	if (   ReadReg(card, 0x80001000) != 0x11
+	    || ReadReg(card, 0x80101000) != 0x22
+	    || ReadReg(card, 0x80201000) != 0x33
+	    || ReadReg(card, 0x80301000) != 0x44)
+		return 7;
+
+	if (   WriteReg(card, 0x80001000, 0x55) != 0
+	    || WriteReg(card, 0x80101000, 0x66) != 0
+	    || WriteReg(card, 0x80201000, 0x77) != 0
+	    || WriteReg(card, 0x80301000, 0x88) != 0)
+		return 8;
+
+	if (   ReadReg(card, 0x80001000) != 0x55
+	    || ReadReg(card, 0x80101000) != 0x66
+	    || ReadReg(card, 0x80201000) != 0x77
+	    || ReadReg(card, 0x80301000) != 0x88)
+		return 9;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_dispatch_tx(avmcard *card)
+{
+	avmcard_dmainfo *dma = card->dma;
+	unsigned long flags;
+	struct sk_buff *skb;
+	__u8 cmd, subcmd;
+	__u16 len;
+	__u32 txlen;
+	int inint;
+	void *p;
+	
+	save_flags(flags);
+	cli();
+
+	inint = card->interrupt;
+
+	if (card->csr & EN_TX_TC_INT) { /* tx busy */
+	        restore_flags(flags);
+		return;
+	}
+
+	skb = skb_dequeue(&dma->send_queue);
+	if (!skb) {
+#ifdef CONFIG_T1PCI_DEBUG
+		printk(KERN_DEBUG "tx(%d): underrun\n", inint);
+#endif
+	        restore_flags(flags);
+		return;
+	}
+
+	len = CAPIMSG_LEN(skb->data);
+
+	if (len) {
+		cmd = CAPIMSG_COMMAND(skb->data);
+		subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+		p = dma->sendbuf;
+
+		if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+			__u16 dlen = CAPIMSG_DATALEN(skb->data);
+			_put_byte(&p, SEND_DATA_B3_REQ);
+			_put_slice(&p, skb->data, len);
+			_put_slice(&p, skb->data + len, dlen);
+		} else {
+			_put_byte(&p, SEND_MESSAGE);
+			_put_slice(&p, skb->data, len);
+		}
+		txlen = (__u8 *)p - (__u8 *)dma->sendbuf;
+#ifdef CONFIG_T1PCI_DEBUG
+		printk(KERN_DEBUG "tx(%d): put msg len=%d\n",
+				inint, txlen);
+#endif
+	} else {
+		txlen = skb->len-2;
+#ifdef CONFIG_T1PCI_POLLDEBUG
+		if (skb->data[2] == SEND_POLLACK)
+			printk(KERN_INFO "%s: ack to t1\n", card->name);
+#endif
+#ifdef CONFIG_T1PCI_DEBUG
+		printk(KERN_DEBUG "tx(%d): put 0x%x len=%d\n",
+				inint, skb->data[2], txlen);
+#endif
+		memcpy(dma->sendbuf, skb->data+2, skb->len-2);
+	}
+	txlen = (txlen + 3) & ~3;
+
+	t1outmeml(card->mbase+AMCC_TXPTR, virt_to_phys(dma->sendbuf));
+	t1outmeml(card->mbase+AMCC_TXLEN, txlen);
+
+	card->csr |= EN_TX_TC_INT;
+
+	if (!inint)
+		t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
+
+	restore_flags(flags);
+	dev_kfree_skb(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(3, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_POLLACK);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	t1pci_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_handle_rx(avmcard *card)
+{
+	avmctrl_info *cinfo = &card->ctrlinfo[0];
+	avmcard_dmainfo *dma = card->dma;
+	struct capi_ctr *ctrl = cinfo->capi_ctrl;
+	struct sk_buff *skb;
+	void *p = dma->recvbuf+4;
+	__u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+	__u8 b1cmd =  _get_byte(&p);
+
+#ifdef CONFIG_T1PCI_DEBUG
+	printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
+#endif
+	
+	switch (b1cmd) {
+	case RECEIVE_DATA_B3_IND:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		DataB3Len = _get_slice(&p, card->databuf);
+
+		if (MsgLen < 30) { /* not CAPI 64Bit */
+			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+			MsgLen = 30;
+			CAPIMSG_SETLEN(card->msgbuf, 30);
+		}
+		if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+			ctrl->handle_capimsg(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_MESSAGE:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			ctrl->handle_capimsg(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_NEW_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+		WindowSize = _get_word(&p);
+
+		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
+
+		break;
+
+	case RECEIVE_FREE_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+
+		if (NCCI != 0xffffffff)
+			ctrl->free_ncci(ctrl, ApplId, NCCI);
+		else ctrl->appl_released(ctrl, ApplId);
+		break;
+
+	case RECEIVE_START:
+#ifdef CONFIG_T1PCI_POLLDEBUG
+		printk(KERN_INFO "%s: poll from t1\n", card->name);
+#endif
+		if (!suppress_pollack)
+			queue_pollack(card);
+		ctrl->resume_output(ctrl);
+		break;
+
+	case RECEIVE_STOP:
+		ctrl->suspend_output(ctrl);
+		break;
+
+	case RECEIVE_INIT:
+
+		cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+		b1_parse_version(cinfo);
+		printk(KERN_INFO "%s: %s-card (%s) now active\n",
+		       card->name,
+		       cinfo->version[VER_CARDTYPE],
+		       cinfo->version[VER_DRIVER]);
+		ctrl->ready(ctrl);
+		break;
+
+	case RECEIVE_TASK_READY:
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen--] = 0;
+		while (    MsgLen >= 0
+		       && (   card->msgbuf[MsgLen] == '\n'
+			   || card->msgbuf[MsgLen] == '\r'))
+			card->msgbuf[MsgLen--] = 0;
+		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+				card->name, ApplId, card->msgbuf);
+		break;
+
+	case RECEIVE_DEBUGMSG:
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen--] = 0;
+		while (    MsgLen >= 0
+		       && (   card->msgbuf[MsgLen] == '\n'
+			   || card->msgbuf[MsgLen] == '\r'))
+			card->msgbuf[MsgLen--] = 0;
+		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+		break;
+
+	default:
+		printk(KERN_ERR "%s: t1pci_interrupt: 0x%x ???\n",
+				card->name, b1cmd);
+		return;
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_handle_interrupt(avmcard *card)
+{
+	__u32 status = t1inmeml(card->mbase+AMCC_INTCSR);
+	__u32 newcsr;
+
+	if ((status & ANY_S5933_INT) == 0) 
+		return;
+
+        newcsr = card->csr | (status & ALL_INT);
+	if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
+	if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
+	t1outmeml(card->mbase+AMCC_INTCSR, newcsr);
+
+	if ((status & RX_TC_INT) != 0) {
+		__u8 *recvbuf = card->dma->recvbuf;
+		__u32 rxlen;
+	   	if (card->dma->recvlen == 0) {
+			card->dma->recvlen = *((__u32 *)recvbuf);
+			rxlen = (card->dma->recvlen + 3) & ~3;
+			t1outmeml(card->mbase+AMCC_RXPTR,
+					virt_to_phys(recvbuf+4));
+			t1outmeml(card->mbase+AMCC_RXLEN, rxlen);
+		} else {
+			t1pci_handle_rx(card);
+	   		card->dma->recvlen = 0;
+			t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(recvbuf));
+			t1outmeml(card->mbase+AMCC_RXLEN, 4);
+		}
+	}
+
+	if ((status & TX_TC_INT) != 0) {
+		card->csr &= ~EN_TX_TC_INT;
+	        t1pci_dispatch_tx(card);
+	} else if (card->csr & EN_TX_TC_INT) {
+		if (t1inmeml(card->mbase+AMCC_TXLEN) == 0) {
+			card->csr &= ~EN_TX_TC_INT;
+			t1pci_dispatch_tx(card);
+		}
+	}
+	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
+}
+
+static void t1pci_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmcard *card;
+
+	card = (avmcard *) devptr;
+
+	if (!card) {
+		printk(KERN_WARNING "t1pci: interrupt: wrong device\n");
+		return;
+	}
+	if (card->interrupt) {
+		printk(KERN_ERR "%s: reentering interrupt hander\n", card->name);
+		return;
+	}
+
+	card->interrupt = 1;
+
+	t1pci_handle_interrupt(card);
+
+	card->interrupt = 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int t1pci_loaded(avmcard *card)
+{
+	unsigned long stop;
+	unsigned char ans;
+	unsigned long tout = 2;
+	unsigned int base = card->port;
+
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_tx_empty(base))
+			break;
+	}
+	if (!b1_tx_empty(base)) {
+		printk(KERN_ERR "%s: t1pci_loaded: tx err, corrupted t4 file ?\n",
+				card->name);
+		return 0;
+	}
+	b1_put_byte(base, SEND_POLLACK);
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_rx_full(base)) {
+			if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
+				return 1;
+			}
+			printk(KERN_ERR "%s: t1pci_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
+			return 0;
+		}
+	}
+	printk(KERN_ERR "%s: t1pci_loaded: firmware not running\n", card->name);
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_send_init(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(15, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_INIT);
+	_put_word(&p, AVM_NAPPS);
+	_put_word(&p, AVM_NCCI_PER_CHANNEL*30);
+	_put_word(&p, card->cardnr - 1);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	t1pci_dispatch_tx(card);
+}
+
+static int t1pci_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned long flags;
+	int retval;
+
+	t1pci_reset(card);
+
+	if ((retval = b1_load_t4file(card, &data->firmware))) {
+		t1pci_reset(card);
+		printk(KERN_ERR "%s: failed to load t4file!!\n",
+					card->name);
+		return retval;
+	}
+
+	if (data->configuration.len > 0 && data->configuration.data) {
+		if ((retval = b1_load_config(card, &data->configuration))) {
+			t1pci_reset(card);
+			printk(KERN_ERR "%s: failed to load config!!\n",
+					card->name);
+			return retval;
+		}
+	}
+
+	if (!t1pci_loaded(card)) {
+		t1pci_reset(card);
+		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+		return -EIO;
+	}
+
+	save_flags(flags);
+	cli();
+
+	card->csr = AVM_FLAG;
+	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
+	t1outmeml(card->mbase+AMCC_MCSR,
+		EN_A2P_TRANSFERS|EN_P2A_TRANSFERS
+		|A2P_HI_PRIORITY|P2A_HI_PRIORITY
+		|RESET_A2P_FLAGS|RESET_P2A_FLAGS);
+	t1outp(card->port, 0x07, 0x30);
+	t1outp(card->port, 0x10, 0xF0);
+
+	card->dma->recvlen = 0;
+	t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(card->dma->recvbuf));
+	t1outmeml(card->mbase+AMCC_RXLEN, 4);
+	card->csr |= EN_RX_TC_INT;
+	t1outmeml(card->mbase+AMCC_INTCSR, card->csr);
+	restore_flags(flags);
+
+        t1pci_send_init(card);
+
+	return 0;
+}
+
+void t1pci_reset_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+
+ 	t1pci_reset(card);
+
+	memset(cinfo->version, 0, sizeof(cinfo->version));
+	ctrl->reseted(ctrl);
+}
+
+static void t1pci_remove_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+
+ 	t1pci_reset(card);
+
+	di->detach_ctr(ctrl);
+	free_irq(card->irq, card);
+	iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+	release_region(card->port, AVMB1_PORTLEN);
+	ctrl->driverdata = 0;
+	kfree(card->ctrlinfo);
+	kfree(card->dma);
+	kfree(card);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------------------------------------------------------------- */
+
+
+void t1pci_register_appl(struct capi_ctr *ctrl,
+				__u16 appl,
+				capi_register_params *rp)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	int want = rp->level3cnt;
+	int nconn;
+	void *p;
+
+	if (want > 0) nconn = want;
+	else nconn = ctrl->profile.nbchannel * -want;
+	if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+	skb = alloc_skb(23, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_REGISTER);
+	_put_word(&p, appl);
+	_put_word(&p, 1024 * (nconn+1));
+	_put_word(&p, nconn);
+	_put_word(&p, rp->datablkcnt);
+	_put_word(&p, rp->datablklen);
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	t1pci_dispatch_tx(card);
+
+	ctrl->appl_registered(ctrl, appl);
+}
+
+/* ------------------------------------------------------------- */
+
+void t1pci_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(7, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_RELEASE);
+	_put_word(&p, appl);
+
+	skb_put(skb, (__u8 *)p - (__u8 *)skb->data);
+	skb_queue_tail(&card->dma->send_queue, skb);
+	t1pci_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+
+static void t1pci_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	skb_queue_tail(&card->dma->send_queue, skb);
+	t1pci_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *t1pci_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->membase : 0
+		);
+	return cinfo->infobuf;
+}
+
+static int t1pci_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned long flags;
+	__u8 flag;
+	int len = 0;
+	char *s;
+	__u32 txaddr, txlen, rxaddr, rxlen, csr;
+
+	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+	len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
+	switch (card->cardtype) {
+	case avm_b1isa: s = "B1 ISA"; break;
+	case avm_b1pci: s = "B1 PCI"; break;
+	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+	case avm_m1: s = "M1"; break;
+	case avm_m2: s = "M2"; break;
+	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+	case avm_t1pci: s = "T1 PCI"; break;
+	case avm_c4: s = "C4"; break;
+	default: s = "???"; break;
+	}
+	len += sprintf(page+len, "%-16s %s\n", "type", s);
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+	if (card->cardtype != avm_m1) {
+        	flag = ((__u8 *)(ctrl->profile.manu))[3];
+        	if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+			"protocol",
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+	}
+	if (card->cardtype != avm_m1) {
+        	flag = ((__u8 *)(ctrl->profile.manu))[5];
+		if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s\n",
+			"linetype",
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+	}
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+	save_flags(flags);
+	cli();
+
+	txaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x2c));
+	txaddr -= (__u32)card->dma->sendbuf;
+	txlen  = t1inmeml(card->mbase+0x30);
+
+	rxaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x24));
+	rxaddr -= (__u32)card->dma->recvbuf;
+	rxlen  = t1inmeml(card->mbase+0x28);
+
+	csr  = t1inmeml(card->mbase+AMCC_INTCSR);
+
+	restore_flags(flags);
+
+        len += sprintf(page+len, "%-16s 0x%lx\n",
+				"csr (cached)", (unsigned long)card->csr);
+        len += sprintf(page+len, "%-16s 0x%lx\n",
+				"csr", (unsigned long)csr);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"txoff", (unsigned long)txaddr);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"txlen", (unsigned long)txlen);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"rxoff", (unsigned long)rxaddr);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"rxlen", (unsigned long)rxlen);
+
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
+           return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p)
+{
+	unsigned long page_offset, base;
+	avmcard *card;
+	avmctrl_info *cinfo;
+	int retval;
+
+	card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC);
+
+	if (!card) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		return -ENOMEM;
+	}
+	memset(card, 0, sizeof(avmcard));
+	card->dma = (avmcard_dmainfo *) kmalloc(sizeof(avmcard_dmainfo), GFP_ATOMIC);
+	if (!card->dma) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(card->dma, 0, sizeof(avmcard_dmainfo));
+        cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC);
+	if (!cinfo) {
+		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		kfree(card->dma);
+		kfree(card);
+		return -ENOMEM;
+	}
+	memset(cinfo, 0, sizeof(avmctrl_info));
+	card->ctrlinfo = cinfo;
+	cinfo->card = card;
+	sprintf(card->name, "t1pci-%x", p->port);
+	card->port = p->port;
+	card->irq = p->irq;
+	card->membase = p->membase;
+	card->cardtype = avm_t1pci;
+
+	if (check_region(card->port, AVMB1_PORTLEN)) {
+		printk(KERN_WARNING
+		       "%s: ports 0x%03x-0x%03x in use.\n",
+		       driver->name, card->port, card->port + AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EBUSY;
+	}
+
+        base = card->membase & PAGE_MASK;
+	page_offset = card->membase - base;
+	card->mbase = ioremap_nocache(base, page_offset + 64);
+
+	t1pci_reset(card);
+
+	if ((retval = t1pci_detect(card)) != 0) {
+		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
+					driver->name, card->port, retval);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EIO;
+	}
+	t1pci_reset(card);
+
+	request_region(p->port, AVMB1_PORTLEN, card->name);
+
+	retval = request_irq(card->irq, t1pci_interrupt, SA_SHIRQ, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+				driver->name, card->irq);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EBUSY;
+	}
+
+	cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
+	if (!cinfo->capi_ctrl) {
+		printk(KERN_ERR "%s: attach controller failed.\n", driver->name);
+                iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK));
+		free_irq(card->irq, card);
+		release_region(card->port, AVMB1_PORTLEN);
+	        kfree(card->ctrlinfo);
+		kfree(card->dma);
+		kfree(card);
+		return -EBUSY;
+	}
+	card->cardnr = cinfo->capi_ctrl->cnr;
+
+	skb_queue_head_init(&card->dma->send_queue);
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static struct capi_driver t1pci_driver = {
+    "t1pci",
+    "0.0",
+    t1pci_load_firmware,
+    t1pci_reset_ctr,
+    t1pci_remove_ctr,
+    t1pci_register_appl,
+    t1pci_release_appl,
+    t1pci_send_message,
+
+    t1pci_procinfo,
+    t1pci_read_proc,
+    0,	/* use standard driver_read_proc */
+
+    0, /* no add_card function */
+};
+
+#ifdef MODULE
+#define t1pci_init init_module
+void cleanup_module(void);
+#endif
+
+static int ncards = 0;
+
+int t1pci_init(void)
+{
+	struct capi_driver *driver = &t1pci_driver;
+	struct pci_dev *dev = NULL;
+	char *p;
+	int retval;
+
+	if ((p = strchr(revision, ':'))) {
+		strncpy(driver->revision, p + 1, sizeof(driver->revision));
+		p = strchr(driver->revision, '$');
+		*p = 0;
+	}
+
+	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
+
+        di = attach_capi_driver(driver);
+
+	if (!di) {
+		printk(KERN_ERR "%s: failed to attach capi_driver\n",
+				driver->name);
+		return -EIO;
+	}
+
+#ifdef CONFIG_PCI
+	if (!pci_present()) {
+		printk(KERN_ERR "%s: no PCI bus present\n", driver->name);
+    		detach_capi_driver(driver);
+		return -EIO;
+	}
+
+	while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, dev))) {
+		struct capicardparams param;
+
+		param.port = dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK;
+		param.irq = dev->irq;
+		param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK;
+
+		printk(KERN_INFO
+			"%s: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n",
+			driver->name, param.port, param.irq, param.membase);
+		retval = t1pci_add_card(driver, &param);
+		if (retval != 0) {
+		        printk(KERN_ERR
+			"%s: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n",
+			driver->name, param.port, param.irq, param.membase);
+#ifdef MODULE
+			cleanup_module();
+#endif
+			return retval;
+		}
+		ncards++;
+	}
+	if (ncards) {
+		printk(KERN_INFO "%s: %d T1-PCI card(s) detected\n",
+				driver->name, ncards);
+		return 0;
+	}
+	printk(KERN_ERR "%s: NO T1-PCI card detected\n", driver->name);
+	return -ESRCH;
+#else
+	printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name);
+	return -EIO;
+#endif
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+    detach_capi_driver(&t1pci_driver);
+}
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/eicon/eicon.h linux.14p6/drivers/isdn/eicon/eicon.h
--- linux.vanilla/drivers/isdn/eicon/eicon.h	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/eicon/eicon.h	Tue Nov 16 17:28:20 1999
@@ -1,4 +1,4 @@
-/* $Id: eicon.h,v 1.14 1999/09/08 20:17:31 armin Exp $
+/* $Id: eicon.h,v 1.17 1999/10/26 21:15:33 armin Exp $
  *
  * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
  *
@@ -21,6 +21,17 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon.h,v $
+ * Revision 1.17  1999/10/26 21:15:33  armin
+ * using define for checking phone number len to avoid buffer overflow.
+ *
+ * Revision 1.16  1999/10/08 22:09:33  armin
+ * Some fixes of cards interface handling.
+ * Bugfix of NULL pointer occurence.
+ * Changed a few log outputs.
+ *
+ * Revision 1.15  1999/09/26 14:17:53  armin
+ * Improved debug and log via readstat()
+ *
  * Revision 1.14  1999/09/08 20:17:31  armin
  * Added microchannel patch from Erik Weber.
  *
@@ -126,6 +137,7 @@
 
 #define MAX_HEADER_LEN 10
 
+#define MAX_STATUS_BUFFER	150
 
 /* Struct for adding new cards */
 typedef struct eicon_cdef {
@@ -233,6 +245,7 @@
 #include <linux/delay.h>
 #include <linux/ctype.h>
 
+#include <linux/isdn.h>
 #include <linux/isdnif.h>
 
 
@@ -480,7 +493,6 @@
 
 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   */
 	int		queued;          /* User-Data Bytes in TX queue */
@@ -497,9 +509,13 @@
 	entity		e;		 /* Entity  			*/
 	char		cpn[32];	 /* remember cpn		*/
 	char		oad[32];	 /* remember oad		*/
+	char		dsa[32];	 /* remember dsa		*/
+	char		osa[32];	 /* remember osa		*/
 	unsigned char   cause[2];	 /* Last Cause			*/
 	unsigned char	si1;
 	unsigned char	si2;
+	unsigned char	plan;
+	unsigned char	screen;
 } eicon_chan;
 
 typedef struct {
@@ -535,7 +551,7 @@
 #define EICON_STATE_LISTEN  15
 #define EICON_STATE_WMCONN  16
 
-#define EICON_MAX_QUEUED  8000 /* 2 * maxbuff */
+#define EICON_MAX_QUEUE  2138
 
 #define EICON_LOCK_TX 0
 #define EICON_LOCK_RX 1
@@ -589,6 +605,8 @@
 	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           */
+	struct sk_buff_head statq;       /* Status-Message queue             */
+	int statq_entries;
 	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      */
@@ -677,6 +695,7 @@
 #endif /* CONFIG_MCA */
 
 extern ulong DebugVar;
+extern void eicon_log(eicon_card * card, int level, const char *fmt, ...);
 
 #endif  /* __KERNEL__ */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/eicon/eicon_idi.c linux.14p6/drivers/isdn/eicon/eicon_idi.c
--- linux.vanilla/drivers/isdn/eicon/eicon_idi.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/eicon/eicon_idi.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: eicon_idi.c,v 1.18 1999/09/07 12:48:05 armin Exp $
+/* $Id: eicon_idi.c,v 1.24 1999/10/26 21:15:33 armin Exp $
  *
  * ISDN lowlevel-module for Eicon.Diehl active cards.
  *        IDI interface 
@@ -6,6 +6,11 @@
  * Copyright 1998,99 by Armin Schindler (mac@melware.de)
  * Copyright 1999    Cytronics & Melware (info@melware.de)
  *
+ * Thanks to	Deutsche Mailbox Saar-Lor-Lux GmbH
+ *		for sponsoring and testing fax
+ *		capabilities with Diva Server cards.
+ *		(dor@deutschemailbox.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)
@@ -21,6 +26,26 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_idi.c,v $
+ * Revision 1.24  1999/10/26 21:15:33  armin
+ * using define for checking phone number len to avoid buffer overflow.
+ *
+ * Revision 1.23  1999/10/11 18:13:25  armin
+ * Added fax capabilities for Eicon Diva Server cards.
+ *
+ * Revision 1.22  1999/10/08 22:09:33  armin
+ * Some fixes of cards interface handling.
+ * Bugfix of NULL pointer occurence.
+ * Changed a few log outputs.
+ *
+ * Revision 1.21  1999/09/26 14:17:53  armin
+ * Improved debug and log via readstat()
+ *
+ * Revision 1.20  1999/09/21 20:35:43  armin
+ * added more error checking.
+ *
+ * Revision 1.19  1999/09/21 20:06:40  armin
+ * Added pointer checks.
+ *
  * Revision 1.18  1999/09/07 12:48:05  armin
  * Prepared for sub-address usage.
  *
@@ -105,7 +130,7 @@
 
 #undef EICON_FULL_SERVICE_OKTETT
 
-char *eicon_idi_revision = "$Revision: 1.18 $";
+char *eicon_idi_revision = "$Revision: 1.24 $";
 
 eicon_manifbuf *manbuf;
 
@@ -289,8 +314,7 @@
 	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);
+	eicon_log(NULL, 8, "idi_req: Ch%d: Call_Res\n", chan->No);
    return(0);
 }
 
@@ -306,8 +330,7 @@
         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 in do_req()\n", chan->No);
+               	eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No);
 		if (skb) 
 			dev_kfree_skb(skb);
 		if (skb2) 
@@ -319,8 +342,7 @@
 	chan2->ptr = chan;
 
 	reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
-	if (DebugVar & 8)
-		printk(KERN_DEBUG "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
+	eicon_log(card, 8, "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
 	if (layer) cmd |= 0x700;
 	switch(cmd) {
 		case ASSIGN:
@@ -359,8 +381,7 @@
 			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);
+			eicon_log(card, 1, "idi_req: Ch%d: Unknown request\n", chan->No);
 			dev_kfree_skb(skb);
 			dev_kfree_skb(skb2);
 			return(-1);
@@ -375,8 +396,10 @@
 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 ((!card) || (!chan))
+		return 1;
+
+	eicon_log(card, 16, "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); 
 	}
@@ -430,15 +453,19 @@
 int
 idi_hangup(eicon_card *card, eicon_chan *chan)
 {
+	if ((!card) || (!chan))
+		return 1;
+
 	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);
+	if (chan->fsm_state != EICON_STATE_NULL) {
+		idi_do_req(card, chan, HANGUP, 0);
+		chan->fsm_state = EICON_STATE_NULL;
+	}
+	eicon_log(card, 8, "idi_req: Ch%d: Hangup\n", chan->No);
 #ifdef CONFIG_ISDN_TTY_FAX
 	chan->fax = 0;
 #endif
@@ -448,14 +475,16 @@
 int
 idi_connect_res(eicon_card *card, eicon_chan *chan)
 {
+	if ((!card) || (!chan))
+		return 1;
+
 	chan->fsm_state = EICON_STATE_IWAIT;
 	idi_do_req(card, chan, CALL_RES, 0);
 	
 	/* check if old NetID has been removed */
 	if (chan->e.B2Id) {
-		if (DebugVar & 1)
-			printk(KERN_WARNING "idi_err: Ch%d: Old NetID %x was not removed.\n",
-				chan->No, chan->e.B2Id);
+		eicon_log(card, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n",
+			chan->No, chan->e.B2Id);
 		idi_do_req(card, chan, REMOVE, 1);
 	}
 
@@ -478,12 +507,14 @@
 	eicon_REQ *reqbuf;
 	eicon_chan_ptr *chan2;
 
+	if ((!card) || (!chan))
+		return 1;
+
         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 in connect_req()\n", chan->No);
+               	eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No);
 		if (skb) 
 			dev_kfree_skb(skb);
 		if (skb2) 
@@ -629,8 +660,7 @@
 	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);
+	eicon_log(card, 8, "idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone);
    return(0);
 }
 
@@ -647,9 +677,10 @@
 	__u16 code;
 	isdn_ctrl cmd;
 
-  memset(message, 0, sizeof(idi_ind_message));
+	memset(message, 0, sizeof(idi_ind_message));
+
+	if ((!len) || (!buffer[pos])) return;
 
-  if ((!len) || (!buffer[pos])) return;
   while(pos <= len) {
 	w = buffer[pos++];
 	if (!w) return;
@@ -679,8 +710,18 @@
 		else code = w;
 		code |= (codeset<<8);
 
+		if (pos + wlen > len) {
+			eicon_log(ccard, 1, "idi_err: Ch%d: IElen %d of %x exceeds Ind_Length (+%d)\n", chan->No, 
+					wlen, code, (pos + wlen) - len);
+			return;
+		}
+
 		switch(code) {
 			case OAD:
+				if (wlen > sizeof(message->oad)) {
+					pos += wlen;
+					break;
+				}
 				j = 1;
 				if (wlen) {
 					message->plan = buffer[pos++];
@@ -693,11 +734,14 @@
 				}
 				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);
+				eicon_log(ccard, 2, "idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No, 
+					message->plan, message->screen, message->oad);
 				break;
 			case RDN:
+				if (wlen > sizeof(message->rdn)) {
+					pos += wlen;
+					break;
+				}
 				j = 1;
 				if (wlen) {
 					if (!(buffer[pos++] & 0x80)) {
@@ -707,92 +751,116 @@
 				}
 				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, 
+				eicon_log(ccard, 2, "idi_inf: Ch%d: RDN= %s\n", chan->No, 
 						message->rdn);
 				break;
 			case CPN:
+				if (wlen > sizeof(message->cpn)) {
+					pos += wlen;
+					break;
+				}
 				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);
+				eicon_log(ccard, 2, "idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No,
+					(__u8)message->cpn[0], message->cpn + 1);
 				break;
 			case DSA:
+				if (wlen > sizeof(message->dsa)) {
+					pos += wlen;
+					break;
+				}
 				pos += 2;
 				for(i=0; i < wlen-2; i++) 
 					message->dsa[i] = buffer[pos++];
-				if (DebugVar & 2)
-					printk(KERN_DEBUG"idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa);
+				eicon_log(ccard, 2, "idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa);
 				break;
 			case OSA:
+				if (wlen > sizeof(message->osa)) {
+					pos += wlen;
+					break;
+				}
 				pos += 2;
 				for(i=0; i < wlen-2; i++) 
 					message->osa[i] = buffer[pos++];
-				if (DebugVar & 2)
-					printk(KERN_DEBUG"idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa);
+				eicon_log(ccard, 2, "idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa);
 				break;
 			case BC:
+				if (wlen > sizeof(message->bc)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "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:
+				if (wlen > sizeof(message->e_bc)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]);
 				break;
 			case LLC:
+				if (wlen > sizeof(message->llc)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "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:
+				if (wlen > sizeof(message->hlc)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "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:
+				if (wlen > sizeof(message->display)) {
+					pos += wlen;
+					break;
+				}
 				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);
+				eicon_log(ccard, 4, "idi_inf: Ch%d: Display: %s\n", chan->No,
+					message->display);
 				break;
 			case 0x600|KEY:
+				if (wlen > sizeof(message->keypad)) {
+					pos += wlen;
+					break;
+				}
 				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);
+				eicon_log(ccard, 4, "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);
-						}
+					switch(buffer[pos] & 127) {
+						case 0:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: User suspended.\n", chan->No);
+							break;
+						case 1:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: User resumed.\n", chan->No);
+							break;
+						case 2:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Bearer service change.\n", chan->No);
+							break;
+						default:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Notification %x.\n", 
+									chan->No, buffer[pos] & 127);
 					}
 					pos += wlen;
 				}
@@ -800,80 +868,91 @@
 			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);
-						}
+					switch(buffer[pos+1] & 127) {
+						case 1:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No);
+							break;
+						case 2:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No);
+							break;
+						case 3:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No);
+							break;
+						case 4:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No);
+							break;
+						case 5:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Interworking has occurred.\n", chan->No);
+							break;
+						case 8:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: In-band information available.\n", chan->No);
+							break;
+						default:
+							eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Progress %x.\n", 
+									chan->No, buffer[pos+1] & 127);
 					}
 				}
 				pos += wlen;
 				break;
 			case CAU:
+				if (wlen > sizeof(message->cau)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "idi_inf: Ch%d: CAU=%d %d\n", chan->No,
+					message->cau[0],message->cau[1]);
 				break;
 			case 0x800|CAU:
+				if (wlen > sizeof(message->e_cau)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "idi_inf: Ch%d: ECAU=%d %d\n", chan->No,
+					message->e_cau[0],message->e_cau[1]);
 				break;
 			case 0x800|CHI:
+				if (wlen > sizeof(message->e_chi)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "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);
+				eicon_log(ccard, 4, "idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt);
 				break;
 			case DT:
+				if (wlen > sizeof(message->dt)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 4, "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:
+				if (wlen > sizeof(message->sin)) {
+					pos += wlen;
+					break;
+				}
 				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]);
+				eicon_log(ccard, 2, "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);
+				eicon_log(ccard, 2, "idi_inf: Ch%d: Called Party Status in ind\n", chan->No);
 				pos += wlen;
 				break;
 			case 0x600|CIF:
@@ -881,8 +960,7 @@
 					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);
+				eicon_log(ccard, 2, "idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num);
 				pos += wlen;
 				cmd.driver = ccard->myid;
 				cmd.command = ISDN_STAT_CINF;
@@ -890,8 +968,11 @@
 				ccard->interface.statcallb(&cmd);
 				break;
 			case 0x600|DATE:
-				if (DebugVar & 2)
-					printk(KERN_DEBUG"idi_inf: Ch%d: Date in ind\n", chan->No);
+				eicon_log(ccard, 2, "idi_inf: Ch%d: Date in ind\n", chan->No);
+				pos += wlen;
+				break;
+			case 0xa1: 
+				eicon_log(ccard, 2, "idi_inf: Ch%d: Sending Complete in ind.\n", chan->No);
 				pos += wlen;
 				break;
 			case 0xe08: 
@@ -911,8 +992,7 @@
 			case 0x880:
 				/* Managment Information Element */
 				if (!manbuf) {
-					if (DebugVar & 1)
-						printk(KERN_WARNING"idi_err: manbuf not allocated\n");
+					eicon_log(ccard, 1, "idi_err: manbuf not allocated\n");
 				}
 				else {
 					memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen);
@@ -924,9 +1004,8 @@
 				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);
+				eicon_log(ccard, 6, "idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n", 
+					chan->No, code, wlen);
 		}
 	}
   }
@@ -967,110 +1046,1095 @@
 int
 idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer)
 {
+	eicon_t30_s	*t30 = (eicon_t30_s *) buffer;
 
-	/* TODO , code follows */
+	if (!chan->fax) {
+		eicon_log(NULL, 1,"idi_T30: fill_in with NULL fax struct, ERROR\n");
+		return 0;
+	}
+	memset(t30, 0, sizeof(eicon_t30_s));
+	t30->station_id_len = EICON_FAXID_LEN;
+	memcpy(&t30->station_id[0], &chan->fax->id[0], EICON_FAXID_LEN);
+	t30->resolution = chan->fax->resolution;
+	t30->rate = chan->fax->rate + 1;	/* eicon rate starts with 1 */
+	t30->format = T30_FORMAT_SFF;
+	t30->pages_low = 0;
+	t30->pages_high = 0;
+	t30->atf = 1;				/* optimised for AT+F command set */
+	t30->code = 0;
+	t30->feature_bits_low = 0;
+	t30->feature_bits_high = 0;
+	t30->control_bits_low = 0;
+	t30->control_bits_high = 0;
+
+	if (chan->fax->nbc) {
+		/* set compression by DCC value */
+  	  switch(chan->fax->compression) {
+		case (0):	/* 1-D modified */
+			break;
+		case (1):	/* 2-D modified Read */
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
+			t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
+			break;
+		case (2):	/* 2-D uncompressed */
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
+			t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
+			t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
+			break;
+		case (3):	/* 2-D modified Read */
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING;
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
+			t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
+			t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING;
+			t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
+			t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
+			break;
+	  }
+	} else {
+		/* set compression to best */
+		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING;
+		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
+		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
+		t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
+		t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING;
+		t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
+		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
+		t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
+	}
+	switch(chan->fax->ecm) {
+		case (0):	/* disable ECM */
+			break;
+		case (1):
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
+			t30->control_bits_low |= T30_CONTROL_BIT_ECM_64_BYTES;
+			t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
+			t30->feature_bits_low |= T30_FEATURE_BIT_ECM_64_BYTES;
+			break;
+		case (2):
+			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
+			t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
+			break;
+	}
 
-	return(0);
+	if (DebugVar & 128) {
+		char st[40];
+		eicon_log(NULL, 128, "sT30:code = %x\n", t30->code);
+		eicon_log(NULL, 128, "sT30:rate = %x\n", t30->rate);
+		eicon_log(NULL, 128, "sT30:res  = %x\n", t30->resolution);
+		eicon_log(NULL, 128, "sT30:format = %x\n", t30->format);
+		eicon_log(NULL, 128, "sT30:pages_low = %x\n", t30->pages_low);
+		eicon_log(NULL, 128, "sT30:pages_high = %x\n", t30->pages_high);
+		eicon_log(NULL, 128, "sT30:atf  = %x\n", t30->atf);
+		eicon_log(NULL, 128, "sT30:control_bits_low = %x\n", t30->control_bits_low);
+		eicon_log(NULL, 128, "sT30:control_bits_high = %x\n", t30->control_bits_high);
+		eicon_log(NULL, 128, "sT30:feature_bits_low = %x\n", t30->feature_bits_low);
+		eicon_log(NULL, 128, "sT30:feature_bits_high = %x\n", t30->feature_bits_high);
+		//eicon_log(NULL, 128, "sT30:universal_5 = %x\n", t30->universal_5);
+		//eicon_log(NULL, 128, "sT30:universal_6 = %x\n", t30->universal_6);
+		//eicon_log(NULL, 128, "sT30:universal_7 = %x\n", t30->universal_7);
+		eicon_log(NULL, 128, "sT30:station_id_len = %x\n", t30->station_id_len);
+		eicon_log(NULL, 128, "sT30:head_line_len = %x\n", t30->head_line_len);
+		strncpy(st, t30->station_id, t30->station_id_len);
+		st[t30->station_id_len] = 0;
+		eicon_log(NULL, 128, "sT30:station_id = <%s>\n", st);
+	}
+	return(sizeof(eicon_t30_s));
 }
 
 /* send fax struct */
 int
 idi_send_edata(eicon_card *card, eicon_chan *chan)
 {
+	struct sk_buff *skb;
+	struct sk_buff *skb2;
+	eicon_REQ *reqbuf;
+	eicon_chan_ptr *chan2;
 
-	/* TODO , code follows */
+	if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) {
+		eicon_log(card, 1, "idi_snd: Ch%d: send edata on state %d !\n", chan->No, chan->fsm_state);
+		return -ENODEV;
+	}
+	eicon_log(card, 128, "idi_snd: Ch%d: edata (fax)\n", chan->No);
+
+	skb = alloc_skb(sizeof(eicon_REQ) + sizeof(eicon_t30_s), GFP_ATOMIC);
+	skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+	if ((!skb) || (!skb2)) {
+		eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_edata()\n", chan->No);
+		if (skb) 
+			dev_kfree_skb(skb);
+		if (skb2) 
+			dev_kfree_skb(skb2);
+		return -ENOMEM;
+	}
 
+	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+	chan2->ptr = chan;
+
+	reqbuf = (eicon_REQ *)skb_put(skb, sizeof(eicon_t30_s) + sizeof(eicon_REQ));
+
+	reqbuf->Req = IDI_N_EDATA;
+	reqbuf->ReqCh = 0;
+	reqbuf->ReqId = 1;
+
+	reqbuf->XBuffer.length = idi_fill_in_T30(chan, reqbuf->XBuffer.P);
+	reqbuf->Reference = 1; /* Net Entity */
+
+	skb_queue_tail(&chan->e.X, skb);
+	skb_queue_tail(&card->sndq, skb2);
+	eicon_schedule_tx(card);
 	return (0);
 }
 
 void
 idi_parse_edata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
 {
+	eicon_t30_s *p = (eicon_t30_s *)buffer;
+	int i;
 
-	/* TODO , code follows */
+	if (DebugVar & 128) {
+		char st[40];
+		eicon_log(ccard, 128, "rT30:len %d , size %d\n", len, sizeof(eicon_t30_s));
+		eicon_log(ccard, 128, "rT30:code = %x\n", p->code);
+		eicon_log(ccard, 128, "rT30:rate = %x\n", p->rate);
+		eicon_log(ccard, 128, "rT30:res  = %x\n", p->resolution);
+		eicon_log(ccard, 128, "rT30:format = %x\n", p->format);
+		eicon_log(ccard, 128, "rT30:pages_low = %x\n", p->pages_low);
+		eicon_log(ccard, 128, "rT30:pages_high = %x\n", p->pages_high);
+		eicon_log(ccard, 128, "rT30:atf  = %x\n", p->atf);
+		eicon_log(ccard, 128, "rT30:control_bits_low = %x\n", p->control_bits_low);
+		eicon_log(ccard, 128, "rT30:control_bits_high = %x\n", p->control_bits_high);
+		eicon_log(ccard, 128, "rT30:feature_bits_low = %x\n", p->feature_bits_low);
+		eicon_log(ccard, 128, "rT30:feature_bits_high = %x\n", p->feature_bits_high);
+		//eicon_log(ccard, 128, "rT30:universal_5 = %x\n", p->universal_5);
+		//eicon_log(ccard, 128, "rT30:universal_6 = %x\n", p->universal_6);
+		//eicon_log(ccard, 128, "rT30:universal_7 = %x\n", p->universal_7);
+		eicon_log(ccard, 128, "rT30:station_id_len = %x\n", p->station_id_len);
+		eicon_log(ccard, 128, "rT30:head_line_len = %x\n", p->head_line_len);
+		strncpy(st, p->station_id, p->station_id_len);
+		st[p->station_id_len] = 0;
+		eicon_log(ccard, 128, "rT30:station_id = <%s>\n", st);
+	}
+	if (!chan->fax) {
+		eicon_log(ccard, 1, "idi_edata: parse to NULL fax struct, ERROR\n");
+		return;
+	}
+	chan->fax->code = p->code;
+	i = (p->station_id_len < FAXIDLEN) ? p->station_id_len : (FAXIDLEN - 1);
+	memcpy(chan->fax->r_id, p->station_id, i);
+	chan->fax->r_id[i] = 0;
+	chan->fax->r_resolution = p->resolution;
+	chan->fax->r_rate = p->rate - 1;
+	chan->fax->r_binary = 0; /* no binary support */
+	chan->fax->r_width = 0;
+	chan->fax->r_length = 2;
+	chan->fax->r_scantime = 0;
+	chan->fax->r_compression = 0;
+	chan->fax->r_ecm = 0;
+	if (p->feature_bits_low & T30_FEATURE_BIT_2D_CODING) {
+		chan->fax->r_compression = 1;
+		if (p->feature_bits_low & T30_FEATURE_BIT_UNCOMPR_ENABLED) {
+			chan->fax->r_compression = 2;
+		}
+	}
+	if (p->feature_bits_low & T30_FEATURE_BIT_T6_CODING) {
+		chan->fax->r_compression = 3;
+	}
 
+	if (p->feature_bits_low & T30_FEATURE_BIT_ECM) {
+		chan->fax->r_ecm = 2;
+		if (p->feature_bits_low & T30_FEATURE_BIT_ECM_64_BYTES)
+			chan->fax->r_ecm = 1;
+	}
 }
 
 void
 idi_fax_send_header(eicon_card *card, eicon_chan *chan, int header)
 {
+	static __u16 wd2sff[] = {
+		1728, 2048, 2432, 1216, 864
+	};
+	static __u16 ln2sff[2][3] = {
+		{ 1143, 1401, 0 } , { 2287, 2802, 0 }
+	};
+	struct sk_buff *skb;
+	eicon_sff_dochead *doc;
+	eicon_sff_pagehead *page;
+	u_char *docp;
+
+	if (!chan->fax) {
+		eicon_log(card, 1, "idi_fax: send head with NULL fax struct, ERROR\n");
+		return;
+	}
+	if (header == 2) { /* DocHeader + PageHeader */
+		skb = alloc_skb(sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead), GFP_ATOMIC);
+	} else {
+		skb = alloc_skb(sizeof(eicon_sff_pagehead), GFP_ATOMIC);
+	}
+	if (!skb) {
+		eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_header()\n", chan->No);
+		return;
+	}
 
-	/* TODO , code follows */
+	if (header == 2) { /* DocHeader + PageHeader */
+		docp = skb_put(skb, sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead));
+		doc = (eicon_sff_dochead *) docp;
+		page = (eicon_sff_pagehead *) (docp + sizeof(eicon_sff_dochead));
+		memset(docp, 0,sizeof(eicon_sff_dochead)  + sizeof(eicon_sff_pagehead));
+		doc->id = 0x66666653;
+		doc->version = 0x01;
+		doc->off1pagehead = sizeof(eicon_sff_dochead);
+	} else {
+		page = (eicon_sff_pagehead *)skb_put(skb, sizeof(eicon_sff_pagehead));
+		memset(page, 0, sizeof(eicon_sff_pagehead));
+	}
 
+	switch(header) {
+		case 1:	/* PageHeaderEnd */
+			page->pageheadid = 254;
+			page->pageheadlen = 0; 
+			break;
+		case 0: /* PageHeader */
+		case 2: /* DocHeader + PageHeader */
+			page->pageheadid = 254;
+			page->pageheadlen = sizeof(eicon_sff_pagehead) - 2;
+			page->resvert = chan->fax->resolution;
+			page->reshoriz = 0; /* always 203 dpi */
+			page->coding = 0; /* always 1D */
+			page->linelength = wd2sff[chan->fax->width];
+			page->pagelength = ln2sff[chan->fax->resolution][chan->fax->length]; 
+			eicon_log(card, 128, "sSFF-Head: linelength = %d\n", page->linelength);
+			eicon_log(card, 128, "sSFF-Head: pagelength = %d\n", page->pagelength);
+			break;
+	}
+	idi_send_data(card, chan, 0, skb, 0);
 }
 
 void
 idi_fax_cmd(eicon_card *card, eicon_chan *chan) 
 {
+	isdn_ctrl cmd;
+
+	if ((!card) || (!chan))
+		return;
+
+	if (!chan->fax) {
+		eicon_log(card, 1, "idi_fax: cmd with NULL fax struct, ERROR\n");
+		return;
+	}
+	switch (chan->fax->code) {
+		case ISDN_TTY_FAX_DT:
+			if (chan->fax->phase == ISDN_FAX_PHASE_B) {
+				idi_send_edata(card, chan);
+				break;
+			}
+			if (chan->fax->phase == ISDN_FAX_PHASE_D) {
+				idi_send_edata(card, chan);
+				break;
+			}
+			break;
+
+		case ISDN_TTY_FAX_DR:
+			if (chan->fax->phase == ISDN_FAX_PHASE_B) {
+				idi_send_edata(card, chan);
+
+				cmd.driver = card->myid;
+				cmd.command = ISDN_STAT_FAXIND;
+				cmd.arg = chan->No;
+				chan->fax->r_code = ISDN_TTY_FAX_CFR;
+				card->interface.statcallb(&cmd);
+
+				cmd.driver = card->myid;
+				cmd.command = ISDN_STAT_FAXIND;
+				cmd.arg = chan->No;
+				chan->fax->r_code = ISDN_TTY_FAX_RID;
+				card->interface.statcallb(&cmd);
+
+				/* telling 1-D compression */
+				chan->fax->r_compression = 0;
+				cmd.driver = card->myid;
+				cmd.command = ISDN_STAT_FAXIND;
+				cmd.arg = chan->No;
+				chan->fax->r_code = ISDN_TTY_FAX_DCS;
+				card->interface.statcallb(&cmd);
 
-	/* TODO , code follows */
+				chan->fax2.NextObject = FAX_OBJECT_DOCU;
+				chan->fax2.PrevObject = FAX_OBJECT_DOCU;
 
+				break;
+			}
+			if (chan->fax->phase == ISDN_FAX_PHASE_D) {
+				idi_send_edata(card, chan);
+				break;
+			}
+			break;
+
+		case ISDN_TTY_FAX_ET:
+				switch(chan->fax->fet) {
+					case 0:
+					case 1:
+						idi_fax_send_header(card, chan, 0);
+						break;
+					case 2:
+						idi_fax_send_header(card, chan, 1);
+						break;
+				}
+			break;
+	}
 }
 
 void
 idi_edata_rcveop(eicon_card *card, eicon_chan *chan)
 {
+	isdn_ctrl cmd;
 
-	/* TODO , code follows */
-
+	if (!chan->fax) {
+		eicon_log(card, 1, "idi_edata: rcveop with NULL fax struct, ERROR\n");
+		return;
+	}
+	cmd.driver = card->myid;
+	cmd.command = ISDN_STAT_FAXIND;
+	cmd.arg = chan->No;
+	chan->fax->r_code = ISDN_TTY_FAX_ET;
+	card->interface.statcallb(&cmd);
 }
 
 void
 idi_reset_fax_stat(eicon_chan *chan)
 {
-
-	/* TODO , code follows */
-
+	chan->fax2.LineLen = 0;
+	chan->fax2.LineData = 0;
+	chan->fax2.LineDataLen = 0;
+	chan->fax2.NullByteExist = 0;
+	chan->fax2.Dle = 0;
+	chan->fax2.PageCount = 0;
+	chan->fax2.Eop = 0;
 }
 
 void
 idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len)
 {
+	isdn_ctrl cmd;
+
+	if (!chan->fax) {
+		eicon_log(ccard, 1, "idi_edata: action with NULL fax struct, ERROR\n");
+		return;
+	}
+	if (chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) {
+		idi_parse_edata(ccard, chan, buffer, len);
+
+		if (chan->fax->phase == ISDN_FAX_PHASE_A) {
+			idi_reset_fax_stat(chan);
+
+			chan->fsm_state = EICON_STATE_ACTIVE;
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_BCONN;
+			cmd.arg = chan->No;
+			ccard->interface.statcallb(&cmd);
+
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_FCON;
+			ccard->interface.statcallb(&cmd);
+
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_RID;
+			ccard->interface.statcallb(&cmd);
+
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_DIS;
+			ccard->interface.statcallb(&cmd);
+
+			if (chan->fax->r_compression != 0) {
+			/* telling fake compression in second DIS message */
+				chan->fax->r_compression = 0;
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_FAXIND;
+				cmd.arg = chan->No;
+				chan->fax->r_code = ISDN_TTY_FAX_DIS;
+				ccard->interface.statcallb(&cmd);
+			}
+
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_SENT; /* OK message */
+			ccard->interface.statcallb(&cmd);
+		} else
+		if (chan->fax->phase == ISDN_FAX_PHASE_D) {
 
-	/* TODO , code follows */
+			if ((chan->fax->code == EDATA_T30_MCF) &&
+			    (chan->fax->fet != 2)) {
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_FAXIND;
+				cmd.arg = chan->No;
+				chan->fax->r_code = ISDN_TTY_FAX_PTS;
+				ccard->interface.statcallb(&cmd);
+			}
 
+			switch(chan->fax->fet) {
+				case 0:	/* new page */
+					/* stay in phase D , wait on cmd +FDT */
+					break;
+				case 1:	/* new document */
+					/* link-level switch to phase B */
+					break;
+				case 2:	/* session end */
+				default:
+					/* idi_send_edata(ccard, chan); */
+					break;
+			}
+		}
+	}
+
+	if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
+		idi_parse_edata(ccard, chan, buffer, len);
+
+		if ((chan->fax->code == EDATA_T30_DCS) &&
+		    (chan->fax->phase == ISDN_FAX_PHASE_A)) {
+			idi_reset_fax_stat(chan);
+
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_BCONN;
+			cmd.arg = chan->No;
+			ccard->interface.statcallb(&cmd);
+
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_FCON_I;
+			ccard->interface.statcallb(&cmd);
+		} else
+		if ((chan->fax->code == EDATA_T30_TRAIN_OK) &&
+		    (chan->fax->phase == ISDN_FAX_PHASE_A)) {
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_RID;
+			ccard->interface.statcallb(&cmd);
+
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK;
+			ccard->interface.statcallb(&cmd);
+		} else
+		if ((chan->fax->code == EDATA_T30_TRAIN_OK) &&
+		    (chan->fax->phase == ISDN_FAX_PHASE_B)) {
+			cmd.driver = ccard->myid;
+			cmd.command = ISDN_STAT_FAXIND;
+			cmd.arg = chan->No;
+			chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK;
+			ccard->interface.statcallb(&cmd);
+		} else
+		if (chan->fax->phase == ISDN_FAX_PHASE_C) {
+			switch(chan->fax->code) {
+				case EDATA_T30_TRAIN_OK:
+					idi_send_edata(ccard, chan);
+					break;
+				case EDATA_T30_MPS:
+					chan->fax->fet = 0;
+					idi_edata_rcveop(ccard, chan);
+					break;
+				case EDATA_T30_EOM:
+					chan->fax->fet = 1;
+					idi_edata_rcveop(ccard, chan);
+					break;
+				case EDATA_T30_EOP:
+					chan->fax->fet = 2;
+					idi_edata_rcveop(ccard, chan);
+					break;
+			}
+		}
+	}
 }
 
 void
 fax_put_rcv(eicon_card *ccard, eicon_chan *chan, u_char *Data, int len)
 {
-
-	/* TODO , code follows */
-
+	struct sk_buff *skb;
+	
+        skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC);
+	if (!skb) {
+		eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_put_rcv()\n", chan->No);
+		return;
+	}
+	skb_reserve(skb, MAX_HEADER_LEN);
+	memcpy(skb_put(skb, len), Data, len);
+	ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
 }
 
 void
 idi_faxdata_rcv(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb)
 {
+	eicon_OBJBUFFER InBuf;
+	eicon_OBJBUFFER LineBuf;
+	unsigned int Length = 0;
+	unsigned int aLength = 0;
+	unsigned int ObjectSize = 0;
+	unsigned int ObjHeadLen = 0;
+	unsigned int ObjDataLen = 0;
+	__u8 Recordtype;
+	__u8 PageHeaderLen;	
+	__u8 Event;
+	eicon_sff_pagehead *ob_page;
+
+	__u16 Cl2Eol = 0x8000;
+
+#	define EVENT_NONE	0
+#	define EVENT_NEEDDATA	1
+
+	if (!chan->fax) {
+		eicon_log(ccard, 1, "idi_fax: rcvdata with NULL fax struct, ERROR\n");
+		return;
+	}
+
+
+	
+	if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
+		InBuf.Data = skb->data;
+		InBuf.Size = skb->len;
+		InBuf.Len  = 0;
+		InBuf.Next = InBuf.Data;
+		LineBuf.Data = chan->fax2.abLine;
+		LineBuf.Size = sizeof(chan->fax2.abLine);
+		LineBuf.Len  = chan->fax2.LineLen;
+		LineBuf.Next = LineBuf.Data + LineBuf.Len;
+
+		Event = EVENT_NONE;
+		while (Event == EVENT_NONE) {
+			switch(chan->fax2.NextObject) {
+				case FAX_OBJECT_DOCU:
+						Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
+						if (Length < sizeof(eicon_sff_dochead)) {
+							Event = EVENT_NEEDDATA;
+							break;
+						}
+						ObjectSize = sizeof(eicon_sff_dochead);
+						Length = ObjectSize;
+						if (LineBuf.Len < Length) {
+							Length -= LineBuf.Len;
+							LineBuf.Len = 0;
+							LineBuf.Next = LineBuf.Data;
+							InBuf.Len += Length;
+							InBuf.Next += Length;
+						} else {
+							LineBuf.Len -= Length;
+							LineBuf.Next = LineBuf.Data + LineBuf.Len;
+							memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
+						}
+						chan->fax2.PrevObject = FAX_OBJECT_DOCU;
+						chan->fax2.NextObject = FAX_OBJECT_PAGE;
+					break;
+
+				case FAX_OBJECT_PAGE:
+						Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
+						if (Length < 2) {
+							Event = EVENT_NEEDDATA;
+							break;
+						}
+						if (LineBuf.Len == 0) {
+							*LineBuf.Next++ = *InBuf.Next++;
+							LineBuf.Len++;
+							InBuf.Len++;
+						}
+						if (LineBuf.Len == 1) {
+							*LineBuf.Next++ = *InBuf.Next++;
+							LineBuf.Len++;
+							InBuf.Len++;
+						}
+						PageHeaderLen = *(LineBuf.Data + 1);
+						ObjectSize = (PageHeaderLen == 0) ? 2 : sizeof(eicon_sff_pagehead);
+						if (Length < ObjectSize) {
+							Event = EVENT_NEEDDATA;
+							break;
+						}
+						Length = ObjectSize;
+						/* extract page dimensions */
+						if (LineBuf.Len < Length) {
+							aLength = Length - LineBuf.Len;
+							memcpy(LineBuf.Next, InBuf.Next, aLength);
+							LineBuf.Next += aLength;
+							InBuf.Next += aLength;
+							LineBuf.Len += aLength;
+							InBuf.Len += aLength;
+						}
+						if (Length > 2) {
+							ob_page = (eicon_sff_pagehead *)LineBuf.Data;
+							switch(ob_page->linelength) {
+								case 2048:
+									chan->fax->r_width = 1;
+									break;
+								case 2432:
+									chan->fax->r_width = 2;
+									break;
+								case 1216:
+									chan->fax->r_width = 3;
+									break;
+								case 864:
+									chan->fax->r_width = 4;
+									break;
+								case 1728:
+								default:
+									chan->fax->r_width = 0;
+							}
+							switch(ob_page->pagelength) {
+								case 1143:
+								case 2287:
+									chan->fax->r_length = 0;
+									break;
+								case 1401:
+								case 2802:
+									chan->fax->r_length = 1;
+									break;
+								default:
+									chan->fax->r_length = 2;
+							}
+							eicon_log(ccard, 128, "rSFF-Head: linelength = %d\n", ob_page->linelength);
+							eicon_log(ccard, 128, "rSFF-Head: pagelength = %d\n", ob_page->pagelength);
+						}
+						LineBuf.Len -= Length;
+						LineBuf.Next = LineBuf.Data + LineBuf.Len;
+						memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
+
+						chan->fax2.PrevObject = FAX_OBJECT_PAGE;
+						chan->fax2.NextObject = FAX_OBJECT_LINE;
+					break;
 
-	/* TODO , code follows */
+				case FAX_OBJECT_LINE:
+						Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
+						if (Length < 1) {
+							Event = EVENT_NEEDDATA;
+							break;
+						}
+						if (LineBuf.Len == 0) {
+							*LineBuf.Next++ = *InBuf.Next++;
+							LineBuf.Len++;
+							InBuf.Len++;
+						}
+						Recordtype = *LineBuf.Data;
+						if (Recordtype == 0) {
+							/* recordtype pixel row (2 byte length) */
+							ObjHeadLen = 3;
+							if (Length < ObjHeadLen) {
+								Event = EVENT_NEEDDATA;
+								break;
+							}
+							while (LineBuf.Len < ObjHeadLen) {
+								*LineBuf.Next++ = *InBuf.Next++;
+								LineBuf.Len++;
+								InBuf.Len++;
+							}
+							ObjDataLen = *((__u16*) (LineBuf.Data + 1));
+							ObjectSize = ObjHeadLen + ObjDataLen;
+							if (Length < ObjectSize) {
+								Event = EVENT_NEEDDATA;
+								break;
+							}
+						} else
+						if ((Recordtype >= 1) && (Recordtype <= 216)) {
+							/* recordtype pixel row (1 byte length) */
+							ObjHeadLen = 1;
+							ObjDataLen = Recordtype;
+							ObjectSize = ObjHeadLen + ObjDataLen;
+							if (Length < ObjectSize) {
+								Event = EVENT_NEEDDATA;
+								break;
+							}
+						} else
+						if ((Recordtype >= 217) && (Recordtype <= 253)) {
+							/* recordtype empty lines */
+							ObjHeadLen = 1;
+							ObjDataLen = 0;
+							ObjectSize = ObjHeadLen + ObjDataLen;
+							LineBuf.Len--;
+							LineBuf.Next = LineBuf.Data + LineBuf.Len;
+							memmove(LineBuf.Data, LineBuf.Data + 1, LineBuf.Len);
+							break;
+						} else
+						if (Recordtype == 254) {
+							/* recordtype page header */
+							chan->fax2.PrevObject = FAX_OBJECT_LINE;
+							chan->fax2.NextObject = FAX_OBJECT_PAGE;
+							break;
+						} else {
+							/* recordtype user information */
+							ObjHeadLen = 2;
+							if (Length < ObjHeadLen) {
+								Event = EVENT_NEEDDATA;
+								break;
+							}
+							while (LineBuf.Len < ObjHeadLen) {
+								*LineBuf.Next++ = *InBuf.Next++;
+								LineBuf.Len++;
+								InBuf.Len++;
+							}
+							ObjDataLen = *(LineBuf.Data + 1);
+							ObjectSize = ObjHeadLen + ObjDataLen;
+							if (ObjDataLen == 0) {
+								/* illegal line coding */
+								LineBuf.Len -= ObjHeadLen;
+								LineBuf.Next = LineBuf.Data + LineBuf.Len;
+								memmove(LineBuf.Data, LineBuf.Data + ObjHeadLen, LineBuf.Len);
+								break;
+							} else {
+								/* user information */
+								if (Length < ObjectSize) {
+									Event = EVENT_NEEDDATA;
+									break;
+								}
+								Length = ObjectSize;
+								if (LineBuf.Len < Length) {
+									Length -= LineBuf.Len;
+									LineBuf.Len = 0;
+									LineBuf.Next = LineBuf.Data;
+									InBuf.Len += Length;
+									InBuf.Next += Length;
+								} else {
+									LineBuf.Len -= Length;
+									LineBuf.Next = LineBuf.Data + LineBuf.Len;
+									memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
+								}
+							}
+							break;	
+						}
+						Length = ObjectSize;
+						if (LineBuf.Len > ObjHeadLen) {
+							fax_put_rcv(ccard, chan, LineBuf.Data + ObjHeadLen,
+									(LineBuf.Len - ObjHeadLen));
+						}
+						Length -= LineBuf.Len;
+						LineBuf.Len = 0;
+						LineBuf.Next = LineBuf.Data;
+						if (Length > 0) {
+							fax_put_rcv(ccard, chan, InBuf.Next, Length);
+							InBuf.Len += Length;
+							InBuf.Next += Length;
+						}
+						fax_put_rcv(ccard, chan, (__u8 *)&Cl2Eol, sizeof(Cl2Eol));
+					break;
+			} /* end of switch (chan->fax2.NextObject) */
+		} /* end of while (Event==EVENT_NONE) */
+		if (InBuf.Len < InBuf.Size) {
+			Length = InBuf.Size - InBuf.Len;
+			if ((LineBuf.Len + Length) > LineBuf.Size) {
+				eicon_log(ccard, 1, "idi_fax: Ch%d: %d bytes dropping, small buffer\n", chan->No,
+					Length);
+				} else {
+					memcpy(LineBuf.Next, InBuf.Next, Length);
+					LineBuf.Len += Length;
+				}
+		}
+		chan->fax2.LineLen = LineBuf.Len;
+	} else { /* CONN_OUT */
+		/* On CONN_OUT we do not need incoming data, drop it */
+		/* maybe later for polling */
+	}
 
+#	undef EVENT_NONE
+#	undef EVENT_NEEDDATA
+
+	return;
 }
 
 int
 idi_fax_send_outbuf(eicon_card *ccard, eicon_chan *chan, eicon_OBJBUFFER *OutBuf)
 {
+	struct sk_buff *skb;
 
-	/* TODO , code follows */
+	skb = alloc_skb(OutBuf->Len, GFP_ATOMIC);
+	if (!skb) {
+		eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_outbuf()\n", chan->No);
+		return(-1);
+	}
+	memcpy(skb_put(skb, OutBuf->Len), OutBuf->Data, OutBuf->Len);
 
-	return(0);
+	OutBuf->Len = 0;
+	OutBuf->Next = OutBuf->Data;
+
+	return(idi_send_data(ccard, chan, 0, skb, 1));
 }
 
 int
 idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb)
 {
+	isdn_ctrl cmd;
+	eicon_OBJBUFFER InBuf;
+	__u8 InData;
+	__u8 InMask;
+	eicon_OBJBUFFER OutBuf;
+	eicon_OBJBUFFER LineBuf;
+	__u32 LineData;
+	unsigned int LineDataLen;
+	__u8 Byte;
+	__u8 Event;
+	int ret = 1;
+
+#	define EVENT_NONE	0
+#	define EVENT_EOD	1
+#	define EVENT_EOL 	2
+#	define EVENT_EOP 	3
 
-	/* TODO , code follows */
+	if ((!ccard) || (!chan))
+		return -1;
 
-	return(0);
+	if (!chan->fax) {
+		eicon_log(ccard, 1, "idi_fax: senddata with NULL fax struct, ERROR\n");
+		return -1;
+	}
+
+	if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
+		/* Simply ignore any data written in data mode when receiving a fax.    */
+		/* This is not completely correct because only XON's should come here.  */
+        	dev_kfree_skb(skb);
+		return 1;
+	}
+
+	if (chan->fax->phase != ISDN_FAX_PHASE_C) {
+        	dev_kfree_skb(skb);
+		return 1;
+	}
+
+        if (chan->queued + skb->len > 1200)
+                return 0;
+
+	InBuf.Data = skb->data;
+	InBuf.Size = skb->len;
+	InBuf.Len  = 0;
+	InBuf.Next = InBuf.Data;
+	InData = 0;
+	InMask = 0;
+
+	LineBuf.Data = chan->fax2.abLine;
+	LineBuf.Size = sizeof(chan->fax2.abLine);
+	LineBuf.Len  = chan->fax2.LineLen;
+	LineBuf.Next = LineBuf.Data + LineBuf.Len;
+	LineData = chan->fax2.LineData;
+	LineDataLen = chan->fax2.LineDataLen;
+
+	OutBuf.Data = chan->fax2.abFrame;
+	OutBuf.Size = sizeof(chan->fax2.abFrame);
+	OutBuf.Len = 0;
+	OutBuf.Next = OutBuf.Data;
+
+	Event = EVENT_NONE;
+
+	chan->fax2.Eop = 0;
+
+	for (;;) {
+	  for (;;) {
+		if (InMask == 0) {
+			if (InBuf.Len >= InBuf.Size) {
+				Event = EVENT_EOD;
+				break;
+			}
+			if ((chan->fax2.Dle != _DLE_) && *InBuf.Next == _DLE_) {
+				chan->fax2.Dle = _DLE_;
+				InBuf.Next++;
+				InBuf.Len++;
+				if (InBuf.Len >= InBuf.Size) {
+					Event = EVENT_EOD;
+					break;
+				}
+			}
+			if (chan->fax2.Dle == _DLE_) {
+				chan->fax2.Dle = 0;
+				if (*InBuf.Next == _ETX_) {
+					Event = EVENT_EOP;
+					break;
+				} else
+				if (*InBuf.Next == _DLE_) {
+					/* do nothing */
+				} else {
+					eicon_log(ccard, 1,
+						"idi_err: Ch%d: unknown DLE escape %02x found\n",
+							chan->No, *InBuf.Next);
+					InBuf.Next++;
+					InBuf.Len++;
+					if (InBuf.Len >= InBuf.Size) {
+						Event = EVENT_EOD;
+						break;
+					}
+				}
+			}
+			InBuf.Len++;
+			InData = *InBuf.Next++;
+			InMask = (chan->fax->bor) ? 0x80 : 0x01;
+		}
+		while (InMask) {
+			LineData >>= 1;
+			LineDataLen++;
+			if (InData & InMask)
+				LineData |= 0x80000000;
+			if (chan->fax->bor)
+				InMask >>= 1;
+			else
+				InMask <<= 1;
+
+			if ((LineDataLen >= T4_EOL_BITSIZE) &&
+			   ((LineData & T4_EOL_MASK_DWORD) == T4_EOL_DWORD)) {
+				Event = EVENT_EOL;
+				if (LineDataLen > T4_EOL_BITSIZE) {
+					Byte = (__u8)
+						((LineData & ~T4_EOL_MASK_DWORD) >>
+						(32 - LineDataLen));
+					if (Byte == 0) {
+						if (! chan->fax2.NullByteExist) {
+							chan->fax2.NullBytesPos = LineBuf.Len;
+							chan->fax2.NullByteExist = 1;
+						}
+					} else {
+						chan->fax2.NullByteExist = 0;
+					}
+					if (LineBuf.Len < LineBuf.Size) {
+						*LineBuf.Next++  = Byte;
+						LineBuf.Len++;
+					}
+				}
+				LineDataLen = 0;
+				break;
+			}
+			if (LineDataLen >= T4_EOL_BITSIZE + 8) {
+				Byte = (__u8)
+					((LineData & ~T4_EOL_MASK_DWORD) >>
+					(32 - T4_EOL_BITSIZE - 8));
+				LineData &= T4_EOL_MASK_DWORD;
+				LineDataLen = T4_EOL_BITSIZE;
+				if (Byte == 0) {
+					if (! chan->fax2.NullByteExist) {
+						chan->fax2.NullBytesPos = LineBuf.Len;
+						chan->fax2.NullByteExist = 1;
+					}
+				} else {
+					chan->fax2.NullByteExist = 0;
+				}
+				if (LineBuf.Len < LineBuf.Size) {
+					*LineBuf.Next++  = Byte; 
+					LineBuf.Len++;
+				}
+			}
+		}
+		if (Event != EVENT_NONE)
+			break;
+	  }
+
+		if ((Event != EVENT_EOL) && (Event != EVENT_EOP))
+			break;
+
+		if ((Event == EVENT_EOP) && (LineDataLen > 0)) {
+			LineData >>= 32 - LineDataLen;
+			LineDataLen = 0;
+			while (LineData != 0) {
+				Byte = (__u8) LineData;
+				LineData >>= 8;
+				if (Byte == 0) {
+					if (! chan->fax2.NullByteExist) {
+						chan->fax2.NullBytesPos = LineBuf.Len;
+						chan->fax2.NullByteExist = 1;
+					}
+				} else {
+					chan->fax2.NullByteExist = 0;
+				}
+				if (LineBuf.Len < LineBuf.Size) {
+					*LineBuf.Next++  = Byte;
+					LineBuf.Len++;
+				}
+				
+			}
+		}
+		if (chan->fax2.NullByteExist) {
+			if (chan->fax2.NullBytesPos == 0) {
+				LineBuf.Len = 0;
+			} else {
+				LineBuf.Len = chan->fax2.NullBytesPos + 1;
+			}
+		}
+		if (LineBuf.Len > 0) {
+			if (OutBuf.Len + LineBuf.Len + SFF_LEN_FLD_SIZE > OutBuf.Size) {
+				ret = idi_fax_send_outbuf(ccard, chan, &OutBuf);
+			}
+			if (LineBuf.Len <= 216) {
+				*OutBuf.Next++ = (__u8) LineBuf.Len;
+				OutBuf.Len++;
+			} else {
+				*OutBuf.Next++ = 0;
+				*((__u16 *) OutBuf.Next)++ = (__u16) LineBuf.Len;
+				OutBuf.Len += 3;
+			}
+			memcpy(OutBuf.Next, LineBuf.Data, LineBuf.Len);
+			OutBuf.Next += LineBuf.Len;
+			OutBuf.Len  += LineBuf.Len;
+		}
+		LineBuf.Len = 0;
+		LineBuf.Next = LineBuf.Data;
+		chan->fax2.NullByteExist = 0;
+		if (Event == EVENT_EOP)
+			break;
+
+		Event = EVENT_NONE;
+	}
+
+	if (Event == EVENT_EOP) {
+		chan->fax2.Eop = 1;
+		chan->fax2.PageCount++;
+		cmd.driver = ccard->myid;
+		cmd.command = ISDN_STAT_FAXIND;
+		cmd.arg = chan->No;
+		chan->fax->r_code = ISDN_TTY_FAX_EOP;
+		ccard->interface.statcallb(&cmd);
+	}
+	if (OutBuf.Len > 0) {
+		ret = idi_fax_send_outbuf(ccard, chan, &OutBuf);
+	}
+
+	chan->fax2.LineLen = LineBuf.Len;
+	chan->fax2.LineData = LineData;
+	chan->fax2.LineDataLen = LineDataLen;
+
+#	undef EVENT_NONE
+#	undef EVENT_EOD
+#	undef EVENT_EOL
+#	undef EVENT_EOP
+
+	if (ret >= 0)
+	        dev_kfree_skb(skb);
+	if (ret == 0)
+		ret = 1;
+	return(ret);
 }
 
 void
 idi_fax_hangup(eicon_card *ccard, eicon_chan *chan)
 {
+	isdn_ctrl cmd;
 
-	/* TODO , code follows */
-
+	if (!chan->fax) {
+		eicon_log(ccard, 1, "idi_fax: hangup with NULL fax struct, ERROR\n");
+		return;
+	}
+	if ((chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) &&
+	    (chan->fax->code == 0)) {
+		cmd.driver = ccard->myid;
+		cmd.command = ISDN_STAT_FAXIND;
+		cmd.arg = chan->No;
+		chan->fax->r_code = ISDN_TTY_FAX_PTS;
+		ccard->interface.statcallb(&cmd);
+	}
+	if ((chan->fax->code > 1) && (chan->fax->code < 120))
+		chan->fax->code += 120;
+	chan->fax->r_code = ISDN_TTY_FAX_HNG;
+	cmd.driver = ccard->myid;
+	cmd.command = ISDN_STAT_FAXIND;
+	cmd.arg = chan->No;
+	ccard->interface.statcallb(&cmd);
 }
 
 #endif	/******** FAX ********/
@@ -1084,20 +2148,17 @@
 	eicon_chan_ptr *chan2;
 
 	if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) {
-		if (DebugVar & 1)
-			printk(KERN_DEBUG"idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state);
+		eicon_log(card, 1, "idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state);
 		return -ENODEV;
 	}
-	if (DebugVar & 8)
-		printk(KERN_DEBUG"idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No,
-				UReq, buffer[0], buffer[1], buffer[2], buffer[3]);
+	eicon_log(card, 8, "idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No,
+			UReq, buffer[0], buffer[1], buffer[2], buffer[3]);
 
 	skb = alloc_skb(sizeof(eicon_REQ) + len + 1, 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 in send_udata()\n", chan->No);
+		eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No);
 		if (skb) 
 			dev_kfree_skb(skb);
 		if (skb2) 
@@ -1131,6 +2192,9 @@
 	u_char buf[6];
 	struct enable_dtmf_s *dtmf_buf = (struct enable_dtmf_s *)buf;
 
+	if ((!ccard) || (!chan))
+		return;
+
 	memset(buf, 0, 6);
 	switch(cmd) {
 		case ISDN_AUDIO_SETDD:
@@ -1162,14 +2226,15 @@
 	'1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D'
 	};
 
+	if ((!ccard) || (!chan))
+		return;
+
 	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);
+			eicon_log(ccard, 16, "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);
+			eicon_log(ccard, 8, "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) &&
@@ -1181,31 +2246,24 @@
 				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); 
-			    }
+			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time);
+			eicon_log(ccard, 8, "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);
+			eicon_log(ccard, 8, "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); 
-			}
+			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time);
+			eicon_log(ccard, 8, "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]);
+			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]);
 			break;
 		case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED:
-			if (DebugVar & 8)
-  				printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No,
-					dtmf_code[buffer[1]]);
+			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No,
+				dtmf_code[buffer[1]]);
 			cmd.driver = ccard->myid;
 			cmd.command = ISDN_STAT_AUDIO;
 			cmd.parm.num[0] = ISDN_AUDIO_DTMF;
@@ -1214,8 +2272,7 @@
 			ccard->interface.statcallb(&cmd);
 			break;
 		default:
-			if (DebugVar & 8)
-				printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]);
+			eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]);
 	}
 }
 
@@ -1223,23 +2280,35 @@
 idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
 {
 	int tmp;
+	char tnum[64];
+	int dlev;
 	int free_buff;
+	ulong flags;
 	struct sk_buff *skb2;
         eicon_IND *ind = (eicon_IND *)skb->data;
 	eicon_chan *chan;
 	idi_ind_message message;
 	isdn_ctrl cmd;
 
+	if (!ccard) {
+		eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ind\n");
+  		dev_kfree_skb(skb);
+		return;
+	}
+
 	if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
+		eicon_log(ccard, 1, "idi_err: Ch??: null chan in handle_ind\n");
   		dev_kfree_skb(skb);
 		return;
 	}
 	
-	if ((DebugVar & 128) || 
-	   ((DebugVar & 16) && (ind->Ind != 8))) {
-        	printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No,
-		        ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
-	}
+	if (ind->Ind != 8)
+		dlev = 144;
+	else
+		dlev = 128;
+
+       	eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%d Id=%x 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 */
@@ -1247,15 +2316,16 @@
 		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);
+				eicon_log(ccard, 8, "idi_ind: Ch%d: Hangup\n", chan->No);
 		                while((skb2 = skb_dequeue(&chan->e.X))) {
 					dev_kfree_skb(skb2);
 				}
-				chan->e.busy = 0;
+				save_flags(flags);
+				cli();
 				chan->queued = 0;
 				chan->waitq = 0;
 				chan->waitpq = 0;
+				restore_flags(flags);
 				if (message.e_cau[0] & 0x7f) {
 					cmd.driver = ccard->myid;
 					cmd.arg = chan->No;
@@ -1269,8 +2339,10 @@
 				if (!chan->e.B2Id)
 					chan->fax = 0;
 #endif
-				if ((chan->fsm_state == EICON_STATE_ACTIVE) ||
-				    (chan->fsm_state == EICON_STATE_WMCONN)) {
+				if (((chan->fsm_state == EICON_STATE_ACTIVE) ||
+				    (chan->fsm_state == EICON_STATE_WMCONN)) ||
+				    ((chan->l2prot == ISDN_PROTO_L2_FAX) &&
+				    (chan->fsm_state == EICON_STATE_OBWAIT))) {
 					chan->fsm_state = EICON_STATE_NULL;
 				} else {
 					if (chan->e.B2Id)
@@ -1284,51 +2356,62 @@
 				}
 				break;
 			case INDICATE_IND:
-				if (DebugVar & 8)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: Indicate_Ind\n", chan->No);
+				eicon_log(ccard, 8, "idi_ind: Ch%d: Indicate_Ind\n", chan->No);
+				if (chan->fsm_state != EICON_STATE_LISTEN) {
+					eicon_log(ccard, 1, "idi_err: Ch%d: Incoming call on wrong state (%d).\n",
+						chan->No, chan->fsm_state);
+					idi_do_req(ccard, chan, HANGUP, 0);
+					break;
+				}
 				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);
-				if (strlen(message.osa)) {
-					strcat(chan->oad, ".");
-					strcat(chan->oad, message.osa);
-				}
+				strcpy(chan->dsa, message.dsa);
+				strcpy(chan->osa, message.osa);
+				chan->plan = message.plan;
+				chan->screen = message.screen;
 				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;
+				strcpy(tnum, chan->cpn);
+				if (strlen(chan->dsa)) {
+					strcat(tnum, ".");
+					strcat(tnum, chan->dsa);
+				}
+				tnum[ISDN_MSNLEN - 1] = 0;
+				strcpy(cmd.parm.setup.eazmsn, tnum);
+				strcpy(tnum, chan->oad);
+				if (strlen(chan->osa)) {
+					strcat(tnum, ".");
+					strcat(tnum, chan->osa);
+				}
+				tnum[ISDN_MSNLEN - 1] = 0;
+				strcpy(cmd.parm.setup.phone, tnum);
+				cmd.parm.setup.plan = chan->plan;
+				cmd.parm.setup.screen = chan->screen;
 				tmp = ccard->interface.statcallb(&cmd);
 				switch(tmp) {
 					case 0: /* no user responding */
 						idi_do_req(ccard, chan, HANGUP, 0);
+						chan->fsm_state = EICON_STATE_NULL;
 						break;
 					case 1: /* alert */
-						if (DebugVar & 8)
-  							printk(KERN_DEBUG"idi_req: Ch%d: Call Alert\n", chan->No);
+						eicon_log(ccard, 8, "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);
+						eicon_log(ccard, 8, "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);
+						eicon_log(ccard, 8, "idi_req: Ch%d: Incomplete Number\n", chan->No);
 					        switch(ccard->type) {
 					                case EICON_CTYPE_MAESTRAP:
 					                case EICON_CTYPE_S2M:
@@ -1342,8 +2425,7 @@
 				}
 				break;
 			case INFO_IND:
-				if (DebugVar & 8)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: Info_Ind\n", chan->No);
+				eicon_log(ccard, 8, "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);
@@ -1351,8 +2433,7 @@
 				}
 				break;
 			case CALL_IND:
-				if (DebugVar & 8)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: Call_Ind\n", chan->No);
+				eicon_log(ccard, 8, "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;
@@ -1376,8 +2457,7 @@
 					idi_hangup(ccard, chan);
 				break;
 			case CALL_CON:
-				if (DebugVar & 8)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: Call_Con\n", chan->No);
+				eicon_log(ccard, 8, "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;
@@ -1387,9 +2467,8 @@
 
 					/* check if old NetID has been removed */
 					if (chan->e.B2Id) {
-						if (DebugVar & 1)
-							printk(KERN_WARNING "idi_err: Ch%d: Old NetID %x was not removed.\n",
-								chan->No, chan->e.B2Id);
+						eicon_log(ccard, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n",
+							chan->No, chan->e.B2Id);
 						idi_do_req(ccard, chan, REMOVE, 1);
 					}
 
@@ -1405,12 +2484,10 @@
 				idi_hangup(ccard, chan);
 				break;
 			case AOC_IND:
-				if (DebugVar & 8)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: Advice of Charge\n", chan->No);
+				eicon_log(ccard, 8, "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);
+				eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind);
 		}
 	}
 	/* Network Layer */
@@ -1424,8 +2501,7 @@
 		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);
+				eicon_log(ccard, 16, "idi_ind: Ch%d: N_Connect_Ack\n", chan->No);
 				if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
 					chan->fsm_state = EICON_STATE_WMCONN;
 					break;
@@ -1445,8 +2521,7 @@
 						}
 					}
 					else {
-						if (DebugVar & 1)
-							printk(KERN_DEBUG "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n");
+						eicon_log(ccard, 1, "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n");
 					}
 #endif
 					break;
@@ -1458,8 +2533,7 @@
 				ccard->interface.statcallb(&cmd);
 				break; 
 			case IDI_N_CONNECT:
-				if (DebugVar & 16)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No);
+				eicon_log(ccard, 16,"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_FAX) {
 					break;
@@ -1475,9 +2549,11 @@
 				ccard->interface.statcallb(&cmd);
 				break; 
 			case IDI_N_DISC:
-				if (DebugVar & 16)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC\n", chan->No);
+				eicon_log(ccard, 16, "idi_ind: Ch%d: N_DISC\n", chan->No);
 				if (chan->e.B2Id) {
+		                	while((skb2 = skb_dequeue(&chan->e.X))) {
+						dev_kfree_skb(skb2);
+					}
 					idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1);
 					idi_do_req(ccard, chan, REMOVE, 1);
 				}
@@ -1487,9 +2563,12 @@
 					idi_fax_hangup(ccard, chan);
 				}
 #endif
+				save_flags(flags);
+				cli();
 				chan->queued = 0;
 				chan->waitq = 0;
 				chan->waitpq = 0;
+				restore_flags(flags);
 				idi_do_req(ccard, chan, HANGUP, 0);
 				if (chan->fsm_state == EICON_STATE_ACTIVE) {
 					cmd.driver = ccard->myid;
@@ -1503,8 +2582,7 @@
 #endif
 				break; 
 			case IDI_N_DISC_ACK:
-				if (DebugVar & 16)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No);
+				eicon_log(ccard, 16, "idi_ind: Ch%d: N_DISC_ACK\n", chan->No);
 #ifdef CONFIG_ISDN_TTY_FAX
 				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
 					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
@@ -1513,13 +2591,11 @@
 #endif
 				break; 
 			case IDI_N_DATA_ACK:
-				if (DebugVar & 16)
-  					printk(KERN_DEBUG"idi_ind: Ch%d: N_DATA_ACK\n", chan->No);
+				eicon_log(ccard, 16, "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);
+				eicon_log(ccard, 128, "idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len);
 				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
 #ifdef CONFIG_ISDN_TTY_FAX
 					idi_faxdata_rcv(ccard, chan, skb);
@@ -1538,27 +2614,26 @@
 				break; 
 #endif
 			default:
-				if (DebugVar & 8)
-					printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind);
+				eicon_log(ccard, 8, "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);
+		eicon_log(ccard, 1, "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No);
 	}
-   if (free_buff) dev_kfree_skb(skb);
+   if (free_buff)
+	dev_kfree_skb(skb);
 }
 
 int
 idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack)
 {
+	ulong flags;
 	isdn_ctrl cmd;
 
 	if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
 		/* I dont know why this happens, just ignoring this RC */
-		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);
+		eicon_log(ccard, 16, "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);
 		return 1;
 	}
 
@@ -1572,27 +2647,27 @@
 	/* Remove an Id */
 	if (chan->e.Req == REMOVE) {
 		if (ack->Reference != chan->e.ref) {
-			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);
+			eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
+				ack->Reference, chan->e.ref);
 			return 0;
 		}
+		save_flags(flags);
+		cli();
 		ccard->IdTable[ack->RcId] = NULL;
-		if (DebugVar & 16)
-			printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%x Ch=%d (%s)\n", chan->No,
-				ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
+		eicon_log(ccard, 16, "idi_ack: Ch%d: Removed : Id=%x 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;
+		restore_flags(flags);
 		return 1;
 	}
 
 	/* Signal layer */
 	if (!chan->e.ReqCh) {
-		if (DebugVar & 16)
-			printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
-				ack->RcId, ack->RcCh, ack->Reference);
+		eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
+			ack->RcId, ack->RcCh, ack->Reference);
 	} else {
 	/* Network layer */
 		switch(chan->e.Req & 0x0f) {
@@ -1606,7 +2681,10 @@
 						cmd.parm.length = chan->waitpq;
 						ccard->interface.statcallb(&cmd);
 					}
+					save_flags(flags);
+					cli();
 					chan->waitpq = 0;
+					restore_flags(flags);
 #ifdef CONFIG_ISDN_TTY_FAX
 					if (chan->l2prot == ISDN_PROTO_L2_FAX) {
 						if (((chan->queued - chan->waitq) < 1) &&
@@ -1620,20 +2698,21 @@
 								ccard->interface.statcallb(&cmd);
 							}
 							else {
-								if (DebugVar & 1)
-									printk(KERN_DEBUG "idi_ack: Sent with NULL fax struct, ERROR\n");
+								eicon_log(ccard, 1, "idi_ack: Sent with NULL fax struct, ERROR\n");
 							}
 						}
 					}
 #endif
 				}
+				save_flags(flags);
+				cli();
 				chan->queued -= chan->waitq;
 				if (chan->queued < 0) chan->queued = 0;
+				restore_flags(flags);
 				break;
 			default:
-				if (DebugVar & 16)
-					printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
-						ack->RcId, ack->RcCh, ack->Reference);
+				eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
+					ack->RcId, ack->RcCh, ack->Reference);
 		}
 	}
 	return 1;
@@ -1643,22 +2722,30 @@
 idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
 {
 	int j;
+	ulong flags;
         eicon_RC *ack = (eicon_RC *)skb->data;
 	eicon_chan *chan;
 	isdn_ctrl cmd;
 	int dCh = -1;
 
+	if (!ccard) {
+		eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ack\n");
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	save_flags(flags);
+	cli();
 	if ((chan = ccard->IdTable[ack->RcId]) != NULL)
 		dCh = chan->No;
-	
+	restore_flags(flags);
 
 	switch (ack->Rc) {
 		case OK_FC:
 		case N_FLOW_CONTROL:
 		case ASSIGN_RC:
-			if (DebugVar & 1)
-				printk(KERN_ERR "idi_ack: Ch%d: unhandled RC 0x%x\n",
-					dCh, ack->Rc);
+			eicon_log(ccard, 1, "idi_ack: Ch%d: unhandled RC 0x%x\n",
+				dCh, ack->Rc);
 			break;
 		case READY_INT:
 		case TIMER_INT:
@@ -1667,8 +2754,7 @@
 
 		case OK:
 			if (!chan) {
-				if (DebugVar & 1)
-					printk(KERN_ERR "idi_ack: Ch%d: OK on chan without Id\n", dCh);
+				eicon_log(ccard, 1, "idi_ack: Ch%d: OK on chan without Id\n", dCh);
 				break;
 			}
 			if (!idi_handle_ack_ok(ccard, chan, ack))
@@ -1677,29 +2763,29 @@
 
 		case ASSIGN_OK:
 			if (chan) {
-				if (DebugVar & 1)
-					printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n",
-						chan->No, chan->e.D3Id, chan->e.B2Id);
+				eicon_log(ccard, 1, "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n",
+					chan->No, chan->e.D3Id, chan->e.B2Id);
 			}
+			save_flags(flags);
+			cli();
 			for(j = 0; j < ccard->nchannels + 1; j++) {
-				if (ccard->bch[j].e.ref == ack->Reference) {
+				if ((ccard->bch[j].e.ref == ack->Reference) &&
+					(ccard->bch[j].e.Req == ASSIGN)) {
 					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 %x assigned (%s)\n", j, 
-							ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
+					chan = &ccard->bch[j];
+					eicon_log(ccard, 16, "idi_ack: Ch%d: Id %x assigned (%s)\n", j, 
+						ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
 					break;
 				}
 			}		
+			restore_flags(flags);
 			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);
+				eicon_log(ccard, 24, "idi_ack: Ch??: ref %d not found for Id %d\n", 
+					ack->Reference, ack->RcId);
 			}
 			break;
 
@@ -1711,9 +2797,19 @@
 		case UNKNOWN_IE:
 		case WRONG_IE:
 		default:
-			if (DebugVar & 1)
-				printk(KERN_ERR "eicon_ack: Ch%d: Not OK !!: Rc=%d Id=%x Ch=%d\n", dCh, 
-					ack->Rc, ack->RcId, ack->RcCh);
+			if (!chan) {
+				eicon_log(ccard, 1, "idi_ack: Ch%d: Not OK !! on chan without Id\n", dCh);
+				break;
+			} else
+			switch (chan->e.Req) {
+				case 12:	/* Alert */
+					eicon_log(ccard, 2, "eicon_err: Ch%d: Alert Not OK : Rc=%d Id=%x Ch=%d\n",
+						dCh, ack->Rc, ack->RcId, ack->RcCh);
+					break;
+				default:
+					eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
+						dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
+			}
 			if (dCh == ccard->nchannels) { /* Management */
 				chan->fsm_state = 2;
 			} else if (dCh >= 0) {
@@ -1726,8 +2822,13 @@
 				ccard->interface.statcallb(&cmd);
 			}
 	}
-	if (chan)
+	save_flags(flags);
+	cli();
+	if (chan) {
+		chan->e.ref = 0;
 		chan->e.busy = 0;
+	}
+	restore_flags(flags);
 	dev_kfree_skb(skb);
 	eicon_schedule_tx(ccard);
 }
@@ -1742,21 +2843,25 @@
         int len, plen = 0, offset = 0;
 	unsigned long flags;
 
+	if ((!card) || (!chan)) {
+		eicon_log(card, 1, "idi_err: Ch??: null card/chan in send_data\n");
+		return -1;
+	}
+
         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);
+		eicon_log(card, 1, "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 */
+	if (len > EICON_MAX_QUEUE)	/* 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))
+	if (chan->queued + len > EICON_MAX_QUEUE)
 		return 0;
-	if (DebugVar & 128)
-		printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len);
+
+	eicon_log(card, 128, "idi_snd: Ch%d: %d bytes\n", chan->No, len);
 
 	save_flags(flags);
 	cli();
@@ -1769,8 +2874,7 @@
 
 	        if ((!xmit_skb) || (!skb2)) {
 			restore_flags(flags);
-			if (DebugVar & 1)
-	        	        printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No);
+        	        eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No);
 			if (xmit_skb) 
 				dev_kfree_skb(skb);
 			if (skb2) 
@@ -1824,8 +2928,7 @@
         skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
 
         if ((!skb) || (!skb2)) {
-		if (DebugVar & 1)
-			printk(KERN_WARNING "idi_err: alloc_skb failed in manage_assign()\n");
+		eicon_log(card, 1, "idi_err: alloc_skb failed in manage_assign()\n");
 		if (skb) 
 			dev_kfree_skb(skb);
 		if (skb2) 
@@ -1867,8 +2970,7 @@
         skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
 
         if ((!skb) || (!skb2)) {
-		if (DebugVar & 1)
-                	printk(KERN_WARNING "idi_err: alloc_skb failed in manage_remove()\n");
+               	eicon_log(card, 1, "idi_err: alloc_skb failed in manage_remove()\n");
 		if (skb) 
 			dev_kfree_skb(skb);
 		if (skb2) 
@@ -1934,8 +3036,7 @@
 	chan->fsm_state = 0;
 
 	if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) {
-		if (DebugVar & 1)
-                	printk(KERN_WARNING "idi_err: alloc_manifbuf failed\n");
+               	eicon_log(card, 1, "idi_err: alloc_manifbuf failed\n");
 		chan->e.D3Id = 0;
 		return -ENOMEM;
 	}
@@ -1949,8 +3050,7 @@
         skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
 
         if ((!skb) || (!skb2)) {
-		if (DebugVar & 1)
-                	printk(KERN_WARNING "idi_err_manif: alloc_skb failed in manage()\n");
+               	eicon_log(card, 1, "idi_err_manif: alloc_skb failed in manage()\n");
 		if (skb) 
 			dev_kfree_skb(skb);
 		if (skb2) 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/eicon/eicon_io.c linux.14p6/drivers/isdn/eicon/eicon_io.c
--- linux.vanilla/drivers/isdn/eicon/eicon_io.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/eicon/eicon_io.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: eicon_io.c,v 1.5 1999/08/31 11:20:11 paul Exp $
+/* $Id: eicon_io.c,v 1.8 1999/10/08 22:09:34 armin Exp $
  *
  * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
  * Code for communicating with hardware.
@@ -24,6 +24,17 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_io.c,v $
+ * Revision 1.8  1999/10/08 22:09:34  armin
+ * Some fixes of cards interface handling.
+ * Bugfix of NULL pointer occurence.
+ * Changed a few log outputs.
+ *
+ * Revision 1.7  1999/09/26 14:17:53  armin
+ * Improved debug and log via readstat()
+ *
+ * Revision 1.6  1999/09/21 20:35:43  armin
+ * added more error checking.
+ *
  * Revision 1.5  1999/08/31 11:20:11  paul
  * various spelling corrections (new checksums may be needed, Karsten!)
  *
@@ -54,19 +65,21 @@
 
 void
 eicon_io_rcv_dispatch(eicon_card *ccard) {
+	ulong flags;
         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");
+	        eicon_log(ccard, 1, "eicon_err: NULL card in rcv_dispatch !\n");
                 return;
         }
 
 	while((skb = skb_dequeue(&ccard->rcvq))) {
         	ind = (eicon_IND *)skb->data;
 
+		save_flags(flags);
+		cli();
         	if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
 			if (DebugVar & 1) {
 				switch(ind->Ind) {
@@ -74,14 +87,16 @@
 						/* doesn't matter if this happens */ 
 						break;
 					default: 
-						printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%x\n", ind->Ind, ind->IndId);
-						printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
+						eicon_log(ccard, 1, "idi: Indication for unknown channel Ind=%d Id=%x\n", ind->Ind, ind->IndId);
+						eicon_log(ccard, 1, "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
 							ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
 				}
 			}
+			restore_flags(flags);
 	                dev_kfree_skb(skb);
 	                continue;
 	        }
+		restore_flags(flags);
 
 		if (chan->e.complete) { /* check for rec-buffer chaining */
 			if (ind->MLength == ind->RBuffer.length) {
@@ -97,10 +112,12 @@
 			}
 		}
 		else {
+			save_flags(flags);
+			cli();
 			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");
+                		eicon_log(ccard, 1, "eicon: buffer incomplete, but 0 in queue\n");
+				restore_flags(flags);
 	                	dev_kfree_skb(skb);
 				continue;	
 			}
@@ -108,8 +125,8 @@
 			skb_new = alloc_skb(((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length),
 					GFP_ATOMIC);
 			if (!skb_new) {
-				if (DebugVar & 1)
-	                		printk(KERN_ERR "eicon_io: skb_alloc failed in rcv_dispatch()\n");
+                		eicon_log(ccard, 1, "eicon_io: skb_alloc failed in rcv_dispatch()\n");
+				restore_flags(flags);
 	                	dev_kfree_skb(skb);
 	                	dev_kfree_skb(skb2);
 				continue;	
@@ -128,12 +145,14 @@
                 	dev_kfree_skb(skb2);
 			if (ind->MLength == ind->RBuffer.length) {
 				chan->e.complete = 2;
+				restore_flags(flags);
 				idi_handle_ind(ccard, skb_new);
 				continue;
 			}
 			else {
 				chan->e.complete = 0;
 				skb_queue_tail(&chan->e.R, skb_new);
+				restore_flags(flags);
 				continue;
 			}
 		}
@@ -145,8 +164,7 @@
         struct sk_buff *skb;
 
         if (!ccard) {
-		if (DebugVar & 1)
-			printk(KERN_WARNING "eicon_io_ack_dispatch: NULL card!\n");
+		eicon_log(ccard, 1, "eicon_err: NULL card in ack_dispatch!\n");
                 return;
         }
 	while((skb = skb_dequeue(&ccard->rackq))) {
@@ -402,13 +420,13 @@
 	int scom = 0;
 	int tmp = 0;
 	int quloop = 1;
+	int dlev = 0;
 
 	pci_card = &ccard->hwif.pci;
 	isa_card = &ccard->hwif.isa;
 
         if (!ccard) {
-		if (DebugVar & 1)
-                	printk(KERN_WARNING "eicon_transmit: NULL card!\n");
+               	eicon_log(ccard, 1, "eicon_transmit: NULL card!\n");
                 return;
         }
 
@@ -441,7 +459,7 @@
 			prram = 0;
 			break;
 		default:
-                	printk(KERN_WARNING "eicon_transmit: unsupported card-type!\n");
+                	eicon_log(ccard, 1, "eicon_transmit: unsupported card-type!\n");
 			return;
 	}
 
@@ -460,16 +478,14 @@
 				}
         	                restore_flags(flags);
                 	        skb_queue_head(&ccard->sndq, skb2);
-				if (DebugVar & 32)
-        	                	printk(KERN_INFO "eicon: transmit: Card not ready\n");
+       	                	eicon_log(ccard, 32, "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");
+       	                	eicon_log(ccard, 32, "eicon: transmit: Card not ready\n");
 	                        return;
         	        }
 		}
@@ -482,8 +498,7 @@
 		  cli();
 		  reqbuf = (eicon_REQ *)skb->data;
 		  if ((reqbuf->Reference) && (chan->e.B2Id == 0) && (reqbuf->ReqId & 0x1f)) {
-			if (DebugVar & 16)
-				printk(KERN_WARNING "eicon: transmit: error Id=0 on %d (Net)\n", chan->No); 
+			eicon_log(ccard, 16, "eicon: transmit: error Id=0 on %d (Net)\n", chan->No); 
 		  } else {
 			if (scom) {
 				ram_outw(ccard, &com->XBuffer.length, reqbuf->XBuffer.length);
@@ -498,7 +513,7 @@
 				ram_outb(ccard, &ReqOut->ReqCh, reqbuf->ReqCh);
 				ram_outb(ccard, &ReqOut->Req, reqbuf->Req); 
 			}
-
+			dlev = 160;
 			if (reqbuf->ReqId & 0x1f) { /* if this is no ASSIGN */
 
 				if (!reqbuf->Reference) { /* Signal Layer */
@@ -520,6 +535,7 @@
 					   ((reqbuf->Req & 0x0f) == 0x01)) { /* Send Data */
 						chan->waitq = reqbuf->XBuffer.length;
 						chan->waitpq += reqbuf->XBuffer.length;
+						dlev = 128;
 					}
 				}
 
@@ -547,13 +563,12 @@
 			else
 				ram_outw(ccard, &prram->NextReq, ram_inw(ccard, &ReqOut->next)); 
 
-			chan->e.busy = 1; 
-			if (DebugVar & 32)
-	                	printk(KERN_DEBUG "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", 
-							reqbuf->Req, 
-							ram_inb(ccard, &ReqOut->ReqId),
-							reqbuf->ReqCh, reqbuf->XBuffer.length,
-							chan->e.ref); 
+			chan->e.busy = 1;
+	               	eicon_log(ccard, dlev, "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", 
+					reqbuf->Req, 
+					ram_inb(ccard, &ReqOut->ReqId),
+					reqbuf->ReqCh, reqbuf->XBuffer.length,
+					chan->e.ref); 
 		  }
 		  restore_flags(flags);
 		  dev_kfree_skb(skb);
@@ -562,8 +577,7 @@
 		} 
 		else {
 		skb_queue_tail(&ccard->sackq, skb2);
-		if (DebugVar & 32)
-                	printk(KERN_INFO "eicon: transmit: busy chan %d\n", chan->No); 
+               	eicon_log(ccard, 128, "eicon: transmit: busy chan %d\n", chan->No); 
 		}
 
 		if (scom)
@@ -604,10 +618,11 @@
 	unsigned char *irqprobe = 0;
 	int scom = 0;
 	int tmp = 0;
+	int dlev = 0;
 
 
         if (!ccard) {
-                printk(KERN_WARNING "eicon_irq: spurious interrupt %d\n", irq);
+                eicon_log(ccard, 1, "eicon_irq: spurious interrupt %d\n", irq);
                 return;
         }
 
@@ -659,7 +674,7 @@
 			prram = 0;
 			break;
 		default:
-                	printk(KERN_WARNING "eicon_irq: unsupported card-type!\n");
+                	eicon_log(ccard, 1, "eicon_irq: unsupported card-type!\n");
 			return;
 	}
 
@@ -709,24 +724,21 @@
 		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 reports no interrupt!\n");
+				eicon_log(ccard, 1, "eicon: IRQ: card reports no interrupt!\n");
 				return;
 			} 
 			break;
 #endif
 		case EICON_CTYPE_MAESTRAP:
 			if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */
-				if (DebugVar & 1)
-					printk(KERN_DEBUG "eicon: IRQ: card reports no interrupt!\n");
+				eicon_log(ccard, 1, "eicon: IRQ: card reports 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 reports no interrupt!\n");
+				eicon_log(ccard, 1, "eicon: IRQ: card reports no interrupt!\n");
 				return;
 			} 
 			break;
@@ -738,8 +750,7 @@
 	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");
+                       	eicon_log(ccard, 64, "eicon: IRQ Rc=READY_INT\n");
 			if (ccard->ReadyInt) {
 				ccard->ReadyInt--;
 				ram_outb(ccard, &com->Rc, 0);
@@ -747,17 +758,15 @@
 		} else {
 			skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
 			if (!skb) {
-				if (DebugVar & 1)
-	                		printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n");
+                		eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
 			} else {
 				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=%x Ch=%d Ref=%d\n",
-						tmp,ack->RcId,ack->RcCh,ack->Reference);
+               	        	eicon_log(ccard, 128, "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n",
+					tmp,ack->RcId,ack->RcCh,ack->Reference);
 				skb_queue_tail(&ccard->rackq, skb);
 				eicon_schedule_ack(ccard);
 			}
@@ -773,8 +782,7 @@
 			int len = ram_inw(ccard, &com->RBuffer.length);
 			skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
 			if (!skb) {
-				if (DebugVar & 1)
-	                		printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n");
+                		eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
 			} else {
 				ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
 				ind->Ind = tmp;
@@ -782,9 +790,12 @@
 				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=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
+				ind->RBuffer.length = len;
+				if ((tmp == 1) || (tmp == 8))
+					dlev = 128;
+				else
+					dlev = 192;
+                       		eicon_log(ccard, dlev, "eicon: IRQ Ind=%d Id=%x 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);
@@ -807,17 +818,15 @@
                         if((Rc=ram_inb(ccard, &RcIn->Rc))) {
 				skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
 				if (!skb) {
-					if (DebugVar & 1)
-	                			printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n");
+                			eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
 				} else {
 					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=%x Ch=%d Ref=%d\n",
-							Rc,ack->RcId,ack->RcCh,ack->Reference);
+        	                	eicon_log(ccard, 128, "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n",
+						Rc,ack->RcId,ack->RcCh,ack->Reference);
 					skb_queue_tail(&ccard->rackq, skb);
 					eicon_schedule_ack(ccard);
 				}
@@ -842,8 +851,7 @@
 				int len = ram_inw(ccard, &IndIn->RBuffer.length);
 				skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
 				if (!skb) {
-					if (DebugVar & 1)
-	                			printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n");
+                			eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
 				} else {
 					ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
 					ind->Ind = Ind;
@@ -852,8 +860,11 @@
 					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=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
+					if ((Ind == 1) || (Ind == 8))
+						dlev = 128;
+					else
+						dlev = 192;
+                	        	eicon_log(ccard, dlev, "eicon: IRQ Ind=%d Id=%x 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);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/eicon/eicon_isa.h linux.14p6/drivers/isdn/eicon/eicon_isa.h
--- linux.vanilla/drivers/isdn/eicon/eicon_isa.h	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/eicon/eicon_isa.h	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: eicon_isa.h,v 1.5 1999/09/08 20:17:31 armin Exp $
+/* $Id: eicon_isa.h,v 1.6 1999/11/15 19:37:04 keil Exp $
  *
  * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
  *
@@ -21,6 +21,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_isa.h,v $
+ * Revision 1.6  1999/11/15 19:37:04  keil
+ * need config.h
+ *
  * Revision 1.5  1999/09/08 20:17:31  armin
  * Added microchannel patch from Erik Weber.
  *
@@ -48,6 +51,7 @@
 #define eicon_isa_h
 
 #ifdef __KERNEL__
+#include <linux/config.h>
 
 /* Factory defaults for ISA-Cards */
 #define EICON_ISA_MEMBASE 0xd0000
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/eicon/eicon_mod.c linux.14p6/drivers/isdn/eicon/eicon_mod.c
--- linux.vanilla/drivers/isdn/eicon/eicon_mod.c	Wed Oct 20 01:12:36 1999
+++ linux.14p6/drivers/isdn/eicon/eicon_mod.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: eicon_mod.c,v 1.15 1999/09/08 20:17:31 armin Exp $
+/* $Id: eicon_mod.c,v 1.19 1999/11/12 13:21:44 armin Exp $
  *
  * ISDN lowlevel-module for Eicon.Diehl active cards.
  * 
@@ -11,6 +11,11 @@
  *
  *              Deutsche Telekom AG for S2M support.
  *
+ *		Deutsche Mailbox Saar-Lor-Lux GmbH
+ *		for sponsoring and testing fax
+ *		capabilities with Diva Server cards.
+ *		(dor@deutschemailbox.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)
@@ -26,6 +31,20 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: eicon_mod.c,v $
+ * Revision 1.19  1999/11/12 13:21:44  armin
+ * Bugfix of undefined reference with CONFIG_MCA
+ *
+ * Revision 1.18  1999/10/11 18:13:25  armin
+ * Added fax capabilities for Eicon Diva Server cards.
+ *
+ * Revision 1.17  1999/10/08 22:09:34  armin
+ * Some fixes of cards interface handling.
+ * Bugfix of NULL pointer occurence.
+ * Changed a few log outputs.
+ *
+ * Revision 1.16  1999/09/26 14:17:53  armin
+ * Improved debug and log via readstat()
+ *
  * Revision 1.15  1999/09/08 20:17:31  armin
  * Added microchannel patch from Erik Weber.
  *
@@ -104,7 +123,7 @@
 static eicon_card *cards = (eicon_card *) NULL;   /* glob. var , contains
                                                      start of card-list   */
 
-static char *eicon_revision = "$Revision: 1.15 $";
+static char *eicon_revision = "$Revision: 1.19 $";
 
 extern char *eicon_pci_revision;
 extern char *eicon_isa_revision;
@@ -185,8 +204,7 @@
 {
 	if ((channel >= 0) && (channel < card->nchannels))
         	return &(card->bch[channel]);
-	if (DebugVar & 1)
-		printk(KERN_WARNING "eicon: Invalid channel %d\n", channel);
+	eicon_log(card, 1, "eicon: Invalid channel %d\n", channel);
 	return NULL;
 }
 
@@ -263,9 +281,8 @@
 			eicon_io_rcv_dispatch(card);
 			break;
 		default:
-			if (DebugVar & 1)
-				printk(KERN_WARNING
-				       "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+			eicon_log(card, 1,
+			       "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
 	}
 }
 
@@ -279,9 +296,8 @@
 			eicon_io_ack_dispatch(card);
 			break;
 		default:
-			if (DebugVar & 1)
-				printk(KERN_WARNING
-			       		"eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+			eicon_log(card, 1,
+		       		"eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
 	}
 }
 
@@ -295,9 +311,8 @@
 			eicon_io_transmit(card);
 			break;
 		default:
-			if (DebugVar & 1)
-				printk(KERN_WARNING
-				       "eicon_transmit: Illegal bustype %d\n", card->bus);
+			eicon_log(card, 1,
+			       "eicon_transmit: Illegal bustype %d\n", card->bus);
 	}
 }
 
@@ -307,8 +322,7 @@
 	int ret_val;
 
 	if (!(xlr = kmalloc(sizeof(xlogreq_t), GFP_KERNEL))) {
-		if (DebugVar & 1)
-			printk(KERN_WARNING "idi_err: alloc_xlogreq_t failed\n");
+		eicon_log(card, 1, "idi_err: alloc_xlogreq_t failed\n");
 		return -ENOMEM;
 	}
 	if (copy_from_user(xlr, xlogreq, sizeof(xlogreq_t))) {
@@ -338,9 +352,8 @@
 	int ret = 0;
 	unsigned long flags;
  
-	if (DebugVar & 16)
-		printk(KERN_WARNING "eicon_cmd 0x%x with arg 0x%lx (0x%lx)\n",
-			c->command, c->arg, (ulong) *c->parm.num);
+	eicon_log(card, 16, "eicon_cmd 0x%x with arg 0x%lx (0x%lx)\n",
+		c->command, c->arg, (ulong) *c->parm.num);
 
         switch (c->command) {
 		case ISDN_CMD_IOCTL:
@@ -360,9 +373,8 @@
 							return card->hwif.pci.PCIram;
 #endif
 						default:
-							if (DebugVar & 1)
-								printk(KERN_WARNING
-								       "eicon: Illegal BUS type %d\n",
+							eicon_log(card, 1,
+							       "eicon: Illegal BUS type %d\n",
 							       card->bus);
 							ret = -ENODEV;
 					}
@@ -389,9 +401,8 @@
 							return 0;
 #endif /* CONFIG_MCA */
 						default:
-							if (DebugVar & 1)
-								printk(KERN_WARNING
-							      		"eicon: Illegal BUS type %d\n",
+							eicon_log(card, 1,
+						      		"eicon: Illegal BUS type %d\n",
 							       card->bus);
 							ret = -ENODEV;
 					}					
@@ -406,9 +417,8 @@
 							return card->hwif.pci.irq;
 #endif
 						default:
-							if (DebugVar & 1)
-								printk(KERN_WARNING
-								       "eicon: Illegal BUS type %d\n",
+							eicon_log(card, 1,
+							       "eicon: Illegal BUS type %d\n",
 							       card->bus);
 							ret = -ENODEV;
 					}
@@ -423,9 +433,8 @@
 							card->hwif.isa.irq = a;
 							return 0;
 						default:
-							if (DebugVar & 1)
-								printk(KERN_WARNING
-							      		"eicon: Illegal BUS type %d\n",
+							eicon_log(card, 1,
+						      		"eicon: Illegal BUS type %d\n",
 							       card->bus);
 							ret = -ENODEV;
 					}					
@@ -441,9 +450,8 @@
 								&(((eicon_codebuf *)a)->isa));
 							break;
 						default:
-							if (DebugVar & 1)
-								printk(KERN_WARNING
-								       "eicon: Illegal BUS type %d\n",
+							eicon_log(card, 1,
+							       "eicon: Illegal BUS type %d\n",
 							       card->bus);
 							ret = -ENODEV;
 					}
@@ -475,9 +483,8 @@
 							}
 							break;
 						default:
-							if (DebugVar & 1)
-								printk(KERN_WARNING
-								       "eicon: Illegal BUS type %d\n",
+							eicon_log(card, 1,
+							       "eicon: Illegal BUS type %d\n",
 							       card->bus);
 							ret = -ENODEV;
 					}
@@ -541,7 +548,7 @@
 					return 0;
 				case EICON_IOCTL_DEBUGVAR:
 					DebugVar = a;
-					printk(KERN_DEBUG"Eicon: Debug Value set to %ld\n", DebugVar);
+					eicon_log(card, 1, "Eicon: Debug Value set to %ld\n", DebugVar);
 					return 0;
 #ifdef MODULE
 				case EICON_IOCTL_FREEIT:
@@ -562,8 +569,7 @@
 			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",
+				eicon_log(card, 1, "Dial on channel %d with state %d\n",
 					chan->No, chan->fsm_state);
 				return -EBUSY;
 			}
@@ -572,7 +578,6 @@
 			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,
@@ -667,20 +672,17 @@
 		case ISDN_CMD_GETEAZ:
 			if (!card->flags & EICON_FLAGS_RUNNING)
 				return -ENODEV;
-			if (DebugVar & 1)
-				printk(KERN_DEBUG "eicon CMD_GETEAZ not implemented\n");
+			eicon_log(card, 1, "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");
+			eicon_log(card, 1, "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");
+			eicon_log(card, 1, "eicon CMD_GETSIL not implemented\n");
 			return 0;
 		case ISDN_CMD_LOCK:
 			MOD_INC_USE_COUNT;
@@ -752,6 +754,52 @@
 static int
 if_readstatus(u_char * buf, int len, int user, int id, int channel)
 {
+	int count = 0;
+	int cnt = 0;
+	ulong flags = 0;
+	u_char *p = buf;
+	struct sk_buff *skb;
+
+        eicon_card *card = eicon_findcard(id);
+	
+        if (card) {
+                if (!card->flags & EICON_FLAGS_RUNNING)
+                        return -ENODEV;
+	
+		save_flags(flags);
+		cli();
+		while((skb = skb_dequeue(&card->statq))) {
+
+			if ((skb->len + count) > len)
+				cnt = len - count;
+			else
+				cnt = skb->len;
+
+			if (user)
+				copy_to_user(p, skb->data, cnt);
+			else
+				memcpy(p, skb->data, cnt);
+
+			count += cnt;
+			p += cnt;
+
+			if (cnt == skb->len) {
+				dev_kfree_skb(skb);
+				if (card->statq_entries > 0)
+					card->statq_entries--;
+			} else {
+				skb_pull(skb, cnt);
+				skb_queue_head(&card->statq, skb);
+				restore_flags(flags);
+				return count;
+			}
+		}
+		card->statq_entries = 0;
+		restore_flags(flags);
+		return count;
+        }
+        printk(KERN_ERR
+               "eicon: if_readstatus called with invalid driverId!\n");
         return 0;
 }
 
@@ -766,14 +814,11 @@
 	len = skb->len;
 	
         if (card) {
-                if (!card->flags & EICON_FLAGS_RUNNING) {
-			dev_kfree_skb(skb);
+                if (!card->flags & EICON_FLAGS_RUNNING)
                         return -ENODEV;
-		}
-        	if (!(chan = find_channel(card, channel))) {
-			dev_kfree_skb(skb);
+        	if (!(chan = find_channel(card, channel)))
 			return -ENODEV;
-		}
+
 		if (chan->fsm_state == EICON_STATE_ACTIVE) {
 #ifdef CONFIG_ISDN_TTY_FAX
 			if (chan->l2prot == ISDN_PROTO_L2_FAX) {
@@ -785,16 +830,115 @@
 				ret = idi_send_data(card, chan, ack, skb, 1);
 			return (ret);
 		} else {
-			dev_kfree_skb(skb);
 			return -ENODEV;
 		}
         }
         printk(KERN_ERR
                "eicon: if_sendbuf called with invalid driverId!\n");
-	dev_kfree_skb(skb);
         return -ENODEV;
 }
 
+/* jiftime() copied from HiSax */
+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);
+}
+
+void
+eicon_putstatus(eicon_card * card, char * buf)
+{
+	ulong flags;
+	int count;
+	isdn_ctrl cmd;
+	u_char *p;
+	struct sk_buff *skb;
+
+	if (!card)
+		return;
+
+	save_flags(flags);
+	cli();
+	count = strlen(buf);
+	skb = alloc_skb(count, GFP_ATOMIC);
+	if (!skb) {
+		restore_flags(flags);
+		printk(KERN_ERR "eicon: could not alloc skb in putstatus\n");
+		return;
+	}
+	p = skb_put(skb, count);
+	memcpy(p, buf, count);
+
+	skb_queue_tail(&card->statq, skb);
+
+	if (card->statq_entries >= MAX_STATUS_BUFFER) {
+		if ((skb = skb_dequeue(&card->statq))) {
+			count -= skb->len;
+			dev_kfree_skb(skb);
+		} else
+			count = 0;
+	} else
+		card->statq_entries++;
+
+	restore_flags(flags);
+        if (count) {
+                cmd.command = ISDN_STAT_STAVAIL;
+                cmd.driver = card->myid;
+                cmd.arg = count;
+		card->interface.statcallb(&cmd);
+        }
+}
+
+/*
+ * Debug and Log 
+ */
+void
+eicon_log(eicon_card * card, int level, const char *fmt, ...)
+{
+	va_list args;
+	char Line[160];
+	u_char *p;
+
+
+	if ((DebugVar & level) || (DebugVar & 256)) {
+		va_start(args, fmt);
+
+		if (DebugVar & level) {
+			if (DebugVar & 256) {
+				/* log-buffer */
+				p = Line;
+				p += jiftime(p, jiffies);
+				*p++ = 32;
+				p += vsprintf(p, fmt, args);
+				*p = 0;	
+				eicon_putstatus(card, Line);
+			} else {
+				/* printk, syslogd */
+				vsprintf(Line, fmt, args);
+				printk(KERN_DEBUG "%s", Line);
+			}
+		}
+
+		va_end(args);
+	}
+}
+
 
 /*
  * Allocate a new card-struct, initialize it
@@ -817,7 +961,7 @@
 	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_log(card, 1,
 			       "eicon: (%s) Could not allocate card-struct.\n", id);
 			return;
 		}
@@ -826,6 +970,8 @@
 		skb_queue_head_init(&card->rcvq);
 		skb_queue_head_init(&card->rackq);
 		skb_queue_head_init(&card->sackq);
+		skb_queue_head_init(&card->statq);
+		card->statq_entries = 0;
 		card->snd_tq.routine = (void *) (void *) eicon_transmit;
 		card->snd_tq.data = card;
 		card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch;
@@ -897,7 +1043,7 @@
 						p = p->next;
 					}
 					if (!p) {
-						printk(KERN_WARNING "eicon_alloccard: Quadro Master not found.\n");
+						eicon_log(card, 1, "eicon_alloccard: Quadro Master not found.\n");
 						kfree(card);
 						return;
 					}
@@ -919,7 +1065,7 @@
 					ISDN_FEATURE_L2_V11019 |
 					ISDN_FEATURE_L2_V11038 |
 					ISDN_FEATURE_L2_MODEM |
-					/* ISDN_FEATURE_L2_FAX | */ 
+					ISDN_FEATURE_L2_FAX | 
 					ISDN_FEATURE_L3_TRANSDSP |
 					ISDN_FEATURE_L3_FAX;
                                 card->hwif.pci.card = (void *)card;
@@ -943,7 +1089,7 @@
 					ISDN_FEATURE_L2_V11019 |
 					ISDN_FEATURE_L2_V11038 |
 					ISDN_FEATURE_L2_MODEM |
-					/* ISDN_FEATURE_L2_FAX | */
+					ISDN_FEATURE_L2_FAX |
 					ISDN_FEATURE_L3_TRANSDSP |
 					ISDN_FEATURE_L3_FAX;
                                 card->hwif.pci.card = (void *)card;
@@ -992,13 +1138,13 @@
 				break;
 #endif
 			default:
-				printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type);
+				eicon_log(card, 1, "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_log(card, 1,
 			       "eicon: (%s) Could not allocate bch-struct.\n", id);
 			kfree(card);
 			return;
@@ -1044,9 +1190,8 @@
 			break;
 #endif
 		default:
-			if (DebugVar & 1)
-				printk(KERN_WARNING
-				       "eicon_registercard: Illegal BUS type %d\n",
+			eicon_log(card, 1,
+			       "eicon_registercard: Illegal BUS type %d\n",
 			       card->bus);
 			return -1;
         }
@@ -1085,9 +1230,8 @@
 			break;
 #endif
 		default:
-			if (DebugVar & 1)
-				printk(KERN_WARNING
-				       "eicon: Invalid BUS type %d\n",
+			eicon_log(card, 1,
+			       "eicon: Invalid BUS type %d\n",
 			       card->bus);
 			break;
         }
@@ -1096,6 +1240,26 @@
 
 static void
 eicon_freecard(eicon_card *card) {
+	int i;
+	struct sk_buff *skb;
+
+	for(i = 0; i < (card->nchannels + 1); i++) {
+		while((skb = skb_dequeue(&card->bch[i].e.X)))
+			dev_kfree_skb(skb);
+		while((skb = skb_dequeue(&card->bch[i].e.R)))
+			dev_kfree_skb(skb);
+	}
+	while((skb = skb_dequeue(&card->sndq)))
+		dev_kfree_skb(skb);
+	while((skb = skb_dequeue(&card->rcvq)))
+		dev_kfree_skb(skb);
+	while((skb = skb_dequeue(&card->rackq)))
+		dev_kfree_skb(skb);
+	while((skb = skb_dequeue(&card->sackq)))
+		dev_kfree_skb(skb);
+	while((skb = skb_dequeue(&card->statq)))
+		dev_kfree_skb(skb);
+
 	eicon_clear_msn(card);
 	kfree(card->bch);
 	kfree(card);
@@ -1141,9 +1305,8 @@
 					break;
 #endif
 				default:
-					if (DebugVar & 1)
-						printk(KERN_WARNING
-						       "eicon: addcard: Invalid BUS type %d\n",
+					printk(KERN_ERR
+					       "eicon: addcard: Invalid BUS type %d\n",
 					       p->bus);
 			}
 		} else
@@ -1155,7 +1318,7 @@
                         p = p->next;
                 } else {
                         /* registering failed, remove card from list, free memory */
-                        printk(KERN_WARNING
+                        printk(KERN_ERR
                                "eicon: Initialization of %s failed\n",
                                p->interface.id);
                         if (q) {
@@ -1222,10 +1385,9 @@
                 printk(KERN_INFO
                         "eicon: No MCA bus, ISDN-interfaces  not probed.\n");
         } else {
-		if (DebugVar & 8)
-               		printk(KERN_DEBUG
-			       "eicon_mca_find_card, irq=%d.\n", 
-                                irq);
+		eicon_log(NULL, 8,
+			"eicon_mca_find_card, irq=%d.\n", 
+				irq);
                	if (!eicon_mca_find_card(0, membase, irq, id))
                        card_count++;
         };
@@ -1346,10 +1508,9 @@
 {
 	int j, curr_slot = 0;
 
-	if (DebugVar & 8)
-        	printk(KERN_DEBUG
-                       "eicon_mca_find_card type: %d, membase: %#x, irq %d \n",
-                       type, membase, irq);
+       	eicon_log(NULL, 8,
+		"eicon_mca_find_card type: %d, membase: %#x, irq %d \n",
+		type, membase, irq);
 	/* find a no-driver-assigned eicon card                               */
 	for (j=0; eicon_mca_adapters[j].adf_id != 0; j++) 
 		{
@@ -1414,11 +1575,10 @@
 	int irq_array1[]={3,4,0,0,2,10,11,12};
 
         adf_pos0 = mca_read_stored_pos(slot,2);
-	if (DebugVar & 8)
-		printk(KERN_DEBUG
-                       "eicon_mca_probe irq=%d, membase=%d\n", 
-                        irq,
-                        membase);
+	eicon_log(NULL, 8,
+		"eicon_mca_probe irq=%d, membase=%d\n", 
+		irq,
+		membase);
 	switch (a_idx) {
 		case 0:                /* P/2-Adapter (== PRI/S2M ? )         */
 			cards_membase= 0xC0000+((adf_pos0>>4)*0x4000);
@@ -1473,7 +1633,7 @@
 		default:
 			return  ENODEV;
 	};
-	/* Uebereinstimmung vorgegebener membase & irq */
+	/* matching membase & irq */
 	if ( 1 == eicon_addcard(type, membase, irq, id)) { 
 		mca_set_adapter_name(slot, eicon_mca_adapters[a_idx].name);
   		mca_set_adapter_procfn(slot, (MCA_ProcFn) eicon_info, cards);
@@ -1486,10 +1646,9 @@
 		/* reset card */
 		outb_p(0,cards_io+1);
 
-		if (DebugVar & 8)
-			printk(KERN_INFO "eicon_addcard: successful for slot # %d.\n", 
+		eicon_log(NULL, 8, "eicon_addcard: successful for slot # %d.\n", 
 			cards->mca_slot+1);
-		return  0 ; /* eicon_addcard hat eine Karte zugefuegt */
+		return  0 ; /* eicon_addcard added a card */
 	} else {
 		return ENODEV;
 	};
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/callc.c linux.14p6/drivers/isdn/hisax/callc.c
--- linux.vanilla/drivers/isdn/hisax/callc.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/callc.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: callc.c,v 2.37 1999/09/20 19:49:47 keil Exp $
+/* $Id: callc.c,v 2.39 1999/10/14 20:25:28 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
@@ -11,6 +11,12 @@
  *              Fritz Elfert
  *
  * $Log: callc.c,v $
+ * Revision 2.39  1999/10/14 20:25:28  keil
+ * add a statistic for error monitoring
+ *
+ * Revision 2.38  1999/10/11 22:16:27  keil
+ * Suspend/Resume is possible without explicit ID too
+ *
  * Revision 2.37  1999/09/20 19:49:47  keil
  * Fix wrong init of PStack
  *
@@ -157,7 +163,7 @@
 #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module))
 #endif	/* MODULE */
 
-const char *lli_revision = "$Revision: 2.37 $";
+const char *lli_revision = "$Revision: 2.39 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
@@ -1478,11 +1484,6 @@
 }
 
 static void
-channel_report(struct Channel *chanp)
-{
-}
-
-static void
 distr_debug(struct IsdnCardState *csta, int debugflags)
 {
 	int i;
@@ -1515,7 +1516,6 @@
 {
 	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;
@@ -1532,20 +1532,16 @@
 		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);
-			}
+			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);
-				}
+			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;
 	}
@@ -1697,9 +1693,8 @@
 		case (ISDN_CMD_IOCTL):
 			switch (ic->arg) {
 				case (0):
-					HiSax_reportcard(csta->cardnr);
-					for (i = 0; i < 2; i++)
-						channel_report(&csta->channel[i]);
+					num = *(unsigned int *) ic->parm.num;
+					HiSax_reportcard(csta->cardnr, num);
 					break;
 				case (1):
 					num = *(unsigned int *) ic->parm.num;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/config.c linux.14p6/drivers/isdn/hisax/config.c
--- linux.vanilla/drivers/isdn/hisax/config.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/config.c	Tue Nov 16 15:10:48 1999
@@ -1,10 +1,19 @@
-/* $Id: config.c,v 2.37 1999/09/20 12:11:08 keil Exp $
+/* $Id: config.c,v 2.40 1999/10/30 13:09:45 keil Exp $
 
  * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
  *
  * $Log: config.c,v $
+ * Revision 2.40  1999/10/30 13:09:45  keil
+ * Version 3.3c
+ *
+ * Revision 2.39  1999/10/16 14:44:45  keil
+ * Fix module parm if only NICCY was selected
+ *
+ * Revision 2.38  1999/10/14 20:25:28  keil
+ * add a statistic for error monitoring
+ *
  * Revision 2.37  1999/09/20 12:11:08  keil
  * Fix hang if no protocol was selected
  *
@@ -483,10 +492,10 @@
 MODULE_PARM(irq, "1-8i");
 MODULE_PARM(mem, "1-8i");
 MODULE_PARM(id, "s");
-#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+#ifdef IO0_IO1
 MODULE_PARM(io0, "1-8i");
 MODULE_PARM(io1, "1-8i");
-#endif /* CONFIG_HISAX_16_3 */
+#endif /* IO0_IO1 */
 #endif /* MODULE */
 
 int nrcards;
@@ -519,9 +528,9 @@
 
 	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
 #ifdef MODULE
-	printk(KERN_INFO "HiSax: Version 3.3a (module)\n");
+	printk(KERN_INFO "HiSax: Version 3.3c (module)\n");
 #else
-	printk(KERN_INFO "HiSax: Version 3.3a (kernel)\n");
+	printk(KERN_INFO "HiSax: Version 3.3c (kernel)\n");
 #endif
 	strcpy(tmp, l1_revision);
 	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
@@ -1369,12 +1378,9 @@
 }
 
 void
-HiSax_reportcard(int cardnr)
+HiSax_reportcard(int cardnr, int sel)
 {
 	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]);
@@ -1382,45 +1388,33 @@
 	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",
+	printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc1 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 setstack_d 0x%lX\n", (ulong) cs->setstack_d);
-	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: dst%d l1.l1hw 0x%lX\n", i, (ulong) stptr->l1.l1hw);
-		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 ERROR_STATISTIC
+	printk(KERN_DEBUG "HiSax: dc errors(rx,crc,tx) %d,%d,%d\n",
+		cs->err_rx, cs->err_crc, cs->err_tx);
+	printk(KERN_DEBUG "HiSax: bc0 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+		cs->bcs[0].err_inv, cs->bcs[0].err_rdo, cs->bcs[0].err_crc, cs->bcs[0].err_tx);
+	printk(KERN_DEBUG "HiSax: bc1 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+		cs->bcs[1].err_inv, cs->bcs[1].err_rdo, cs->bcs[1].err_crc, cs->bcs[1].err_tx);
+	if (sel == 99) {
+		cs->err_rx  = 0;
+		cs->err_crc = 0;
+		cs->err_tx  = 0;
+		cs->bcs[0].err_inv = 0;
+		cs->bcs[0].err_rdo = 0;
+		cs->bcs[0].err_crc = 0;
+		cs->bcs[0].err_tx  = 0;
+		cs->bcs[1].err_inv = 0;
+		cs->bcs[1].err_rdo = 0;
+		cs->bcs[1].err_crc = 0;
+		cs->bcs[1].err_tx  = 0;
 	}
+#endif
 }
 
 
@@ -1599,9 +1593,7 @@
 
 	nrcards = 0;
 	HiSaxVersion();
-	if (id)			/* If id= string used */
-		HiSax_id = id;
-	/* Initialize all 8 structs, even though we only accept
+	/* Initialize all structs, even though we only accept
 	   two pcmcia cards
 	   */
 	for (i = 0; i < HISAX_MAX_CARDS; i++) {
@@ -1650,9 +1642,7 @@
 
 	nrcards = 0;
 	HiSaxVersion();
-	if (id)			/* If id= string used */
-		HiSax_id = id;
-	/* Initialize all 8 structs, even though we only accept
+	/* Initialize all structs, even though we only accept
 	   two pcmcia cards
 	   */
 	for (i = 0; i < HISAX_MAX_CARDS; i++) {
@@ -1701,12 +1691,10 @@
 
 	nrcards = 0;
 	HiSaxVersion();
-	if (id)			/* If id= string used */
-		HiSax_id = id;
-	/* Initialize all 16 structs, even though we only accept
+	/* Initialize all 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];
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/hfc_2bds0.c linux.14p6/drivers/isdn/hisax/hfc_2bds0.c
--- linux.vanilla/drivers/isdn/hisax/hfc_2bds0.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/hfc_2bds0.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: hfc_2bds0.c,v 1.9 1999/07/01 08:11:35 keil Exp $
+/* $Id: hfc_2bds0.c,v 1.10 1999/10/14 20:25:28 keil Exp $
  *
  *  specific routines for CCD's HFC 2BDS0
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: hfc_2bds0.c,v $
+ * Revision 1.10  1999/10/14 20:25:28  keil
+ * add a statistic for error monitoring
+ *
  * Revision 1.9  1999/07/01 08:11:35  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -260,6 +263,9 @@
 		if (cs->debug & L1_DEB_WARN)
 			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
 		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
 		cli();
 		while ((idx++ < count) && WaitNoBusy(cs))
 			ReadReg(cs, HFCD_DATA_NODEB, cip);
@@ -302,6 +308,9 @@
 				debugl1(cs, "FIFO CRC error");
 				dev_kfree_skb(skb);
 				skb = NULL;
+#ifdef ERROR_STATISTIC
+				bcs->err_crc++;
+#endif
 			}
 		}
 	}
@@ -741,6 +750,9 @@
 				printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n");
 				dev_kfree_skb(skb);
 				skb = NULL;
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
 			} else {
 				cli();
 				WaitNoBusy(cs);
@@ -757,6 +769,9 @@
 					debugl1(cs, "FIFO CRC error");
 					dev_kfree_skb(skb);
 					skb = NULL;
+#ifdef ERROR_STATISTIC
+					cs->err_crc++;
+#endif
 				} else {
 					skb_queue_tail(&cs->rq, skb);
 					sched_event_D(cs, D_RCVBUFREADY);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/hfc_2bs0.c linux.14p6/drivers/isdn/hisax/hfc_2bs0.c
--- linux.vanilla/drivers/isdn/hisax/hfc_2bs0.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/hfc_2bs0.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: hfc_2bs0.c,v 1.9 1999/07/01 08:11:36 keil Exp $
+/* $Id: hfc_2bs0.c,v 1.10 1999/10/14 20:25:28 keil Exp $
 
  *  specific routines for CCD's HFC 2BS0
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: hfc_2bs0.c,v $
+ * Revision 1.10  1999/10/14 20:25:28  keil
+ * add a statistic for error monitoring
+ *
  * Revision 1.9  1999/07/01 08:11:36  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -217,6 +220,9 @@
 		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
 				       HFC_CHANNEL(bcs->channel));
 		WaitForBusy(cs);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
 		return (NULL);
 	}
 	if (!(skb = dev_alloc_skb(count - 3)))
@@ -252,6 +258,9 @@
 			debugl1(cs, "FIFO CRC error");
 			dev_kfree_skb(skb);
 			skb = NULL;
+#ifdef ERROR_STATISTIC
+			bcs->err_crc++;
+#endif
 		}
 		WaitNoBusy(cs);
 		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/hfc_pci.c linux.14p6/drivers/isdn/hisax/hfc_pci.c
--- linux.vanilla/drivers/isdn/hisax/hfc_pci.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/hfc_pci.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: hfc_pci.c,v 1.20 1999/09/07 06:18:55 werner Exp $
+/* $Id: hfc_pci.c,v 1.23 1999/11/07 17:01:55 keil Exp $
 
  * hfc_pci.c     low level driver for CCD´s hfc-pci based cards
  *
@@ -23,6 +23,17 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: hfc_pci.c,v $
+ * Revision 1.23  1999/11/07 17:01:55  keil
+ * fix for 2.3 pci structs
+ *
+ * Revision 1.22  1999/10/10 20:14:27  werner
+ *
+ * Correct B2-chan usage in conjuntion with echo mode. First implementation of NT-leased line mode.
+ *
+ * Revision 1.21  1999/10/02 17:47:49  werner
+ *
+ * Changed init order, added correction for page alignment with shared mem
+ *
  * Revision 1.20  1999/09/07 06:18:55  werner
  *
  * Added io parameter for HFC-PCI based cards. Needed only with multiple cards
@@ -103,33 +114,36 @@
 
 extern const char *CardType[];
 
-static const char *hfcpci_revision = "$Revision: 1.20 $";
+static const char *hfcpci_revision = "$Revision: 1.23 $";
 
 /* table entry in the PCI devices list */
 typedef struct {
-  int vendor_id; 
-  int device_id;
-  char *vendor_name;
-  char *card_name;
-  } PCI_ENTRY;
-
-static const PCI_ENTRY id_list[] = {
-  {0x1397,0x2BD0,"CCD/Billion/Asuscom","2BD0"},
-  {0x1397,0xB000,"Billion","B000"},
-  {0x1397,0xB006,"Billion","B006"},
-  {0x1397,0xB007,"Billion","B007"},
-  {0x1397,0xB008,"Billion","B008"},
-  {0x1397,0xB009,"Billion","B009"},
-  {0x1397,0xB00A,"Billion","B00A"},
-  {0x1397,0xB00B,"Billion","B00B"},
-  {0x1397,0xB00C,"Billion","B00C"},
-  {0x1043,0x0675,"Asuscom/Askey","675"},
-  {0x0871,0xFFA2,"German telekom","T-Concept"},
-  {0x0871,0xFFA1,"German telekom","A1T"},
-  {0x1051,0x0100,"Motorola MC145575","MC145575"},
-  {0x1397,0xB100,"Seyeon","B100"},
-  {0x15B0,0x2BD0,"Zoltrix","2BD0"},
-  {0,0,NULL,NULL},      
+	int vendor_id;
+	int device_id;
+	char *vendor_name;
+	char *card_name;
+} PCI_ENTRY;
+
+#define NT_T1_COUNT 20		/* number of 3.125ms interrupts for G2 timeout */
+
+static const PCI_ENTRY id_list[] =
+{
+	{0x1397, 0x2BD0, "CCD/Billion/Asuscom", "2BD0"},
+	{0x1397, 0xB000, "Billion", "B000"},
+	{0x1397, 0xB006, "Billion", "B006"},
+	{0x1397, 0xB007, "Billion", "B007"},
+	{0x1397, 0xB008, "Billion", "B008"},
+	{0x1397, 0xB009, "Billion", "B009"},
+	{0x1397, 0xB00A, "Billion", "B00A"},
+	{0x1397, 0xB00B, "Billion", "B00B"},
+	{0x1397, 0xB00C, "Billion", "B00C"},
+	{0x1043, 0x0675, "Asuscom/Askey", "675"},
+	{0x0871, 0xFFA2, "German telekom", "T-Concept"},
+	{0x0871, 0xFFA1, "German telekom", "A1T"},
+	{0x1051, 0x0100, "Motorola MC145575", "MC145575"},
+	{0x1397, 0xB100, "Seyeon", "B100"},
+	{0x15B0, 0x2BD0, "Zoltrix", "2BD0"},
+	{0, 0, NULL, NULL},
 };
 
 
@@ -156,9 +170,21 @@
 void
 release_io_hfcpci(struct IsdnCardState *cs)
 {
+	int flags;
+
+	save_flags(flags);
+	cli();
+	cs->hw.hfcpci.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	restore_flags(flags);
+	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
+	Write_hfc(cs, HFCPCI_CIRM, 0);	/* Reset Off */
 #if CONFIG_PCI
-	pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0);	/* disabe memory mapped ports + busmaster */
-#endif	/* CONFIG_PCI */
+	pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0);	/* disable memory mapped ports + busmaster */
+#endif				/* CONFIG_PCI */
 	releasehfcpci(cs);
 	del_timer(&cs->hw.hfcpci.timer);
 	kfree(cs->hw.hfcpci.share_start);
@@ -175,11 +201,15 @@
 {
 	long flags;
 
+	save_flags(flags);
+	cli();
 	pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO);	/* enable memory mapped ports, disable busmaster */
+	cs->hw.hfcpci.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+
 	printk(KERN_INFO "HFC_PCI: resetting card\n");
 	pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER);	/* enable memory ports + busmaster */
 	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
-	save_flags(flags);
 	sti();
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout((30 * HZ) / 1000);	/* Timeout 30ms */
@@ -192,25 +222,23 @@
 	cs->hw.hfcpci.fifo_en = 0x30;	/* only D fifos enabled */
 	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
 
-	cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */
+	cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK;	/* no echo connect , threshold */
 	Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
 
 	Write_hfc(cs, HFCPCI_CLKDEL, 0x0e);	/* ST-Bit delay for TE-Mode */
 	cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE;
 	Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);	/* S/T Auto awake */
-	cs->hw.hfcpci.bswapped = 0; /* no exchange */
+	cs->hw.hfcpci.bswapped = 0;	/* no exchange */
+	cs->hw.hfcpci.nt_mode = 0;	/* we are in TE mode */
 	cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
 	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
 
-	cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE;
-	cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | 
-	                       HFCPCI_INTS_L1STATE | HFCPCI_CLTIMER;
+	cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+	    HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
 	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
-	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
 
 	/* Clear already pending ints */
 	if (Read_hfc(cs, HFCPCI_INT_S1));
-	if (Read_hfc(cs, HFCPCI_INT_S2));
 
 	Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2);	/* HFC ST 2 */
 	udelay(10);
@@ -218,24 +246,29 @@
 	cs->hw.hfcpci.mst_m = HFCPCI_MASTER;	/* HFC Master Mode */
 
 	Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
-	cs->hw.hfcpci.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */
+	cs->hw.hfcpci.sctrl = 0x40;	/* set tx_lo mode, error in datasheet ! */
 	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
 	cs->hw.hfcpci.sctrl_r = 0;
 	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
 
-        /* Init GCI/IOM2 in master mode */
+	/* Init GCI/IOM2 in master mode */
 	/* Slots 0 and 1 are set for B-chan 1 and 2 */
 	/* D- and monitor/CI channel are not enabled */
 	/* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */
-	/* STIO2 is used as data input, B1+B2 from IOM->ST */ 
+	/* STIO2 is used as data input, B1+B2 from IOM->ST */
 	/* ST B-channel send disabled -> continous 1s */
 	/* The IOM slots are always enabled */
-	cs->hw.hfcpci.conn = 0x36; /* set data flow directions */
+	cs->hw.hfcpci.conn = 0x36;	/* set data flow directions */
 	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
-	Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */
-	Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */
-	Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */
-	Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */
+	Write_hfc(cs, HFCPCI_B1_SSL, 0x80);	/* B1-Slot 0 STIO1 out enabled */
+	Write_hfc(cs, HFCPCI_B2_SSL, 0x81);	/* B2-Slot 1 STIO1 out enabled */
+	Write_hfc(cs, HFCPCI_B1_RSL, 0x80);	/* B1-Slot 0 STIO2 in enabled */
+	Write_hfc(cs, HFCPCI_B2_RSL, 0x81);	/* B2-Slot 1 STIO2 in enabled */
+
+	/* Finally enable IRQ output */
+	cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE;
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	if (Read_hfc(cs, HFCPCI_INT_S2));
 	restore_flags(flags);
 }
 
@@ -326,7 +359,7 @@
 		count -= 3;
 		ptr = skb_put(skb, count);
 
- 		if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL)
+		if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL)
 			maxlen = count;		/* complete transfer */
 		else
 			maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;	/* maximum */
@@ -418,54 +451,56 @@
 /*******************************************************************************/
 /* check for transparent receive data and read max one threshold size if avail */
 /*******************************************************************************/
-int hfcpci_empty_fifo_trans(struct BCState *bcs, bzfifo_type *bz, u_char *bdata)
-{ unsigned short *z1r, *z2r;
-  int new_z2, fcnt, maxlen;
-  struct sk_buff *skb;
-  u_char *ptr, *ptr1;
-
-  z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */
-  z2r = z1r + 1; 
-
-  if (!(fcnt = *z1r - *z2r)) 
-    return(0); /* no data avail */
-
-  if (fcnt <= 0) 
-    fcnt += B_FIFO_SIZE; /* bytes actually buffered */  
-  if (fcnt > HFCPCI_BTRANS_THRESHOLD)
-    fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */
-
-  new_z2 = *z2r + fcnt;	/* new position in fifo */
-  if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
-    new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
+int
+hfcpci_empty_fifo_trans(struct BCState *bcs, bzfifo_type * bz, u_char * bdata)
+{
+	unsigned short *z1r, *z2r;
+	int new_z2, fcnt, maxlen;
+	struct sk_buff *skb;
+	u_char *ptr, *ptr1;
+
+	z1r = &bz->za[MAX_B_FRAMES].z1;		/* pointer to z reg */
+	z2r = z1r + 1;
+
+	if (!(fcnt = *z1r - *z2r))
+		return (0);	/* no data avail */
+
+	if (fcnt <= 0)
+		fcnt += B_FIFO_SIZE;	/* bytes actually buffered */
+	if (fcnt > HFCPCI_BTRANS_THRESHOLD)
+		fcnt = HFCPCI_BTRANS_THRESHOLD;		/* limit size */
+
+	new_z2 = *z2r + fcnt;	/* new position in fifo */
+	if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+		new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
 
-  if (!(skb = dev_alloc_skb(fcnt)))
+	if (!(skb = dev_alloc_skb(fcnt)))
 		printk(KERN_WARNING "HFCPCI: receive out of memory\n");
-  else {
-    ptr = skb_put(skb, fcnt);
-    if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
-      maxlen = fcnt; /* complete transfer */
-    else
-      maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r;	/* maximum */
-
-    ptr1 = bdata + (*z2r - B_SUB_VAL);	/* start of data */
-    memcpy(ptr, ptr1, maxlen);	/* copy data */
-    fcnt -= maxlen;
-
-    if (fcnt) {	/* rest remaining */
-      ptr += maxlen;
-      ptr1 = bdata;	/* start of buffer */
-      memcpy(ptr, ptr1, fcnt);	/* rest */
-    }
-    cli();
-    skb_queue_tail(&bcs->rqueue, skb);
-    sti();
-    hfcpci_sched_event(bcs, B_RCVBUFREADY); 
-  }
-  
-  *z2r = new_z2; /* new position */
-  return(1);
-} /* hfcpci_empty_fifo_trans */ 
+	else {
+		ptr = skb_put(skb, fcnt);
+		if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
+			maxlen = fcnt;	/* complete transfer */
+		else
+			maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r;	/* maximum */
+
+		ptr1 = bdata + (*z2r - B_SUB_VAL);	/* start of data */
+		memcpy(ptr, ptr1, maxlen);	/* copy data */
+		fcnt -= maxlen;
+
+		if (fcnt) {	/* rest remaining */
+			ptr += maxlen;
+			ptr1 = bdata;	/* start of buffer */
+			memcpy(ptr, ptr1, fcnt);	/* rest */
+		}
+		cli();
+		skb_queue_tail(&bcs->rqueue, skb);
+		sti();
+		hfcpci_sched_event(bcs, B_RCVBUFREADY);
+	}
+
+	*z2r = new_z2;		/* new position */
+	return (1);
+}				/* hfcpci_empty_fifo_trans */
 
 /**********************************/
 /* B-channel main receive routine */
@@ -526,11 +561,10 @@
 			receive = 1;
 		else
 			receive = 0;
-	} else 
-	  if (bcs->mode == L1_MODE_TRANS)
-	    receive = hfcpci_empty_fifo_trans(bcs, bz, bdata);
-	  else
-	    receive = 0;
+	} else if (bcs->mode == L1_MODE_TRANS)
+		receive = hfcpci_empty_fifo_trans(bcs, bz, bdata);
+	else
+		receive = 0;
 	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
 	if (count && receive)
 		goto Begin;
@@ -622,7 +656,7 @@
 	bzfifo_type *bz;
 	u_char *bdata;
 	u_char new_f1, *src, *dst;
-        unsigned short *z1t, *z2t;
+	unsigned short *z1t, *z2t;
 
 	if (!bcs->tx_skb)
 		return;
@@ -641,57 +675,53 @@
 	}
 
 	if (bcs->mode == L1_MODE_TRANS) {
-	  z1t = &bz->za[MAX_B_FRAMES].z1;
-	  z2t = z1t + 1;
-	  if (cs->debug & L1_DEB_HSCX)
-	          debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)",
-		  bcs->channel, *z1t, *z2t);
-	  fcnt = *z2t - *z1t;
-	  if (fcnt <= 0)
-	          fcnt += B_FIFO_SIZE;	/* fcnt contains available bytes in fifo */
-	  fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */
-
-	  while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) {
-	    if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) { 
-	      /* data is suitable for fifo */
-	      count = bcs->tx_skb->len;
-
-	      new_z1 = *z1t + count;	/* new buffer Position */
-	      if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
-		new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
-	      src = bcs->tx_skb->data;	/* source pointer */
-	      dst = bdata + (*z1t - B_SUB_VAL);
-	      maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t; /* end of fifo */
-	      if (maxlen > count)
-		maxlen = count;	/* limit size */
-	      memcpy(dst, src, maxlen);	/* first copy */
+		z1t = &bz->za[MAX_B_FRAMES].z1;
+		z2t = z1t + 1;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)",
+				bcs->channel, *z1t, *z2t);
+		fcnt = *z2t - *z1t;
+		if (fcnt <= 0)
+			fcnt += B_FIFO_SIZE;	/* fcnt contains available bytes in fifo */
+		fcnt = B_FIFO_SIZE - fcnt;	/* remaining bytes to send */
+
+		while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) {
+			if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) {
+				/* data is suitable for fifo */
+				count = bcs->tx_skb->len;
+
+				new_z1 = *z1t + count;	/* new buffer Position */
+				if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+					new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
+				src = bcs->tx_skb->data;	/* source pointer */
+				dst = bdata + (*z1t - B_SUB_VAL);
+				maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t;	/* end of fifo */
+				if (maxlen > count)
+					maxlen = count;		/* limit size */
+				memcpy(dst, src, maxlen);	/* first copy */
+
+				count -= maxlen;	/* remaining bytes */
+				if (count) {
+					dst = bdata;	/* start of buffer */
+					src += maxlen;	/* new position */
+					memcpy(dst, src, count);
+				}
+				bcs->tx_cnt -= bcs->tx_skb->len;
+				fcnt += bcs->tx_skb->len;
+				*z1t = new_z1;	/* now send data */
+			} else if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded",
+					bcs->channel, bcs->tx_skb->len);
 
-	      count -= maxlen;	/* remaining bytes */
-	      if (count) {
-		dst = bdata;	/* start of buffer */
-		src += maxlen;	/* new position */
-		memcpy(dst, src, count);
-	      }
-	      bcs->tx_cnt -= bcs->tx_skb->len;
-	      fcnt += bcs->tx_skb->len;
-	      *z1t = new_z1; /* now send data */
-	    }
-            else 
-	      if (cs->debug & L1_DEB_HSCX)
-		debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded",
-			bcs->channel, bcs->tx_skb->len);
-
-	    dev_kfree_skb(bcs->tx_skb);
-            cli();
-            bcs->tx_skb = skb_dequeue(&bcs->squeue); /* fetch next data */
-            sti();
-	  }  
-	  test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
-	  restore_flags(flags);
-	  return;
+			dev_kfree_skb(bcs->tx_skb);
+			cli();
+			bcs->tx_skb = skb_dequeue(&bcs->squeue);	/* fetch next data */
+			sti();
+		}
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		restore_flags(flags);
+		return;
 	}
-
-
 	if (cs->debug & L1_DEB_HSCX)
 		debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)",
 			bcs->channel, bz->f1, bz->f2,
@@ -757,51 +787,107 @@
 	return;
 }
 
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+		case (PH_PULL | REQUEST):
+		case (PH_PULL | INDICATION):
+			st->l1.l1hw(st, pr, arg);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			break;
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+
+
 /***********************/
 /* set/reset echo mode */
-/***********************/ 
+/***********************/
 static int
-hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic)
+hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
 {
-  int flags;
-  int i = *(unsigned int *) ic->parm.num;
-  
-  if (cs->chanlimit > 1)
-    return(-EINVAL);
-
-  save_flags(flags);
-  cli();
-  if (i) {
-    cs->logecho = 1;
-    cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */
-    cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC;
-    cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX;
-  }
-  else {
-    cs->logecho = 0;
-    cs->hw.hfcpci.trm &= ~0x20; /* enable echo chan */
-    cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC;
-    cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX;
-  }
-    cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
-    cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
-    cs->hw.hfcpci.conn |= 0x10; /* B2-IOM -> B2-ST */ 
-    cs->hw.hfcpci.ctmt &= ~2;
-  Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
-  Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
-  Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
-  Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
-  Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
-  Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
-  Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
-  restore_flags(flags);
-  return(0);
-} /* hfcpci_auxcmd */ 
+	int flags;
+	int i = *(unsigned int *) ic->parm.num;
+
+	if ((ic->arg == 98) &&
+	    (!(cs->hw.hfcpci.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) {
+		save_flags(flags);
+		cli();
+		Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0);	/* HFC ST G0 */
+		udelay(10);
+		cs->hw.hfcpci.sctrl |= SCTRL_MODE_NT;
+		Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);	/* set NT-mode */
+		udelay(10);
+		Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1);	/* HFC ST G1 */
+		udelay(10);
+		Write_hfc(cs, HFCPCI_STATES, 1 | HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
+		cs->dc.hfcpci.ph_state = 1;
+		cs->hw.hfcpci.nt_mode = 1;
+		cs->hw.hfcpci.nt_timer = 0;
+		cs->stlist->l2.l2l1 = dch_nt_l2l1;
+		restore_flags(flags);
+		debugl1(cs, "NT mode activated");
+		return (0);
+	}
+	if ((cs->chanlimit > 1) || (cs->hw.hfcpci.bswapped) ||
+	    (cs->hw.hfcpci.nt_mode) || (ic->arg != 12))
+		return (-EINVAL);
+
+	save_flags(flags);
+	cli();
+	if (i) {
+		cs->logecho = 1;
+		cs->hw.hfcpci.trm |= 0x20;	/* enable echo chan */
+		cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC;
+		cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX;
+	} else {
+		cs->logecho = 0;
+		cs->hw.hfcpci.trm &= ~0x20;	/* disable echo chan */
+		cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC;
+		cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX;
+	}
+	cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
+	cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
+	cs->hw.hfcpci.conn |= 0x10;	/* B2-IOM -> B2-ST */
+	cs->hw.hfcpci.ctmt &= ~2;
+	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+	Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+	restore_flags(flags);
+	return (0);
+}				/* hfcpci_auxcmd */
 
 /*****************************/
 /* E-channel receive routine */
 /*****************************/
-static void receive_emsg(struct IsdnCardState *cs)
+static void
+receive_emsg(struct IsdnCardState *cs)
 {
 	long flags;
 	int rcnt;
@@ -838,55 +924,54 @@
 		if (cs->debug & L1_DEB_ISAC)
 			debugl1(cs, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)",
 				zp->z1, zp->z2, rcnt);
-                new_z2 = zp->z2 + rcnt; /* new position in fifo */
+		new_z2 = zp->z2 + rcnt;		/* new position in fifo */
 		if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
-		  new_z2 -= B_FIFO_SIZE; /* buffer wrap */
+			new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
 		new_f2 = (bz->f2 + 1) & MAX_B_FRAMES;
-	        if ((rcnt > 256 + 3) || (count < 4) ||
+		if ((rcnt > 256 + 3) || (count < 4) ||
 		    (*(bdata + (zp->z1 - B_SUB_VAL)))) {
-		  if (cs->debug & L1_DEB_WARN)
-			debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt);
-		  bz->za[new_f2].z2 = new_z2;
-		  bz->f2 = new_f2;	/* next buffer */
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt);
+			bz->za[new_f2].z2 = new_z2;
+			bz->f2 = new_f2;	/* next buffer */
 		} else {
-		    total = rcnt;
-		    rcnt -= 3;
-		    ptr = e_buffer;
-
-		    if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL)
-			maxlen = rcnt;		/* complete transfer */
-		    else
-			maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;	/* maximum */
+			total = rcnt;
+			rcnt -= 3;
+			ptr = e_buffer;
 
-		    ptr1 = bdata + (zp->z2 - B_SUB_VAL);	/* start of data */
-		    memcpy(ptr, ptr1, maxlen);	/* copy data */
-		    rcnt -= maxlen;
+			if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL)
+				maxlen = rcnt;	/* complete transfer */
+			else
+				maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;	/* maximum */
 
-		    if (rcnt) {	/* rest remaining */
-			ptr += maxlen;
-			ptr1 = bdata;	/* start of buffer */
-			memcpy(ptr, ptr1, rcnt);	/* rest */
-		    }
-		    bz->za[new_f2].z2 = new_z2;
-		    bz->f2 = new_f2;	/* next buffer */
-		    if (cs->debug & DEB_DLOG_HEX) {
-		      	ptr = cs->dlog;
-			if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) {
-			  *ptr++ = 'E';
-			  *ptr++ = 'C';
-			  *ptr++ = 'H';
-			  *ptr++ = 'O';
-			  *ptr++ = ':';
-			  ptr += QuickHex(ptr, e_buffer, total - 3);
-			  ptr--;
-			  *ptr++ = '\n';
-			  *ptr = 0;
-			  HiSax_putstatus(cs, NULL, cs->dlog);
-			} else
-			  HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3);
-		    }
+			ptr1 = bdata + (zp->z2 - B_SUB_VAL);	/* start of data */
+			memcpy(ptr, ptr1, maxlen);	/* copy data */
+			rcnt -= maxlen;
 
-	}
+			if (rcnt) {	/* rest remaining */
+				ptr += maxlen;
+				ptr1 = bdata;	/* start of buffer */
+				memcpy(ptr, ptr1, rcnt);	/* rest */
+			}
+			bz->za[new_f2].z2 = new_z2;
+			bz->f2 = new_f2;	/* next buffer */
+			if (cs->debug & DEB_DLOG_HEX) {
+				ptr = cs->dlog;
+				if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) {
+					*ptr++ = 'E';
+					*ptr++ = 'C';
+					*ptr++ = 'H';
+					*ptr++ = 'O';
+					*ptr++ = ':';
+					ptr += QuickHex(ptr, e_buffer, total - 3);
+					ptr--;
+					*ptr++ = '\n';
+					*ptr = 0;
+					HiSax_putstatus(cs, NULL, cs->dlog);
+				} else
+					HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3);
+			}
+		}
 
 		rcnt = bz->f1 - bz->f2;
 		if (rcnt < 0)
@@ -902,7 +987,7 @@
 		goto Begin;
 	restore_flags(flags);
 	return;
-} /* receive_emsg */
+}				/* receive_emsg */
 
 /*********************/
 /* Interrupt handler */
@@ -921,6 +1006,9 @@
 		printk(KERN_WARNING "HFC-PCI: Spurious interrupt!\n");
 		return;
 	}
+	if (!(cs->hw.hfcpci.int_m2 & 0x08))
+		return;		/* not initialised */
+
 	if (HFCPCI_ANYINT & (stat = Read_hfc(cs, HFCPCI_STATUS))) {
 		val = Read_hfc(cs, HFCPCI_INT_S1);
 		if (cs->debug & L1_DEB_ISAC)
@@ -933,7 +1021,7 @@
 			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
 			"locked" : "unlocked");
 	val &= cs->hw.hfcpci.int_m1;
-	if (val & 0x40) {	/* TE state machine irq */
+	if (val & 0x40) {	/* state machine irq */
 		exval = Read_hfc(cs, HFCPCI_STATES) & 0xf;
 		if (cs->debug & L1_DEB_ISAC)
 			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state,
@@ -942,6 +1030,14 @@
 		sched_event_D_pci(cs, D_L1STATECHANGE);
 		val &= ~0x40;
 	}
+	if (val & 0x80) {	/* timer irq */
+		if (cs->hw.hfcpci.nt_mode) {
+			if ((--cs->hw.hfcpci.nt_timer) < 0)
+				sched_event_D_pci(cs, D_L1STATECHANGE);
+		}
+		val &= ~0x80;
+		Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+	}
 	while (val) {
 		save_flags(flags);
 		cli();
@@ -956,24 +1052,23 @@
 			cs->hw.hfcpci.int_s1 = exval;
 		}
 		if (val & 0x08) {
-			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
 				if (cs->debug)
 					debugl1(cs, "hfcpci spurious 0x08 IRQ");
 			} else
 				main_rec_hfcpci(bcs);
 		}
-		if (val & 0x10) { 
-		        if (cs->logecho)
-			  receive_emsg(cs);
-			else    
-			if (!(bcs = Sel_BCS(cs, 1))) {
+		if (val & 0x10) {
+			if (cs->logecho)
+				receive_emsg(cs);
+			else if (!(bcs = Sel_BCS(cs, 1))) {
 				if (cs->debug)
 					debugl1(cs, "hfcpci spurious 0x10 IRQ");
 			} else
 				main_rec_hfcpci(bcs);
 		}
 		if (val & 0x01) {
-			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
 				if (cs->debug)
 					debugl1(cs, "hfcpci spurious 0x01 IRQ");
 			} else {
@@ -1082,6 +1177,7 @@
 {
 	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
 	struct sk_buff *skb = arg;
+	int flags;
 
 	switch (pr) {
 		case (PH_DATA | REQUEST):
@@ -1157,13 +1253,46 @@
 			Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
 			break;
 		case (HW_DEACTIVATE | REQUEST):
-      			cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER;
+			cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER;
 			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
 			break;
 		case (HW_INFO3 | REQUEST):
 			cs->hw.hfcpci.mst_m |= HFCPCI_MASTER;
 			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
 			break;
+		case (HW_TESTLOOP | REQUEST):
+			switch ((int) arg) {
+				case (1):
+					Write_hfc(cs, HFCPCI_B1_SSL, 0x80);	/* tx slot */
+					Write_hfc(cs, HFCPCI_B1_RSL, 0x80);	/* rx slot */
+					save_flags(flags);
+					cli();
+					cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~7) | 1;
+					Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+					restore_flags(flags);
+					break;
+
+				case (2):
+					Write_hfc(cs, HFCPCI_B2_SSL, 0x81);	/* tx slot */
+					Write_hfc(cs, HFCPCI_B2_RSL, 0x81);	/* rx slot */
+					save_flags(flags);
+					cli();
+					cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~0x38) | 0x08;
+					Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+					restore_flags(flags);
+					break;
+
+				default:
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "hfcpci_l1hw loop invalid %4x", (int) arg);
+					return;
+			}
+			save_flags(flags);
+			cli();
+			cs->hw.hfcpci.trm |= 0x80;	/* enable IOM-loop */
+			Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+			restore_flags(flags);
+			break;
 		default:
 			if (cs->debug & L1_DEB_WARN)
 				debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr);
@@ -1203,113 +1332,127 @@
 {
 	struct IsdnCardState *cs = bcs->cs;
 	bzfifo_type *bzr, *bzt;
-	int flags;
+	int flags, fifo2;
 
 	if (cs->debug & L1_DEB_HSCX)
 		debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d",
 			mode, bc, bcs->channel);
 	bcs->mode = mode;
 	bcs->channel = bc;
-	if (cs->chanlimit > 1) { 
-	  cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */
-	  cs->hw.hfcpci.sctrl_e &= ~0x80; 
-	}
-	else {
-	  if (bc) {
-	    cs->hw.hfcpci.bswapped = 1; /* B1 and B2 exchanged */
-	    cs->hw.hfcpci.sctrl_e |= 0x80;
-	    bc = 0; /* B1 controller used */
-	  }
-	  else {
-	    cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */
-	    cs->hw.hfcpci.sctrl_e &= ~0x80; 
-	  }  
-	}  
+	fifo2 = bc;
 	save_flags(flags);
 	cli();
+	if (cs->chanlimit > 1) {
+		cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+		cs->hw.hfcpci.sctrl_e &= ~0x80;
+	} else {
+		if (bc) {
+			if (mode != L1_MODE_NULL) {
+				cs->hw.hfcpci.bswapped = 1;	/* B1 and B2 exchanged */
+				cs->hw.hfcpci.sctrl_e |= 0x80;
+			} else {
+				cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+				cs->hw.hfcpci.sctrl_e &= ~0x80;
+			}
+			fifo2 = 0;
+		} else {
+			cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+			cs->hw.hfcpci.sctrl_e &= ~0x80;
+		}
+	}
 	switch (mode) {
 		case (L1_MODE_NULL):
 			if (bc) {
 				cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
 				cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
-				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
-				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC);
 			} else {
 				cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA;
 				cs->hw.hfcpci.sctrl_r &= ~SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+			} else {
 				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
-				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
 			}
 			break;
 		case (L1_MODE_TRANS):
 			if (bc) {
-				cs->hw.hfcpci.ctmt |= 2;
-				cs->hw.hfcpci.conn &= ~0x18;
 				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
 				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
-				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
-				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC);
-		                bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
-		                bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
 			} else {
-				cs->hw.hfcpci.ctmt |= 1;
-				cs->hw.hfcpci.conn &= ~0x03;
 				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
 				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+				cs->hw.hfcpci.ctmt |= 2;
+				cs->hw.hfcpci.conn &= ~0x18;
+				bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+				bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
+			} else {
 				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
-				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC);
-		                bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
-		                bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.ctmt |= 1;
+				cs->hw.hfcpci.conn &= ~0x03;
+				bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
+				bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
 			}
 			bzr->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1;
-                        bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1; 
+			bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1;
 			bzr->f1 = MAX_B_FRAMES;
-			bzr->f2 = bzr->f1; /* init F pointers to remain constant */
+			bzr->f2 = bzr->f1;	/* init F pointers to remain constant */
 			bzt->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1;
-                        bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1; 
+			bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1;
 			bzt->f1 = MAX_B_FRAMES;
-			bzt->f2 = bzt->f1; /* init F pointers to remain constant */
+			bzt->f2 = bzt->f1;	/* init F pointers to remain constant */
 			break;
 		case (L1_MODE_HDLC):
 			if (bc) {
-				cs->hw.hfcpci.ctmt &= ~2;
-				cs->hw.hfcpci.conn &= ~0x18;
 				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
 				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
-				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
-				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC);
 			} else {
-				cs->hw.hfcpci.ctmt &= ~1;
-				cs->hw.hfcpci.conn &= ~0x3;
 				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
 				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+				cs->hw.hfcpci.ctmt &= ~2;
+				cs->hw.hfcpci.conn &= ~0x18;
+			} else {
 				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
-				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.ctmt &= ~1;
+				cs->hw.hfcpci.conn &= ~0x03;
 			}
 			break;
 		case (L1_MODE_EXTRN):
 			if (bc) {
-			        cs->hw.hfcpci.conn |= 0x10;
+				cs->hw.hfcpci.conn |= 0x10;
 				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
 				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
 				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
-				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC);
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
 			} else {
-			        cs->hw.hfcpci.conn |= 0x02;
+				cs->hw.hfcpci.conn |= 0x02;
 				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
 				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
 				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
-				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
 			}
 			break;
 	}
+	Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);
 	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
-	restore_flags(flags);
 	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
 	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
 	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
 	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
 	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+	restore_flags(flags);
 }
 
 /******************************/
@@ -1429,29 +1572,72 @@
 static void
 hfcpci_bh(struct IsdnCardState *cs)
 {
+	int flags;
 /*      struct PStack *stptr;
  */
 	if (!cs)
 		return;
 	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
-		switch (cs->dc.hfcpci.ph_state) {
-			case (0):
-				l1_msg(cs, HW_RESET | INDICATION, NULL);
-				break;
-			case (3):
-				l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
-				break;
-			case (8):
-				l1_msg(cs, HW_RSYNC | INDICATION, NULL);
-				break;
-			case (6):
-				l1_msg(cs, HW_INFO2 | INDICATION, NULL);
-				break;
-			case (7):
-				l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
-				break;
-			default:
-				break;
+		if (!cs->hw.hfcpci.nt_mode)
+			switch (cs->dc.hfcpci.ph_state) {
+				case (0):
+					l1_msg(cs, HW_RESET | INDICATION, NULL);
+					break;
+				case (3):
+					l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+					break;
+				case (8):
+					l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+					break;
+				case (6):
+					l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+					break;
+				case (7):
+					l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+					break;
+				default:
+					break;
+		} else {
+			switch (cs->dc.hfcpci.ph_state) {
+				case (2):
+					save_flags(flags);
+					cli();
+					if (cs->hw.hfcpci.nt_timer < 0) {
+						cs->hw.hfcpci.nt_timer = 0;
+						cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+						Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+						/* Clear already pending ints */
+						if (Read_hfc(cs, HFCPCI_INT_S1));
+
+						Write_hfc(cs, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
+						udelay(10);
+						Write_hfc(cs, HFCPCI_STATES, 4);
+						cs->dc.hfcpci.ph_state = 4;
+					} else {
+						cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_TIMER;
+						Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+						cs->hw.hfcpci.ctmt &= ~HFCPCI_AUTO_TIMER;
+						cs->hw.hfcpci.ctmt |= HFCPCI_TIM3_125;
+						Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+						Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+						cs->hw.hfcpci.nt_timer = NT_T1_COUNT;
+						Write_hfc(cs, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);	/* allow G2 -> G3 transition */
+					}
+					restore_flags(flags);
+					break;
+				case (1):
+				case (3):
+				case (4):
+					save_flags(flags);
+					cli();
+					cs->hw.hfcpci.nt_timer = 0;
+					cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+					Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+					restore_flags(flags);
+					break;
+				default:
+					break;
+			}
 		}
 	}
 	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
@@ -1551,9 +1737,10 @@
 	   setup_hfcpci(struct IsdnCard *card))
 {
 	struct IsdnCardState *cs = card->cs;
+	unsigned short cmd;
 	char tmp[64];
 	int i;
-        struct pci_dev *tmp_hfcpci = NULL;
+	struct pci_dev *tmp_hfcpci = NULL;
 
 	strcpy(tmp, hfcpci_revision);
 	printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp));
@@ -1569,22 +1756,22 @@
 			return (0);
 		}
 		i = 0;
-                while (id_list[i].vendor_id) {
-		  tmp_hfcpci = pci_find_device(id_list[i].vendor_id,
-					       id_list[i].device_id,
-					       dev_hfcpci);
-		  i++;
-		  if (tmp_hfcpci) {
-		    if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->base_address[0] & PCI_BASE_ADDRESS_IO_MASK)))
-		      continue;
-		    else
-		      break;
-		  }  
-		}  
-					      
+		while (id_list[i].vendor_id) {
+			tmp_hfcpci = pci_find_device(id_list[i].vendor_id,
+						     id_list[i].device_id,
+						     dev_hfcpci);
+			i++;
+			if (tmp_hfcpci) {
+				if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK)))
+					continue;
+				else
+					break;
+			}
+		}
+
 		if (tmp_hfcpci) {
-		        i--;
-		        dev_hfcpci = tmp_hfcpci; /* old device */
+			i--;
+			dev_hfcpci = tmp_hfcpci;	/* old device */
 			cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number;
 			cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn;
 			cs->irq = dev_hfcpci->irq;
@@ -1593,11 +1780,44 @@
 				return (0);
 			}
 			cs->hw.hfcpci.pci_io = (char *) dev_hfcpci->base_address[ 1];
-			printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n",id_list[i].vendor_name,id_list[i].card_name);
+			printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name);
 		} else {
 			printk(KERN_WARNING "HFC-PCI: No PCI card found\n");
 			return (0);
 		}
+		if (((int) cs->hw.hfcpci.pci_io & (PAGE_SIZE - 1))) {
+			printk(KERN_WARNING "HFC-PCI shared mem address will be corrected\n");
+			pcibios_write_config_word(cs->hw.hfcpci.pci_bus,
+					     cs->hw.hfcpci.pci_device_fn,
+						  PCI_COMMAND,
+						  0x0103);	/* set SERR */
+			pcibios_read_config_word(cs->hw.hfcpci.pci_bus,
+					     cs->hw.hfcpci.pci_device_fn,
+						 PCI_COMMAND,
+						 &cmd);
+			pcibios_write_config_word(cs->hw.hfcpci.pci_bus,
+					     cs->hw.hfcpci.pci_device_fn,
+						  PCI_COMMAND,
+						  cmd & ~2);
+			(int) cs->hw.hfcpci.pci_io &= ~(PAGE_SIZE - 1);
+			pcibios_write_config_dword(cs->hw.hfcpci.pci_bus,
+					     cs->hw.hfcpci.pci_device_fn,
+						   PCI_BASE_ADDRESS_1,
+					     (int) cs->hw.hfcpci.pci_io);
+			pcibios_write_config_word(cs->hw.hfcpci.pci_bus,
+					     cs->hw.hfcpci.pci_device_fn,
+						  PCI_COMMAND,
+						  cmd);
+			pcibios_read_config_dword(cs->hw.hfcpci.pci_bus,
+					     cs->hw.hfcpci.pci_device_fn,
+						  PCI_BASE_ADDRESS_1,
+					 (void *) &cs->hw.hfcpci.pci_io);
+			if (((int) cs->hw.hfcpci.pci_io & (PAGE_SIZE - 1))) {
+				printk(KERN_WARNING "HFC-PCI unable to align address %x\n", (unsigned) cs->hw.hfcpci.pci_io);
+				return (0);
+			}
+			dev_hfcpci->base_address[1] = (int) cs->hw.hfcpci.pci_io;
+		}
 		if (!cs->hw.hfcpci.pci_io) {
 			printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
 			return (0);
@@ -1613,16 +1833,16 @@
 		    (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000;
 		pcibios_write_config_dword(cs->hw.hfcpci.pci_bus,
 				       cs->hw.hfcpci.pci_device_fn, 0x80,
-					   (u_int) virt_to_bus(cs->hw.hfcpci.fifos));
+			       (u_int) virt_to_bus(cs->hw.hfcpci.fifos));
 		cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256);
 		printk(KERN_INFO
-		 "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n",
+		       "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n",
 		       (u_int) cs->hw.hfcpci.pci_io,
 		       (u_int) cs->hw.hfcpci.fifos,
 		       (u_int) virt_to_bus(cs->hw.hfcpci.fifos),
 		       cs->irq, HZ);
 		pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO);	/* enable memory mapped ports, disable busmaster */
-		cs->hw.hfcpci.int_m2 = 0; /* disable alle interrupts */
+		cs->hw.hfcpci.int_m2 = 0;	/* disable alle interrupts */
 		cs->hw.hfcpci.int_m1 = 0;
 		Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
 		Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
@@ -1647,7 +1867,7 @@
 
 	reset_hfcpci(cs);
 	cs->cardmsg = &hfcpci_card_msg;
-	cs->auxcmd  = &hfcpci_auxcmd;
+	cs->auxcmd = &hfcpci_auxcmd;
 	return (1);
 #else
 	printk(KERN_WARNING "HFC-PCI: NO_PCI_BIOS\n");
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/hfc_pci.h linux.14p6/drivers/isdn/hisax/hfc_pci.h
--- linux.vanilla/drivers/isdn/hisax/hfc_pci.h	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/hfc_pci.h	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: hfc_pci.h,v 1.6 1999/08/28 21:04:29 werner Exp $
+/* $Id: hfc_pci.h,v 1.7 1999/10/10 20:13:06 werner Exp $
 
  *  specific defines for CCD's HFC 2BDS0 PCI chips
  *
@@ -21,6 +21,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: hfc_pci.h,v $
+ * Revision 1.7  1999/10/10 20:13:06  werner
+ *
+ * Corrected timer constant
+ *
  * Revision 1.6  1999/08/28 21:04:29  werner
  * Implemented full audio support (transparent mode)
  *
@@ -129,7 +133,7 @@
 
 /* bits in CTMT (Write) */
 #define HFCPCI_CLTIMER    0x80
-#define HFCPCI_TIM3_125   0x00
+#define HFCPCI_TIM3_125   0x04
 #define HFCPCI_TIM25      0x10
 #define HFCPCI_TIM50      0x14
 #define HFCPCI_TIM400     0x18
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/hisax.h linux.14p6/drivers/isdn/hisax/hisax.h
--- linux.vanilla/drivers/isdn/hisax/hisax.h	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/hisax.h	Tue Nov 16 17:27:11 1999
@@ -1,8 +1,18 @@
-/* $Id: hisax.h,v 2.35 1999/09/04 06:35:09 keil Exp $
+/* $Id: hisax.h,v 2.38 1999/11/14 23:37:03 keil Exp $
 
  *   Basic declarations, defines and prototypes
  *
  * $Log: hisax.h,v $
+ * Revision 2.38  1999/11/14 23:37:03  keil
+ * new ISA memory mapped IO
+ *
+ * Revision 2.37  1999/10/14 20:25:28  keil
+ * add a statistic for error monitoring
+ *
+ * Revision 2.36  1999/10/10 20:16:15  werner
+ *
+ * Added variable to hfcpci union.
+ *
  * Revision 2.35  1999/09/04 06:35:09  keil
  * Winbond W6692 support
  *
@@ -140,6 +150,8 @@
 #include <linux/tty.h>
 #include <linux/serial_reg.h>
 
+#undef ERROR_STATISTIC
+
 #define REQUEST		0
 #define CONFIRM		1
 #define INDICATION	2
@@ -581,6 +593,12 @@
 	int event;
 	int  (*BC_SetStack) (struct PStack *, struct BCState *);
 	void (*BC_Close) (struct BCState *);
+#ifdef ERROR_STATISTIC
+	int err_crc;
+	int err_tx;
+	int err_rdo;
+	int err_inv;
+#endif
 	union {
 		struct hscx_hw hscx;
 		struct hdlc_hw hdlc;
@@ -648,7 +666,8 @@
 
 struct teles0_hw {
 	unsigned int cfg_reg;
-	unsigned int membase;
+	unsigned long membase;
+	unsigned long phymem;
 };
 
 struct avm_hw {
@@ -755,7 +774,8 @@
 	unsigned char fifo;
         unsigned char fifo_en;
         unsigned char bswapped;
-  /*	unsigned int *send; */
+        unsigned char nt_mode;
+        int nt_timer;
 	unsigned char pci_bus;
         unsigned char pci_device_fn;
         unsigned char *pci_io; /* start of PCI IO memory */
@@ -787,8 +807,9 @@
 
 struct isurf_hw {
 	unsigned int reset;
-	unsigned int isac;
-	unsigned int isar;
+	unsigned long phymem;
+	unsigned long isac;
+	unsigned long isar;
 	struct isar_reg isar_r;
 };
 
@@ -961,6 +982,11 @@
 	int event;
 	struct tq_struct tqueue;
 	struct timer_list dbusytimer;
+#ifdef ERROR_STATISTIC
+	int err_crc;
+	int err_tx;
+	int err_rx;
+#endif
 };
 
 #define  MON0_RX	1
@@ -1355,7 +1381,7 @@
 int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
 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);
+void HiSax_reportcard(int cardnr, int sel);
 int QuickHex(char *txt, u_char * p, int cnt);
 void LogFrame(struct IsdnCardState *cs, u_char * p, int size);
 void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/hscx_irq.c linux.14p6/drivers/isdn/hisax/hscx_irq.c
--- linux.vanilla/drivers/isdn/hisax/hscx_irq.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/hscx_irq.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: hscx_irq.c,v 1.12 1999/07/01 08:11:42 keil Exp $
+/* $Id: hscx_irq.c,v 1.13 1999/10/14 20:25:28 keil Exp $
 
  * hscx_irq.c     low level b-channel stuff for Siemens HSCX
  *
@@ -7,6 +7,9 @@
  * This is an include file for fast inline IRQ stuff
  *
  * $Log: hscx_irq.c,v $
+ * Revision 1.13  1999/10/14 20:25:28  keil
+ * add a statistic for error monitoring
+ *
  * Revision 1.12  1999/07/01 08:11:42  keil
  * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel
  *
@@ -181,16 +184,28 @@
 	if (val & 0x80) {	/* RME */
 		r = READHSCX(cs, hscx, HSCX_RSTA);
 		if ((r & 0xf0) != 0xa0) {
-			if (!(r & 0x80))
+			if (!(r & 0x80)) {
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, "HSCX invalid frame");
-			if ((r & 0x40) && bcs->mode)
+#ifdef ERROR_STATISTIC
+				bcs->err_inv++;
+#endif
+			}
+			if ((r & 0x40) && bcs->mode) {
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, "HSCX RDO mode=%d",
 						bcs->mode);
-			if (!(r & 0x20))
+#ifdef ERROR_STATISTIC
+				bcs->err_rdo++;
+#endif
+			}
+			if (!(r & 0x20)) {
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, "HSCX CRC error");
+#ifdef ERROR_STATISTIC
+				bcs->err_crc++;
+#endif
+			}
 			WriteHSCXCMDR(cs, hscx, 0x80);
 		} else {
 			count = READHSCX(cs, hscx, HSCX_RBCL) & (
@@ -265,6 +280,9 @@
 			if (bcs->mode == 1)
 				hscx_fill_fifo(bcs);
 			else {
+#ifdef ERROR_STATISTIC
+				bcs->err_tx++;
+#endif
 				/* Here we lost an TX interrupt, so
 				   * restart transmitting the whole frame.
 				 */
@@ -295,6 +313,9 @@
 				/* Here we lost an TX interrupt, so
 				   * restart transmitting the whole frame.
 				 */
+#ifdef ERROR_STATISTIC
+				bcs->err_tx++;
+#endif
 				if (bcs->tx_skb) {
 					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
 					bcs->tx_cnt += bcs->hw.hscx.count;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/isac.c linux.14p6/drivers/isdn/hisax/isac.c
--- linux.vanilla/drivers/isdn/hisax/isac.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/isac.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isac.c,v 1.23 1999/08/25 16:50:52 keil Exp $
+/* $Id: isac.c,v 1.24 1999/10/14 20:25:28 keil Exp $
 
  * isac.c   ISAC specific routines
  *
@@ -9,6 +9,9 @@
  *		../../../Documentation/isdn/HiSax.cert
  *
  * $Log: isac.c,v $
+ * Revision 1.24  1999/10/14 20:25:28  keil
+ * add a statistic for error monitoring
+ *
  * Revision 1.23  1999/08/25 16:50:52  keil
  * Fix bugs which cause 2.3.14 hangs (waitqueue init)
  *
@@ -281,12 +284,20 @@
 	if (val & 0x80) {	/* RME */
 		exval = cs->readisac(cs, ISAC_RSTA);
 		if ((exval & 0x70) != 0x20) {
-			if (exval & 0x40)
+			if (exval & 0x40) {
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, "ISAC RDO");
-			if (!(exval & 0x20))
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			}
+			if (!(exval & 0x20)) {
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, "ISAC CRC error");
+#ifdef ERROR_STATISTIC
+				cs->err_crc++;
+#endif
+			}
 			cs->writeisac(cs, ISAC_CMDR, 0x80);
 		} else {
 			count = cs->readisac(cs, ISAC_RBCL) & 0x1f;
@@ -371,6 +382,9 @@
 		if (exval & 0x40) {  /* XDU */
 			debugl1(cs, "ISAC XDU");
 			printk(KERN_WARNING "HiSax: ISAC XDU\n");
+#ifdef ERROR_STATISTIC
+			cs->err_tx++;
+#endif
 			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))
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/isar.c linux.14p6/drivers/isdn/hisax/isar.c
--- linux.vanilla/drivers/isdn/hisax/isar.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/isar.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isar.c,v 1.6 1999/08/31 11:20:20 paul Exp $
+/* $Id: isar.c,v 1.7 1999/10/14 20:25:29 keil Exp $
 
  * isar.c   ISAR (Siemens PSB 7110) specific routines
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: isar.c,v $
+ * Revision 1.7  1999/10/14 20:25:29  keil
+ * add a statistic for error monitoring
+ *
  * Revision 1.6  1999/08/31 11:20:20  paul
  * various spelling corrections (new checksums may be needed, Karsten!)
  *
@@ -480,6 +483,12 @@
 			if (cs->debug & L1_DEB_WARN)
 				debugl1(cs, "isar frame error %x len %d",
 					ireg->cmsb, ireg->clsb);
+#ifdef ERROR_STATISTIC
+			if (ireg->cmsb & HDLC_ERR_RER)
+				bcs->err_inv++;
+			if (ireg->cmsb & HDLC_ERR_CER)
+				bcs->err_crc++;
+#endif
 			bcs->hw.isar.rcvidx = 0;
 			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
 		} else {
@@ -790,10 +799,19 @@
 			check_send(cs, ireg->cmsb);
 			break;
 		case ISAR_IIS_BSTEV:
-			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+#ifdef ERROR_STATISTIC
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				if (ireg->cmsb == BSTEV_TBO)
+					bcs->err_tx++;
+				if (ireg->cmsb == BSTEV_RBO)
+					bcs->err_rdo++;
+			}
+#endif
 			if (cs->debug & L1_DEB_WARN)
 				debugl1(cs, "Buffer STEV dpath%d msb(%x)",
 					ireg->iis>>6, ireg->cmsb);
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			break;
 		case ISAR_IIS_PSTEV:
 			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
 				rcv_mbox(cs, ireg, (u_char *)ireg->par);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/isar.h linux.14p6/drivers/isdn/hisax/isar.h
--- linux.vanilla/drivers/isdn/hisax/isar.h	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/isar.h	Tue Nov 16 15:10:48 1999
@@ -1,10 +1,13 @@
-/* $Id: isar.h,v 1.5 1999/08/25 16:59:59 keil Exp $
+/* $Id: isar.h,v 1.6 1999/10/14 20:25:29 keil Exp $
  * isar.h   ISAR (Siemens PSB 7110) specific defines
  *
  * Author Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: isar.h,v $
+ * Revision 1.6  1999/10/14 20:25:29  keil
+ * add a statistic for error monitoring
+ *
  * Revision 1.5  1999/08/25 16:59:59  keil
  * Make ISAR V32bis modem running
  * Make LL->HL interface open for additional commands
@@ -178,12 +181,17 @@
 #define HDLC_FSD	0x20
 #define HDLC_FST	0x20
 #define HDLC_ERROR	0x1c
+#define HDLC_ERR_FAD	0x10
+#define HDLC_ERR_RER	0x08
+#define HDLC_ERR_CER	0x01
 #define SART_NMD	0x01
 
 #define BSTAT_RDM0	0x1
 #define BSTAT_RDM1	0x2
 #define BSTAT_RDM2	0x4
 #define BSTAT_RDM3	0x8
+#define BSTEV_TBO	0x1f
+#define BSTEV_RBO	0x2f
 
 extern int ISARVersion(struct IsdnCardState *cs, char *s);
 extern void isar_int_main(struct IsdnCardState *cs);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/isurf.c linux.14p6/drivers/isdn/hisax/isurf.c
--- linux.vanilla/drivers/isdn/hisax/isurf.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/isurf.c	Tue Nov 16 15:10:48 1999
@@ -1,10 +1,13 @@
-/* $Id: isurf.c,v 1.6 1999/09/04 06:20:06 keil Exp $
+/* $Id: isurf.c,v 1.7 1999/11/14 23:37:03 keil Exp $
 
  * isurf.c  low level stuff for Siemens I-Surf/I-Talk cards
  *
  * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * $Log: isurf.c,v $
+ * Revision 1.7  1999/11/14 23:37:03  keil
+ * new ISA memory mapped IO
+ *
  * Revision 1.6  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -37,7 +40,7 @@
 
 extern const char *CardType[];
 
-static const char *ISurf_revision = "$Revision: 1.6 $";
+static const char *ISurf_revision = "$Revision: 1.7 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -50,7 +53,7 @@
 
 #define ISURF_ISAR_OFFSET	0
 #define ISURF_ISAC_OFFSET	0x100
-
+#define ISURF_IOMEM_SIZE	0x400
 /* Interface functions */
 
 static u_char
@@ -223,8 +226,7 @@
  		return(0);
 	if (card->para[1] && card->para[2]) {
 		cs->hw.isurf.reset = card->para[1];
-		cs->hw.isurf.isar = card->para[2] + ISURF_ISAR_OFFSET;
-		cs->hw.isurf.isac = card->para[2] + ISURF_ISAC_OFFSET;
+		cs->hw.isurf.phymem = card->para[2];
 		cs->irq = card->para[0];
 	} else {
 		printk(KERN_WARNING "HiSax: %s port/mem not set\n",
@@ -240,11 +242,12 @@
 	} else {
 		request_region(cs->hw.isurf.reset, 1, "isurf isdn");
 	}
-
+	cs->hw.isurf.isar = cs->hw.isurf.phymem + ISURF_ISAR_OFFSET;
+	cs->hw.isurf.isac = cs->hw.isurf.phymem + ISURF_ISAC_OFFSET;
 	printk(KERN_INFO
-	       "ISurf: defined at 0x%x 0x%x IRQ %d\n",
+	       "ISurf: defined at 0x%x 0x%lx IRQ %d\n",
 	       cs->hw.isurf.reset,
-	       cs->hw.isurf.isar,
+	       cs->hw.isurf.phymem,
 	       cs->irq);
 
 	cs->cardmsg = &ISurf_card_msg;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/l3dss1.c linux.14p6/drivers/isdn/hisax/l3dss1.c
--- linux.vanilla/drivers/isdn/hisax/l3dss1.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/l3dss1.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: l3dss1.c,v 2.19 1999/08/25 16:55:23 keil Exp $
+/* $Id: l3dss1.c,v 2.20 1999/10/11 22:16:27 keil Exp $
 
  * EURO/DSS1 D-channel protocol
  *
@@ -13,6 +13,9 @@
  *              Fritz Elfert
  *
  * $Log: l3dss1.c,v $
+ * Revision 2.20  1999/10/11 22:16:27  keil
+ * Suspend/Resume is possible without explicit ID too
+ *
  * Revision 2.19  1999/08/25 16:55:23  keil
  * Fix for test case TC10011
  *
@@ -90,7 +93,7 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 2.19 $";
+const char *dss1_revision = "$Revision: 2.20 $";
 
 #define EXT_BEARER_CAPS 1
 
@@ -2520,14 +2523,13 @@
 	u_char *msg = pc->chan->setup.phone;
 
 	MsgHead(p, pc->callref, MT_SUSPEND);
-
-	*p++ = IE_CALL_ID;
 	l = *msg++;
 	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
 		*p++ = l;
 		for (i = 0; i < l; i++)
 			*p++ = *msg++;
-	} else {
+	} else if (l) {
 		l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
 		return;
 	}
@@ -2596,13 +2598,13 @@
 
 	MsgHead(p, pc->callref, MT_RESUME);
 
-	*p++ = IE_CALL_ID;
 	l = *msg++;
 	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
 		*p++ = l;
 		for (i = 0; i < l; i++)
 			*p++ = *msg++;
-	} else {
+	} else if (l) {
 		l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
 		return;
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/md5sums.asc linux.14p6/drivers/isdn/hisax/md5sums.asc
--- linux.vanilla/drivers/isdn/hisax/md5sums.asc	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/md5sums.asc	Tue Nov 16 15:10:48 1999
@@ -6,14 +6,14 @@
 # Eicon Technology Diva 2.01 PCI cards in the moment.
 # Read ../../../Documentation/isdn/HiSax.cert for more informations.
 # 
-2760872030085b2d36aa203b44e9570e  isac.c
+3c2b1c96274cba97a8261d1cecc662b8  isac.c
 a9a15d069dbacb383cc24c238cb5ebbe  isdnl1.c
 bb51bd223040b511c18f091da5ab6456  isdnl2.c
 b7aa7f97b2374967a4aca7c52991142c  isdnl3.c
 a23fbf8879c1432b04640b8b04bdf419  tei.c
-c07fd0729b93ebffc3dbfc88cc334be1  callc.c
+d7072dbbeeb7c4c45f3810ed13cf5545  callc.c
 bf9605b36429898f7be6630034e83230  cert.c
-3fe91ac7175e1c15ad5f15b6d71b2756  l3dss1.c
+0e500813968adacaea2ef22c9cdd89eb  l3dss1.c
 2d748ced0eea375b21fe7ea91ca7917c  l3_1tr6.c
 72ee065c70473949a676f06f8ce83ebf  elsa.c
 1b26dd9b0f132744a7be2ce2ab1eda83  diva.c
@@ -23,9 +23,9 @@
 Version: 2.6.3i
 Charset: noconv
 
-iQCVAwUBN+/NfDpxHvX/mS9tAQEC4gQAlAfRvVnILSrDUtH83RzPd6nCLONiHpKn
-nmyE3BkGk2DO1D8azCjx3X/9x7ozEm8pL5UN2kZuu5p9EO0ISzM5ZmcCi0jxAOq5
-c493UJmnWWOw02G0pNw+EsmRH9mUh1t7IyUEL+UpUMNyAEQRqq66k764YyUkKF88
-fup3c64mSKw=
-=bmv8
+iQCVAwUBOCwLCTpxHvX/mS9tAQEZ5wP7B6kHy8fHHHANxqU1XAiJR5DvttJtJGi1
+0MDX2lNAPbfZQolTkkmmWgYBSnLTLVpM2jxj5pdnKWMoEp0embrb0fw1Nx+DkcJ1
+OfogKy0HvBPfZdwyKV+CZymXZystw/n47hekOTSNLkAOMA6BXcWrS6SLpfv6fEri
+Cdp0XgIaIWQ=
+=Cmob
 -----END PGP SIGNATURE-----
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/netjet.c linux.14p6/drivers/isdn/hisax/netjet.c
--- linux.vanilla/drivers/isdn/hisax/netjet.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/netjet.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: netjet.c,v 1.15 1999/09/04 06:20:06 keil Exp $
+/* $Id: netjet.c,v 1.16 1999/10/14 20:25:29 keil Exp $
 
  * netjet.c     low level stuff for Traverse Technologie NETJet ISDN cards
  *
@@ -7,6 +7,9 @@
  * Thanks to Traverse Technologie Australia for documents and informations
  *
  * $Log: netjet.c,v $
+ * Revision 1.16  1999/10/14 20:25:29  keil
+ * add a statistic for error monitoring
+ *
  * Revision 1.15  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -78,7 +81,7 @@
 
 extern const char *CardType[];
 
-const char *NETjet_revision = "$Revision: 1.15 $";
+const char *NETjet_revision = "$Revision: 1.16 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -528,19 +531,26 @@
 							debugl1(bcs->cs, "tiger: frame not byte aligned");
 							state=HDLC_FLAG_SEARCH;
 							bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+							bcs->err_inv++;
+#endif
 						} else {
 							if (bcs->cs->debug & L1_DEB_HSCX)
 								debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x",
 									i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
 							if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
 								got_frame(bcs, (bitcnt>>3)-3);
-							} else
+							} else {
 								if (bcs->cs->debug) {
 									debugl1(bcs->cs, "tiger FCS error");
 									printframe(bcs->cs, bcs->hw.tiger.rcvbuf,
 										(bitcnt>>3)-1, "rec");
 									bcs->hw.tiger.r_err++;
 								}
+#ifdef ERROR_STATISTIC
+							bcs->err_crc++;
+#endif
+							}
 							state=HDLC_FLAG_FOUND;
 						}
 						bitcnt=0;
@@ -562,6 +572,9 @@
 						r_val=0; 
 						state=HDLC_FLAG_SEARCH;
 						bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+						bcs->err_inv++;
+#endif
 					} else {
 						bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val;
 						bcs->hw.tiger.r_fcs = 
@@ -586,6 +599,12 @@
 	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);
+#ifdef ERROR_STATISTIC
+		if (cs->bcs[0].mode)
+			cs->bcs[0].err_rdo++;
+		if (cs->bcs[1].mode)
+			cs->bcs[1].err_rdo++;
+#endif
 		return;
 	} else {
 		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ;
@@ -757,6 +776,12 @@
 	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);
+#ifdef ERROR_STATISTIC
+		if (cs->bcs[0].mode)
+			cs->bcs[0].err_tx++;
+		if (cs->bcs[1].mode)
+			cs->bcs[1].err_tx++;
+#endif
 		return;
 	} else {
 		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/sedlbauer.c linux.14p6/drivers/isdn/hisax/sedlbauer.c
--- linux.vanilla/drivers/isdn/hisax/sedlbauer.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/sedlbauer.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: sedlbauer.c,v 1.17 1999/09/04 06:20:06 keil Exp $
+/* $Id: sedlbauer.c,v 1.18 1999/11/13 21:25:03 keil Exp $
 
  * sedlbauer.c  low level stuff for Sedlbauer cards
  *              includes support for the Sedlbauer speed star (speed star II),
@@ -17,6 +17,9 @@
  *            Edgar Toernig
  *
  * $Log: sedlbauer.c,v $
+ * Revision 1.18  1999/11/13 21:25:03  keil
+ * Support for Speedfax+ PCI
+ *
  * Revision 1.17  1999/09/04 06:20:06  keil
  * Changes from kernel set_current_state()
  *
@@ -79,12 +82,13 @@
  * ---------------------------------------------------------------------
  * Speed Card	ISAC_HSCX	DIP-SWITCH
  * Speed Win	ISAC_HSCX	ISAPNP
- * Speed Fax+	ISAC_ISAR	ISAPNP		#HDLC works#
+ * Speed Fax+	ISAC_ISAR	ISAPNP		Full analog support
  * Speed Star	ISAC_HSCX	CARDMGR
  * Speed Win2	IPAC		ISAPNP
  * ISDN PC/104	IPAC		DIP-SWITCH
  * Speed Star2	IPAC		CARDMGR
- * Speed PCI	IPAC		PNP		
+ * Speed PCI	IPAC		PCI PNP		
+ * Speed Fax+ 	ISAC_ISAR	PCI PNP		Full analog support
  *
  * Important:
  * For the sedlbauer speed fax+ to work properly you have to download 
@@ -106,15 +110,18 @@
 
 extern const char *CardType[];
 
-const char *Sedlbauer_revision = "$Revision: 1.17 $";
+const char *Sedlbauer_revision = "$Revision: 1.18 $";
 
 const char *Sedlbauer_Types[] =
 	{"None", "speed card/win", "speed star", "speed fax+", 
-	"speed win II / ISDN PC/104", "speed star II", "speed pci"};
+	"speed win II / ISDN PC/104", "speed star II", "speed pci",
+	"speed fax+ pci"};
 
 #ifdef SEDLBAUER_PCI
 #define PCI_VENDOR_SEDLBAUER	0xe159
-#define PCI_SPEEDPCI_ID	0x02
+#define PCI_SPEEDPCI_ID		0x02
+#define PCI_SUBVENDOR_SEDLBAUER	0x51
+#define PCI_SUB_ID_SPEEDFAXP	0x01
 #endif
  
 #define SEDL_SPEED_CARD_WIN	1
@@ -123,6 +130,7 @@
 #define SEDL_SPEED_WIN2_PC104 	4
 #define SEDL_SPEED_STAR2 	5
 #define SEDL_SPEED_PCI   	6
+#define SEDL_SPEEDFAX_PCI	7
 
 #define SEDL_CHIP_TEST		0
 #define SEDL_CHIP_ISAC_HSCX	1
@@ -153,12 +161,19 @@
 #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_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_IPAC_PCI_BASE		0
+#define SEDL_IPAC_PCI_ADR		0xc0
+#define SEDL_IPAC_PCI_IPAC		0xc8
+#define SEDL_ISAR_PCI_ADR		0xc8
+#define SEDL_ISAR_PCI_ISAC		0xd0
+#define SEDL_ISAR_PCI_ISAR		0xe0
+#define SEDL_ISAR_PCI_ISAR_RESET_ON	0x01
+#define SEDL_ISAR_PCI_ISAR_RESET_OFF	0x18
+#define SEDL_ISAR_PCI_LED1		0x08
+#define SEDL_ISAR_PCI_LED2		0x10
 
 #define SEDL_RESET      0x3	/* same as DOS driver */
 
@@ -235,24 +250,25 @@
 static u_char
 ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
 {
-        return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));}
+	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);
+	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);
+	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);
+	writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
 }
 
 static u_char
@@ -485,6 +501,17 @@
 			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 if ((cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) &&
+			(cs->hw.sedl.bus == SEDL_BUS_PCI)) {
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+			save_flags(flags);
+			sti();
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout((20*HZ)/1000);
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout((20*HZ)/1000);
+			restore_flags(flags);
 		} else {		
 			byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
 			save_flags(flags);
@@ -537,6 +564,24 @@
 			return(0);
 		case CARD_TEST:
 			return(0);
+		case MDL_INFO_CONN:
+			if (cs->subtyp != SEDL_SPEEDFAX_PCI)
+				return(0);
+			if ((long) arg)
+				cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED2;
+			else
+				cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED1;
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			break;
+		case MDL_INFO_REL:
+			if (cs->subtyp != SEDL_SPEEDFAX_PCI)
+				return(0);
+			if ((long) arg)
+				cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED2;
+			else
+				cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED1;
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			break;
 	}
 	return(0);
 }
@@ -551,6 +596,8 @@
 	int bytecnt, ver, val;
 	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
+	u16 sub_vendor_id, sub_id;
+	long flags;
 
 	strcpy(tmp, Sedlbauer_revision);
 	printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
@@ -598,15 +645,40 @@
 			printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
 			return(0);
 		}
-		 cs->irq_flags |= SA_SHIRQ;
+		cs->irq_flags |= SA_SHIRQ;
 		cs->hw.sedl.bus = SEDL_BUS_PCI;
-		cs->hw.sedl.chip = SEDL_CHIP_IPAC;
-		cs->subtyp = SEDL_SPEED_PCI;
+		pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_VENDOR_ID,
+			&sub_vendor_id);
+		pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_ID,
+			&sub_id);
+		printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n",
+			sub_vendor_id, sub_id);
+		printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n",
+			cs->hw.sedl.cfg_reg);
+		if ((sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER) &&
+			(sub_id == PCI_SUB_ID_SPEEDFAXP)) {
+			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+			cs->subtyp = SEDL_SPEEDFAX_PCI;
+			cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg +
+						SEDL_ISAR_PCI_ISAR_RESET_ON;
+			cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg +
+						SEDL_ISAR_PCI_ISAR_RESET_OFF;
+		} else {
+			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);
+		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+		save_flags(flags);
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout((10*HZ)/1000);
+		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+		restore_flags(flags);
 #else
 		printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
 		return (0);
@@ -688,7 +760,7 @@
                 cs->writeisacfifo = &WriteISACfifo_IPAC;
                 cs->irq_func = &sedlbauer_interrupt_ipac;
 
-		val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID);
+		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 {
@@ -698,18 +770,31 @@
 		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;
+			if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ISAR;
+			} else {
+				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);
 			cs->irq_func = &sedlbauer_interrupt_isar;
 			cs->auxcmd = &isar_auxcmd;
 			ISACVersion(cs, "Sedlbauer:");
-		
 			cs->BC_Read_Reg = &ReadISAR;
 			cs->BC_Write_Reg = &WriteISAR;
 			cs->BC_Send_Data = &isar_fill_fifo;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/teles0.c linux.14p6/drivers/isdn/hisax/teles0.c
--- linux.vanilla/drivers/isdn/hisax/teles0.c	Sat Aug 14 02:27:26 1999
+++ linux.14p6/drivers/isdn/hisax/teles0.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: teles0.c,v 2.9 1999/07/12 21:05:31 keil Exp $
+/* $Id: teles0.c,v 2.10 1999/11/14 23:37:03 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,9 @@
  *              Beat Doebeli
  *
  * $Log: teles0.c,v $
+ * Revision 2.10  1999/11/14 23:37:03  keil
+ * new ISA memory mapped IO
+ *
  * Revision 2.9  1999/07/12 21:05:31  keil
  * fix race in IRQ handling
  * added watchdog for lost IRQs
@@ -58,71 +61,72 @@
 
 extern const char *CardType[];
 
-const char *teles0_revision = "$Revision: 2.9 $";
+const char *teles0_revision = "$Revision: 2.10 $";
 
+#define TELES_IOMEM_SIZE	0x400
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
 
 static inline u_char
-readisac(unsigned int adr, u_char off)
+readisac(unsigned long adr, u_char off)
 {
 	return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off);
 }
 
 static inline void
-writeisac(unsigned int adr, u_char off, u_char data)
+writeisac(unsigned long adr, u_char off, u_char data)
 {
 	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
 }
 
 
 static inline u_char
-readhscx(unsigned int adr, int hscx, u_char off)
+readhscx(unsigned long adr, int hscx, u_char off)
 {
 	return readb(adr + (hscx ? 0x1c0 : 0x180) +
 		     ((off & 1) ? 0x1ff : 0) + off);
 }
 
 static inline void
-writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+writehscx(unsigned long adr, int hscx, u_char off, u_char data)
 {
 	writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
 	       ((off & 1) ? 0x1ff : 0) + off); mb();
 }
 
 static inline void
-read_fifo_isac(unsigned int adr, u_char * data, int size)
+read_fifo_isac(unsigned long adr, u_char * data, int size)
 {
 	register int i;
-	register u_char *ad = (u_char *) ((long)adr + 0x100);
+	register u_char *ad = (u_char *)adr + 0x100;
 	for (i = 0; i < size; i++)
 		data[i] = readb(ad);
 }
 
 static inline void
-write_fifo_isac(unsigned int adr, u_char * data, int size)
+write_fifo_isac(unsigned long adr, u_char * data, int size)
 {
 	register int i;
-	register u_char *ad = (u_char *) ((long)adr + 0x100);
+	register u_char *ad = (u_char *)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)
+read_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size)
 {
 	register int i;
-	register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
+	register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
 	for (i = 0; i < size; i++)
 		data[i] = readb(ad);
 }
 
 static inline void
-write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+write_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size)
 {
 	int i;
-	register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
+	register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
 	for (i = 0; i < size; i++) {
 		writeb(data[i], ad); mb();
 	}
@@ -262,7 +266,7 @@
 			default:
 				return(1);
 		}
-		cfval |= ((cs->hw.teles0.membase >> 9) & 0xF0);
+		cfval |= ((cs->hw.teles0.phymem >> 9) & 0xF0);
 		byteout(cs->hw.teles0.cfg_reg + 4, cfval);
 		HZDELAY(HZ / 10 + 1);
 		byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
@@ -318,10 +322,9 @@
 		   "Teles0: membase configured DOSish, assuming 0x%lx\n",
 		       (unsigned long) card->para[1]);
 	}
-	cs->hw.teles0.membase = card->para[1];
 	cs->irq = card->para[0];
 	if (cs->hw.teles0.cfg_reg) {
-		if (check_region((cs->hw.teles0.cfg_reg), 8)) {
+		if (check_region(cs->hw.teles0.cfg_reg, 8)) {
 			printk(KERN_WARNING
 			  "HiSax: %s config port %x-%x already in use\n",
 			       CardType[card->typ],
@@ -358,8 +361,10 @@
 	}
 	/* 16.0 and 8.0 designed for IOM1 */
 	test_and_set_bit(HW_IOM1, &cs->HW_Flags);
+	cs->hw.teles0.phymem = card->para[1];
+	cs->hw.teles0.membase = cs->hw.teles0.phymem;
 	printk(KERN_INFO
-	       "HiSax: %s config irq:%d mem:0x%X cfg:0x%X\n",
+	       "HiSax: %s config irq:%d mem:0x%lX cfg:0x%X\n",
 	       CardType[cs->typ], cs->irq,
 	       cs->hw.teles0.membase, cs->hw.teles0.cfg_reg);
 	if (reset_teles0(cs)) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/hisax/telespci.c linux.14p6/drivers/isdn/hisax/telespci.c
--- linux.vanilla/drivers/isdn/hisax/telespci.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/hisax/telespci.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: telespci.c,v 2.9 1999/08/11 21:01:34 keil Exp $
+/* $Id: telespci.c,v 2.10 1999/11/15 14:20:05 keil Exp $
 
  * telespci.c     low level stuff for Teles PCI isdn cards
  *
@@ -7,6 +7,9 @@
  *
  *
  * $Log: telespci.c,v $
+ * Revision 2.10  1999/11/15 14:20:05  keil
+ * 64Bit compatibility
+ *
  * Revision 2.9  1999/08/11 21:01:34  keil
  * new PCI codefix
  *
@@ -44,7 +47,7 @@
 #include <linux/pci.h>
 
 extern const char *CardType[];
-const char *telespci_revision = "$Revision: 2.9 $";
+const char *telespci_revision = "$Revision: 2.10 $";
 
 #define ZORAN_PO_RQ_PEN	0x02000000
 #define ZORAN_PO_WR	0x00800000
@@ -66,7 +69,7 @@
 				} while (portdata & ZORAN_PO_RQ_PEN)
 
 static inline u_char
-readisac(unsigned int adr, u_char off)
+readisac(unsigned long adr, u_char off)
 {
 	register unsigned int portdata;
 
@@ -83,7 +86,7 @@
 }
 
 static inline void
-writeisac(unsigned int adr, u_char off, u_char data)
+writeisac(unsigned long adr, u_char off, u_char data)
 {
 	register unsigned int portdata;
 
@@ -99,7 +102,7 @@
 }
 
 static inline u_char
-readhscx(unsigned int adr, int hscx, u_char off)
+readhscx(unsigned long adr, int hscx, u_char off)
 {
 	register unsigned int portdata;
 
@@ -115,7 +118,7 @@
 }
 
 static inline void
-writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+writehscx(unsigned long adr, int hscx, u_char off, u_char data)
 {
 	register unsigned int portdata;
 
@@ -130,7 +133,7 @@
 }
 
 static inline void
-read_fifo_isac(unsigned int adr, u_char * data, int size)
+read_fifo_isac(unsigned long adr, u_char * data, int size)
 {
 	register unsigned int portdata;
 	register int i;
@@ -148,7 +151,7 @@
 }
 
 static void
-write_fifo_isac(unsigned int adr, u_char * data, int size)
+write_fifo_isac(unsigned long adr, u_char * data, int size)
 {
 	register unsigned int portdata;
 	register int i;
@@ -165,7 +168,7 @@
 }
 
 static inline void
-read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+read_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size)
 {
 	register unsigned int portdata;
 	register int i;
@@ -183,7 +186,7 @@
 }
 
 static inline void
-write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+write_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size)
 {
 	unsigned int portdata;
 	register int i;
@@ -324,7 +327,7 @@
 			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],
+		cs->hw.teles0.membase = (u_long) 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);
@@ -348,7 +351,7 @@
 	/* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
 
 	printk(KERN_INFO
-	       "HiSax: %s config irq:%d mem:%x\n",
+	       "HiSax: %s config irq:%d mem:%lx\n",
 	       CardType[cs->typ], cs->irq,
 	       cs->hw.teles0.membase);
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_common.c linux.14p6/drivers/isdn/isdn_common.c
--- linux.vanilla/drivers/isdn/isdn_common.c	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/isdn_common.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.87 1999/09/12 16:19:39 detabc Exp $
+/* $Id: isdn_common.c,v 1.93 1999/11/04 13:11:36 keil Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -21,6 +21,31 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.93  1999/11/04 13:11:36  keil
+ * Reinit of v110 structs
+ *
+ * Revision 1.92  1999/10/31 15:59:50  he
+ * more skb headroom checks
+ *
+ * Revision 1.91  1999/10/28 22:48:45  armin
+ * Bugfix: isdn_free_channel() now frees the channel,
+ * even when the usage of the ttyI has changed.
+ *
+ * Revision 1.90  1999/10/27 21:21:17  detabc
+ * Added support for building logically-bind-group's per interface.
+ * usefull for outgoing call's with more then one isdn-card.
+ *
+ * Switchable support to dont reset the hangup-timeout for
+ * receive frames. Most part's of the timru-rules for receiving frames
+ * are now obsolete. If the input- or forwarding-firewall deny
+ * the frame, the line will be not hold open.
+ *
+ * Revision 1.89  1999/10/16 14:46:47  keil
+ * replace kmalloc with vmalloc for the big dev struct
+ *
+ * Revision 1.88  1999/10/02 00:39:26  he
+ * Fixed a 2.3.x wait queue initialization (was causing panics)
+ *
  * Revision 1.87  1999/09/12 16:19:39  detabc
  * added abc features
  * low cost routing for net-interfaces (only the HL side).
@@ -371,6 +396,7 @@
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/poll.h>
+#include <linux/vmalloc.h>
 #include <linux/isdn.h>
 #include "isdn_common.h"
 #include "isdn_tty.h"
@@ -390,7 +416,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.87 $";
+static char *isdn_revision = "$Revision: 1.93 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -2098,13 +2124,19 @@
 	save_flags(flags);
 	cli();
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if (((dev->usage[i] & ISDN_USAGE_MASK) == usage) &&
+		if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) &&
 		    (dev->drvmap[i] == di) &&
 		    (dev->chanmap[i] == ch)) {
 			dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
 			strcpy(dev->num[i], "???");
 			dev->ibytes[i] = 0;
 			dev->obytes[i] = 0;
+// 20.10.99 JIM, try to reinitialize v110 !
+			dev->v110emu[i] = 0;
+			atomic_set(&(dev->v110use[i]), 0);
+			isdn_v110_close(dev->v110[i]);
+			dev->v110[i] = NULL;
+// 20.10.99 JIM, try to reinitialize v110 !
 			isdn_info_update();
 			isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
 		}
@@ -2186,8 +2218,33 @@
 		/* V.110 must always be acknowledged */
 		ack = 1;
 		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb);
-	} else
-		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
+	} else {
+		int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+
+		if( skb_headroom(skb) < hl ){
+			/* 
+			 * This should only occur when new HL driver with
+			 * increased hl_hdrlen was loaded after netdevice
+			 * was created and connected to the new driver.
+			 *
+			 * The V.110 branch (re-allocates on its own) does
+			 * not need this
+			 */
+			struct sk_buff * skb_tmp;
+
+			skb_tmp = skb_realloc_headroom(skb, hl);
+			printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed");
+			if (!skb_tmp) return -ENOMEM; /* 0 better? */
+			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp);
+			if( ret > 0 ){
+				dev_kfree_skb(skb);
+			} else {
+				dev_kfree_skb(skb_tmp);
+			}
+		} else {
+			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
+		}
+	}
 	if (ret > 0) {
 		dev->obytes[idx] += ret;
 		if (dev->v110[idx]) {
@@ -2470,8 +2527,7 @@
 	int i;
 	char tmprev[50];
 
-	sti();
-	if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
+	if (!(dev = (isdn_dev *) vmalloc(sizeof(isdn_dev)))) {
 		printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
 		return -EIO;
 	}
@@ -2487,7 +2543,7 @@
 	}
 	if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
 		printk(KERN_WARNING "isdn: Could not register control devices\n");
-		kfree(dev);
+		vfree(dev);
 		return -EIO;
 	}
 	if ((i = isdn_tty_modem_init()) < 0) {
@@ -2496,7 +2552,7 @@
 			tty_unregister_driver(&dev->mdm.cua_modem);
 		if (i <= -2)
 			tty_unregister_driver(&dev->mdm.tty_modem);
-		kfree(dev);
+		vfree(dev);
 		unregister_chrdev(ISDN_MAJOR, "isdn");
 		return -EIO;
 	}
@@ -2508,7 +2564,7 @@
 		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 			kfree(dev->mdm.info[i].xmit_buf - 4);
 		unregister_chrdev(ISDN_MAJOR, "isdn");
-		kfree(dev);
+		vfree(dev);
 		return -EIO;
 	}
 #endif                          /* CONFIG_ISDN_PPP */
@@ -2577,7 +2633,7 @@
 		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
 	} else {
 		del_timer(&dev->timer);
-		kfree(dev);
+		vfree(dev);
 		printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
 	}
 	restore_flags(flags);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_common.h linux.14p6/drivers/isdn/isdn_common.h
--- linux.vanilla/drivers/isdn/isdn_common.h	Wed Oct 20 01:12:37 1999
+++ linux.14p6/drivers/isdn/isdn_common.h	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.h,v 1.16 1999/07/01 08:29:54 keil Exp $
+/* $Id: isdn_common.h,v 1.17 1999/10/27 21:21:17 detabc Exp $
 
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
@@ -21,6 +21,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.h,v $
+ * Revision 1.17  1999/10/27 21:21:17  detabc
+ * Added support for building logically-bind-group's per interface.
+ * usefull for outgoing call's with more then one isdn-card.
+ *
+ * Switchable support to dont reset the hangup-timeout for
+ * receive frames. Most part's of the timru-rules for receiving frames
+ * are now obsolete. If the input- or forwarding-firewall deny
+ * the frame, the line will be not hold open.
+ *
  * Revision 1.16  1999/07/01 08:29:54  keil
  * compatibility to 2.3 kernel
  *
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_net.c linux.14p6/drivers/isdn/isdn_net.c
--- linux.vanilla/drivers/isdn/isdn_net.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/isdn/isdn_net.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.92 1999/09/13 23:25:17 he Exp $
+/* $Id: isdn_net.c,v 1.95 1999/10/27 21:21:17 detabc Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -21,6 +21,24 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.95  1999/10/27 21:21:17  detabc
+ * Added support for building logically-bind-group's per interface.
+ * usefull for outgoing call's with more then one isdn-card.
+ *
+ * Switchable support to dont reset the hangup-timeout for
+ * receive frames. Most part's of the timru-rules for receiving frames
+ * are now obsolete. If the input- or forwarding-firewall deny
+ * the frame, the line will be not hold open.
+ *
+ * Revision 1.94  1999/10/02 11:07:02  he
+ * Changed tbusy logic in indn_net.c
+ *
+ * Revision 1.93  1999/09/23 22:22:41  detabc
+ * added tcp-keepalive-detect with local response (ipv4 only)
+ * added host-only-interface support
+ * (source ipaddr == interface ipaddr) (ipv4 only)
+ * ok with kernel 2.3.18 and 2.2.12
+ *
  * Revision 1.92  1999/09/13 23:25:17  he
  * serialized xmitting frames from isdn_ppp and BSENT statcallb
  *
@@ -375,6 +393,49 @@
 #endif
 
 
+#ifndef ISDN_NEW_TBUSY
+#define ISDN_NEW_TBUSY
+#endif
+#ifdef ISDN_NEW_TBUSY
+/*
+ * Outline of new tbusy handling: 
+ *
+ * Old method, roughly spoken, consisted of setting tbusy when entering
+ * isdn_net_start_xmit() and at several other locations and clearing
+ * it from isdn_net_start_xmit() thread when sending was successful.
+ *
+ * With 2.3.x multithreaded network core, to prevent problems, tbusy should
+ * only be set by the isdn_net_start_xmit() thread and only when a tx-busy
+ * condition is detected. Other threads (in particular isdn_net_stat_callb())
+ * are only allowed to clear tbusy.
+ *
+ * -HE
+ */
+
+/*
+ * Tell upper layers that the network device is ready to xmit more frames.
+ */
+static void __inline__ isdn_net_dev_xon(struct device * dev)
+{
+	dev->tbusy = 0;
+	mark_bh(NET_BH);
+}
+
+static void __inline__ isdn_net_lp_xon(isdn_net_local * lp)
+{
+	lp->netdev->dev.tbusy = 0;
+	if(lp->master) lp->master->tbusy = 0;
+	mark_bh(NET_BH);
+}
+
+/*
+ * Ask upper layers to temporarily cease passing us more xmit frames.
+ */
+static void __inline__ isdn_net_dev_xoff(struct device * dev)
+{
+	dev->tbusy = 1;
+}
+#endif
 
 /* Prototypes */
 
@@ -382,7 +443,7 @@
 static int isdn_net_start_xmit(struct sk_buff *, struct device *);
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
 
-char *isdn_net_revision = "$Revision: 1.92 $";
+char *isdn_net_revision = "$Revision: 1.95 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -424,7 +485,11 @@
 	save_flags(flags);
 	cli();                  /* Avoid glitch on writes to CMD regs */
 	dev->interrupt = 0;
+#ifdef ISDN_NEW_TBUSY
+	isdn_net_dev_xon(dev);
+#else
 	dev->tbusy = 0;
+#endif
 #ifdef CONFIG_ISDN_X25
 	if( cprot && cprot -> pops && dops )
 		cprot -> pops -> restart ( cprot, dev, dops );
@@ -638,13 +703,19 @@
 							mdev = &lp->netdev->dev;
 						if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) {
 							lp->sav_skb = NULL;
+#ifndef ISDN_NEW_TBUSY
 							mark_bh(NET_BH);
+#endif
 						} else {
 							return 1;
 						}
 					}
+#ifdef ISDN_NEW_TBUSY
+					isdn_net_lp_xon(lp);
+#else
 					if (test_and_clear_bit(0, (void *) &(p->dev.tbusy)))
 						mark_bh(NET_BH);
+#endif
 				}
 				return 1;
 			case ISDN_STAT_DCONN:
@@ -727,7 +798,6 @@
 						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)
 							isdn_ppp_wakeup_daemon(lp);
@@ -738,11 +808,15 @@
 							if( pops->connect_ind)
 								pops->connect_ind(cprot);
 #endif /* CONFIG_ISDN_X25 */
+						/* Immediately send first skb to speed up arp */
 						if (lp->first_skb) {
 
 							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
 								lp->first_skb = NULL;
 						}
+#ifdef ISDN_NEW_TBUSY
+						if(! lp->first_skb) isdn_net_lp_xon(lp);
+#else
 						else {
 							/*
 							 * dev.tbusy is usually cleared implicitly by isdn_net_xmit(,,lp->first_skb).
@@ -751,6 +825,7 @@
 							lp->netdev->dev.tbusy = 0;
 							mark_bh(NET_BH);
 						}
+#endif /* ISDN_NEW_TBUSY */
 						return 1;
 				}
 				break;
@@ -1185,6 +1260,7 @@
 					break;
 			}
 			printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+
 			       p[12], p[13], p[14], p[15],
 			       p[16], p[17], p[18], p[19],
 			       addinfo);
@@ -1203,8 +1279,12 @@
  * standard send-routine, else send directly.
  *
  * Return: 0 on success, !0 on failure.
+ */
+#ifndef ISDN_NEW_TBUSY
+/*
  * Side-effects: ndev->tbusy is cleared on success.
  */
+#endif
 int
 isdn_net_send_skb(struct device *ndev, isdn_net_local * lp,
 		  struct sk_buff *skb)
@@ -1215,13 +1295,17 @@
 	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
 	if (ret == len) {
 		lp->transcount += len;
+#ifndef ISDN_NEW_TBUSY
 		clear_bit(0, (void *) &(ndev->tbusy));
+#endif
 		return 0;
 	}
 	if (ret < 0) {
 		dev_kfree_skb(skb);
 		lp->stats.tx_errors++;
+#ifndef ISDN_NEW_TBUSY
 		clear_bit(0, (void *) &(ndev->tbusy));
+#endif
 		return 0;
 	}
 	return 1;
@@ -1267,7 +1351,11 @@
 			if (lp->srobin == ndev)
 				ret = isdn_net_send_skb(ndev, lp, skb);
 			else
+#ifdef ISDN_NEW_TBUSY
+				ret = isdn_net_start_xmit(skb, lp->srobin);
+#else
 				ret = ndev->tbusy = isdn_net_start_xmit(skb, lp->srobin);
+#endif
 			lp->srobin = (slp->slave) ? slp->slave : ndev;
 			slp = (isdn_net_local *) (lp->srobin->priv);
 			if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0)))
@@ -1327,8 +1415,13 @@
 		if (!lp->dialstate)
 			lp->stats.tx_errors++;
 		ndev->trans_start = jiffies;
+#ifdef ISDN_NEW_TBUSY
+		isdn_net_dev_xon(ndev);
+#endif
 	}
+#ifndef ISDN_NEW_TBUSY
 	ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */
+#endif
 #ifdef CONFIG_ISDN_X25
 /* At this point hard_start_xmit() passes control to the encapsulation
    protocol (if present).
@@ -1342,7 +1435,11 @@
    when a dl_establish request is received from the upper layer.
 */
 	if( cprot ) {
-		return  cprot -> pops -> encap_and_xmit ( cprot , skb);
+		int ret = cprot -> pops -> encap_and_xmit ( cprot , skb);
+#ifdef ISDN_NEW_TBUSY
+		if(ret) isdn_net_dev_xoff(ndev);
+#endif
+		return ret;
 	} else
 #endif
 	/* auto-dialing xmit function */
@@ -1361,7 +1458,9 @@
 			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);
+#ifndef ISDN_NEW_TBUSY
 				ndev->tbusy = 0;
+#endif
 				return 0;
 			}
 			if (lp->phone[1]) {
@@ -1377,7 +1476,9 @@
 					if(jiffies < lp->dialwait_timer) {
 						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
 						dev_kfree_skb(skb);
+#ifndef ISDN_NEW_TBUSY
 						ndev->tbusy = 0;
+#endif
 						restore_flags(flags);
 						return 0;
 					} else
@@ -1386,22 +1487,28 @@
 
 				/* Grab a free ISDN-Channel */
 				if (((chi =
-				     isdn_get_free_channel(ISDN_USAGE_NET,
-							   lp->l2_proto,
-							   lp->l3_proto,
-							   lp->pre_device,
-						 lp->pre_channel)) < 0) &&
+				     isdn_get_free_channel(
+					 		ISDN_USAGE_NET,
+							lp->l2_proto,
+							lp->l3_proto,
+							lp->pre_device,
+						 	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)) {
+				     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");
 					dev_kfree_skb(skb);
+#ifndef ISDN_NEW_TBUSY
 					ndev->tbusy = 0;
+#endif
 					return 0;
 				}
 				/* Log packet, which triggered dialing */
@@ -1421,6 +1528,9 @@
 					}
 					restore_flags(flags);
 					isdn_net_dial();	/* Initiate dialing */
+#ifdef ISDN_NEW_TBUSY
+					isdn_net_dev_xoff(ndev);
+#endif
 					return 1;	/* let upper layer requeue skb packet */
 				}
 #endif
@@ -1434,7 +1544,9 @@
 				}
 				lp->first_skb = skb;
 				/* Initiate dialing */
+#ifndef ISDN_NEW_TBUSY
 				ndev->tbusy = 0;
+#endif
 				restore_flags(flags);
 				isdn_net_dial();
 				return 0;
@@ -1442,21 +1554,37 @@
 				isdn_net_unreachable(ndev, skb,
 						     "No phone number");
 				dev_kfree_skb(skb);
+#ifndef ISDN_NEW_TBUSY
 				ndev->tbusy = 0;
+#endif
 				return 0;
 			}
 		} else {
-			/* Connection is established, try sending */
+			/* Device is connected to an ISDN channel */ 
 			ndev->trans_start = jiffies;
 			if (!lp->dialstate) {
+				/* ISDN connection is established, try sending */
+				int ret;
 				if (lp->first_skb) {
-					if (isdn_net_xmit(ndev, lp, lp->first_skb))
+					if (isdn_net_xmit(ndev, lp, lp->first_skb)){
+#ifdef ISDN_NEW_TBUSY
+						isdn_net_dev_xoff(ndev);
+#endif
 						return 1;
+}
 					lp->first_skb = NULL;
 				}
-				return (isdn_net_xmit(ndev, lp, skb));
+				ret = (isdn_net_xmit(ndev, lp, skb));
+#ifdef ISDN_NEW_TBUSY
+				if(ret) isdn_net_dev_xoff(ndev);
+#endif
+				return ret;
 			} else
+#ifdef ISDN_NEW_TBUSY
+				isdn_net_dev_xoff(ndev);
+#else
 				ndev->tbusy = 1;
+#endif
 		}
 	}
 	return 1;
@@ -2068,7 +2196,6 @@
 	isdn_net_phone *n;
 	ulong flags;
 	char nr[32];
-
 	/* Search name in netdev-chain */
 	save_flags(flags);
 	cli();
@@ -2280,10 +2407,15 @@
 					       lp->name, nr, eaz);
 					if (lp->phone[1]) {
 						/* Grab a free ISDN-Channel */
-						if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+						if ((chi = 
+							isdn_get_free_channel(
+								ISDN_USAGE_NET,
+								lp->l2_proto,
 							    lp->l3_proto,
-							  lp->pre_device,
-						 lp->pre_channel)) < 0) {
+							  	lp->pre_device,
+						 		lp->pre_channel)
+								) < 0) {
+
 							printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
 							restore_flags(flags);
 							return 0;
@@ -2390,10 +2522,14 @@
 			cli();
 
 			/* Grab a free ISDN-Channel */
-			if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
-							 lp->l3_proto,
-							 lp->pre_device,
-						 lp->pre_channel)) < 0) {
+			if ((chi = 
+						isdn_get_free_channel(
+							ISDN_USAGE_NET,
+							lp->l2_proto,
+							lp->l3_proto,
+							lp->pre_device,
+						 	lp->pre_channel)
+							) < 0) {
 				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
 				restore_flags(flags);
 				return -EAGAIN;
@@ -3153,4 +3289,3 @@
 	restore_flags(flags);
 	return 0;
 }
-
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_ppp.c linux.14p6/drivers/isdn/isdn_ppp.c
--- linux.vanilla/drivers/isdn/isdn_ppp.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/isdn/isdn_ppp.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.54 1999/09/13 23:25:17 he Exp $
+/* $Id: isdn_ppp.c,v 1.60 1999/11/04 20:29:55 he Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,28 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.60  1999/11/04 20:29:55  he
+ * applied Andre Beck's reset_free fix
+ *
+ * Revision 1.59  1999/10/31 15:59:50  he
+ * more skb headroom checks
+ *
+ * Revision 1.58  1999/10/30 13:13:01  keil
+ * Henners isdn_ppp_skb_push:under fix
+ *
+ * Revision 1.57  1999/10/05 22:47:17  he
+ * Removed dead ISDN_SYNCPPP_READDRESS code (obsoleted by sysctl_ip_dynaddr
+ * and network address translation)
+ *
+ * Revision 1.56  1999/09/29 16:01:06  he
+ * replaced dev_alloc_skb() for downstream skbs by equivalent alloc_skb()
+ *
+ * Revision 1.55  1999/09/23 22:07:51  detabc
+ *
+ * make ipc_head common usable (for use compressor with raw-ip)
+ * add function before netif_rx(). needed for ipv4-tcp-keepalive-detect.
+ * ~
+ *
  * Revision 1.54  1999/09/13 23:25:17  he
  * serialized xmitting frames from isdn_ppp and BSENT statcallb
  *
@@ -218,10 +240,6 @@
 
 /* TODO: right tbusy handling when using MP */
 
-/*
- * experimental for dynamic addressing: readdress IP frames
- */
-#undef ISDN_SYNCPPP_READDRESS
 #define CONFIG_ISDN_CCP 1
 
 #include <linux/config.h>
@@ -264,6 +282,7 @@
 				    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(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);
@@ -287,9 +306,10 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.54 $";
+char *isdn_ppp_revision = "$Revision: 1.60 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+
 static struct isdn_ppp_compressor *ipc_head = NULL;
 
 /*
@@ -360,10 +380,6 @@
 		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
 
 	is->lp = NULL;          /* link is down .. set lp to NULL */
-#ifdef ISDN_SYNCPPP_READDRESS
-	is->old_pa_addr = 0x0;
-	is->old_pa_dstaddr = 0x0;
-#endif
 	lp->ppp_slot = -1;      /* is this OK ?? */
 	restore_flags(flags);
 
@@ -608,9 +624,9 @@
 	is->comp_stat    = is->link_comp_stat    = NULL;
         is->decomp_stat  = is->link_decomp_stat  = NULL;
 
+	/* Clean up if necessary */
 	if(is->reset)
-		kfree(is->reset);
-	is->reset = NULL;
+		isdn_ppp_ccp_reset_free(is);
 
 	/* this slot is ready for new connections */
 	is->state = 0;
@@ -1462,12 +1478,6 @@
 	ipts = ippp_table[mlp->ppp_slot];
 
 	if (!(ipts->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
-#ifdef ISDN_SYNCPPP_READDRESS
-		if (!ipts->old_pa_addr)
-			ipts->old_pa_addr = mdev->pa_addr;
-		if (!ipts->old_pa_dstaddr)
-			ipts->old_pa_dstaddr = mdev->pa_dstaddr;
-#endif
 		if (ipts->debug & 0x1)
 			printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name);
 		return 1;
@@ -1476,21 +1486,6 @@
 	switch (ntohs(skb->protocol)) {
 		case ETH_P_IP:
 			proto = PPP_IP;
-#ifdef ISDN_SYNCPPP_READDRESS
-			if (ipts->old_pa_addr != mdev->pa_addr) {
-				struct iphdr *ipfr;
-				ipfr = (struct iphdr *) skb->data;
-				if(ipts->debug & 0x4)
-					printk(KERN_DEBUG "IF-address changed from %lx to %lx\n", ipts->old_pa_addr, mdev->pa_addr);
-				if (ipfr->version == 4) {
-					if (ipfr->saddr == ipts->old_pa_addr) {
-						printk(KERN_DEBUG "readdressing %lx to %lx\n", ipfr->saddr, mdev->pa_addr);
-						ipfr->saddr = mdev->pa_addr;
-					}
-				}
-			}
-			/* dstaddr change not so important */
-#endif
 			break;
 		case ETH_P_IPX:
 			proto = PPP_IPX;	/* untested */
@@ -1521,8 +1516,6 @@
 
 	/* Pull off the fake header we stuck on earlier to keep
      * the fragemntation code happy.
-     * this will break the ISDN_SYNCPPP_READDRESS hack a few lines
-     * above. So, enabling this is no longer allowed
      */
 	skb_pull(skb,IPPP_MAX_HEADER);
 
@@ -1540,7 +1533,13 @@
 		 * sk_buff. old call to dev_alloc_skb only reserved
 		 * 16 bytes, now we are looking what the driver want.
 		 */
-		hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+		hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER;
+		/* 
+		 * Note: hl might still be insufficient because the method
+		 * above does not account for a possibible MPPP slave channel
+		 * which had larger HL header space requirements than the
+		 * master.
+		 */
 		new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC);
 		if (new_skb) {
 			u_char *buf;
@@ -2227,18 +2226,20 @@
 {
 	struct sk_buff *skb;
 	unsigned char *p;
-	int count;
+	int count, hl;
 	unsigned long flags;
 	int cnt = 0;
 	isdn_net_local *lp = is->lp;
 
 	/* Alloc large enough skb */
-	skb = dev_alloc_skb(len + 16);
+	hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+	skb = alloc_skb(len + hl + 16,GFP_ATOMIC);
 	if(!skb) {
 		printk(KERN_WARNING
 		       "ippp: CCP cannot send reset - out of memory\n");
 		return;
 	}
+	skb_reserve(skb, hl);
 
 	/* We may need to stuff an address and control field first */
 	if(!(is->pppcfg & SC_COMP_AC)) {
@@ -2293,15 +2294,34 @@
 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)
+	if(!r) {
+		printk(KERN_ERR "ippp_ccp: failed to allocate reset data"
+		       " structure - no mem\n");
 		return NULL;
+	}
 	memset(r, 0, sizeof(struct ippp_ccp_reset));
+	printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r);
 	is->reset = r;
 	return r;
 }
 
+/* Destroy the reset state vector. Kill all pending timers first. */
+static void isdn_ppp_ccp_reset_free(struct ippp_struct *is)
+{
+	unsigned int id;
+
+	printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n",
+	       is->reset);
+	for(id = 0; id < 256; id++) {
+		if(is->reset->rs[id]) {
+			isdn_ppp_ccp_reset_free_state(is, (unsigned char)id);
+		}
+	}
+	kfree(is->reset);
+	is->reset = NULL;
+}
+
 /* 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)
@@ -2667,9 +2687,11 @@
 	}
 
 	/* Allow for at least 150 % expansion (for now) */
-	skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32);
+	skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 +
+		skb_headroom(skb_in), GFP_ATOMIC);
 	if(!skb_out)
 		return skb_in;
+	skb_reserve(skb_out, skb_headroom(skb_in));
 
 	ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
 	if(!ret) {
@@ -2898,7 +2920,6 @@
 	}
 }
 
-
 int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
 {
 	ipc->next = ipc_head;
@@ -2932,6 +2953,16 @@
 	if(is->debug & 0x10)
 		printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
 			(data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
+
+	/* If is has no valid reset state vector, we cannot allocate a
+	   decompressor. The decompressor would cause reset transactions
+	   sooner or later, and they need that vector. */
+
+	if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) {
+		printk(KERN_ERR "ippp_ccp: no reset data structure - can't"
+		       " allow decompression.\n");
+		return -ENOMEM;
+	}
 
 	while(ipc) {
 		if(ipc->num == num) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_tty.c linux.14p6/drivers/isdn/isdn_tty.c
--- linux.vanilla/drivers/isdn/isdn_tty.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/isdn/isdn_tty.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.74 1999/09/04 06:20:04 keil Exp $
+/* $Id: isdn_tty.c,v 1.80 1999/11/07 13:34:30 armin Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -20,6 +20,27 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.80  1999/11/07 13:34:30  armin
+ * Fixed AT command line editor
+ *
+ * Revision 1.79  1999/10/29 18:35:08  armin
+ * Check number len in isdn_get_msnstr() to avoid buffer overflow.
+ *
+ * Revision 1.78  1999/10/28 23:03:51  armin
+ * Bugfix: now freeing channel on modem_hup() even when
+ * usage on ttyI has changed and error-report for
+ * AT-commands on wrong channel-state.
+ *
+ * Revision 1.77  1999/10/26 21:13:14  armin
+ * using define for checking phone number len in isdn_tty_getdial()
+ *
+ * Revision 1.76  1999/10/11 22:16:26  keil
+ * Suspend/Resume is possible without explicit ID too
+ *
+ * Revision 1.75  1999/10/08 18:59:32  armin
+ * Bugfix of too small MSN buffer and checking phone number
+ * in isdn_tty_getdial()
+ *
  * Revision 1.74  1999/09/04 06:20:04  keil
  * Changes from kernel set_current_state()
  *
@@ -351,7 +372,7 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.74 $";
+char *isdn_tty_revision = "$Revision: 1.80 $";
 
 
 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
@@ -1017,7 +1038,6 @@
 isdn_tty_modem_hup(modem_info * info, int local)
 {
 	isdn_ctrl cmd;
-	int usage;
 
 	if (!info)
 		return;
@@ -1071,10 +1091,7 @@
 		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
 		info->emu.mdmreg[REG_RINGCNT] = 0;
-		usage = isdn_calc_usage(info->emu.mdmreg[REG_SI1I],
-					info->emu.mdmreg[REG_L2PROT]);
-		isdn_free_channel(info->isdn_driver, info->isdn_channel,
-				  usage);
+		isdn_free_channel(info->isdn_driver, info->isdn_channel, 0);
 	}
 	info->isdn_driver = -1;
 	info->isdn_channel = -1;
@@ -1111,8 +1128,8 @@
 	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;
+	if ((info->isdn_driver >= 0)) {
+		cmd.parm.cmsg.Length = l+18;
 		cmd.parm.cmsg.Command = CAPI_FACILITY;
 		cmd.parm.cmsg.Subcommand = CAPI_REQ;
 		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
@@ -1150,10 +1167,6 @@
 	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];
@@ -1207,7 +1220,7 @@
 		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
-		cmd.parm.cmsg.Length = l+17;
+		cmd.parm.cmsg.Length = l+18;
 		cmd.parm.cmsg.Command = CAPI_FACILITY;
 		cmd.parm.cmsg.Subcommand = CAPI_REQ;
 		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
@@ -3091,9 +3104,12 @@
 static void
 isdn_tty_get_msnstr(char *n, char **p)
 {
-	while ((*p[0] >= '0' && *p[0] <= '9') ||
+	int limit = ISDN_MSNLEN - 1;
+
+	while (((*p[0] >= '0' && *p[0] <= '9') ||
 	       /* Why a comma ??? */
-	       (*p[0] == ','))
+	       (*p[0] == ',')) &&
+		(limit--))
 		*n++ = *p[0]++;
 	*n = '\0';
 }
@@ -3105,16 +3121,18 @@
 isdn_tty_getdial(char *p, char *q,int cnt)
 {
 	int first = 1;
-	int limit=39;	/* MUST match the size in isdn_tty_parse to avoid
-				buffer overflow */
+	int limit = ISDN_MSNLEN - 1;	/* MUST match the size of interface var to avoid
+					buffer overflow */
 
 	while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) {
 		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) ||
-		    (*p == '*') || (*p == '#'))
+		    (*p == '*') || (*p == '#')) {
 			*q++ = *p;
-		p++;
-		if(!--limit)
+			limit--;
+		}
+		if(!limit)
 			break;
+		p++;
 		first = 0;
 	}
 	*q = 0;
@@ -3258,6 +3276,8 @@
 		case 'F':
 			/* &F -Set Factory-Defaults */
 			p[0]++;
+			if (info->msr & UART_MSR_DCD)
+				PARSE_ERROR1;
 			isdn_tty_reset_profile(m);
 			isdn_tty_modem_reset_regs(info, 1);
 			break;
@@ -3909,6 +3929,12 @@
 				break;
 			case 'D':
 				/* D - Dial */
+				if (info->msr & UART_MSR_DCD)
+					PARSE_ERROR;
+				if (info->msr & UART_MSR_RI) {
+					isdn_tty_modem_result(3, info);
+					return;
+				}
 				isdn_tty_getdial(++p, ds, sizeof ds);
 				p += strlen(p);
 				if (!strlen(m->msn))
@@ -4103,7 +4129,7 @@
 			c = *p;
 		total++;
 		if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) {
-			/* Separator (CR oder LF) */
+			/* Separator (CR or LF) */
 			m->mdmcmd[m->mdmcmdl] = 0;
 			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
 				eb[0] = c;
@@ -4116,7 +4142,7 @@
 			continue;
 		}
 		if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) {
-			/* Backspace-Funktion */
+			/* Backspace-Function */
 			if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
 				if (m->mdmcmdl)
 					m->mdmcmdl--;
@@ -4134,18 +4160,24 @@
 			if (m->mdmcmdl < 255) {
 				c = my_toupper(c);
 				switch (m->mdmcmdl) {
-					case 0:
-						if (c == 'A')
-							m->mdmcmd[m->mdmcmdl] = c;
-						break;
 					case 1:
-						if (c == 'T')
+						if (c == 'T') {
+							m->mdmcmd[m->mdmcmdl] = c;
+							m->mdmcmd[++m->mdmcmdl] = 0;
+							break;
+						} else
+							m->mdmcmdl = 0;
+						/* Fall through, check for 'A' */
+					case 0:
+						if (c == 'A') {
 							m->mdmcmd[m->mdmcmdl] = c;
+							m->mdmcmd[++m->mdmcmdl] = 0;
+						}
 						break;
 					default:
 						m->mdmcmd[m->mdmcmdl] = c;
+						m->mdmcmd[++m->mdmcmdl] = 0;
 				}
-				m->mdmcmd[++m->mdmcmdl] = 0;
 			}
 		}
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_v110.c linux.14p6/drivers/isdn/isdn_v110.c
--- linux.vanilla/drivers/isdn/isdn_v110.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/isdn/isdn_v110.c	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_v110.c,v 1.2 1998/02/22 19:44:25 fritz Exp $
+/* $Id: isdn_v110.c,v 1.3 1999/10/30 09:49:28 keil Exp $
 
  * Linux ISDN subsystem, V.110 related functions (linklevel).
  *
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_v110.c,v $
+ * Revision 1.3  1999/10/30 09:49:28  keil
+ * Reinit of v110 structs
+ *
  * Revision 1.2  1998/02/22 19:44:25  fritz
  * Bugfixes and improvements regarding V.110, V.110 now running.
  *
@@ -36,7 +39,7 @@
 
 #undef ISDN_V110_DEBUG
 
-char *isdn_v110_revision = "$Revision: 1.2 $";
+char *isdn_v110_revision = "$Revision: 1.3 $";
 
 #define V110_38400 255
 #define V110_19200  15
@@ -148,7 +151,7 @@
 }
 
 /* isdn_v110_close frees private V.110 data structures */
-static void
+void
 isdn_v110_close(isdn_v110_stream * v)
 {
 	if (v == NULL)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_v110.h linux.14p6/drivers/isdn/isdn_v110.h
--- linux.vanilla/drivers/isdn/isdn_v110.h	Thu Apr  2 01:21:03 1998
+++ linux.14p6/drivers/isdn/isdn_v110.h	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_v110.h,v 1.1 1998/02/20 17:32:11 fritz Exp $
+/* $Id: isdn_v110.h,v 1.2 1999/10/30 09:49:28 keil Exp $
 
  * Linux ISDN subsystem, V.110 related functions (linklevel).
  *
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_v110.h,v $
+ * Revision 1.2  1999/10/30 09:49:28  keil
+ * Reinit of v110 structs
+ *
  * Revision 1.1  1998/02/20 17:32:11  fritz
  * First checkin (not yet completely functionable).
  *
@@ -41,5 +44,6 @@
 extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *);
 
 extern int isdn_v110_stat_callback(int, isdn_ctrl *);
+extern void isdn_v110_close(isdn_v110_stream * v);
 
 #endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/pcbit/drv.c linux.14p6/drivers/isdn/pcbit/drv.c
--- linux.vanilla/drivers/isdn/pcbit/drv.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/isdn/pcbit/drv.c	Tue Nov 16 15:10:48 1999
@@ -87,9 +87,11 @@
 	dev_pcbit[board] = dev;
 	memset(dev, 0, sizeof(struct pcbit_dev));
 
-	if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF )
+	if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) {
+		dev->ph_mem = mem_base;
 		dev->sh_mem = (unsigned char*) mem_base;
-	else
+	}
+	else 
 	{
 		printk("memory address invalid");
 		kfree(dev);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/pcbit/pcbit.h linux.14p6/drivers/isdn/pcbit/pcbit.h
--- linux.vanilla/drivers/isdn/pcbit/pcbit.h	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/isdn/pcbit/pcbit.h	Tue Nov 16 15:10:48 1999
@@ -46,6 +46,7 @@
 	/* board */
 
 	volatile unsigned char* sh_mem;		/* RDP address	*/
+	unsigned long ph_mem;
 	unsigned int irq;
 	unsigned int id;
 	unsigned int interrupt;			/* set during interrupt 
@@ -166,10 +167,3 @@
 #define L2_ERROR    6
 
 #endif
-
-
-
-
-
-
-
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/macintosh/macserial.c linux.14p6/drivers/macintosh/macserial.c
--- linux.vanilla/drivers/macintosh/macserial.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/macintosh/macserial.c	Fri Oct 22 22:26:41 1999
@@ -6,7 +6,9 @@
  * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  *
- * $Id: macserial.c,v 1.24.2.3 1999/09/10 02:05:58 paulus Exp $
+ * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>.
+ *
+ * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $
  */
 
 #include <linux/config.h>
@@ -28,6 +30,7 @@
 #ifdef CONFIG_SERIAL_CONSOLE
 #include <linux/console.h>
 #endif
+#include <linux/slab.h>
 
 #include <asm/io.h>
 #include <asm/pgtable.h>
@@ -42,6 +45,7 @@
 #ifdef CONFIG_KGDB
 #include <asm/kgdb.h>
 #endif
+#include <asm/dbdma.h>
 
 #include "macserial.h"
 
@@ -53,6 +57,8 @@
 };
 #endif
 
+#define SUPPORT_SERIAL_DMA
+
 /*
  * It would be nice to dynamically allocate everything that
  * depends on NUM_SERIAL, so we could support any number of
@@ -128,6 +134,13 @@
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 static int set_scc_power(struct mac_serial * info, int state);
 static int setup_scc(struct mac_serial * info);
+static void dbdma_reset(volatile struct dbdma_regs *dma);
+static void dbdma_flush(volatile struct dbdma_regs *dma);
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void dma_init(struct mac_serial * info);
+static void rxdma_start(struct mac_serial * info, int current);
+static void rxdma_to_tty(struct mac_serial * info);
 
 static struct tty_struct *serial_table[NUM_CHANNELS];
 static struct termios *serial_termios[NUM_CHANNELS];
@@ -153,7 +166,7 @@
 __openfirmware
 #endif /* MODULE */
 static inline int serial_paranoia_check(struct mac_serial *info,
-					dev_t device, const char *routine)
+                                       dev_t device, const char *routine)
 {
 #ifdef SERIAL_PARANOIA_CHECK
 	static const char *badmagic =
@@ -177,7 +190,7 @@
  * Reading and writing Z8530 registers.
  */
 static inline unsigned char read_zsreg(struct mac_zschannel *channel,
-				       unsigned char reg)
+                                      unsigned char reg)
 {
 	unsigned char retval;
 	unsigned long flags;
@@ -197,7 +210,7 @@
 }
 
 static inline void write_zsreg(struct mac_zschannel *channel,
-			       unsigned char reg, unsigned char value)
+                              unsigned char reg, unsigned char value)
 {
 	unsigned long flags;
 
@@ -290,6 +303,39 @@
 }
 
 /*
+ * Reset a Descriptor-Based DMA channel.
+ */
+static void dbdma_reset(volatile struct dbdma_regs *dma)
+{
+	int i;
+
+	out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
+
+	/*
+	 * Yes this looks peculiar, but apparently it needs to be this
+	 * way on some machines.  (We need to make sure the DBDMA
+	 * engine has actually got the write above and responded
+	 * to it. - paulus)
+	 */
+	for (i = 200; i > 0; --i)
+		if (ld_le32(&dma->control) & RUN)
+			udelay(1);
+}
+
+/*
+ * Tells a DBDMA channel to stop and write any buffered data
+ * it might have to memory.
+ */
+static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma)
+{
+	int i = 0;
+
+	out_le32(&dma->control, (FLUSH << 16) | FLUSH);
+	while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100))
+		udelay(1);
+}
+
+/*
  * ----------------------------------------------------------------------
  *
  * Here starts the interrupt handling routines.  All of the following
@@ -312,6 +358,22 @@
 	mark_bh(MACSERIAL_BH);
 }
 
+/* Work out the flag value for a z8530 status value. */
+static _INLINE_ int stat_to_flag(int stat)
+{
+	int flag;
+
+	if (stat & Rx_OVR) {
+		flag = TTY_OVERRUN;
+	} else if (stat & FRM_ERR) {
+		flag = TTY_FRAME;
+	} else if (stat & PAR_ERR) {
+		flag = TTY_PARITY;
+	} else
+		flag = 0;
+	return flag;
+}
+
 static _INLINE_ void receive_chars(struct mac_serial *info,
 				   struct pt_regs *regs)
 {
@@ -349,14 +411,7 @@
 			if (flip_max_cnt < tty->flip.count)
 				flip_max_cnt = tty->flip.count;
 		}
-		if (stat & Rx_OVR) {
-			flag = TTY_OVERRUN;
-		} else if (stat & FRM_ERR) {
-			flag = TTY_FRAME;
-		} else if (stat & PAR_ERR) {
-			flag = TTY_PARITY;
-		} else
-			flag = 0;
+		flag = stat_to_flag(stat);
 		if (flag)
 			/* reset the error indication */
 			write_zsreg(info->zs_channel, 0, ERR_RES);
@@ -452,6 +507,32 @@
 	info->read_reg_zero = status;
 }
 
+static _INLINE_ void receive_special_dma(struct mac_serial *info)
+{
+	unsigned char stat, flag;
+	volatile struct dbdma_regs *rd = &info->rx->dma;
+	int where = RX_BUF_SIZE;
+
+	spin_lock(&info->rx_dma_lock);
+	if ((ld_le32(&rd->status) & ACTIVE) != 0)
+		dbdma_flush(rd);
+	if (in_le32(&rd->cmdptr)
+	    == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1))
+		where -= in_le16(&info->rx->res_count);
+	where--;
+	
+	stat = read_zsreg(info->zs_channel, R1);
+
+	flag = stat_to_flag(stat);
+	if (flag) {
+		info->rx_flag_buf[info->rx_cbuf][where] = flag;
+		/* reset the error indication */
+		write_zsreg(info->zs_channel, 0, ERR_RES);
+	}
+
+	spin_unlock(&info->rx_dma_lock);
+}
+
 /*
  * This is the serial driver's generic interrupt routine
  */
@@ -461,6 +542,12 @@
 	unsigned char zs_intreg;
 	int shift;
 
+	if (!(info->flags & ZILOG_INITIALIZED)) {
+		printk("rs_interrupt: irq %d, port not initialized\n", irq);
+		disable_irq(irq);
+		return;
+	}
+
 	/* NOTE: The read register 3, which holds the irq status,
 	 *       does so for both channels on each chip.  Although
 	 *       the status value itself must be read from the A
@@ -477,19 +564,21 @@
 	for (;;) {
 		zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
 #ifdef SERIAL_DEBUG_INTR
-		printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg);
+		printk("rs_interrupt: irq %d, zs_intreg 0x%x\n",
+		       irq, (int)zs_intreg);
 #endif	
 
 		if ((zs_intreg & CHAN_IRQMASK) == 0)
 			break;
 
-		if (!(info->flags & ZILOG_INITIALIZED)) {
-			printk("rs_interrupt: irq %d, port not initialized\n", irq);
-			break;
+		if (zs_intreg & CHBRxIP) {
+			/* If we are doing DMA, we only ask for interrupts
+			   on characters with errors or special conditions. */
+			if (info->dma_initted)
+				receive_special_dma(info);
+			else
+				receive_chars(info, regs);
 		}
-
-		if (zs_intreg & CHBRxIP)
-			receive_chars(info, regs);
 		if (zs_intreg & CHBTxIP)
 			transmit_chars(info);
 		if (zs_intreg & CHBEXT)
@@ -497,6 +586,39 @@
 	}
 }
 
+/* Transmit DMA interrupt - not used at present */
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+/*
+ * Receive DMA interrupt.
+ */
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mac_serial *info = (struct mac_serial *) dev_id;
+	volatile struct dbdma_cmd *cd;
+
+	if (!info->dma_initted)
+		return;
+	spin_lock(&info->rx_dma_lock);
+	/* First, confirm that this interrupt is, indeed, coming */
+	/* from Rx DMA */
+	cd = info->rx_cmds[info->rx_cbuf] + 2;
+	if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) {
+		spin_unlock(&info->rx_dma_lock);
+		return;
+	}
+	if (info->rx_fbuf != RX_NO_FBUF) {
+		info->rx_cbuf = info->rx_fbuf;
+		if (++info->rx_fbuf == info->rx_nbuf)
+			info->rx_fbuf = 0;
+		if (info->rx_fbuf == info->rx_ubuf)
+			info->rx_fbuf = RX_NO_FBUF;
+	}
+	spin_unlock(&info->rx_dma_lock);
+}
+
 /*
  * -------------------------------------------------------------------
  * Here ends the serial interrupt routines.
@@ -592,10 +714,6 @@
 	}
 }
 
-static void rs_timer(void)
-{
-}
-
 static int startup(struct mac_serial * info, int can_sleep)
 {
 	int delay;
@@ -631,6 +749,10 @@
 
 	info->flags |= ZILOG_INITIALIZED;
 	enable_irq(info->irq);
+	if (info->dma_initted) {
+//		enable_irq(info->tx_dma_irq);
+		enable_irq(info->rx_dma_irq);
+	}
 
 	if (delay) {
 		if (can_sleep) {
@@ -644,6 +766,187 @@
 	return 0;
 }
 
+static _INLINE_ void rxdma_start(struct mac_serial * info, int current)
+{
+	volatile struct dbdma_regs *rd = &info->rx->dma;
+	volatile struct dbdma_cmd *cd = info->rx_cmds[current];
+
+//printk(KERN_DEBUG "SCC: rxdma_start\n");
+
+	st_le32(&rd->cmdptr, virt_to_bus(cd));
+	out_le32(&rd->control, (RUN << 16) | RUN);
+}
+
+static void rxdma_to_tty(struct mac_serial *info)
+{
+	struct tty_struct	*tty = info->tty;
+	volatile struct dbdma_regs *rd = &info->rx->dma;
+	unsigned long flags;
+	int residue, available, space, do_queue;
+
+	if (!tty)
+		return;
+
+	do_queue = 0;
+	spin_lock_irqsave(&info->rx_dma_lock, flags);
+more:
+	space = TTY_FLIPBUF_SIZE - tty->flip.count;
+	if (!space) {
+		do_queue++;
+		goto out;
+	}
+	residue = 0;
+	if (info->rx_ubuf == info->rx_cbuf) {
+		if ((ld_le32(&rd->status) & ACTIVE) != 0) {
+			dbdma_flush(rd);
+			if (in_le32(&rd->cmdptr)
+			    == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1))
+				residue = in_le16(&info->rx->res_count);
+		}
+	}
+	available = RX_BUF_SIZE - residue - info->rx_done_bytes;
+	if (available > space)
+		available = space;
+	if (available) {
+		memcpy(tty->flip.char_buf_ptr,
+		       info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes,
+		       available);
+		memcpy(tty->flip.flag_buf_ptr,
+		       info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+		       available);
+		tty->flip.char_buf_ptr += available;
+		tty->flip.count += available;
+		tty->flip.flag_buf_ptr += available;
+		memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+		       0, available);
+		info->rx_done_bytes += available;
+		do_queue++;
+	}
+	if (info->rx_done_bytes == RX_BUF_SIZE) {
+		volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf];
+
+		if (info->rx_ubuf == info->rx_cbuf)
+			goto out;
+		/* mark rx_char_buf[rx_ubuf] free */
+		st_le16(&cd->command, DBDMA_NOP);
+		cd++;
+		st_le32(&cd->cmd_dep, 0);
+		st_le32((unsigned int *)&cd->res_count, 0);
+		cd++;
+		st_le16(&cd->xfer_status, 0);
+
+		if (info->rx_fbuf == RX_NO_FBUF) {
+			info->rx_fbuf = info->rx_ubuf;
+			if (!(ld_le32(&rd->status) & ACTIVE)) {
+				dbdma_reset(&info->rx->dma);
+				rxdma_start(info, info->rx_ubuf);
+				info->rx_cbuf = info->rx_ubuf;
+			}
+		}
+		info->rx_done_bytes = 0;
+		if (++info->rx_ubuf == info->rx_nbuf)
+			info->rx_ubuf = 0;
+		if (info->rx_fbuf == info->rx_ubuf)
+			info->rx_fbuf = RX_NO_FBUF;
+		goto more;
+	}
+out:
+	spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+	if (do_queue)
+		queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static void poll_rxdma(void *private_)
+{
+	struct mac_serial	*info = (struct mac_serial *) private_;
+	unsigned long flags;
+
+	rxdma_to_tty(info);
+	spin_lock_irqsave(&info->rx_dma_lock, flags);
+	mod_timer(&info->poll_dma_timer, RX_DMA_TIMER);
+	spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+}
+
+static void dma_init(struct mac_serial * info)
+{
+	int i, size;
+	volatile struct dbdma_cmd *cd;
+	unsigned char *p;
+
+//printk(KERN_DEBUG "SCC: dma_init\n");
+
+	info->rx_nbuf = 8;
+
+	/* various mem set up */
+	size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2)
+		+ (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds)
+		   + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf))
+		* info->rx_nbuf;
+	info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA);
+	if (info->dma_priv == NULL)
+		return;
+	memset(info->dma_priv, 0, size);
+
+	info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv;
+	info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf);
+	info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf;
+	p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf);
+	for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+		info->rx_char_buf[i] = p;
+	for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+		info->rx_flag_buf[i] = p;
+
+	/* a bit of DMA programming */
+	cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p);
+	st_le16(&cd->command, DBDMA_NOP);
+	cd++;
+	st_le16(&cd->req_count, RX_BUF_SIZE);
+	st_le16(&cd->command, INPUT_MORE);
+	st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0]));
+	cd++;
+	st_le16(&cd->req_count, 4);
+	st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+	st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+	st_le32(&cd->cmd_dep, DBDMA_STOP);
+	for (i = 1; i < info->rx_nbuf; i++) {
+		info->rx_cmds[i] = ++cd;
+		st_le16(&cd->command, DBDMA_NOP);
+		cd++;
+		st_le16(&cd->req_count, RX_BUF_SIZE);
+		st_le16(&cd->command, INPUT_MORE);
+		st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i]));
+		cd++;
+		st_le16(&cd->req_count, 4);
+		st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+		st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+		st_le32(&cd->cmd_dep, DBDMA_STOP);
+	}
+	cd++;
+	st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS);
+	st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0]));
+
+	/* setup DMA to our liking */
+	dbdma_reset(&info->rx->dma);
+	st_le32(&info->rx->dma.intr_sel, 0x10001);
+	st_le32(&info->rx->dma.br_sel, 0x10001);
+	out_le32(&info->rx->dma.wait_sel, 0x10001);
+
+	/* set various flags */
+	info->rx_ubuf = 0;
+	info->rx_cbuf = 0;
+	info->rx_fbuf = info->rx_ubuf + 1;
+	if (info->rx_fbuf == info->rx_nbuf)
+		info->rx_fbuf = RX_NO_FBUF;
+	info->rx_done_bytes = 0;
+
+	/* setup polling */
+	init_timer(&info->poll_dma_timer);
+	info->poll_dma_timer.function = (void *)&poll_rxdma;
+	info->poll_dma_timer.data = (unsigned long)info;
+
+	info->dma_initted = 1;
+}
+
 static int setup_scc(struct mac_serial * info)
 {
 	unsigned long flags;
@@ -669,6 +972,12 @@
 	info->xmit_fifo_size = 1;
 
 	/*
+	 * Reset DMAs
+	 */
+	if (info->has_dma)
+		dma_init(info);
+
+	/*
 	 * Clear the interrupt registers.
 	 */
 	write_zsreg(info->zs_channel, 0, ERR_RES);
@@ -682,7 +991,23 @@
 	/*
 	 * Finally, enable sequencing and interrupts
 	 */
-	info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+	if (!info->dma_initted) {
+		/* interrupt on ext/status changes, all received chars,
+		   transmit ready */
+		info->curregs[1] = (info->curregs[1] & ~0x18)
+				| (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+	} else {
+		/* interrupt on ext/status changes, W/Req pin is
+		   receive DMA request */
+		info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB))
+				| (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
+		write_zsreg(info->zs_channel, 1, info->curregs[1]);
+		/* enable W/Req pin */
+		info->curregs[1] |= WT_RDY_ENAB;
+		write_zsreg(info->zs_channel, 1, info->curregs[1]);
+		/* enable interrupts on transmit ready and receive errors */
+		info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB;
+	}
 	info->pendregs[1] = info->curregs[1];
 	info->curregs[3] |= (RxENABLE | Rx8);
 	info->pendregs[3] = info->curregs[3];
@@ -708,6 +1033,14 @@
 
 	restore_flags(flags);
 
+	if (info->dma_initted) {
+		spin_lock_irqsave(&info->rx_dma_lock, flags);
+		rxdma_start(info, 0);
+		info->poll_dma_timer.expires = RX_DMA_TIMER;
+		add_timer(&info->poll_dma_timer);
+		spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+	}
+
 	return 0;
 }
 
@@ -729,7 +1062,14 @@
 	
 		return;
 	}
-	
+
+	if (info->has_dma) {
+		del_timer(&info->poll_dma_timer);
+		dbdma_reset(info->tx_dma);
+		dbdma_reset(&info->rx->dma);
+		disable_irq(info->tx_dma_irq);
+		disable_irq(info->rx_dma_irq);
+	}
 	disable_irq(info->irq);
 
 	info->pendregs[1] = info->curregs[1] = 0;
@@ -755,6 +1095,12 @@
 		info->xmit_buf = 0;
 	}
 
+	if (info->has_dma && info->dma_priv) {
+		kfree(info->dma_priv);
+		info->dma_priv = NULL;
+		info->dma_initted = 0;
+	}
+
 	memset(info->curregs, 0, sizeof(info->curregs));
 	memset(info->curregs, 0, sizeof(info->pendregs));
 
@@ -1052,7 +1398,6 @@
 	if (!tty || !info->xmit_buf || !tmp_buf)
 		return 0;
 
-	save_flags(flags);
 	if (from_user) {
 		down(&tmp_buf_sem);
 		while (1) {
@@ -1068,6 +1413,7 @@
 					ret = -EFAULT;
 				break;
 			}
+			save_flags(flags);
 			cli();
 			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				       SERIAL_XMIT_SIZE - info->xmit_head));
@@ -1083,6 +1429,7 @@
 		up(&tmp_buf_sem);
 	} else {
 		while (1) {
+			save_flags(flags);
 			cli();		
 			c = MIN(count,
 				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
@@ -1104,7 +1451,6 @@
 	if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
 	    && !info->tx_active)
 		transmit_chars(info);
-	restore_flags(flags);
 	return ret;
 }
 
@@ -1133,12 +1479,13 @@
 static void rs_flush_buffer(struct tty_struct *tty)
 {
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
 				
 	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
 		return;
-	cli();
+	save_flags(flags); cli();
 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-	sti();
+	restore_flags(flags);
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 	    tty->ldisc.write_wakeup)
@@ -1158,7 +1505,6 @@
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
 	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
-	char	buf[64];
 	
 	printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty));
 #endif
@@ -1195,7 +1541,6 @@
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
 	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
-	char	buf[64];
 	
 	printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty));
 #endif
@@ -1473,6 +1818,7 @@
 	save_flags(flags); cli();
 	
 	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
@@ -1498,6 +1844,7 @@
 		info->count = 0;
 	}
 	if (info->count) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
@@ -1518,8 +1865,12 @@
 	printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
 #endif
 	tty->closing = 1;
-	if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+	if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) {
+		restore_flags(flags);
 		tty_wait_until_sent(tty, info->closing_wait);
+		save_flags(flags); cli();
+	}
+
 	/*
 	 * At this point we stop accepting input.  To do this, we
 	 * disable the receiver and receive interrupts.
@@ -1539,7 +1890,9 @@
 #ifdef SERIAL_DEBUG_OPEN
 		printk("waiting end of Rx...\n");
 #endif
+		restore_flags(flags);
 		rs_wait_until_sent(tty, info->timeout);
+		save_flags(flags); cli();
 	}
 
 	shutdown(info);
@@ -1565,6 +1918,7 @@
 	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
 			 ZILOG_CLOSING);
 	wake_up_interruptible(&info->close_wait);
+	MOD_DEC_USE_COUNT;
 }
 
 /*
@@ -1603,7 +1957,6 @@
 		char_time = MIN(char_time, timeout);
 	while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
 		current->state = TASK_INTERRUPTIBLE;
-		current->counter = 0;	/* make us low-priority */
 		schedule_timeout(char_time);
 		if (signal_pending(current))
 			break;
@@ -1775,14 +2128,19 @@
 	int 			retval, line;
 	unsigned long		page;
 
+	MOD_INC_USE_COUNT;
 	line = MINOR(tty->device) - tty->driver.minor_start;
-	if ((line < 0) || (line >= zs_channels_found))
+	if ((line < 0) || (line >= zs_channels_found)) {
+		MOD_DEC_USE_COUNT;
 		return -ENODEV;
+	}
 	info = zs_soft + line;
 
 #ifdef CONFIG_KGDB
-	if (info->kgdb_channel)
+	if (info->kgdb_channel) {
+		MOD_DEC_USE_COUNT;
 		return -ENODEV;
+	}
 #endif
 	if (serial_paranoia_check(info, tty->device, "rs_open"))
 		return -ENODEV;
@@ -1865,7 +2223,58 @@
 
 static void show_serial_version(void)
 {
-	printk("PowerMac Z8530 serial driver version 1.01\n");
+	printk("PowerMac Z8530 serial driver version 2.0\n");
+}
+
+/*
+ * Initialize one channel, both the mac_serial and mac_zschannel
+ * structs.  We use the dev_node field of the mac_serial struct.
+ */
+static void
+chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
+	  struct mac_zschannel *zs_chan_a)
+{
+	struct device_node *ch = zss->dev_node;
+	char *conn;
+	int len;
+
+	zss->irq = ch->intrs[0].line;
+	zss->has_dma = 0;
+#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA)
+	if (ch->n_addrs == 3 && ch->n_intrs == 3)
+		zss->has_dma = 1;
+#endif
+	zss->dma_initted = 0;
+
+	zs_chan->control = (volatile unsigned char *)
+		ioremap(ch->addrs[0].address, 0x1000);
+	zs_chan->data = zs_chan->control + 0x10;
+	spin_lock_init(&zs_chan->lock);
+	zs_chan->parent = zss;
+	zss->zs_channel = zs_chan;
+	zss->zs_chan_a = zs_chan_a;
+
+	/* setup misc varariables */
+	zss->kgdb_channel = 0;
+	zss->is_cobalt_modem = device_is_compatible(ch, "cobalt");
+
+	/* XXX tested only with wallstreet PowerBook,
+	   should do no harm anyway */
+	conn = get_property(ch, "AAPL,connector", &len);
+	zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0);
+
+	if (zss->has_dma) {
+		zss->dma_priv = NULL;
+		/* it seems that the last two addresses are the
+		   DMA controllers */
+		zss->tx_dma = (volatile struct dbdma_regs *)
+			ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100);
+		zss->rx = (volatile struct mac_dma *)
+			ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100);
+		zss->tx_dma_irq = ch->intrs[1].line;
+		zss->rx_dma_irq = ch->intrs[2].line;
+		spin_lock_init(&zss->rx_dma_lock);
+	}
 }
 
 /* Ask the PROM how many Z8530s we have and initialize their zs_channels */
@@ -1874,51 +2283,63 @@
 {
 	struct device_node *dev, *ch;
 	struct mac_serial **pp;
-	int n, lenp;
-	char *conn;
+	int n, chip, nchan;
+	struct mac_zschannel *zs_chan;
+	int chan_a_index;
 
 	n = 0;
 	pp = &zs_chain;
+	zs_chan = zs_channels;
 	for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
+		nchan = 0;
+		chip = n;
 		if (n >= NUM_CHANNELS) {
 			printk("Sorry, can't use %s: no more channels\n",
 			       dev->full_name);
 			continue;
 		}
+		chan_a_index = 0;
 		for (ch = dev->child; ch != 0; ch = ch->sibling) {
+			if (nchan >= 2) {
+				printk(KERN_WARNING "SCC: Only 2 channels per "
+					"chip are supported\n");
+				break;
+			}
 			if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) {
 				printk("Can't use %s: %d addrs %d intrs\n",
 				      ch->full_name, ch->n_addrs, ch->n_intrs);
 				continue;
 			}
-			zs_channels[n].control = (volatile unsigned char *)
-				ioremap(ch->addrs[0].address, 0x1000);
-			zs_channels[n].data = zs_channels[n].control + 0x10;
-			spin_lock_init(&zs_channels[n].lock);
-			zs_soft[n].zs_channel = &zs_channels[n];
-			zs_soft[n].dev_node = ch;
-			zs_soft[n].irq = ch->intrs[0].line;
-			zs_soft[n].zs_channel->parent = &zs_soft[n];
-			zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt");
-
-			/* XXX tested only with wallstreet PowerBook,
-			   should do no harm anyway */
-			conn = get_property(ch, "AAPL,connector", &lenp);
-			zs_soft[n].is_pwbk_ir =
-				conn && (strcmp(conn, "infrared") == 0);
-
-			/* XXX this assumes the prom puts chan A before B */
-			if (n & 1)
-				zs_soft[n].zs_chan_a = &zs_channels[n-1];
-			else
-				zs_soft[n].zs_chan_a = &zs_channels[n];
 
+			/* The channel with the higher address
+			   will be the A side. */
+			if (nchan > 0 &&
+			    ch->addrs[0].address
+			    > zs_soft[n-1].dev_node->addrs[0].address)
+				chan_a_index = 1;
+
+			/* minimal initialization for now */
+			zs_soft[n].dev_node = ch;
 			*pp = &zs_soft[n];
 			pp = &zs_soft[n].zs_next;
+			++nchan;
 			++n;
 		}
+		if (nchan == 0)
+			continue;
+
+		/* set up A side */
+		chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan);
+		++zs_chan;
+
+		/* set up B side, if it exists */
+		if (nchan > 1)
+			chan_init(&zs_soft[chip + 1 - chan_a_index],
+				  zs_chan, zs_chan - 1);
+		++zs_chan;
 	}
 	*pp = 0;
+
 	zs_channels_found = n;
 #ifdef CONFIG_PMAC_PBOOK
 	if (n)
@@ -1935,8 +2356,6 @@
 
 	/* Setup base handler, and timer table. */
 	init_bh(MACSERIAL_BH, do_serial_bh);
-	timer_table[RS_TIMER].fn = rs_timer;
-	timer_table[RS_TIMER].expires = 0;
 
 	/* Find out how many Z8530 SCCs we have */
 	if (zs_chain == 0)
@@ -1948,6 +2367,18 @@
 	/* Register the interrupt handler for each one */
 	save_flags(flags); cli();
 	for (i = 0; i < zs_channels_found; ++i) {
+		if (zs_soft[i].has_dma) {
+			if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
+					"SCC-txdma", &zs_soft[i]))
+				printk(KERN_ERR "macserial: can't get irq %d\n",
+				       zs_soft[i].tx_dma_irq);
+			disable_irq(zs_soft[i].tx_dma_irq);
+			if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0,
+					"SCC-rxdma", &zs_soft[i]))
+				printk(KERN_ERR "macserial: can't get irq %d\n",
+				       zs_soft[i].rx_dma_irq);
+			disable_irq(zs_soft[i].rx_dma_irq);
+		}
 		if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
 				"SCC", &zs_soft[i]))
 			printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2107,8 +2538,13 @@
 	for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
 		set_scc_power(info, 0);
 	save_flags(flags); cli();
-	for (i = 0; i < zs_channels_found; ++i)
+	for (i = 0; i < zs_channels_found; ++i) {
 		free_irq(zs_soft[i].irq, &zs_soft[i]);
+		if (zs_soft[i].has_dma) {
+			free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
+			free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
+		}
+	}
 	restore_flags(flags);
 	tty_unregister_driver(&callout_driver);
 	tty_unregister_driver(&serial_driver);
@@ -2238,6 +2674,8 @@
 	if (zs_chain == 0)
 		return -1;
 
+	set_scc_power(info, 1);
+
 	/* Reset the channel */
 	write_zsreg(info->zs_channel, R9, CHRA);
 
@@ -2481,14 +2919,13 @@
 	if (zs_chain == 0)
 		probe_sccs();
 
-	set_scc_power(&zs_soft[n], 1);
+	set_scc_power(&zs_soft[tty_num], 1);
 	
 	zs_kgdbchan = zs_soft[tty_num].zs_channel;
 	zs_soft[tty_num].change_needed = 0;
 	zs_soft[tty_num].clk_divisor = 16;
 	zs_soft[tty_num].zs_baud = 38400;
 	zs_soft[tty_num].kgdb_channel = 1;     /* This runs kgdb */
-	zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
 
 	/* Turn on transmitter/receiver at 8-bits/char */
         kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/macintosh/macserial.h linux.14p6/drivers/macintosh/macserial.h
--- linux.vanilla/drivers/macintosh/macserial.h	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/macintosh/macserial.h	Fri Oct 22 22:26:41 1999
@@ -92,6 +92,13 @@
 	struct mac_serial*	parent;
 };
 
+struct mac_dma {
+	volatile struct dbdma_regs	dma;
+	volatile unsigned short		res_count;
+	volatile unsigned short		command;
+	volatile unsigned int		buf_addr;
+};
+
 struct mac_serial {
 	struct mac_serial *zs_next;	/* For IRQ servicing chain */
 	struct mac_zschannel *zs_channel; /* Channel registers */
@@ -156,6 +163,28 @@
 	struct termios		callout_termios;
 	struct wait_queue	*open_wait;
 	struct wait_queue	*close_wait;
+
+	volatile struct dbdma_regs *tx_dma;
+	int			tx_dma_irq;
+	volatile struct dbdma_cmd *tx_cmds;
+	volatile struct mac_dma *rx;
+	int 			rx_dma_irq;
+	volatile struct dbdma_cmd **rx_cmds;
+	unsigned char		**rx_char_buf;
+	unsigned char		**rx_flag_buf;
+#define	RX_BUF_SIZE	256
+	int			rx_nbuf;
+	int			rx_done_bytes;
+	int			rx_ubuf;
+	int			rx_fbuf;
+#define	RX_NO_FBUF	(-1)
+	int			rx_cbuf;
+	spinlock_t		rx_dma_lock;
+	int			has_dma;
+	int			dma_initted;
+	void			*dma_priv;
+	struct timer_list	poll_dma_timer;
+#define RX_DMA_TIMER	(jiffies + 10*HZ/1000)
 };
 
 
@@ -226,9 +255,9 @@
 #define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
 #define	INT_ERR_Rx	0x18	/* Int on error only */
 
-#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
-#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
-#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+#define	WT_RDY_RT	0x20	/* W/Req reflects recv if 1, xmit if 0 */
+#define	WT_FN_RDYFN	0x40	/* W/Req pin is DMA request if 1, wait if 0 */
+#define	WT_RDY_ENAB	0x80	/* Enable W/Req pin */
 
 /* Write Register #2 (Interrupt Vector) */
 
@@ -286,6 +315,9 @@
 
 /* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
 
+/* Write Register 7' (Some enhanced feature control) */
+#define	ENEXREAD	0x40	/* Enable read of some write registers */
+
 /* Write Register 8 (transmit buffer) */
 
 /* Write Register 9 (Master interrupt control) */
@@ -346,7 +378,9 @@
 #define	SNRZI	0xe0	/* Set NRZI mode */
 
 /* Write Register 15 (external/status interrupt control) */
+#define	EN85C30	1	/* Enable some 85c30-enhanced registers */
 #define	ZCIE	2	/* Zero count IE */
+#define	ENSTFIFO 4	/* Enable status FIFO (SDLC) */
 #define	DCDIE	8	/* DCD IE */
 #define	SYNCIE	0x10	/* Sync/hunt IE */
 #define	CTSIE	0x20	/* CTS IE */
@@ -382,6 +416,15 @@
 #define	END_FR		0x80	/* End of Frame (SDLC) */
 
 /* Read Register 2 (channel b only) - Interrupt vector */
+#define	CHB_Tx_EMPTY	0x00
+#define	CHB_EXT_STAT	0x02
+#define	CHB_Rx_AVAIL	0x04
+#define	CHB_SPECIAL	0x06
+#define	CHA_Tx_EMPTY	0x08
+#define	CHA_EXT_STAT	0x0a
+#define	CHA_Rx_AVAIL	0x0c
+#define	CHA_SPECIAL	0x0e
+#define	STATUS_MASK	0x06
 
 /* Read Register 3 (interrupt pending register) ch a only */
 #define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/macintosh/mediabay.c linux.14p6/drivers/macintosh/mediabay.c
--- linux.vanilla/drivers/macintosh/mediabay.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/macintosh/mediabay.c	Fri Oct 22 22:26:41 1999
@@ -94,6 +94,12 @@
  */
 #define MB_IDE_WAIT	1500
 
+/*
+ * Wait at least this many ticks after resetting an IDE device before
+ * believing its ready bit.
+ */
+#define MB_IDE_MINWAIT	250
+
 static void poll_media_bay(int which);
 static void set_media_bay(int which, int id);
 static int media_bay_task(void *);
@@ -285,7 +291,10 @@
 #endif
 			}
 #ifdef CONFIG_BLK_DEV_IDE
-		} else if (bay->cd_timer && (--bay->cd_timer == 0 || MB_IDE_READY(i))
+		} else if (bay->cd_timer
+			   && (--bay->cd_timer == 0
+			       || (bay->cd_timer < MB_IDE_WAIT - MB_IDE_MINWAIT
+				   && MB_IDE_READY(i)))
 			   && bay->cd_index < 0) {
 			bay->cd_timer = 0;
 			printk(KERN_DEBUG "Registering IDE, base:0x%08lx, irq:%d\n", bay->cd_base, bay->cd_irq);
@@ -299,11 +308,13 @@
 		}
 
 		bay->previous_id = bay->content_id;
-		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(1);
-		if (signal_pending(current))
-			return 0;
-		i = (i+1)%media_bay_count;
+		if (++i >= media_bay_count) {
+			i = 0;
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+			if (signal_pending(current))
+				return 0;
+		}
 	}
 }
 
@@ -405,25 +416,25 @@
 			feature_set(bay->dev_node, FEATURE_Mediabay_enable);
 			/* I suppose this is enough delay to stabilize MB_CONTENT ... */
 			mdelay(10);
-			/* We re-enable the bay using it's previous content only if
-			   it did not change */
-			if (MB_CONTENTS(i) == bay->content_id) {
-				set_media_bay(i, bay->content_id);
-				if (bay->content_id != MB_NO) {
-					mdelay(400);
-					/* Clear the bay reset */
-					feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
-					/* This small delay makes sure the device has time
-					   to assert the BUSY bit (used by IDE sleep) */
-					udelay(100);
-					/* We reset the state machine timers in case we were in the
-					   middle of a wait loop */
-					if (bay->reset_timer)
-						bay->reset_timer = MB_RESET_COUNT;
-					if (bay->cd_timer)
-						bay->cd_timer = MB_IDE_WAIT;
-				}
-			}
+			/* We re-enable the bay using it's previous content
+			   only if it did not change */
+			if (MB_CONTENTS(i) != bay->content_id)
+				continue;
+			set_media_bay(i, bay->content_id);
+			if (bay->content_id == MB_NO)
+				continue;
+			mdelay(400);
+			/* Clear the bay reset */
+			feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
+			/* This small delay makes sure the device has time
+			   to assert the BUSY bit (used by IDE sleep) */
+			udelay(100);
+			/* We reset the state machine timers in case we were
+			   in the middle of a wait loop */
+			if (bay->reset_timer)
+				bay->reset_timer = MB_RESET_COUNT;
+			if (bay->cd_timer)
+				bay->cd_timer = MB_IDE_WAIT;
 		}
 		break;
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/misc/parport_pc.c linux.14p6/drivers/misc/parport_pc.c
--- linux.vanilla/drivers/misc/parport_pc.c	Sat Aug 14 02:27:27 1999
+++ linux.14p6/drivers/misc/parport_pc.c	Wed Oct 27 19:05:02 1999
@@ -32,6 +32,9 @@
  * only in register addresses (eg because your registers are on 32-bit
  * word boundaries) then you can alter the constants in parport_pc.h to
  * accomodate this.
+ *
+ * Note that the ECP registers may not start at offset 0x400 for PCI cards,
+ * but rather will start at port->base_hi.
  */
 
 #include <linux/config.h>
@@ -43,6 +46,7 @@
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
+#include <linux/pci.h>
 
 #include <asm/io.h>
 
@@ -62,27 +66,27 @@
 
 void parport_pc_write_epp(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+EPPDATA);
+	outb(d, EPPDATA(p));
 }
 
 unsigned char parport_pc_read_epp(struct parport *p)
 {
-	return inb(p->base+EPPDATA);
+	return inb(EPPDATA(p));
 }
 
 void parport_pc_write_epp_addr(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+EPPADDR);
+	outb(d, EPPADDR(p));
 }
 
 unsigned char parport_pc_read_epp_addr(struct parport *p)
 {
-	return inb(p->base+EPPADDR);
+	return inb(EPPADDR(p));
 }
 
 int parport_pc_check_epp_timeout(struct parport *p)
 {
-	if (!(inb(p->base+STATUS) & 1))
+	if (!(inb(STATUS(p)) & 1))
 		return 0;
 	parport_pc_epp_clear_timeout(p);
 	return 1;
@@ -90,24 +94,24 @@
 
 unsigned char parport_pc_read_configb(struct parport *p)
 {
-	return inb(p->base+CONFIGB);
+	return inb(CONFIGB(p));
 }
 
 void parport_pc_write_data(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+DATA);
+	outb(d, DATA(p));
 }
 
 unsigned char parport_pc_read_data(struct parport *p)
 {
-	return inb(p->base+DATA);
+	return inb(DATA(p));
 }
 
 void parport_pc_write_control(struct parport *p, unsigned char d)
 {
 	struct parport_pc_private *priv = p->private_data;
 	priv->ctr = d;/* update soft copy */
-	outb(d, p->base+CONTROL);
+	outb(d, CONTROL(p));
 }
 
 unsigned char parport_pc_read_control(struct parport *p)
@@ -121,34 +125,34 @@
 	struct parport_pc_private *priv = p->private_data;
 	unsigned char ctr = priv->ctr;
 	ctr = (ctr & ~mask) ^ val;
-	outb (ctr, p->base+CONTROL);
+	outb (ctr, CONTROL(p));
 	return priv->ctr = ctr; /* update soft copy */
 }
 
 void parport_pc_write_status(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+STATUS);
+	outb(d, STATUS(p));
 }
 
 unsigned char parport_pc_read_status(struct parport *p)
 {
-	return inb(p->base+STATUS);
+	return inb(STATUS(p));
 }
 
 void parport_pc_write_econtrol(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+ECONTROL);
+	outb(d, ECONTROL(p));
 }
 
 unsigned char parport_pc_read_econtrol(struct parport *p)
 {
-	return inb(p->base+ECONTROL);
+	return inb(ECONTROL(p));
 }
 
 unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned char old = inb(p->base+ECONTROL);
-	outb(((old & ~mask) ^ val), p->base+ECONTROL);
+	unsigned char old = inb(ECONTROL(p));
+	outb(((old & ~mask) ^ val), ECONTROL(p));
 	return old;
 }
 
@@ -159,12 +163,12 @@
 
 void parport_pc_write_fifo(struct parport *p, unsigned char v)
 {
-	outb (v, p->base+CONFIGA);
+	outb (v, CONFIGA(p));
 }
 
 unsigned char parport_pc_read_fifo(struct parport *p)
 {
-	return inb (p->base+CONFIGA);
+	return inb (CONFIGA(p));
 }
 
 void parport_pc_disable_irq(struct parport *p)
@@ -183,7 +187,7 @@
 		free_irq(p->irq, p);
 	release_region(p->base, p->size);
 	if (p->modes & PARPORT_MODE_PCECR)
-		release_region(p->base+0x400, 3);
+		release_region(p->base_hi, 3);
 }
 
 int parport_pc_claim_resources(struct parport *p)
@@ -195,7 +199,7 @@
 			return err;
 	request_region(p->base, p->size, p->name);
 	if (p->modes & PARPORT_MODE_PCECR)
-		request_region(p->base+0x400, 3, p->name);
+		request_region(p->base_hi, 3, p->name);
 	return 0;
 }
 
@@ -223,8 +227,8 @@
 {
 	size_t got = 0;
 	for (; got < length; got++) {
-		*((char*)buf)++ = inb (p->base+EPPDATA);
-		if (inb (p->base+STATUS) & 0x01)
+		*((char*)buf)++ = inb (EPPDATA(p));
+		if (inb (STATUS(p)) & 0x01)
 			break;
 	}
 	return got;
@@ -234,8 +238,8 @@
 {
 	size_t written = 0;
 	for (; written < length; written++) {
-		outb (*((char*)buf)++, p->base+EPPDATA);
-		if (inb (p->base+STATUS) & 0x01)
+		outb (*((char*)buf)++, EPPDATA(p));
+		if (inb (STATUS(p)) & 0x01)
 			break;
 	}
 	return written;
@@ -370,11 +374,11 @@
 	 * allow reads, so read_control just returns a software
 	 * copy. Some ports _do_ allow reads, so bypass the software
 	 * copy here.  In addition, some bits aren't writable. */
-	r = inb (pb->base+CONTROL);
+	r = inb (CONTROL(pb));
 	if ((r & 0xf) == w) {
 		w = 0xe;
 		parport_pc_write_control (pb, w);
-		r = inb (pb->base+CONTROL);
+		r = inb (CONTROL(pb));
 		parport_pc_write_control (pb, 0xc);
 		if ((r & 0xf) == w)
 			return PARPORT_MODE_PCSPP;
@@ -754,13 +758,16 @@
 
 /* --- Initialisation code -------------------------------- */
 
-static int probe_one_port(unsigned long int base, int irq, int dma)
+static int probe_one_port(unsigned long int base,
+			  unsigned long int base_hi,
+			  int irq, int dma)
 {
 	struct parport *p;
 	int probedirq = PARPORT_IRQ_NONE;
 	if (check_region(base, 3)) return 0;
 	if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops)))
 		return 0;
+	p->base_hi = base_hi;
 	p->private_data = kmalloc (sizeof (struct parport_pc_private),
 				   GFP_KERNEL);
 	if (!p->private_data) {
@@ -770,16 +777,14 @@
 		return 0;
 	}
 	((struct parport_pc_private *) (p->private_data))->ctr = 0xc;
-	if (p->base != 0x3bc) {
-		if (!check_region(base+0x400,3)) {
-			p->modes |= parport_ECR_present(p);	
-			p->modes |= parport_ECP_supported(p);
-			p->modes |= parport_ECPPS2_supported(p);
-		}
-		if (!check_region(base+0x3, 5)) {
-			p->modes |= parport_EPP_supported(p);
-			p->modes |= parport_ECPEPP_supported(p);
-		}
+	if (base_hi && !check_region(base_hi,3)) {
+		p->modes |= parport_ECR_present(p);
+		p->modes |= parport_ECP_supported(p);
+		p->modes |= parport_ECPPS2_supported(p);
+	}
+	if (p->base != 0x3bc && !check_region(base+0x3, 5)) {
+		p->modes |= parport_EPP_supported(p);
+		p->modes |= parport_ECPEPP_supported(p);
 	}
 	if (!parport_SPP_supported(p)) {
 		/* No port. */
@@ -791,6 +796,8 @@
 	p->size = (p->modes & (PARPORT_MODE_PCEPP 
 			       | PARPORT_MODE_PCECPEPP))?8:3;
 	printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
+	if (p->base_hi && (p->modes & PARPORT_MODE_PCECP))
+		printk(" (0x%lx)", p->base_hi);
 	if (p->irq == PARPORT_IRQ_AUTO) {
 		p->irq = PARPORT_IRQ_NONE;
 		parport_irq_probe(p);
@@ -844,6 +851,124 @@
 	return 1;
 }
 
+/* Look for PCI parallel port cards. */
+static int parport_pc_init_pci (int irq, int dma)
+{
+	struct {
+		unsigned int vendor;
+		unsigned int device;
+		unsigned int numports;
+		struct {
+			unsigned long lo;
+			unsigned long hi; /* -ve if not there */
+		} addr[4];
+	} cards[] = {
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, 1,
+		  { { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, 1,
+		  { { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, 1,
+		  { { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, 2,
+		  { { 2, 3 }, { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, 1,
+		  { { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, 1,
+		  { { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, 1,
+		  { { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, 1,
+		  { { 0, 1 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, 2,
+		  { { 0, 1 }, { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, 2,
+		  { { 1, 2 }, { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, 2,
+		  { { 1, 2 }, { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, 2,
+		  { { 1, 2 }, { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, 1,
+		  { { 1, 2 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, 1,
+		  { { 1, 2 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, 1,
+		  { { 1, 2 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, 1,
+		  { { 0, -1 }, } },
+		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, 1,
+		  { { 0, -1 }, } },
+		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, 1,
+		  { { 0, -1 }, } },
+		{ 0, }
+	};
+
+	struct pci_dev *pcidev;
+	int count = 0;
+	int i;
+
+	if (!pci_present ())
+		return 0;
+
+	for (i = 0; cards[i].vendor; i++) {
+		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++) {
+				unsigned long lo = cards[i].addr[n].lo;
+				unsigned long hi = cards[i].addr[n].hi;
+				unsigned long io_lo, io_hi;
+				io_lo = pcidev->base_address[lo];
+				io_hi = ((hi < 0) ? 0 :
+					 pcidev->base_address[hi]);
+				io_lo &= PCI_BASE_ADDRESS_IO_MASK;
+				io_hi &= PCI_BASE_ADDRESS_IO_MASK;
+				if (irq == PARPORT_IRQ_AUTO) {
+					if (probe_one_port (io_lo,
+							    io_hi,
+							    pcidev->irq,
+							    dma))
+						count++;
+				} else if (probe_one_port (io_lo, io_hi,
+							   irq, dma))
+					count++;
+			}
+		}
+	}
+
+	/* Look for parallel controllers that we don't know about. */
+	for (pcidev = pci_devices; pcidev; pcidev = pcidev->next) {
+		const int class_noprogif = pcidev->class & ~0xff;
+		if (class_noprogif != (PCI_CLASS_COMMUNICATION_PARALLEL << 8))
+			continue;
+
+		for (i = 0; cards[i].vendor; i++)
+			if ((cards[i].vendor == pcidev->vendor) &&
+			    (cards[i].device == pcidev->device))
+				break;
+		if (cards[i].vendor)
+			/* We know about this one. */
+			continue;
+
+		printk (KERN_INFO
+			"Unknown PCI parallel I/O card (%04x/%04x)\n"
+			"Please send 'lspci' output to "
+			"tim@cyberelk.demon.co.uk\n",
+			pcidev->vendor, pcidev->device);
+	}
+
+	return count;
+}
+
 int parport_pc_init(int *io, int *irq, int *dma)
 {
 	int count = 0, i = 0;
@@ -851,13 +976,16 @@
 		/* Only probe the ports we were given. */
 		user_specified = 1;
 		do {
-			count += probe_one_port(*(io++), *(irq++), *(dma++));
+			unsigned long int io_hi = 0x400 + *io;
+			count += probe_one_port(*(io++), io_hi,
+						*(irq++), *(dma++));
 		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
 	} else {
 		/* Probe all the likely ports. */
-		count += probe_one_port(0x3bc, irq[0], dma[0]);
-		count += probe_one_port(0x378, irq[0], dma[0]);
-		count += probe_one_port(0x278, irq[0], dma[0]);
+		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;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/misc/parport_share.c linux.14p6/drivers/misc/parport_share.c
--- linux.vanilla/drivers/misc/parport_share.c	Sat Aug 14 02:26:48 1999
+++ linux.14p6/drivers/misc/parport_share.c	Wed Oct 27 19:05:02 1999
@@ -108,6 +108,7 @@
 	tmp->cad_lock = RW_LOCK_UNLOCKED;
 	spin_lock_init(&tmp->waitlist_lock);
 	spin_lock_init(&tmp->pardevice_lock);
+	tmp->base_hi = base + 0x400;
 
 	name = kmalloc(15, GFP_KERNEL);
 	if (!name) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/Config.in linux.14p6/drivers/net/Config.in
--- linux.vanilla/drivers/net/Config.in	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/net/Config.in	Tue Nov 16 15:23:52 1999
@@ -46,6 +46,7 @@
   if [ "$CONFIG_PPC" = "y" ]; then
     tristate 'MACE (Power Mac ethernet) support' CONFIG_MACE
     tristate 'BMAC (G3 ethernet) support' CONFIG_BMAC
+    tristate 'Symbios 53c885 (Synergy ethernet) support' CONFIG_NCR885E
   fi
   if [ "$CONFIG_ZORRO" = "y" ]; then
     tristate 'Ariadne support' CONFIG_ARIADNE
@@ -93,7 +94,6 @@
   fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139
-      tristate 'SiS 900 PCI Fast Ethernet Adapter support' CONFIG_SIS900
       tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN
   fi
   bool 'Other ISA cards' CONFIG_NET_ISA
@@ -143,6 +143,7 @@
     tristate 'PCI NE2000 support' CONFIG_NE2K_PCI
     tristate 'TI ThunderLAN support' CONFIG_TLAN
     tristate 'VIA Rhine support' CONFIG_VIA_RHINE
+    tristate 'SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
       tristate 'SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100
@@ -165,7 +166,9 @@
 fi
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+  if [ "$CONFIG_INET" = "y" ]; then
+    bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+  fi
   if [ "$CONFIG_HIPPI" = "y" ]; then
     tristate 'Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
     if [ "$CONFIG_ROADRUNNER" != "n" ]; then
@@ -262,6 +265,24 @@
 #
 dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m
 
+#
+# COMX drivers
+#
+tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX
+if [ "$CONFIG_COMX" != "n" ]; then
+    dep_tristate '  Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX
+    dep_tristate '  Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX
+    dep_tristate '  Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX
+    dep_tristate '  Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX
+    if [ "$CONFIG_LAPB" = "y" ]; then
+        dep_tristate '  Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX
+    fi
+    if [ "$CONFIG_LAPB" = "m" ]; then
+        dep_tristate '  Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB
+    fi
+    dep_tristate '  Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
+fi
+
 tristate 'Frame relay DLCI support' CONFIG_DLCI
 if [ "$CONFIG_DLCI" != "n" ]; then
   int '  Max open DLCI' CONFIG_DLCI_COUNT 24
@@ -288,7 +309,6 @@
 fi
 
 endmenu
-
 
 #
 # X.25 network drivers
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/Makefile linux.14p6/drivers/net/Makefile
--- linux.vanilla/drivers/net/Makefile	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/net/Makefile	Sat Nov 13 15:15:45 1999
@@ -836,14 +836,66 @@
   endif
 endif
 
-# If anything built-in uses syncppp, then build it into the kernel also.
-# If not, but a module uses it, build as a module.
+#
+# COMX drivers
+#
+ifeq ($(CONFIG_COMX),y)
+LX_OBJS += comx.o
+else 
+  ifeq ($(CONFIG_COMX),m)
+  MX_OBJS += comx.o
+  endif
+endif
 
-ifdef CONFIG_SYNCPPP_BUILTIN
-LX_OBJS += syncppp.o
-else
-  ifdef CONFIG_SYNCPPP_MODULE
-  MX_OBJS += syncppp.o
+ifeq ($(CONFIG_COMX_HW_COMX),y)
+L_OBJS += comx-hw-comx.o
+else 
+  ifeq ($(CONFIG_COMX_HW_COMX),m)
+  M_OBJS += comx-hw-comx.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_LOCOMX),y)
+L_OBJS += comx-hw-locomx.o
+CONFIG_85230_BUILTIN=y
+else 
+  ifeq ($(CONFIG_COMX_HW_LOCOMX),m)
+  M_OBJS += comx-hw-locomx.o
+  CONFIG_85230_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_MIXCOM),y)
+L_OBJS += comx-hw-mixcom.o
+else 
+  ifeq ($(CONFIG_COMX_HW_MIXCOM),m)
+  M_OBJS += comx-hw-mixcom.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_PPP),y)
+L_OBJS += comx-proto-ppp.o
+CONFIG_SYNCPPP_BUILTIN = y
+else 
+  ifeq ($(CONFIG_COMX_PROTO_PPP),m)
+  M_OBJS += comx-proto-ppp.o
+  CONFIG_SYNCPPP_MODULE = y
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_LAPB),y)
+L_OBJS += comx-proto-lapb.o
+else 
+  ifeq ($(CONFIG_COMX_PROTO_LAPB),m)
+  M_OBJS += comx-proto-lapb.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_FR),y)
+L_OBJS += comx-proto-fr.o
+else 
+  ifeq ($(CONFIG_COMX_PROTO_FR),m)
+  M_OBJS += comx-proto-fr.o
   endif
 endif
 
@@ -852,9 +904,22 @@
 
 ifdef CONFIG_85230_BUILTIN
 LX_OBJS += z85230.o
+CONFIG_SYNCPPP_BUILTIN=y
 else
   ifdef CONFIG_85230_MODULE
   MX_OBJS += z85230.o
+  CONFIG_SYNCPPP_MODULE=y
+  endif
+endif
+
+# If anything built-in uses syncppp, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+
+ifdef CONFIG_SYNCPPP_BUILTIN
+LX_OBJS += syncppp.o
+else
+  ifdef CONFIG_SYNCPPP_MODULE
+  MX_OBJS += syncppp.o
   endif
 endif
 
@@ -1086,6 +1151,14 @@
 else
   ifeq ($(CONFIG_BMAC),m)
   M_OBJS += bmac.o
+  endif
+endif
+
+ifeq ($(CONFIG_NCR885E),y)
+L_OBJS += ncr885e.o
+else
+  ifeq ($(CONFIG_NCR885E),m)
+  M_OBJS += ncr885e.o
   endif
 endif
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/Space.c linux.14p6/drivers/net/Space.c
--- linux.vanilla/drivers/net/Space.c	Wed Oct 20 01:12:38 1999
+++ linux.14p6/drivers/net/Space.c	Sat Nov 13 15:15:45 1999
@@ -103,6 +103,7 @@
 extern int tlan_probe(struct device *);
 extern int mace_probe(struct device *);
 extern int bmac_probe(struct device *);
+extern int ncr885e_probe(struct device *);
 extern int cs89x0_probe(struct device *dev);
 extern int ethertap_probe(struct device *dev);
 extern int ether1_probe (struct device *dev);
@@ -453,6 +454,9 @@
 #endif
 #ifdef CONFIG_BMAC
 	{bmac_probe, 0},
+#endif
+#ifdef CONFIG_NCR885E
+	{ncr885e_probe, 0},
 #endif
 	{NULL, 0},
 };
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/arlan-proc.c linux.14p6/drivers/net/arlan-proc.c
--- linux.vanilla/drivers/net/arlan-proc.c	Sat Aug 14 02:27:28 1999
+++ linux.14p6/drivers/net/arlan-proc.c	Fri Oct 22 22:16:55 1999
@@ -1,11 +1,11 @@
 #include <linux/config.h>
 #include "arlan.h"
+#include <linux/sysctl.h>
+#include <linux/version.h>
 
 #ifdef CONFIG_PROC_FS
 
 
-#include <linux/sysctl.h>
-#include <linux/version.h>
 
 /* void enableReceive(struct device* dev);
 */
@@ -1001,6 +1001,11 @@
 	{0}
 };
 #endif
+#else
+static ctl_table arlan_table[MAX_ARLANS + 1] =
+{
+	{0}
+};
 #endif
 
 static int mmtu = 1234;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-hw-comx.c linux.14p6/drivers/net/comx-hw-comx.c
--- linux.vanilla/drivers/net/comx-hw-comx.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx-hw-comx.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,1298 @@
+/*
+ * Hardware-level driver for the COMX and HICOMX cards
+ * for Linux kernel 2.2.X
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Rewritten by: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ *		- port back to kernel, add support builtin driver 
+ *		- cleaned up the source code a bit
+ *
+ * Version 0.81 (99/06/22):
+ *		- cleaned up the board load functions, no more long reset
+ *		  timeouts
+ *		- lower modem lines on close
+ *		- some interrupt handling fixes
+ *
+ * Version 0.82 (99/08/24):
+ *		- fix multiple board support
+ * 
+ */
+
+#define VERSION "0.82"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
+MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
+
+#define	COMX_readw(dev, offset)	(readw(dev->mem_start + offset + \
+	(unsigned int)(((struct comx_privdata *)\
+	((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+	* COMX_CHANNEL_OFFSET))
+
+#define COMX_WRITE(dev, offset, value)	(writew(value, dev->mem_start + offset \
+	+ (unsigned int)(((struct comx_privdata *) \
+	((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+	* COMX_CHANNEL_OFFSET))
+
+#define COMX_CMD(dev, cmd)	(COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
+
+struct comx_firmware {
+	int	len;
+	unsigned char *data;
+};
+
+struct comx_privdata {
+	struct comx_firmware *firmware;
+	u16	clock;
+	char	channel;		// channel no.
+	int	memory_size;
+	short	io_extent;
+	u_long	histogram[5];
+};
+
+struct device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
+extern struct comx_hardware hicomx_hw;
+extern struct comx_hardware comx_hw;
+extern struct comx_hardware cmx_hw;
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void COMX_board_on(struct device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
+	    COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_board_off(struct device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
+	   COMX_ENABLE_BOARD_IT), dev->base_addr);
+}
+
+static void HICOMX_board_on(struct device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
+	   HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void HICOMX_board_off(struct device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
+	   HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_set_clock(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+
+	COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
+}
+
+static struct device *COMX_access_board(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct device *ret;
+	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	if (memory_used[mempos] == dev) {
+		restore_flags(flags);
+		return dev;
+	}
+
+	if (ch->twin && memory_used[mempos] == ch->twin) {
+		memory_used[mempos] = dev;
+		ret = ch->twin;
+	} else {	
+		ret = memory_used[mempos];
+		if (ret) ch->HW_board_off(ret);
+		memory_used[mempos] = dev;
+		ch->HW_board_on(dev);
+	}
+	restore_flags(flags);
+	return ret;
+}
+
+static void COMX_release_board(struct device *dev, struct device *savep)
+{
+	unsigned long flags;
+	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+	struct comx_channel *ch = dev->priv;
+
+	save_flags(flags); cli();
+	if (memory_used[mempos] == savep) {
+		restore_flags(flags);
+		return;
+	}
+
+	memory_used[mempos] = savep;
+	if (!ch->twin || ch->twin != savep) {
+		ch->HW_board_off(dev);
+		if (savep) ch->HW_board_on(savep);
+	}
+	restore_flags(flags);
+}
+
+static int COMX_txe(struct device *dev) 
+{
+	struct device *savep;
+	struct comx_channel *ch = dev->priv;
+	int rc = 0;
+
+	savep = ch->HW_access_board(dev);
+	if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
+		rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
+	}
+	ch->HW_release_board(dev,savep);
+	return rc;
+}
+
+static int COMX_send_packet(struct device *dev, struct sk_buff *skb)
+{
+	struct device *savep;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	int ret = FRAME_DROPPED;
+
+	savep = ch->HW_access_board(dev);	
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
+	}
+
+	if (skb->len > COMX_MAX_TX_SIZE) {
+		dev_kfree_skb(skb);
+		return FRAME_DROPPED;
+	}
+
+	if ((ch->line_status & LINE_UP) && COMX_readw(dev, OFF_A_L2_TxEMPTY)) {
+		int lensave = skb->len;
+		int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
+		word *data = (word *)skb->data;
+
+		writew((unsigned short)skb->len, dev->mem_start + dest);
+		dest += 2;
+		while (skb->len > 1) {
+			writew(*data++, dev->mem_start + dest);
+			dest += 2; skb->len -= 2;
+		}
+		if (skb->len == 1) {
+			writew(*((byte *)data), dev->mem_start + dest);
+		}
+		writew(0, dev->mem_start + (int)hw->channel * 
+		   COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
+		ch->stats.tx_packets++;	
+		ch->stats.tx_bytes += lensave; 
+		ret = FRAME_ACCEPTED;
+	} else {
+		ch->stats.tx_dropped++;
+		printk(KERN_INFO "%s: frame dropped\n",dev->name);
+	}
+	
+	ch->HW_release_board(dev, savep);
+	dev_kfree_skb(skb);
+	return ret;
+}
+
+static inline int comx_read_buffer(struct device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	word rbuf_offs;
+	struct sk_buff *skb;
+	word len;
+	int i=0;
+	word *writeptr;
+
+	i = 0;
+	rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
+	len = readw(dev->mem_start + rbuf_offs);
+	if ((skb = dev_alloc_skb(len + 16)) == NULL) {
+		ch->stats.rx_dropped++;
+		COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+		return 0;
+	}
+	rbuf_offs += 2;
+	skb_reserve(skb, 16);
+	skb_put(skb, len);
+	skb->dev = dev;
+	writeptr = (word *)skb->data;
+	while (i < len) {
+		*writeptr++ = readw(dev->mem_start + rbuf_offs);
+		rbuf_offs += 2; 
+		i += 2;
+	}
+	COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+	ch->stats.rx_packets++;
+	ch->stats.rx_bytes += len;
+	if (ch->debug_flags & DEBUG_HW_RX) {
+		comx_debug_skb(dev, skb, "COMX_interrupt receiving");
+	}
+	ch->LINE_rx(dev, skb);
+	return 1;
+}
+
+static inline char comx_line_change(struct device *dev, char linestat)
+{
+	struct comx_channel *ch=dev->priv;
+	char idle=1;
+	
+	
+	if (linestat & LINE_UP) { /* Vonal fol */
+		if (ch->lineup_delay) {
+			if (!test_and_set_bit(0, &ch->lineup_pending)) {
+				ch->lineup_timer.function = comx_lineup_func;
+				ch->lineup_timer.data = (unsigned long)dev;
+				ch->lineup_timer.expires = jiffies +
+					HZ*ch->lineup_delay;
+				add_timer(&ch->lineup_timer);
+				idle=0;
+			}
+		} else {
+			idle=0;
+			ch->LINE_status(dev, ch->line_status |= LINE_UP);
+		}
+	} else { /* Vonal le */
+		idle=0;
+		if (test_and_clear_bit(0, &ch->lineup_pending)) {
+			del_timer(&ch->lineup_timer);
+		} else {
+			ch->line_status &= ~LINE_UP;
+			if (ch->LINE_status) {
+				ch->LINE_status(dev, ch->line_status);
+			}
+		}
+	}
+	return idle;
+}
+
+
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = dev_id;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct device *interrupted;
+	unsigned long jiffs;
+	char idle = 0;
+	int count = 0;
+	word tmp;
+
+	if (dev == NULL) {
+		printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
+		return;
+	}
+
+	if (dev->interrupt) {
+		printk(KERN_ERR "%s: re-entering interrupt handler!\n", dev->name);
+		return;
+	}
+
+	printk("%s: entering interrupt handler\n", dev->name);
+
+	jiffs = jiffies;
+
+	dev->interrupt = 1;
+	interrupted = ch->HW_access_board(dev);
+
+	while (!idle && count < 5000) {
+		char channel = 0;
+		idle = 1;
+
+		while (channel < 2) {
+			char linestat = 0;
+			char buffers_emptied = 0;
+
+			if (channel == 1) {
+				if (ch->twin) {
+					dev->interrupt = 0;
+					dev = ch->twin;
+					dev->interrupt = 1;
+					ch = dev->priv;
+					hw = ch->HW_privdata;
+				} else {
+					break;
+				}
+			} else {
+				COMX_WRITE(dev, OFF_A_L1_REPENA, 
+				    COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
+			}
+			channel++;
+
+			if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != 
+			   (HW_OPEN | LINE_OPEN)) {
+				continue;
+			}
+	
+			/* Collect stats */
+			tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
+			COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
+			ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
+			ch->stats.rx_over_errors += tmp & 0xff;
+			tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
+			COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
+			ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
+			ch->stats.rx_missed_errors += tmp & 0xff;
+
+			if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
+				while (COMX_readw(dev, OFF_A_L2_DAV)) {
+					idle=0;
+					buffers_emptied+=comx_read_buffer(dev);
+				}
+			}
+
+			if (COMX_readw(dev, OFF_A_L2_TxEMPTY) && ch->LINE_tx) {
+				ch->LINE_tx(dev);
+			}
+
+			if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+				linestat &= ~LINE_UP;
+			} else {
+				linestat |= LINE_UP;
+			}
+
+			if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
+				ch->stats.tx_carrier_errors++;
+				idle &= comx_line_change(dev,linestat);
+			}
+				
+			hw->histogram[(int)buffers_emptied]++;
+		}
+		count++;
+	}
+
+	if(count==5000) {
+		printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
+	}
+
+	ch->HW_release_board(dev, interrupted);
+	printk("%s: leaving interrupt handler\n", dev->name);
+	dev->interrupt = 0;
+}
+
+static int COMX_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long jiffs;
+	struct device *savep;
+
+	if (!dev->base_addr || !dev->irq || !dev->mem_start) {
+		return -ENODEV;
+	}
+
+	if (!ch->twin || 
+	   (!(((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN))) {
+		if (check_region(dev->base_addr, hw->io_extent)) {
+			return -EAGAIN;
+		}
+		if (request_irq(dev->irq, COMX_interrupt, SA_INTERRUPT, dev->name, 
+		   (void *)dev)) {
+			printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
+			return -EAGAIN;
+		}
+		ch->init_status |= IRQ_ALLOCATED;
+		request_region(dev->base_addr, hw->io_extent, dev->name);
+		if (!ch->HW_load_board || ch->HW_load_board(dev)) {
+			release_region(dev->base_addr, hw->io_extent);
+			free_irq(dev->irq, (void *)dev);
+			ch->init_status &= ~IRQ_ALLOCATED;
+			return -ENODEV;
+		}
+	}
+
+	savep = ch->HW_access_board(dev);
+	COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
+
+	if (ch->HW_set_clock) {
+		ch->HW_set_clock(dev);
+	}
+
+	COMX_CMD(dev, COMX_CMD_INIT); 
+	jiffs = jiffies;
+	while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) {
+		schedule_timeout(1);
+	}
+	
+	ch->HW_release_board(dev, savep);
+	if (jiffies > jiffs + HZ) {
+		printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
+		release_region(dev->base_addr, hw->io_extent);
+		free_irq(dev->irq, (void *)dev);
+		return -EIO;
+	}
+
+	savep = ch->HW_access_board(dev);
+
+	/* Ide kellene irni, hogy DTE vagy DCE ? */
+	COMX_CMD(dev, COMX_CMD_OPEN);
+
+	ch->init_status |= HW_OPEN;
+	
+	/* Ez eleg ciki, de ilyen a rendszer */
+	if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+		ch->line_status &= ~LINE_UP;
+	} else {
+		ch->line_status |= LINE_UP;
+	}
+	
+	if (ch->LINE_status) {
+		ch->LINE_status(dev, ch->line_status);
+	}
+
+	ch->HW_release_board(dev, savep);
+
+	for ( ; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IRQ) == 0 
+		    || strcmp(procfile->name, FILENAME_IO) == 0
+		    || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+		    || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+		    || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+		    || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+			procfile->mode = S_IFREG | 0444;
+		
+		}
+	}	
+	
+	return 0;	
+}
+
+static int COMX_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_channel *twin_ch;
+	struct device *savep;
+
+	savep = ch->HW_access_board(dev);
+
+	COMX_CMD(dev, COMX_CMD_CLOSE);
+	udelay(1000);
+	COMX_CMD(dev, COMX_CMD_EXIT);
+
+	ch->HW_release_board(dev, savep);
+
+	if (ch->init_status & IRQ_ALLOCATED) {
+		free_irq(dev->irq, (void *)dev);
+		ch->init_status &= ~IRQ_ALLOCATED;
+	}
+	release_region(dev->base_addr, hw->io_extent);
+
+	if (ch->twin && (twin_ch = ch->twin->priv) && 
+	    (twin_ch->init_status & HW_OPEN)) {
+		/* Pass the irq to the twin */
+		if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name, 
+		   (void *)ch->twin) == 0) {
+			twin_ch->init_status |= IRQ_ALLOCATED;
+		}
+	}
+
+	for ( ; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IRQ) == 0 
+		    || strcmp(procfile->name, FILENAME_IO) == 0
+		    || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+		    || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+		    || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+		    || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+			procfile->mode = S_IFREG | 0644;
+		}
+	}
+	
+	ch->init_status &= ~HW_OPEN;
+	return 0;
+}
+
+static int COMX_statistics(struct device *dev, char *page)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct device *savep;
+	int len = 0;
+
+	savep = ch->HW_access_board(dev);
+
+	len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
+		"MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
+		"TxEMPTY: %02x, TxBUFP: %02x\n",
+		(ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
+		(ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
+		(ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
+		(ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
+		COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
+		(COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
+		COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
+		COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
+		COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
+		COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
+		COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
+
+	len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
+		"hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
+		hw->histogram[2],hw->histogram[3],hw->histogram[4]);
+
+	ch->HW_release_board(dev, savep);
+
+	return len;
+}
+
+static int COMX_load_board(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_firmware *fw = hw->firmware;
+	word board_segment = dev->mem_start >> 16;
+	unsigned long jiff;
+	unsigned char id1, id2;
+	int len;
+	byte *COMX_address;
+
+	if (!fw || !fw->len) {
+		struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+		struct comx_privdata *twin_hw;
+
+		if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+			return -EAGAIN;
+		}
+
+		if (!(fw = twin_hw->firmware) || !fw->len) {
+			return -EAGAIN;
+		}
+	}
+
+	id1 = fw->data[OFF_FW_L1_ID]; 
+	id2 = fw->data[OFF_FW_L1_ID + 1];
+
+	if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
+		printk(KERN_ERR "%s: incorrect firmware, load aborted\n", 
+			dev->name);
+		return -EAGAIN;
+	}
+
+	printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, 
+		(char *)(fw->data + OFF_FW_L1_ID + 2));
+
+	id1 = fw->data[OFF_FW_L2_ID]; 
+	id2 = fw->data[OFF_FW_L2_ID + 1];
+	if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+		printk(KERN_INFO "with Layer 2 code %s\n", 
+			(char *)(fw->data + OFF_FW_L2_ID + 2));
+	}
+
+	outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
+	/* 10 usec should be enough here */
+	udelay(100);
+	outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+	writeb(0, dev->mem_start + COMX_JAIL_OFFSET);	
+	jiff=jiffies;
+	while(jiffies < jiff + HZ && readb(dev->mem_start + COMX_JAIL_OFFSET) 
+	    != COMX_JAIL_VALUE) {
+		schedule_timeout(1);
+	}	
+	
+	if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+		printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
+			dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
+		outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+		return -ENODEV;
+	}
+
+	writeb(0x55, dev->mem_start + 0x18ff);
+	jiff=jiffies;
+	while(jiffies < jiff + 3*HZ && readb(dev->mem_start + 0x18ff) != 0) {
+		schedule_timeout(1);
+	}
+
+	if(readb(dev->mem_start + 0x18ff) != 0) {
+		printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
+			dev->name);
+		outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+		return -ENODEV;
+	}		
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (fw->len > len) {
+		writeb(fw->data[len++], COMX_address++);
+	}
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+		len++;
+	}
+
+	if (len != fw->len) {
+		printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+			"instead of 0x%02x\n", dev->name, len, 
+			readb(COMX_address - 1), fw->data[len]);
+		outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+		return -EAGAIN;
+	}
+
+	writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+	jiff = jiffies;
+	while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) {
+		schedule_timeout(1);
+	}
+
+	if (jiffies > jiff + 3*HZ) {
+		printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+			dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+		outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+		return -EAGAIN;
+	}
+
+	outb_p(board_segment | COMX_DISABLE_BOARD_MEM, dev->base_addr);
+	ch->init_status |= FW_LOADED;
+	return 0;
+}
+
+static int CMX_load_board(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_firmware *fw = hw->firmware;
+	word board_segment = dev->mem_start >> 16;
+	unsigned long jiff;
+	#if 0
+	unsigned char id1, id2;
+	#endif
+	int len;
+	byte *COMX_address;
+
+	if (!fw || !fw->len) {
+		struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+		struct comx_privdata *twin_hw;
+
+		if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+			return -EAGAIN;
+		}
+
+		if (!(fw = twin_hw->firmware) || !fw->len) {
+			return -EAGAIN;
+		}
+	}
+
+	/* Ide kell olyat tenni, hogy ellenorizze az ID-t */
+
+	if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
+		printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
+			inb_p(dev->base_addr));
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, 
+		(char *)(fw->data + OFF_FW_L1_ID + 2));
+
+	outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, 
+		dev->base_addr);
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (fw->len > len) {
+		writeb(fw->data[len++], COMX_address++);
+	}
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+		len++;
+	}
+
+	outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+	if (len != fw->len) {
+		printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+			"instead of 0x%02x\n", dev->name, len, 
+			readb(COMX_address - 1), fw->data[len]);
+		outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+		return -EAGAIN;
+	}
+
+	jiff = jiffies;
+	while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) {
+		schedule_timeout(1);
+	}
+
+	if (jiffies > jiff + 3*HZ) {
+		printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+			dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+		outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+		return -EAGAIN;
+	}
+
+	outb_p(board_segment | COMX_DISABLE_BOARD_MEM, dev->base_addr);
+	ch->init_status |= FW_LOADED;
+	return 0;
+}
+
+static int HICOMX_load_board(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_firmware *fw = hw->firmware;
+	word board_segment = dev->mem_start >> 12;
+	unsigned long jiff;
+	unsigned char id1, id2;
+	int len;
+	word *HICOMX_address;
+	char id = 1;
+
+	if (!fw || !fw->len) {
+		struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+		struct comx_privdata *twin_hw;
+
+		if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+			return -EAGAIN;
+		}
+
+		if (!(fw = twin_hw->firmware) || !fw->len) {
+			return -EAGAIN;
+		}
+	}
+
+	while (id != 4) {
+		if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
+			break;
+		}
+	}
+
+	if (id != 4) {
+		printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
+			dev->name, (unsigned int)dev->base_addr, id - 1,
+			inb_p(dev->base_addr + id - 1));
+		return -1;	
+	}
+
+	id1 = fw->data[OFF_FW_L1_ID]; 
+	id2 = fw->data[OFF_FW_L1_ID + 1];
+	if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
+		printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
+		return -EAGAIN;
+	}
+
+	printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, 
+		(char *)(fw->data + OFF_FW_L1_ID + 2));
+
+	id1 = fw->data[OFF_FW_L2_ID]; 
+	id2 = fw->data[OFF_FW_L2_ID + 1];
+	if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+		printk(KERN_INFO "with Layer 2 code %s\n", 
+			(char *)(fw->data + OFF_FW_L2_ID + 2));
+	}
+
+	outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+	udelay(10);	
+	outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+	outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
+
+	len = 0;
+	HICOMX_address = (word *)dev->mem_start;
+	while (fw->len > len) {
+		writeb(fw->data[len++], HICOMX_address++);
+	}
+
+	len = 0;
+	HICOMX_address = (word *)dev->mem_start;
+	while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
+		len++;
+	}
+
+	if (len != fw->len) {
+		printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+			"instead of 0x%02x\n", dev->name, len, 
+			readw(HICOMX_address - 1) & 0xff, fw->data[len]);
+		outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+		outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+		return -EAGAIN;
+	}
+
+	outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+	outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+	outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+	jiff = jiffies;
+	while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) {
+		schedule_timeout(1);
+	}
+
+	if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+		printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+			dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+		outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+		return -EAGAIN;
+	}
+
+	outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+	ch->init_status |= FW_LOADED;
+	return 0;
+}
+
+static struct device *comx_twin_check(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+	struct comx_privdata *hw = ch->HW_privdata;
+
+	struct device *twin;
+	struct comx_channel *ch_twin;
+	struct comx_privdata *hw_twin;
+
+
+	for ( ; procfile ; procfile = procfile->next) {
+	
+		if(!S_ISDIR(procfile->mode)) {
+			continue;
+		}
+	
+		twin=procfile->data;
+		ch_twin=twin->priv;
+		hw_twin=ch_twin->HW_privdata;
+
+
+		if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
+		   dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
+	  	   dev->mem_start == twin->mem_start &&
+		   hw->channel == (1 - hw_twin->channel) &&
+		   ch->hardware == ch_twin->hardware) {
+		   	return twin;
+		}
+	}
+	return NULL;
+}
+
+static int comxhw_write_proc(struct file *file, const char *buffer, 
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct device *dev = entry->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	char *page;
+
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if(ch->init_status & HW_OPEN) {
+		return -EAGAIN;	
+	}
+	
+	if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
+		if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+			return -ENOMEM;
+		}
+		copy_from_user(page, buffer, count = (min(count, PAGE_SIZE)));
+		if (*(page + count - 1) == '\n') {
+			*(page + count - 1) = 0;
+		}
+	} else {
+		byte *tmp;
+
+		if (!hw->firmware) {
+			if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), 
+			    GFP_KERNEL)) == NULL) {
+			    	return -ENOMEM;
+			}
+			hw->firmware->len = 0;
+			hw->firmware->data = NULL;
+		}
+		
+		if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
+			return -ENOMEM;
+		}
+		
+		/* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
+		if (hw->firmware && hw->firmware->len && file->f_pos 
+		    && hw->firmware->len < count + file->f_pos) {
+			memcpy(tmp, hw->firmware->data, hw->firmware->len);
+		}
+		if (hw->firmware->data) {
+			kfree(hw->firmware->data);
+		}
+		copy_from_user(tmp + file->f_pos, buffer, count);
+		hw->firmware->len = entry->size = file->f_pos + count;
+		hw->firmware->data = tmp;
+		file->f_pos += count;
+		return count;
+	}
+
+	if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+		hw->channel = simple_strtoul(page, NULL, 0);
+		if (hw->channel >= MAX_CHANNELNO) {
+			printk(KERN_ERR "Invalid channel number\n");
+			hw->channel = 0;
+		}
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+		dev->irq = simple_strtoul(page, NULL, 0);
+		if (dev->irq == 2) {
+			dev->irq = 9;
+		}
+		if (dev->irq < 3 || dev->irq > 15) {
+			printk(KERN_ERR "comxhw: Invalid irq number\n");
+			dev->irq = 0;
+		}
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_IO) == 0) {
+		dev->base_addr = simple_strtoul(page, NULL, 0);
+		if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300 
+		   || dev->base_addr > 0x3fc) {
+			printk(KERN_ERR "Invalid io value\n");
+			dev->base_addr = 0;
+		}
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
+		dev->mem_start = simple_strtoul(page, NULL, 0);
+		if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
+			dev->mem_start *= 16;
+		}
+		if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
+		    || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
+			printk(KERN_ERR "Invalid memory page\n");
+			dev->mem_start = 0;
+		}
+		dev->mem_end = dev->mem_start + hw->memory_size;
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+		if (strncmp("ext", page, 3) == 0) {
+			hw->clock = 0;
+		} else {
+			int kbps;
+
+			kbps = simple_strtoul(page, NULL, 0);
+			hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
+		}
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static int comxhw_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct device *dev = file->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	int len = 0;
+
+
+	if (strcmp(file->name, FILENAME_IO) == 0) {
+		len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
+	} else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+		len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
+	} else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+		len = sprintf(page, "%01d\n", hw->channel);
+	} else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
+		len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
+	} else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+		len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
+	} else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+		if (hw->clock) {
+			len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
+		} else {
+			len = sprintf(page, "external\n");
+		}
+	} else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
+		len = min(FILE_PAGESIZE, min(count, 
+			hw->firmware ? (hw->firmware->len - off) :  0));
+		if (len < 0) {
+			len = 0;
+		}
+		*start = hw->firmware ? (hw->firmware->data + off) : NULL;
+		if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
+			*eof = 1;
+		}
+		return len;
+	}	
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return(min(count, len - off));
+}
+
+/* Called on echo comx >boardtype */
+static int COMX_init(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw;
+	struct proc_dir_entry *new_file;
+
+	if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), 
+	    GFP_KERNEL)) == NULL) {
+	    	return -ENOMEM;
+	}
+	memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
+
+	if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
+		hw->memory_size = COMX_MEMORY_SIZE;
+		hw->io_extent = COMX_IO_EXTENT;
+		dev->base_addr = COMX_DEFAULT_IO;
+		dev->irq = COMX_DEFAULT_IRQ;
+		dev->mem_start = COMX_DEFAULT_MEMADDR;
+		dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
+	} else if (ch->hardware == &hicomx_hw) {
+		hw->memory_size = HICOMX_MEMORY_SIZE;
+		hw->io_extent = HICOMX_IO_EXTENT;
+		dev->base_addr = HICOMX_DEFAULT_IO;
+		dev->irq = HICOMX_DEFAULT_IRQ;
+		dev->mem_start = HICOMX_DEFAULT_MEMADDR;
+		dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
+	} else {
+		printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+	}
+
+	if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
+	    == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 6;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
+	    == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 5;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 2;		// Ezt tudjuk
+	new_file->nlink = 1;
+
+	if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+		if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+		   ch->procdir)) == NULL) {
+		   	return -EIO;
+		}
+		new_file->data = (void *)new_file;
+		new_file->read_proc = &comxhw_read_proc;
+		new_file->write_proc = &comxhw_write_proc;
+		new_file->ops = &comx_normal_inode_ops;
+		new_file->size = 9;
+		new_file->nlink = 1;
+	}
+
+	if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 8;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = NULL;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if (ch->hardware == &comx_hw) {
+		ch->HW_board_on = COMX_board_on;
+		ch->HW_board_off = COMX_board_off;
+		ch->HW_load_board = COMX_load_board;
+	} else if (ch->hardware == &cmx_hw) {
+		ch->HW_board_on = COMX_board_on;
+		ch->HW_board_off = COMX_board_off;
+		ch->HW_load_board = CMX_load_board;
+		ch->HW_set_clock = COMX_set_clock;
+	} else if (ch->hardware == &hicomx_hw) {
+		ch->HW_board_on = HICOMX_board_on;
+		ch->HW_board_off = HICOMX_board_off;
+		ch->HW_load_board = HICOMX_load_board;
+		ch->HW_set_clock = COMX_set_clock;
+	} else {
+		printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+	}
+
+	ch->HW_access_board = COMX_access_board;
+	ch->HW_release_board = COMX_release_board;
+	ch->HW_txe = COMX_txe;
+	ch->HW_open = COMX_open;
+	ch->HW_close = COMX_close;
+	ch->HW_send_packet = COMX_send_packet;
+	ch->HW_statistics = COMX_statistics;
+
+	if ((ch->twin = comx_twin_check(dev)) != NULL) {
+		struct comx_channel *twin_ch = ch->twin->priv;
+
+		twin_ch->twin = dev;
+	}
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* Called on echo valami >boardtype */
+static int COMX_exit(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+
+	if (hw->firmware) {
+		if (hw->firmware->data) kfree(hw->firmware->data);
+		kfree(hw->firmware);
+	} if (ch->twin) {
+		struct comx_channel *twin_ch = ch->twin->priv;
+
+		twin_ch->twin = NULL;
+	}
+	
+	kfree(ch->HW_privdata);
+	remove_proc_entry(FILENAME_IO, ch->procdir);
+	remove_proc_entry(FILENAME_IRQ, ch->procdir);
+	remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+	remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
+	remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
+	remove_proc_entry(FILENAME_TWIN, ch->procdir);
+	if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+		remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+	}
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int COMX_dump(struct device *dev)
+{
+	printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
+	return 0;
+}
+
+static struct comx_hardware comx_hw = {
+	"comx",
+	VERSION,
+	COMX_init,
+	COMX_exit,
+	COMX_dump,
+	NULL
+};
+
+static struct comx_hardware cmx_hw = {
+	"cmx",
+	VERSION,
+	COMX_init,
+	COMX_exit,
+	COMX_dump,
+	NULL
+};
+
+static struct comx_hardware hicomx_hw = {
+	"hicomx",
+	VERSION,
+	COMX_init,
+	COMX_exit,
+	COMX_dump,
+	NULL
+};
+
+#ifdef MODULE
+#define comx_hw_comx_init init_module
+#endif
+
+__initfunc(int comx_hw_comx_init(void))
+{
+	comx_register_hardware(&comx_hw);
+	comx_register_hardware(&cmx_hw);
+	comx_register_hardware(&hicomx_hw);
+	memset(memory_used, 0, sizeof(memory_used));
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_hardware("comx");
+	comx_unregister_hardware("cmx");
+	comx_unregister_hardware("hicomx");
+}
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-hw-locomx.c linux.14p6/drivers/net/comx-hw-locomx.c
--- linux.vanilla/drivers/net/comx-hw-locomx.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx-hw-locomx.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,498 @@
+/*
+ * Hardware driver for the LoCOMX card, using the generic z85230
+ * functions
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu> 
+ * and the hostess_sv11 driver
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.10 (99/06/17):
+ *		- rewritten for the z85230 layer
+ *
+ * Version 0.11 (99/06/21):
+ *		- some printk's fixed
+ *		- get rid of a memory leak (it was impossible though :))
+ * 
+ * Version 0.12 (99/07/07):
+ *		- check CTS for modem lines, not DCD (which is always high
+ *		  in case of this board)
+ * Version 0.13 (99/07/08):
+ *		- Fix the transmitter status check
+ *		- Handle the net device statistics better
+ */
+
+#define VERSION "0.13"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+
+#include "comx.h"
+#include "z85230.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
+
+#define RX_DMA 3
+#define TX_DMA 1
+#define LOCOMX_ID 0x33
+#define LOCOMX_IO_EXTENT 8
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+u8 z8530_locomx[] = {
+	11,     TCRTxCP,
+	14,     DTRREQ,
+	255
+};
+
+struct locomx_data {
+	int	io_extent;
+	struct	z8530_dev board;
+	struct timer_list status_timer;
+};
+
+static int LOCOMX_txe(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+
+	return (!hw->board.chanA.tx_next_skb);
+}
+
+
+static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
+{
+	struct device *dev=c->netdevice;
+	struct comx_channel *ch=dev->priv;
+	
+	if (ch->debug_flags & DEBUG_HW_RX) {
+		comx_debug_skb(dev, skb, "locomx_rx receiving");
+	}
+	ch->LINE_rx(dev,skb);
+}
+
+static int LOCOMX_send_packet(struct device *dev, struct sk_buff *skb) 
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
+	}
+
+	if (!(ch->line_status & LINE_UP)) {
+		return FRAME_DROPPED;
+	}
+
+	if(z8530_queue_xmit(&hw->board.chanA,skb)) {
+		printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name);
+		return FRAME_DROPPED;
+	}
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name);
+	}
+
+	if(!hw->board.chanA.tx_next_skb) {
+		return FRAME_QUEUED;
+	} else {
+		return FRAME_ACCEPTED;
+	}
+}
+
+static void locomx_status_timerfun(unsigned long d)
+{
+	struct device *dev=(struct device *)d;
+	struct comx_channel *ch=dev->priv;
+	struct locomx_data *hw=ch->HW_privdata;
+
+	if(!(ch->line_status & LINE_UP) &&
+	    (hw->board.chanA.status & CTS)) {
+		ch->LINE_status(dev, ch->line_status | LINE_UP);
+	}
+	if((ch->line_status & LINE_UP) &&
+	    !(hw->board.chanA.status & CTS)) {
+		ch->LINE_status(dev, ch->line_status & ~LINE_UP);
+	}
+	mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
+}
+
+
+static int LOCOMX_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long flags;
+	int ret;
+
+	if (!dev->base_addr || !dev->irq) {
+		return -ENODEV;
+	}
+
+	if (check_region(dev->base_addr, hw->io_extent)) {
+		return -EAGAIN;
+	}
+
+	request_region(dev->base_addr, hw->io_extent, dev->name);
+
+	hw->board.chanA.ctrlio=dev->base_addr + 5;
+	hw->board.chanA.dataio=dev->base_addr + 7;
+	
+	hw->board.irq=dev->irq;
+	hw->board.chanA.netdevice=dev;
+	hw->board.chanA.dev=&hw->board;
+	hw->board.name=dev->name;
+	hw->board.chanA.txdma=TX_DMA;
+	hw->board.chanA.rxdma=RX_DMA;
+	hw->board.chanA.irqs=&z8530_nop;
+	hw->board.chanB.irqs=&z8530_nop;
+
+	if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT, 
+	    dev->name, &hw->board)) {
+		printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name, 
+			dev->irq);
+		ret=-EAGAIN;
+		goto irq_fail;
+	}
+	if(request_dma(TX_DMA,"LoCOMX (TX)")) {
+		printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n", 
+			dev->name, TX_DMA);
+		ret=-EAGAIN;
+		goto dma1_fail;
+	}
+
+	if(request_dma(RX_DMA,"LoCOMX (RX)")) {
+		printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n", 
+			dev->name, RX_DMA);
+		ret=-EAGAIN;
+		goto dma2_fail;
+	}
+	
+	save_flags(flags); 
+	cli();
+
+	if(z8530_init(&hw->board)!=0)
+	{
+		printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name);
+		ret=-ENODEV;
+		goto z8530_fail;
+	}
+
+	hw->board.chanA.dcdcheck=CTS;
+
+	z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
+	z8530_channel_load(&hw->board.chanA, z8530_locomx);
+	z8530_channel_load(&hw->board.chanB, z8530_dead_port);
+
+	z8530_describe(&hw->board, "I/O", dev->base_addr);
+
+	if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
+		goto z8530_fail;
+	}
+
+	restore_flags(flags);
+
+
+	hw->board.active=1;
+	hw->board.chanA.rx_function=locomx_rx;
+
+	ch->init_status |= HW_OPEN;
+	if (hw->board.chanA.status & DCD) {
+		ch->line_status |= LINE_UP;
+	} else {
+		ch->line_status &= ~LINE_UP;
+	}
+
+	comx_status(dev, ch->line_status);
+
+	init_timer(&hw->status_timer);
+	hw->status_timer.function=locomx_status_timerfun;
+	hw->status_timer.data=(unsigned long)dev;
+	hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
+	add_timer(&hw->status_timer);
+
+	for (; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+		     strcmp(procfile->name, FILENAME_IRQ) == 0) {
+			procfile->mode = S_IFREG |  0444;
+		}
+	}
+	return 0;
+
+z8530_fail:
+	restore_flags(flags);
+	free_dma(RX_DMA);
+dma2_fail:
+	free_dma(TX_DMA);
+dma1_fail:
+	free_irq(dev->irq, &hw->board);
+irq_fail:
+	release_region(dev->base_addr, hw->io_extent);
+	return ret;
+}
+
+static int LOCOMX_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+
+	hw->board.chanA.rx_function=z8530_null_rx;
+	dev->tbusy=1;
+	z8530_sync_dma_close(dev, &hw->board.chanA);
+
+	z8530_shutdown(&hw->board);
+
+	del_timer(&hw->status_timer);
+	free_dma(RX_DMA);
+	free_dma(TX_DMA);
+	free_irq(dev->irq,&hw->board);
+	release_region(dev->base_addr,8);
+
+	for (; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+		    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+			procfile->mode = S_IFREG |  0644;
+		}
+	}
+
+	ch->init_status &= ~HW_OPEN;
+	return 0;
+}
+
+static int LOCOMX_statistics(struct device *dev,char *page)
+{
+	int len = 0;
+
+	len += sprintf(page + len, "Hello\n");
+
+	return len;
+}
+
+static int LOCOMX_dump(struct device *dev) {
+	printk(KERN_INFO "LOCOMX_dump called\n");
+	return(-1);
+}
+
+static int locomx_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct device *dev = file->parent->data;
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_IO) == 0) {
+		len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr);
+	} else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+		len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+	} else {
+		printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
+			file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return ( min(count, len - off) );
+}
+
+static int locomx_write_proc(struct file *file, const char *buffer,
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct device *dev = (struct device *)entry->parent->data;
+	int val;
+	char *page;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "hw_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_IO) == 0) {
+		val = simple_strtoul(page, NULL, 0);
+		if (val != 0x360 && val != 0x368 && val != 0x370 && 
+		   val != 0x378) {
+			printk(KERN_ERR "LoCOMX: incorrect io address!\n");	
+		} else {
+			dev->base_addr = val;
+		}
+	} else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+		val = simple_strtoul(page, NULL, 0);
+		if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
+			printk(KERN_ERR "LoCOMX: incorrect irq value!\n");
+		} else {
+			dev->irq = val;
+		}	
+	} else {
+		printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n", 
+			entry->name);
+		free_page((unsigned long)page);
+		return -EBADF;
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+
+
+static int LOCOMX_init(struct device *dev) 
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+	struct locomx_data *hw;
+	struct proc_dir_entry *new_file;
+
+	/* Alloc data for private structure */
+	if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data), 
+	   GFP_KERNEL)) == NULL) {
+	   	return -ENOMEM;
+	}
+
+	memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
+	hw->io_extent = LOCOMX_IO_EXTENT;
+
+	/* Register /proc files */
+	if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &locomx_read_proc;
+	new_file->write_proc = &locomx_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
+	    ch->procdir)) == NULL)  {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &locomx_read_proc;
+	new_file->write_proc = &locomx_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+/* 	No clock yet */
+/*
+	if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &locomx_read_proc;
+	new_file->write_proc = &locomx_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+*/
+
+	ch->HW_access_board = NULL;
+	ch->HW_release_board = NULL;
+	ch->HW_txe = LOCOMX_txe;
+	ch->HW_open = LOCOMX_open;
+	ch->HW_close = LOCOMX_close;
+	ch->HW_send_packet = LOCOMX_send_packet;
+	ch->HW_statistics = LOCOMX_statistics;
+	ch->HW_set_clock = NULL;
+
+	ch->current_stats = &hw->board.chanA.stats;
+	memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
+
+	dev->base_addr = LOCOMX_DEFAULT_IO;
+	dev->irq = LOCOMX_DEFAULT_IRQ;
+	
+	
+	/* O.K. Count one more user on this module */
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+static int LOCOMX_exit(struct device *dev)
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+	ch->HW_access_board = NULL;
+	ch->HW_release_board = NULL;
+	ch->HW_txe = NULL;
+	ch->HW_open = NULL;
+	ch->HW_close = NULL;
+	ch->HW_send_packet = NULL;
+	ch->HW_statistics = NULL;
+	ch->HW_set_clock = NULL;
+	memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
+	ch->current_stats = &ch->stats;
+
+	kfree(ch->HW_privdata);
+
+	remove_proc_entry(FILENAME_IO, ch->procdir);
+	remove_proc_entry(FILENAME_IRQ, ch->procdir);
+//	remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct comx_hardware locomx_hw = {
+	"locomx",
+	VERSION,
+	LOCOMX_init, 
+	LOCOMX_exit,
+	LOCOMX_dump,
+	NULL
+};
+	
+#ifdef MODULE
+#define comx_hw_locomx_init init_module
+#endif
+
+__initfunc(int comx_hw_locomx_init(void))
+{
+	comx_register_hardware(&locomx_hw);
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_hardware("locomx");
+	return;
+}
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-hw-mixcom.c linux.14p6/drivers/net/comx-hw-mixcom.c
--- linux.vanilla/drivers/net/comx-hw-mixcom.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx-hw-mixcom.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,945 @@
+/* 
+ * Hardware driver for the MixCom synchronous serial board 
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton driver code and a preliminary hscx driver by 
+ * Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.60 (99/06/11):
+ *		- ported to the kernel, now works as builtin code
+ *
+ * Version 0.61 (99/06/11):
+ *		- recognize the one-channel MixCOM card (id byte = 0x13)
+ *		- printk fixes
+ * 
+ * Version 0.62 (99/07/15):
+ *		- fixes according to the new hw docs 
+ *		- report line status when open
+ *
+ * Version 0.63 (99/09/21):
+ *		- line status report fixes
+ */
+
+#define VERSION "0.63"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "mixcom.h"
+#include "hscx.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
+
+#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
+	HW_privdata))
+
+#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
+	(1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
+
+#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
+	(1 - channel) * MIXCOM_CHANNEL_OFFSET)
+
+/* Values used to set the IRQ line */
+static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
+
+static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
+
+struct mixcom_privdata {
+	u16	clock;
+	char	channel;
+	char	txbusy;
+	struct sk_buff *sending;
+	unsigned tx_ptr;
+	struct sk_buff *recving;
+	unsigned rx_ptr;
+	unsigned char status;
+	char	card_has_status;
+};
+
+static inline void wr_hscx(struct device *dev, int reg, unsigned char val) 
+{
+	outb(val, dev->base_addr + reg);
+}
+
+static inline unsigned char rd_hscx(struct device *dev, int reg)
+{
+	return inb(dev->base_addr + reg);
+}
+
+static inline void hscx_cmd(struct device *dev, int cmd)
+{
+	unsigned long jiffs = jiffies;
+	unsigned char cec;
+	unsigned delay = 0;
+
+	while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && 
+	    (jiffs + HZ > jiffies)) {
+		udelay(1);
+		if (++delay > (100000 / HZ)) break;
+	}
+	if (cec) {
+		printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
+	} else {
+		wr_hscx(dev, HSCX_CMDR, cmd);
+	}
+}
+
+static inline void hscx_fill_fifo(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	register word to_send = hw->sending->len - hw->tx_ptr;
+
+
+	outsb(dev->base_addr + HSCX_FIFO,
+        	&(hw->sending->data[hw->tx_ptr]), min(to_send, 32));
+	if (to_send <= 32) {
+        	hscx_cmd(dev, HSCX_XTF | HSCX_XME);
+	        kfree_skb(hw->sending);
+        	hw->sending = NULL; 
+        	hw->tx_ptr = 0;
+        } else {
+	        hscx_cmd(dev, HSCX_XTF);
+        	hw->tx_ptr += 32;
+        }
+}
+
+static inline void hscx_empty_fifo(struct device *dev, int cnt)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	if (hw->recving == NULL) {
+        	if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
+	                ch->stats.rx_dropped++;
+        	        hscx_cmd(dev, HSCX_RHR);
+                } else {
+	                skb_reserve(hw->recving, 16);
+        	        skb_put(hw->recving, HSCX_MTU);
+                }
+	        hw->rx_ptr = 0;
+        }
+	if (cnt > 32 || !cnt || hw->recving == NULL) {
+        	printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
+		        cnt, (void *)hw->recving);
+	        return;
+        }
+        
+	insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
+	hw->rx_ptr += cnt;
+	hscx_cmd(dev, HSCX_RMC);
+}
+
+
+static int MIXCOM_txe(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	return !test_bit(0, &hw->txbusy);
+}
+
+static int mixcom_probe(struct device *dev)
+{
+	unsigned long flags;
+	int id, vstr, ret=0;
+
+	save_flags(flags); cli();
+
+	id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET);
+
+ 	if (id != MIXCOM_ID ) {
+		ret=-ENODEV;
+		printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
+		goto out;
+	}
+
+	vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
+	if(vstr>=sizeof(hscx_versions)/sizeof(char*) || 
+	    hscx_versions[vstr]==NULL) {
+		printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
+		ret = -ENODEV;
+	} else {
+		printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
+		ret = 0;
+	}
+
+out:
+
+	restore_flags(flags);
+	return ret;
+}
+
+#if 0
+static void MIXCOM_set_clock(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	if (hw->clock) {
+		;
+	} else {
+		;
+	}
+}
+#endif
+
+static void mixcom_board_on(struct device *dev)
+{
+	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+	udelay(1000);
+	outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, 
+		MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+	udelay(1000);
+}
+
+static void mixcom_board_off(struct device *dev)
+{
+	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+	udelay(1000);
+}
+
+static void mixcom_off(struct device *dev)
+{
+	wr_hscx(dev, HSCX_CCR1, 0x0);
+}
+
+static void mixcom_on(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
+	wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
+	wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
+	wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
+	wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
+	hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
+
+	if (ch->HW_set_clock) ch->HW_set_clock(dev);
+
+}
+
+static int MIXCOM_send_packet(struct device *dev, struct sk_buff *skb) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	unsigned long flags;
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
+	}
+
+	if (!(ch->line_status & LINE_UP)) {
+		return FRAME_DROPPED;
+	}
+
+	if (skb->len > HSCX_MTU) {
+		ch->stats.tx_errors++;	
+		return FRAME_ERROR;
+	}
+
+	save_flags(flags); cli();
+
+	if (test_and_set_bit(0, &hw->txbusy)) {
+		printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
+		restore_flags(flags);
+		return FRAME_DROPPED;
+	}
+
+
+	hw->sending = skb;
+	hw->tx_ptr = 0;
+	hw->txbusy = 1;
+//	atomic_inc(&skb->users);	// save it
+	hscx_fill_fifo(dev);
+	restore_flags(flags);
+
+	ch->stats.tx_packets++;
+	ch->stats.tx_bytes += skb->len; 
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
+	}
+
+	return FRAME_ACCEPTED;
+}
+
+static inline void mixcom_receive_frame(struct device *dev) 
+{
+	struct comx_channel *ch=dev->priv;
+	struct mixcom_privdata *hw=ch->HW_privdata;
+	register byte rsta;
+	register word length;
+
+	rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | 
+		HSCX_CRC | HSCX_RAB);
+	length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | 
+		rd_hscx(dev, HSCX_RBCL);
+
+	if ( length > hw->rx_ptr ) {
+		hscx_empty_fifo(dev, length - hw->rx_ptr);
+	}
+	
+	if (!(rsta & HSCX_VFR)) {
+		ch->stats.rx_length_errors++;
+	}
+	if (rsta & HSCX_RDO) {
+		ch->stats.rx_over_errors++;
+	}
+	if (!(rsta & HSCX_CRC)) {
+		ch->stats.rx_crc_errors++;
+	}
+	if (rsta & HSCX_RAB) {
+		ch->stats.rx_frame_errors++;
+	}
+	ch->stats.rx_packets++; 
+	ch->stats.rx_bytes += length;
+
+	if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
+		skb_trim(hw->recving, hw->rx_ptr - 1);
+		if (ch->debug_flags & DEBUG_HW_RX) {
+			comx_debug_skb(dev, hw->recving,
+				"MIXCOM_interrupt receiving");
+		}
+		hw->recving->dev = dev;
+		if (ch->LINE_rx) {
+			ch->LINE_rx(dev, hw->recving);
+		}
+	}
+	else if(hw->recving) {
+		kfree_skb(hw->recving);
+	}
+	hw->recving = NULL; 
+	hw->rx_ptr = 0;
+}
+
+
+static inline void mixcom_extended_interrupt(struct device *dev) 
+{
+	struct comx_channel *ch=dev->priv;
+	struct mixcom_privdata *hw=ch->HW_privdata;
+	register byte exir;
+
+	exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
+
+	if (exir & HSCX_RFO) {
+		ch->stats.rx_over_errors++;
+		if (hw->rx_ptr) {
+			kfree_skb(hw->recving);
+			hw->recving = NULL; hw->rx_ptr = 0;
+		}
+		printk(KERN_ERR "MIXCOM: rx overrun\n");
+		hscx_cmd(dev, HSCX_RHR);
+	}
+
+	if (exir & HSCX_XDU) { // xmit underrun
+		ch->stats.tx_errors++;
+		ch->stats.tx_aborted_errors++;
+		if (hw->tx_ptr) {
+			kfree_skb(hw->sending);
+			hw->sending = NULL; 
+			hw->tx_ptr = 0;
+		}
+		hscx_cmd(dev, HSCX_XRES);
+		clear_bit(0, &hw->txbusy);
+		if (ch->LINE_tx) {
+			ch->LINE_tx(dev);
+		}
+		printk(KERN_ERR "MIXCOM: tx underrun\n");
+	}
+
+	if (exir & HSCX_CSC) {        
+		ch->stats.tx_carrier_errors++;
+		if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
+			if (test_and_clear_bit(0, &ch->lineup_pending)) {
+               			del_timer(&ch->lineup_timer);
+			} else if (ch->line_status & LINE_UP) {
+        		       	ch->line_status &= ~LINE_UP;
+                		if (ch->LINE_status) {
+                      			ch->LINE_status(dev,ch->line_status);
+                      		}
+		      	}
+		}
+		if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & 
+		    HSCX_CTS)) { // Vonal fol
+			if (!test_and_set_bit(0,&ch->lineup_pending)) {
+				ch->lineup_timer.function = comx_lineup_func;
+	        	        ch->lineup_timer.data = (unsigned long)dev;
+        	        	ch->lineup_timer.expires = jiffies + HZ * 
+        	        		ch->lineup_delay;
+	                	add_timer(&ch->lineup_timer);
+		                hscx_cmd(dev, HSCX_XRES);
+        		        clear_bit(0, &hw->txbusy);
+                		if (hw->sending) {
+					kfree_skb(hw->sending);
+				}
+				hw->sending=NULL;
+				hw->tx_ptr = 0;
+			}
+		}
+	}
+}
+
+
+static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags;
+	struct device *dev = (struct device *)dev_id;
+	struct comx_channel *ch, *twin_ch;
+	struct mixcom_privdata *hw, *twin_hw;
+	register unsigned char ista;
+
+	if (dev==NULL) {
+		printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
+		return;
+	}
+
+	ch = dev->priv; 
+	hw = ch->HW_privdata;
+
+	save_flags(flags); cli(); 
+
+	while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | 
+	    HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
+		register byte ista2 = 0;
+
+		if (ista & HSCX_RME) {
+			mixcom_receive_frame(dev);
+		}
+		if (ista & HSCX_RPF) {
+			hscx_empty_fifo(dev, 32);
+		}
+		if (ista & HSCX_XPR) {
+			if (hw->tx_ptr) {
+				hscx_fill_fifo(dev);
+			} else {
+				clear_bit(0, &hw->txbusy);
+               			ch->LINE_tx(dev);
+			}
+		}
+		
+		if (ista & HSCX_EXB) {
+			mixcom_extended_interrupt(dev);
+		}
+		
+		if ((ista & HSCX_EXA) && ch->twin)  {
+			mixcom_extended_interrupt(ch->twin);
+		}
+	
+		if ((ista & HSCX_ICA) && ch->twin &&
+		    (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
+		    (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
+			if (ista2 & HSCX_RME) {
+				mixcom_receive_frame(ch->twin);
+			}
+			if (ista2 & HSCX_RPF) {
+				hscx_empty_fifo(ch->twin, 32);
+			}
+			if (ista2 & HSCX_XPR) {
+				twin_ch=ch->twin->priv;
+				twin_hw=twin_ch->HW_privdata;
+				if (twin_hw->tx_ptr) {
+					hscx_fill_fifo(ch->twin);
+				} else {
+					clear_bit(0, &twin_hw->txbusy);
+					ch->LINE_tx(ch->twin);
+				}
+			}
+		}
+	}
+
+	restore_flags(flags);
+	return;
+}
+
+static int MIXCOM_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long flags; 
+
+	if (!dev->base_addr || !dev->irq) return -ENODEV;
+
+
+	if(hw->channel==1) {
+		if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & 
+		    IRQ_ALLOCATED)) {
+			printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
+			return -EAGAIN;
+		}
+	}
+
+
+	/* Is our hw present at all ? Not checking for channel 0 if it is already 
+	   open */
+	if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
+		if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) {
+			return -EAGAIN;
+		}
+		if (mixcom_probe(dev)) {
+			return -ENODEV;
+		}
+	}
+
+	save_flags(flags); cli();
+
+	if(hw->channel==1) {
+		request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+	} 
+
+	if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
+		if (request_irq(dev->irq, MIXCOM_interrupt, 0, 
+		    dev->name, (void *)dev)) {
+			printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
+			return -EAGAIN;
+		}
+		ch->init_status|=IRQ_ALLOCATED;
+		request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+		mixcom_board_on(dev);
+	}
+
+	mixcom_on(dev);
+
+	restore_flags(flags);
+
+	hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
+	if(hw->status != 0xff) {
+		printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
+		hw->card_has_status=1;
+	}
+
+	hw->txbusy = 0;
+	ch->init_status |= HW_OPEN;
+	
+	if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
+		ch->line_status |= LINE_UP;
+	} else {
+		ch->line_status &= ~LINE_UP;
+	}
+
+	ch->LINE_status(dev, ch->line_status);
+
+	for (; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+		    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+		    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+		    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+			procfile->mode = S_IFREG |  0444;
+		}
+	}
+
+	return 0;
+}
+
+static int MIXCOM_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long flags;
+
+
+	save_flags(flags); cli();
+
+	mixcom_off(dev);
+
+	/* This is channel 0, twin is not open, we can safely turn off everything */
+	if(hw->channel==0 && (!(TWIN(dev)) || 
+	    !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
+		mixcom_board_off(dev);
+		free_irq(dev->irq, dev);
+		release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+		ch->init_status &= ~IRQ_ALLOCATED;
+	}
+
+	/* This is channel 1, channel 0 has already been shutdown, we can release
+	   this one too */
+	if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+		if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
+			mixcom_board_off(TWIN(dev));
+			free_irq(TWIN(dev)->irq, TWIN(dev));
+			release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
+			COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
+		}
+	}
+
+	/* the ioports for channel 1 can be safely released */
+	if(hw->channel==1) {
+		release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+	}
+
+	restore_flags(flags);
+
+	/* If we don't hold any hardware open */
+	if(!(ch->init_status & IRQ_ALLOCATED)) {
+		for (; procfile ; procfile = procfile->next) {
+			if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+			    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+			    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+			    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+				procfile->mode = S_IFREG |  0644;
+			}
+		}
+	}
+
+	/* channel 0 was only waiting for us to close channel 1 
+	   close it completely */
+   
+	if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+		for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; 
+		    procfile ; procfile = procfile->next) {
+			if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+			    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+			    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+			    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+				procfile->mode = S_IFREG |  0644;
+			}
+		}
+	}
+	
+	ch->init_status &= ~HW_OPEN;
+	return 0;
+}
+
+static int MIXCOM_statistics(struct device *dev,char *page)
+{
+	struct comx_channel *ch = dev->priv;
+	// struct mixcom_privdata *hw = ch->HW_privdata;
+	int len = 0;
+
+	if(ch->init_status && IRQ_ALLOCATED) {
+		len += sprintf(page + len, "Mixcom board: hardware open\n");
+	}
+
+	return len;
+}
+
+static int MIXCOM_dump(struct device *dev) {
+	return 0;
+}
+
+static int mixcom_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct device *dev = file->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_IO) == 0) {
+		len = sprintf(page, "0x%x\n", 
+			(unsigned int)MIXCOM_BOARD_BASE(dev));
+	} else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+		len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+	} else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+		if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
+			else len = sprintf(page, "external\n");
+	} else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+		len = sprintf(page, "%01d\n", hw->channel);
+	} else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+		if (ch->twin) {
+			len = sprintf(page, "%s\n",ch->twin->name);
+		} else {
+			len = sprintf(page, "none\n");
+		}
+	} else {
+		printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+	*start = page + off;
+	if (count >= len - off) *eof = 1;
+	return ( min(count, len - off) );
+}
+
+
+static struct device *mixcom_twin_check(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	struct device *twin;
+	struct comx_channel *ch_twin;
+	struct mixcom_privdata *hw_twin;
+
+
+	for ( ; procfile ; procfile = procfile->next) {
+		if(!S_ISDIR(procfile->mode)) continue;
+                
+        	twin = procfile->data;
+	        ch_twin = twin->priv;
+        	hw_twin = ch_twin->HW_privdata;
+
+
+	        if (twin != dev && dev->irq && dev->base_addr && 
+        	    dev->irq == twin->irq && 
+        	    ch->hardware == ch_twin->hardware &&
+		    dev->base_addr == twin->base_addr + 
+		    (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
+		    hw->channel == (1 - hw_twin->channel)) {
+	        	if  (!TWIN(twin) || TWIN(twin)==dev) {
+	        		return twin;
+	        	}
+		}
+        }
+	return NULL;
+}
+
+
+static void setup_twin(struct device* dev) 
+{
+
+	if(TWIN(dev) && TWIN(TWIN(dev))) {
+		TWIN(TWIN(dev))=NULL;
+	}
+	if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
+		if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
+			TWIN(dev)=NULL;
+		} else {
+			TWIN(TWIN(dev))=dev;
+		}
+	}	
+}
+
+static int mixcom_write_proc(struct file *file, const char *buffer,
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct device *dev = (struct device *)entry->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	char *page;
+	int value;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_IO) == 0) {
+		value = simple_strtoul(page, NULL, 0);
+		if (value != 0x180 && value != 0x280 && value != 0x380) {
+			printk(KERN_ERR "MIXCOM: incorrect io address!\n");
+		} else {
+			dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
+		}
+	} else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+		value = simple_strtoul(page, NULL, 0); 
+		if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
+			printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
+		} else {
+			dev->irq = value;	
+		}
+	} else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+		if (strncmp("ext", page, 3) == 0) {
+			hw->clock = 0;
+		} else {
+			int kbps;
+
+			kbps = simple_strtoul(page, NULL, 0);
+			if (!kbps) {
+				hw->clock = 0;
+			} else {
+				hw->clock = kbps;
+			}
+			if (hw->clock < 32 || hw->clock > 2000) {
+				hw->clock = 0;
+				printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
+			}
+		}
+		if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
+			ch->HW_set_clock(dev);
+		}
+	} else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+		value = simple_strtoul(page, NULL, 0);
+        	if (value > 2) {
+                	printk(KERN_ERR "Invalid channel number\n");
+	        } else {
+        		dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
+	        	hw->channel = value;
+		}	        
+	} else {
+		printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
+			entry->name);
+		return -EBADF;
+	}
+
+	setup_twin(dev);
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static int MIXCOM_init(struct device *dev) {
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw;
+	struct proc_dir_entry *new_file;
+
+	if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), 
+	    GFP_KERNEL)) == NULL) {
+	    	return -ENOMEM;
+	}
+
+	memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
+
+	if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+#if 0
+	if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+#endif
+
+	if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	setup_twin(dev);
+
+	/* Fill in ch_struct hw specific pointers */
+	ch->HW_access_board = NULL;
+	ch->HW_release_board = NULL;
+	ch->HW_txe = MIXCOM_txe;
+	ch->HW_open = MIXCOM_open;
+	ch->HW_close = MIXCOM_close;
+	ch->HW_send_packet = MIXCOM_send_packet;
+	ch->HW_statistics = MIXCOM_statistics;
+	ch->HW_set_clock = NULL;
+
+	dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
+	dev->irq = MIXCOM_DEFAULT_IRQ;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int MIXCOM_exit(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	if(hw->channel==0 && TWIN(dev)) {
+		return -EBUSY;
+	}
+
+	if(hw->channel==1 && TWIN(dev)) {
+		TWIN(TWIN(dev))=NULL;
+	}
+
+	kfree(ch->HW_privdata);
+	remove_proc_entry(FILENAME_IO, ch->procdir);
+	remove_proc_entry(FILENAME_IRQ, ch->procdir);
+#if 0
+	remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+#endif
+	remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+	remove_proc_entry(FILENAME_TWIN, ch->procdir);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct comx_hardware mixcomhw = {
+	"mixcom",
+	VERSION,
+	MIXCOM_init, 
+	MIXCOM_exit,
+	MIXCOM_dump,
+	NULL
+};
+	
+/* Module management */
+
+#ifdef MODULE
+#define comx_hw_mixcom_init init_module
+#endif
+
+__initfunc(int comx_hw_mixcom_init(void))
+{
+	return(comx_register_hardware(&mixcomhw));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+	comx_unregister_hardware("mixcom");
+}
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-proto-fr.c linux.14p6/drivers/net/comx-proto-fr.c
--- linux.vanilla/drivers/net/comx-proto-fr.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx-proto-fr.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,1012 @@
+/*
+ * Frame-relay protocol module for the COMX driver 
+ * for Linux 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.70 (99/06/14):
+ *		- cleaned up the source code a bit
+ *		- ported back to kernel, now works as builtin code 
+ *
+ * Version 0.71 (99/06/25):
+ *		- use skb priorities and queues for sending keepalive
+ * 		- use device queues for slave->master data transmit
+ *		- set IFF_RUNNING only line protocol up
+ *		- fixes on slave device flags
+ * 
+ * Version 0.72 (99/07/09):
+ *		- handle slave tbusy with master tbusy (should be fixed)
+ *		- fix the keepalive timer addition/deletion
+ */
+
+#define VERSION "0.72"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <linux/pkt_sched.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
+MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
+	"for Linux kernel 2.2.X");
+
+#define	FRAD_UI		0x03
+#define	NLPID_IP	0xcc
+#define	NLPID_Q933_LMI	0x08
+#define	NLPID_CISCO_LMI	0x09	
+#define Q933_ENQ	0x75
+#define	Q933_LINESTAT	0x51
+#define	Q933_COUNTERS	0x53
+
+#define	MAXALIVECNT	3		/* No. of failures */
+
+struct fr_data {
+	u16	dlci;
+	struct	device *master;
+	char	keepa_pend;
+	char	keepa_freq;
+	char	keepalivecnt, keeploopcnt;
+	struct	timer_list keepa_timer;
+	u8	local_cnt, remote_cnt;
+};
+
+static struct comx_protocol fr_master_protocol;
+static struct comx_protocol fr_slave_protocol;
+static struct comx_hardware fr_dlci;
+
+static void fr_keepalive_send(struct device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct sk_buff *skb;
+	u8 *fr_packet;
+	
+	skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
+	
+	if(skb==NULL)
+		return;
+               
+        skb_reserve(skb, dev->hard_header_len);
+        
+        fr_packet=(u8*)skb_put(skb, 13);
+                 
+	fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
+	fr_packet[1] = (fr->dlci & 15) << 4 | 1;	// EA bit 1
+	fr_packet[2] = FRAD_UI;
+	fr_packet[3] = NLPID_Q933_LMI;
+	fr_packet[4] = 0;
+	fr_packet[5] = Q933_ENQ;
+	fr_packet[6] = Q933_LINESTAT;
+	fr_packet[7] = 0x01;
+	fr_packet[8] = 0x01;
+	fr_packet[9] = Q933_COUNTERS;
+	fr_packet[10] = 0x02;
+	fr_packet[11] = ++fr->local_cnt;
+	fr_packet[12] = fr->remote_cnt;
+
+	skb->dev = dev;
+	skb->priority = TC_PRIO_CONTROL;
+	dev_queue_xmit(skb);
+}
+
+static void fr_keepalive_timerfun(unsigned long d) 
+{
+	struct device *dev = (struct device *)d;
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	struct device *sdev;
+
+	if (ch->init_status & LINE_OPEN) {
+		if (fr->keepalivecnt == MAXALIVECNT) {
+			comx_status(dev, ch->line_status & ~PROTO_UP);
+			dev->flags &= ~IFF_RUNNING;
+			for (; dir ; dir = dir->next) {
+				if(!S_ISDIR(dir->mode)) {
+				    continue;
+				}
+	
+				if ((sdev = dir->data) && (sch = sdev->priv) && 
+				    (sdev->type == ARPHRD_DLCI) && 
+				    (sfr = sch->LINE_privdata) 
+				    && (sfr->master == dev) && 
+				    (sdev->flags & IFF_UP)) {
+					sdev->flags &= ~IFF_RUNNING;
+					comx_status(sdev, 
+						sch->line_status & ~PROTO_UP);
+				}
+			}
+		}
+		if (fr->keepalivecnt <= MAXALIVECNT) {
+			++fr->keepalivecnt;
+		}
+		fr_keepalive_send(dev);
+	}
+	mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
+}
+
+static void fr_rx_lmi(struct device *dev, struct sk_buff *skb, 
+	u16 dlci, u8 nlpid) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	struct device *sdev;
+
+	if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
+		return;
+	}
+
+	fr->remote_cnt = skb->data[7];
+	if (skb->data[8] == fr->local_cnt) { // keepalive UP!
+		fr->keepalivecnt = 0;
+		if ((ch->line_status & LINE_UP) && 
+		    !(ch->line_status & PROTO_UP)) {
+			comx_status(dev, ch->line_status |= PROTO_UP);
+			dev->flags |= IFF_RUNNING;
+			for (; dir ; dir = dir->next) {
+				if(!S_ISDIR(dir->mode)) {
+				    continue;
+				}
+	
+				if ((sdev = dir->data) && (sch = sdev->priv) && 
+				    (sdev->type == ARPHRD_DLCI) && 
+				    (sfr = sch->LINE_privdata) 
+				    && (sfr->master == dev) && 
+				    (sdev->flags & IFF_UP)) {
+					sdev->flags |= IFF_RUNNING;
+					comx_status(sdev, 
+						sch->line_status | PROTO_UP);
+				}
+			}
+		}
+	}
+}
+
+static void fr_set_keepalive(struct device *dev, int keepa) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+
+	if (!keepa && fr->keepa_freq) { // switch off
+		fr->keepa_freq = 0;
+		if (ch->line_status & LINE_UP) {
+			comx_status(dev, ch->line_status | PROTO_UP);
+			dev->flags |= IFF_RUNNING;
+			del_timer(&fr->keepa_timer);
+		}
+		return;
+	}
+
+	if (keepa) { // bekapcs
+		if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
+			del_timer(&fr->keepa_timer);
+		}
+		fr->keepa_freq = keepa;
+		fr->local_cnt = fr->remote_cnt = 0;
+		fr->keepa_timer.expires = jiffies + HZ;
+		fr->keepa_timer.function = fr_keepalive_timerfun;
+		fr->keepa_timer.data = (unsigned long)dev;
+		ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+		dev->flags &= ~IFF_RUNNING;
+		comx_status(dev, ch->line_status);
+		if(ch->line_status & LINE_UP) {
+			add_timer(&fr->keepa_timer);
+		}
+	}
+}
+
+static void fr_rx(struct device *dev, struct sk_buff *skb) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct device *sdev = dev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	u16 dlci;
+	u8 nlpid;
+
+	if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
+		kfree_skb(skb);
+		return;
+	}
+
+	/* Itt majd ki kell talalni, melyik slave kapja a csomagot */
+	dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
+	if ((nlpid = skb->data[3]) == 0) { // Optional padding 
+		nlpid = skb->data[4];
+		skb_pull(skb, 1);
+	}
+	skb_pull(skb, 4);	/* DLCI and header throw away */
+
+	if (ch->debug_flags & DEBUG_COMX_DLCI) {
+		comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", 
+			dlci, nlpid);
+		comx_debug_skb(dev, skb, "Contents");
+	}
+
+	/* Megkeressuk, kihez tartozik */
+	for (; dir ; dir = dir->next) {
+		if(!S_ISDIR(dir->mode)) {
+			continue;
+		}
+		if ((sdev = dir->data) && (sch = sdev->priv) && 
+		    (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+		    (sfr->master == dev) && (sfr->dlci == dlci)) {
+			skb->dev = sdev;	
+			if (ch->debug_flags & DEBUG_COMX_DLCI) {
+				comx_debug(dev, "Passing it to %s\n",sdev->name);
+			}
+			if (dev != sdev) {
+				sch->stats.rx_packets++;
+				sch->stats.rx_bytes += skb->len;
+			}
+			break;
+		}
+	}
+	switch(nlpid) {
+		case NLPID_IP:
+			skb->protocol = htons(ETH_P_IP);
+			skb->mac.raw = skb->data;
+			comx_rx(sdev, skb);
+			break;
+		case NLPID_Q933_LMI:
+			fr_rx_lmi(dev, skb, dlci, nlpid);
+		default:
+			kfree_skb(skb);
+			break;
+	}
+}
+
+static int fr_tx(struct device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct device *sdev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	int cnt = 1;
+
+	/* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, 
+	   es annal a slave-nel aki eppen kuldott.
+	   Egy helyen akkor all, ha a master kuldott.
+	   Ez megint jo lesz majd, ha utemezni akarunk */
+	   
+	/* This should be fixed, the slave tbusy should be set when 
+	   the masters queue is full and reset when not */
+
+	for (; dir ; dir = dir->next) {
+		if(!S_ISDIR(dir->mode)) {
+		    continue;
+		}
+		if ((sdev = dir->data) && (sch = sdev->priv) && 
+		    (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+		    (sfr->master == dev) && (sdev->tbusy)) {
+			clear_bit(0, &sdev->tbusy);
+//			printk("%s: clearing tbusy\n",sdev->name);
+			cnt++;
+		}
+	}
+
+	clear_bit(0, &dev->tbusy);	// ezt nem talalja meg, mert ez FRAD
+//	printk("%s: clearing tbusy\n", dev->name);
+
+
+	mark_bh(NET_BH);
+	return 0;
+}
+
+static void fr_status(struct device *dev, unsigned short status)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct device *sdev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+
+	if (status & LINE_UP) {
+		if (!fr->keepa_freq) {
+			status |= PROTO_UP;
+		}
+	} else {
+		status &= ~(PROTO_UP | PROTO_LOOP);
+	}
+
+	if (dev == fr->master && fr->keepa_freq) {
+		if (status & LINE_UP) {
+			fr->keepa_timer.expires = jiffies + HZ;
+			add_timer(&fr->keepa_timer);
+			fr->keepalivecnt = MAXALIVECNT + 1;
+			fr->keeploopcnt = 0;
+		} else {
+			del_timer(&fr->keepa_timer);
+		}
+	}
+		
+	/* Itt a status valtozast vegig kell vinni az osszes slave-n */
+	for (; dir ; dir = dir->next) {
+		if(!S_ISDIR(dir->mode)) {
+		    continue;
+		}
+	
+		if ((sdev = dir->data) && (sch = sdev->priv) && 
+		    (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && 
+		    (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+			if(status & LINE_UP) {
+				sdev->tbusy = 0; 
+//				printk("%s: clearing tbusy\n",sdev->name);
+			}
+			comx_status(sdev, status);
+			if(status & (PROTO_UP | PROTO_LOOP)) {
+				dev->flags |= IFF_RUNNING;
+			} else {
+				dev->flags &= ~IFF_RUNNING;
+			}
+		}
+	}
+}
+
+static int fr_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *comxdir = ch->procdir;
+	struct comx_channel *mch;
+
+	if (!(ch->init_status & HW_OPEN)) {
+		return -ENODEV;
+	}
+
+	if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
+	    (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
+		printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
+		return -EINVAL;
+	}
+
+	if (!fr->master) {
+		return -ENODEV;
+	}
+	mch = fr->master->priv;
+	if (fr->master != dev && (!(mch->init_status & LINE_OPEN) 
+	   || (mch->protocol != &fr_master_protocol))) {
+		printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
+			"unable to open %s\n", fr->master->name, dev->name);
+		return -ENODEV;
+	}
+
+	ch->init_status |= LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+	dev->flags &= ~IFF_RUNNING;
+
+	if (fr->master == dev) {
+		if (fr->keepa_freq) {
+			fr->keepa_timer.function = fr_keepalive_timerfun;
+			fr->keepa_timer.data = (unsigned long)dev;
+			add_timer(&fr->keepa_timer);
+		} else {
+			if (ch->line_status & LINE_UP) {
+				ch->line_status |= PROTO_UP;
+				dev->flags |= IFF_RUNNING;
+			}
+		}
+	} else {
+		ch->line_status = mch->line_status;
+		if(fr->master->flags & IFF_RUNNING) {
+			dev->flags |= IFF_RUNNING;
+		}
+	}
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+		   strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+		   strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+			comxdir->mode = S_IFREG | 0444;
+		}
+	}
+//	comx_status(dev, ch->line_status);
+	return 0;
+}
+
+static int fr_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *comxdir = ch->procdir;
+
+	if (fr->master == dev) { // Ha master 
+		struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+		struct device *sdev = dev;
+		struct comx_channel *sch;
+		struct fr_data *sfr;
+
+		if (!(ch->init_status & HW_OPEN)) {
+			return -ENODEV;
+		}
+
+		if (fr->keepa_freq) {
+			del_timer(&fr->keepa_timer);
+		}
+		
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+				continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) && 
+			    (sfr = sch->LINE_privdata) &&
+			    (sfr->master == dev) && 
+			    (sch->init_status & LINE_OPEN)) {
+				dev_close(sdev);
+			}
+		}
+	}
+
+	ch->init_status &= ~LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+	dev->flags &= ~IFF_RUNNING;
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+		    strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+		    strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+			comxdir->mode = S_IFREG | 0444;
+		}
+	}
+
+	return 0;
+}
+
+static int fr_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_channel *sch, *mch;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct fr_data *sfr;
+	struct device *sdev;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+	if (!fr->master) {
+		printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
+		return 0;
+	}
+
+	mch = fr->master->priv;
+
+	/* Ennek majd a slave utemezeskor lesz igazan jelentosege */
+	if (ch->debug_flags & DEBUG_COMX_DLCI) {
+		comx_debug_skb(dev, skb, "Sending frame");
+	}
+
+	if (dev != fr->master) {
+		struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+		newskb->dev=fr->master;
+		dev_queue_xmit(newskb);
+		dev_kfree_skb(skb);
+		ch->stats.tx_packets++;
+		ch->stats.tx_bytes += skb->len;
+	} else {
+		set_bit(0, &dev->tbusy);
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+			    continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+			    (sfr->master == dev) && (sdev->tbusy)) {
+				set_bit(0, &sdev->tbusy);
+//				printk("%s: clearing tbusy\n",sdev->name);
+			}
+		}
+		 	
+//		printk("%s: set tbusy\n", dev->name);
+		switch(mch->HW_send_packet(dev, skb)) {
+			case FRAME_QUEUED:
+				clear_bit(0, &dev->tbusy);
+//				printk("%s: clear tbusy\n", dev->name);
+				break;
+			case FRAME_ACCEPTED:
+			case FRAME_DROPPED:
+				break;
+			case FRAME_ERROR:
+				printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
+					dev->name, skb->len);
+				break;
+		}
+	}
+	return 0;
+}
+
+static int fr_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+
+	skb_push(skb, dev->hard_header_len);	  
+	/* Put in DLCI */
+	skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
+	skb->data[1] = (fr->dlci & 15) << 4 | 1;	// EA bit 1
+	skb->data[2] = FRAD_UI;
+	skb->data[3] = NLPID_IP;
+
+	return dev->hard_header_len;  
+}
+
+static int fr_statistics(struct device *dev, char *page) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	int len = 0;
+
+	if (fr->master == dev) {
+		struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+		struct device *sdev;
+		struct comx_channel *sch;
+		struct fr_data *sfr;
+		int slaves = 0;
+
+		len += sprintf(page + len, 
+			"This is a Frame Relay master device\nSlaves: ");
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+				continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) &&
+			    (sfr = sch->LINE_privdata) && 
+			    (sfr->master == dev) && (sdev != dev)) {
+				slaves++;
+				len += sprintf(page + len, "%s ", sdev->name);
+			}
+		}
+		len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
+		if (fr->keepa_freq) {
+			len += sprintf(page + len, "Line keepalive (value %d) "
+				"status %s [%d]\n", fr->keepa_freq, 
+				ch->line_status & PROTO_LOOP ? "LOOP" :
+				ch->line_status & PROTO_UP ? "UP" : "DOWN", 
+				fr->keepalivecnt);
+		} else {
+			len += sprintf(page + len, "Line keepalive protocol "
+				"is not set\n");
+		}
+	} else {		// if slave
+		len += sprintf(page + len, 
+			"This is a Frame Relay slave device, master: %s\n",
+			fr->master ? fr->master->name : "(not set)");
+	}
+	return len;
+}
+
+static int fr_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct device *dev = file->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = NULL;
+	int len = 0;
+
+	if (ch) {
+		fr = ch->LINE_privdata;
+	}
+
+	if (strcmp(file->name, FILENAME_DLCI) == 0) {
+		len = sprintf(page, "%04d\n", fr->dlci);
+	} else if (strcmp(file->name, FILENAME_MASTER) == 0) {
+		len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
+			"(none)");
+	} else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
+		len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) 
+			: sprintf(page, "off\n");
+	} else {
+		printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) *eof = 1;
+	return ( min(count, len - off) );
+}
+
+static int fr_write_proc(struct file *file, const char *buffer, 
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct device *dev = entry->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = NULL; 
+	char *page;
+
+	if (ch) {
+		fr = ch->LINE_privdata;
+	}
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count);
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_DLCI) == 0) {
+		u16 dlci_new = simple_strtoul(page, NULL, 10);
+
+		if (dlci_new > 1023) {
+			printk(KERN_ERR "Invalid DLCI value\n");
+		}
+		else fr->dlci = dlci_new;
+	} else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
+		struct device *new_master = dev_get(page);
+
+		if (new_master && new_master->type == ARPHRD_FRAD) {
+			struct comx_channel *sch = new_master->priv;
+			struct fr_data *sfr = sch->LINE_privdata;
+
+			if (sfr && sfr->master == new_master) {
+				fr->master = new_master;
+				/* Megorokli a master statuszat */
+				ch->line_status = sch->line_status;
+			}
+		}
+	} else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
+		int keepa_new = -1;
+
+		if (strcmp(page, KEEPALIVE_OFF) == 0) {
+			keepa_new = 0;
+		} else {
+			keepa_new = simple_strtoul(page, NULL, 10);
+		}
+
+		if (keepa_new < 0 || keepa_new > 100) {
+			printk(KERN_ERR "invalid keepalive\n");
+		} else {
+			if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
+				fr_set_keepalive(dev, 0);
+			}
+			if (keepa_new) {
+				fr_set_keepalive(dev, keepa_new);
+			}
+		}
+	} else {
+		printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", 
+			entry->name);
+		return -EBADF;
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static int fr_exit(struct device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct device *sdev = dev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+	/* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
+	if (fr->master && fr->master == dev) {
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+				continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) && 
+			    (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+				dev_close(sdev);
+				sfr->master = NULL;
+			}
+		}
+	}
+	dev->flags		= 0;
+	dev->type		= 0;
+	dev->mtu		= 0;
+	dev->hard_header_len    = 0;
+
+	ch->LINE_rx	= NULL;
+	ch->LINE_tx	= NULL;
+	ch->LINE_status = NULL;
+	ch->LINE_open	= NULL;
+	ch->LINE_close	= NULL;
+	ch->LINE_xmit	= NULL;
+	ch->LINE_header	= NULL;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = NULL;
+
+	ch->LINE_status = 0;
+
+	if (fr->master != dev) { // if not master, remove dlci
+		remove_proc_entry(FILENAME_DLCI, ch->procdir);
+		remove_proc_entry(FILENAME_MASTER, ch->procdir);
+	} else {
+		if (fr->keepa_freq) {
+			fr_set_keepalive(dev, 0);
+		}
+		remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
+		remove_proc_entry(FILENAME_DLCI, ch->procdir);
+	}
+
+	kfree(fr);
+	ch->LINE_privdata = NULL;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int fr_master_init(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr;
+	struct proc_dir_entry *new_file;
+
+	if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
+	    GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(fr, 0, sizeof(struct fr_data));
+	fr->master = dev;	// this means master
+	fr->dlci = 0;		// let's say default
+
+	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+	dev->type	= ARPHRD_FRAD;
+	dev->mtu	= 1500;
+	dev->hard_header_len    = 4;		
+	dev->addr_len	= 0;
+
+	ch->LINE_rx	= fr_rx;
+	ch->LINE_tx	= fr_tx;
+	ch->LINE_status = fr_status;
+	ch->LINE_open	= fr_open;
+	ch->LINE_close	= fr_close;
+	ch->LINE_xmit	= fr_xmit;
+	ch->LINE_header	= fr_header;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = fr_statistics;
+
+	if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -ENOMEM;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 5;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -ENOMEM;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 4;
+	new_file->nlink = 1;
+
+	fr_set_keepalive(dev, 0);
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int fr_slave_init(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr;
+	struct proc_dir_entry *new_file;
+
+	if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
+	    GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(fr, 0, sizeof(struct fr_data));
+
+	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+	dev->type	= ARPHRD_DLCI;
+	dev->mtu	= 1500;
+	dev->hard_header_len    = 4;		
+	dev->addr_len	= 0;
+
+	ch->LINE_rx	= fr_rx;
+	ch->LINE_tx	= fr_tx;
+	ch->LINE_status = fr_status;
+	ch->LINE_open	= fr_open;
+	ch->LINE_close	= fr_close;
+	ch->LINE_xmit	= fr_xmit;
+	ch->LINE_header	= fr_header;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = fr_statistics;
+
+	if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -ENOMEM;
+	}
+	
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 5;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->size = 10;
+	new_file->nlink = 1;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->init_status |= HW_OPEN;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->init_status &= ~HW_OPEN;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_txe(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+
+	if (!fr->master) {
+		return 0;
+	}
+
+	ch = fr->master->priv;
+	fr = ch->LINE_privdata;
+	return ch->HW_txe(fr->master);
+}
+
+static int dlci_statistics(struct device *dev, char *page) 
+{
+	return 0;
+}
+
+static int dlci_init(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->HW_open = dlci_open;
+	ch->HW_close = dlci_close;
+	ch->HW_txe = dlci_txe;
+	ch->HW_statistics = dlci_statistics;
+
+	/* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_exit(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->HW_open = NULL;
+	ch->HW_close = NULL;
+	ch->HW_txe = NULL;
+	ch->HW_statistics = NULL;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_dump(struct device *dev)
+{
+	printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
+	return -1;
+}
+
+static struct comx_protocol fr_master_protocol = {
+	"frad", 
+	VERSION,
+	ARPHRD_FRAD, 
+	fr_master_init, 
+	fr_exit, 
+	NULL 
+};
+
+static struct comx_protocol fr_slave_protocol = {
+	"ietf-ip", 
+	VERSION,
+	ARPHRD_DLCI, 
+	fr_slave_init, 
+	fr_exit, 
+	NULL 
+};
+
+static struct comx_hardware fr_dlci = { 
+	"dlci", 
+	VERSION,
+	dlci_init, 
+	dlci_exit, 
+	dlci_dump, 
+	NULL 
+};
+
+#ifdef MODULE
+#define comx_proto_fr_init init_module
+#endif
+
+__initfunc(int comx_proto_fr_init(void))
+{
+	int ret; 
+
+	if ((ret = comx_register_hardware(&fr_dlci))) {
+		return ret;
+	}
+	if ((ret = comx_register_protocol(&fr_master_protocol))) {
+		return ret;
+	}
+	return comx_register_protocol(&fr_slave_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_hardware(fr_dlci.name);
+	comx_unregister_protocol(fr_master_protocol.name);
+	comx_unregister_protocol(fr_slave_protocol.name);
+}
+#endif /* MODULE */
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-proto-lapb.c linux.14p6/drivers/net/comx-proto-lapb.c
--- linux.vanilla/drivers/net/comx-proto-lapb.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx-proto-lapb.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,553 @@
+/*
+ * LAPB protocol module for the COMX driver 
+ * for Linux kernel 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/14):
+ *		- cleaned up the source code a bit
+ *		- ported back to kernel, now works as non-module
+ * 
+ */
+
+#define VERSION "0.80"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/lapb.h>
+#include <linux/init.h>
+
+#include	"comx.h"
+#include	"comxhw.h"
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir);
+
+static void comxlapb_rx(struct device *dev, struct sk_buff *skb) 
+{
+	if (!dev || !dev->priv) {
+		dev_kfree_skb(skb);
+	} else {
+		lapb_data_received(dev->priv, skb);
+	}
+}
+
+static int comxlapb_tx(struct device *dev) 
+{
+	clear_bit(0, &dev->tbusy);
+	mark_bh(NET_BH);
+	return 0;
+}
+
+static int comxlapb_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+	return dev->hard_header_len;  
+}
+
+static void comxlapb_status(struct device *dev, unsigned short status)
+{
+	struct comx_channel *ch;
+
+	if (!dev || !(ch = dev->priv)) {
+		return;
+	}
+	if (status & LINE_UP) {
+		clear_bit(0, &dev->tbusy);
+	}
+	comx_status(dev, status);
+}
+
+static int comxlapb_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	int err = 0;
+
+	if (!(ch->init_status & HW_OPEN)) {
+		return -ENODEV;
+	}
+
+	err = lapb_connect_request(ch);
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: lapb opened, error code: %d\n", 
+			dev->name, err);
+	}
+
+	if (!err) {
+		ch->init_status |= LINE_OPEN;
+		MOD_INC_USE_COUNT;
+	}
+	return err;
+}
+
+static int comxlapb_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (!(ch->init_status & HW_OPEN)) {
+		return -ENODEV;
+	}
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: lapb closed\n", dev->name);
+	}
+
+	lapb_disconnect_request(ch);
+
+	ch->init_status &= ~LINE_OPEN;
+	ch->line_status &= ~PROTO_UP;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int comxlapb_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct sk_buff *skb2;
+
+	if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
+		return -ENODEV;
+	}
+
+	if (dev->type == ARPHRD_X25) { // first byte tells what to do 
+		switch(skb->data[0]) {
+			case 0x00:	
+				break;	// transmit
+			case 0x01:	
+				lapb_connect_request(ch);
+				kfree_skb(skb);
+				return 0;
+			case 0x02:	
+				lapb_disconnect_request(ch);
+			default:
+				kfree_skb(skb);
+				return 0;
+		}
+		skb_pull(skb,1);
+	}
+
+	if (test_and_set_bit(0, &dev->tbusy)) {
+		ch->stats.tx_errors++;
+		return 1;
+	}
+
+	if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+		lapb_data_request(ch, skb2);
+	}
+
+	return FRAME_ACCEPTED;
+}
+
+static int comxlapb_statistics(struct device *dev, char *page) 
+{
+	struct lapb_parms_struct parms;
+	int len = 0;
+
+	len += sprintf(page + len, "Line status: ");
+	if (lapb_getparms(dev->priv, &parms) != LAPB_OK) {
+		len += sprintf(page + len, "not initialized\n");
+		return len;
+	}
+	len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
+		"window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE", 
+		parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
+		parms.t1timer, parms.t1, parms.t2timer, parms.t2, 
+		parms.n2count, parms.n2, parms.window);
+
+	return len;
+}
+
+static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct device *dev = file->parent->data;
+	struct lapb_parms_struct parms;
+	int len = 0;
+
+	if (lapb_getparms(dev->priv, &parms)) {
+		return -ENODEV;
+	}
+
+	if (strcmp(file->name, FILENAME_T1) == 0) {
+		len += sprintf(page + len, "%02u / %02u\n", 
+			parms.t1timer, parms.t1);
+	} else if (strcmp(file->name, FILENAME_T2) == 0) {
+		len += sprintf(page + len, "%02u / %02u\n", 
+			parms.t2timer, parms.t2);
+	} else if (strcmp(file->name, FILENAME_N2) == 0) {
+		len += sprintf(page + len, "%02u / %02u\n", 
+			parms.n2count, parms.n2);
+	} else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
+		len += sprintf(page + len, "%u\n", parms.window);
+	} else if (strcmp(file->name, FILENAME_MODE) == 0) {
+		len += sprintf(page + len, "%s, %s\n", 
+			parms.mode & LAPB_DCE ? "DCE" : "DTE",
+			parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
+	} else {
+		printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return ( min(count, len - off) );
+}
+
+static int comxlapb_write_proc(struct file *file, const char *buffer, 
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct device *dev = entry->parent->data;
+	struct lapb_parms_struct parms;
+	unsigned long parm;
+	char *page;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (lapb_getparms(dev->priv, &parms)) {
+		return -ENODEV;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count);
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_T1) == 0) {
+		parm=simple_strtoul(page,NULL,10);
+		if (parm > 0 && parm < 100) {
+			parms.t1=parm;
+			lapb_setparms(dev->priv, &parms);
+		}
+	} else if (strcmp(entry->name, FILENAME_T2) == 0) {
+		parm=simple_strtoul(page, NULL, 10);
+		if (parm > 0 && parm < 100) {
+			parms.t2=parm;
+			lapb_setparms(dev->priv, &parms);
+		}
+	} else if (strcmp(entry->name, FILENAME_N2) == 0) {
+		parm=simple_strtoul(page, NULL, 10);
+		if (parm > 0 && parm < 100) {
+			parms.n2=parm;
+			lapb_setparms(dev->priv, &parms);
+		}
+	} else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
+		parms.window = simple_strtoul(page, NULL, 10);
+		lapb_setparms(dev->priv, &parms);
+	} else if (strcmp(entry->name, FILENAME_MODE) == 0) {
+		if (comx_strcasecmp(page, "dte") == 0) {
+			parms.mode &= ~(LAPB_DCE | LAPB_DTE); 
+			parms.mode |= LAPB_DTE;
+		} else if (comx_strcasecmp(page, "dce") == 0) {
+			parms.mode &= ~(LAPB_DTE | LAPB_DCE); 
+			parms.mode |= LAPB_DCE;
+		} else if (comx_strcasecmp(page, "std") == 0 || 
+		    comx_strcasecmp(page, "standard") == 0) {
+			parms.mode &= ~LAPB_EXTENDED; 
+			parms.mode |= LAPB_STANDARD;
+		} else if (comx_strcasecmp(page, "ext") == 0 || 
+		    comx_strcasecmp(page, "extended") == 0) {
+			parms.mode &= ~LAPB_STANDARD; 
+			parms.mode |= LAPB_EXTENDED;
+		}
+		lapb_setparms(dev->priv, &parms);
+	} else {
+		printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n", 
+			entry->name);
+		return -EBADF;
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static void comxlapb_connected(void *token, int reason)
+{
+	struct comx_channel *ch = token; 
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(ch->dev, "%s: lapb connected, reason: %d\n", 
+			ch->dev->name, reason);
+	}
+
+	if (ch->dev->type == ARPHRD_X25) {
+		unsigned char *p;
+		struct sk_buff *skb;
+
+		if ((skb = dev_alloc_skb(1)) == NULL) {
+			printk(KERN_ERR "comxlapb: out of memory!\n");
+			return;
+		}
+		p = skb_put(skb,1);
+		*p = 0x01;		// link established
+		skb->dev = ch->dev;
+		skb->protocol = htons(ETH_P_X25);
+		skb->mac.raw = skb->data;
+		skb->pkt_type = PACKET_HOST;
+
+		netif_rx(skb);
+	}
+
+	for (; comxdir; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+			comxdir->mode = S_IFREG | 0444;
+		}
+	}
+
+
+	ch->line_status |= PROTO_UP;
+	comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_disconnected(void *token, int reason)
+{
+	struct comx_channel *ch = token; 
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n", 
+			ch->dev->name, reason);
+	}
+
+	if (ch->dev->type == ARPHRD_X25) {
+		unsigned char *p;
+		struct sk_buff *skb;
+
+		if ((skb = dev_alloc_skb(1)) == NULL) {
+			printk(KERN_ERR "comxlapb: out of memory!\n");
+			return;
+		}
+		p = skb_put(skb,1);
+		*p = 0x02;		// link disconnected
+		skb->dev = ch->dev;
+		skb->protocol = htons(ETH_P_X25);
+		skb->mac.raw = skb->data;
+		skb->pkt_type = PACKET_HOST;
+
+		netif_rx(skb);
+	}
+
+	for (; comxdir; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+			comxdir->mode = S_IFREG | 0644;
+		}
+	}
+	
+	ch->line_status &= ~PROTO_UP;
+	comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_data_indication(void *token, struct sk_buff *skb)
+{
+	struct comx_channel *ch = token; 
+
+	if (ch->dev->type == ARPHRD_X25) {
+		skb_push(skb, 1);
+		skb->data[0] = 0;	// indicate data for X25
+		skb->protocol = htons(ETH_P_X25);
+	} else {
+		skb->protocol = htons(ETH_P_IP);
+	}
+
+	skb->dev = ch->dev;
+	skb->mac.raw = skb->data;
+	comx_rx(ch->dev, skb);
+}
+
+static void comxlapb_data_transmit(void *token, struct sk_buff *skb)
+{
+	struct comx_channel *ch = token; 
+
+	if (ch->HW_send_packet) {
+		ch->HW_send_packet(ch->dev, skb);
+	}
+}
+
+static int comxlapb_exit(struct device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+
+	dev->flags		= 0;
+	dev->type		= 0;
+	dev->mtu		= 0;
+	dev->hard_header_len    = 0;
+
+	ch->LINE_rx	= NULL;
+	ch->LINE_tx	= NULL;
+	ch->LINE_status = NULL;
+	ch->LINE_open	= NULL;
+	ch->LINE_close	= NULL;
+	ch->LINE_xmit	= NULL;
+	ch->LINE_header	= NULL;
+	ch->LINE_statistics = NULL;
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: unregistering lapb\n", dev->name);
+	}
+	lapb_unregister(dev->priv);
+
+	remove_proc_entry(FILENAME_T1, ch->procdir);
+	remove_proc_entry(FILENAME_T2, ch->procdir);
+	remove_proc_entry(FILENAME_N2, ch->procdir);
+	remove_proc_entry(FILENAME_MODE, ch->procdir);
+	remove_proc_entry(FILENAME_WINDOW, ch->procdir);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int comxlapb_init(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct lapb_register_struct lapbreg;
+
+	dev->mtu		= 1500;
+	dev->hard_header_len    = 4;
+	dev->addr_len		= 0;
+
+	ch->LINE_rx	= comxlapb_rx;
+	ch->LINE_tx	= comxlapb_tx;
+	ch->LINE_status = comxlapb_status;
+	ch->LINE_open	= comxlapb_open;
+	ch->LINE_close	= comxlapb_close;
+	ch->LINE_xmit	= comxlapb_xmit;
+	ch->LINE_header	= comxlapb_header;
+	ch->LINE_statistics = comxlapb_statistics;
+
+	lapbreg.connect_confirmation = comxlapb_connected;
+	lapbreg.connect_indication = comxlapb_connected;
+	lapbreg.disconnect_confirmation = comxlapb_disconnected;
+	lapbreg.disconnect_indication = comxlapb_disconnected;
+	lapbreg.data_indication = comxlapb_data_indication;
+	lapbreg.data_transmit = comxlapb_data_transmit;
+	if (lapb_register(dev->priv, &lapbreg)) {
+		return -ENOMEM;
+	}
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: lapb registered\n", dev->name);
+	}
+
+	if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
+		return -ENOMEM;
+	}
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int comxlapb_init_lapb(struct device *dev) 
+{
+	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+	dev->type	= ARPHRD_LAPB;
+
+	return(comxlapb_init(dev));
+}
+
+static int comxlapb_init_x25(struct device *dev)
+{
+	dev->flags		= IFF_NOARP;
+	dev->type		= ARPHRD_X25;
+
+	return(comxlapb_init(dev));
+}
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir)
+{
+	struct proc_dir_entry *new_file;
+
+	if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+		new_file->data = (void *)new_file;
+		new_file->read_proc = &comxlapb_read_proc;
+		new_file->write_proc = &comxlapb_write_proc;
+		new_file->ops = &comx_normal_inode_ops;
+		new_file->size = size;
+		new_file->nlink = 1;
+	}
+	return(new_file);
+}
+
+static struct comx_protocol comxlapb_protocol = {
+	"lapb", 
+	VERSION,
+	ARPHRD_LAPB, 
+	comxlapb_init_lapb, 
+	comxlapb_exit, 
+	NULL 
+};
+
+static struct comx_protocol comx25_protocol = {
+	"x25", 
+	VERSION,
+	ARPHRD_X25, 
+	comxlapb_init_x25, 
+	comxlapb_exit, 
+	NULL 
+};
+
+#ifdef MODULE
+#define comx_proto_lapb_init init_module
+#endif
+
+__initfunc(int comx_proto_lapb_init(void))
+{
+	int ret;
+
+	if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
+		return ret;
+	}
+	return comx_register_protocol(&comx25_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_protocol(comxlapb_protocol.name);
+	comx_unregister_protocol(comx25_protocol.name);
+}
+#endif /* MODULE */
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-proto-ppp.c linux.14p6/drivers/net/comx-proto-ppp.c
--- linux.vanilla/drivers/net/comx-proto-ppp.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx-proto-ppp.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,265 @@
+/*
+ * Synchronous PPP / Cisco-HDLC driver for the COMX boards
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Version 0.10 (99/06/10):
+ *		- written the first code :)
+ *
+ * Version 0.20 (99/06/16):
+ *		- added hdlc protocol 
+ *		- protocol up is IFF_RUNNING
+ *
+ * Version 0.21 (99/07/15):
+ *		- some small fixes with the line status
+ *
+ * Version 0.22 (99/08/05):
+ *		- don't test IFF_RUNNING but the pp_link_state of the sppp
+ *
+ */
+
+#define VERSION "0.22"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include	"syncppp.h"
+#include	"comx.h"
+
+MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
+
+static struct comx_protocol syncppp_protocol;
+static struct comx_protocol hdlc_protocol;
+
+struct syncppp_data {
+	struct timer_list status_timer;
+};
+
+static void syncppp_status_timerfun(unsigned long d) {
+	struct device *dev=(struct device *)d;
+	struct comx_channel *ch=dev->priv;
+	struct syncppp_data *spch=ch->LINE_privdata;
+	struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+        
+	if(!(ch->line_status & PROTO_UP) && 
+	    (sp->pp_link_state==SPPP_LINK_UP)) {
+    		comx_status(dev, ch->line_status | PROTO_UP);
+	}
+	if((ch->line_status & PROTO_UP) &&
+	    (sp->pp_link_state==SPPP_LINK_DOWN)) {
+	    	comx_status(dev, ch->line_status & ~PROTO_UP);
+	}
+	mod_timer(&spch->status_timer,jiffies + HZ*3);
+}
+
+static int syncppp_tx(struct device *dev) 
+{
+	clear_bit(0, &dev->tbusy);
+	mark_bh(NET_BH);
+	return 0;
+}
+
+static void syncppp_status(struct device *dev, unsigned short status)
+{
+	status &= ~(PROTO_UP | PROTO_LOOP);
+	if(status & LINE_UP) {
+		dev->tbusy = 0; /* Just to be sure */
+		sppp_open(dev);
+	} else 	{
+		/* Line went down */
+		sppp_close(dev);
+	}
+	comx_status(dev, status);
+}
+
+static int syncppp_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct syncppp_data *spch = ch->LINE_privdata;
+
+	if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+
+	ch->init_status |= LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+	if(ch->line_status & LINE_UP) {
+		sppp_open(dev);
+	}
+
+	init_timer(&spch->status_timer);
+	spch->status_timer.function=syncppp_status_timerfun;
+	spch->status_timer.data=(unsigned long)dev;
+	spch->status_timer.expires=jiffies + HZ*3;
+	add_timer(&spch->status_timer);
+	
+	return 0;
+}
+
+static int syncppp_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct syncppp_data *spch = ch->LINE_privdata;
+
+	if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+	del_timer(&spch->status_timer);
+	
+	sppp_close(dev);
+
+	ch->init_status &= ~LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+	return 0;
+}
+
+static int syncppp_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (test_and_set_bit(0, &dev->tbusy)) {
+		ch->stats.tx_errors++;
+        	return 0;
+	}	
+	switch(ch->HW_send_packet(dev, skb)) {
+		case FRAME_QUEUED:
+			clear_bit(0, &dev->tbusy);
+			break;
+		case FRAME_ACCEPTED:
+		case FRAME_DROPPED:
+			break;
+		case FRAME_ERROR:
+			printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
+				dev->name, skb->len);
+		break;
+	}
+	return 0;
+}
+
+
+static int syncppp_statistics(struct device *dev, char *page) 
+{
+	int len = 0;
+
+	len += sprintf(page + len, " ");
+	return len;
+}
+
+
+static int syncppp_exit(struct device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+
+	sppp_detach(dev);
+
+	dev->flags = 0;
+	dev->type = 0;
+	dev->mtu = 0;
+
+	ch->LINE_rx = NULL;
+	ch->LINE_tx = NULL;
+	ch->LINE_status = NULL;
+	ch->LINE_open = NULL;
+	ch->LINE_close = NULL;
+	ch->LINE_xmit = NULL;
+	ch->LINE_header	= NULL;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = NULL;
+
+	kfree(ch->LINE_privdata);
+	ch->LINE_privdata = NULL;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int syncppp_init(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct ppp_device *pppdev = (struct ppp_device*)dev;
+
+	ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
+
+	sppp_attach(pppdev);
+
+	if(ch->protocol == &hdlc_protocol) {
+		pppdev->sppp.pp_flags |= PP_CISCO;
+		dev->type = ARPHRD_HDLC;
+	} else {
+		pppdev->sppp.pp_flags &= ~PP_CISCO;
+		dev->type = ARPHRD_PPP;
+	}
+
+	ch->LINE_rx = sppp_input;
+	ch->LINE_tx = syncppp_tx;
+	ch->LINE_status = syncppp_status;
+	ch->LINE_open = syncppp_open;
+	ch->LINE_close = syncppp_close;
+	ch->LINE_xmit = syncppp_xmit;
+	ch->LINE_header	= NULL;
+	ch->LINE_statistics = syncppp_statistics;
+
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static struct comx_protocol syncppp_protocol = {
+	"ppp", 
+	VERSION,
+	ARPHRD_PPP, 
+	syncppp_init, 
+	syncppp_exit, 
+	NULL 
+};
+
+static struct comx_protocol hdlc_protocol = {
+	"hdlc", 
+	VERSION,
+	ARPHRD_PPP, 
+	syncppp_init, 
+	syncppp_exit, 
+	NULL 
+};
+
+
+#ifdef MODULE
+#define comx_proto_ppp_init init_module
+#endif
+
+__initfunc(int comx_proto_ppp_init(void))
+{
+	int ret;
+
+	if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
+		return ret;
+	}
+	return comx_register_protocol(&syncppp_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_protocol(syncppp_protocol.name);
+	comx_unregister_protocol(hdlc_protocol.name);
+}
+#endif /* MODULE */
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx.c linux.14p6/drivers/net/comx.c
--- linux.vanilla/drivers/net/comx.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx.c	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,1230 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ * 
+ * for Linux kernel 2.2.X
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ *		- clean up source code (playing a bit of indent)
+ *		- port back to kernel, add support for non-module versions
+ *		- add support for board resets when channel protocol is down
+ *		- reset the device structure after protocol exit
+ *		  the syncppp driver needs it
+ *		- add support for /proc/comx/protocols and 
+ *		  /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ *		- comment out the board reset support code, the locomx
+ *		  driver seems not buggy now
+ *		- printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ *		- Handle stats correctly if the lowlevel driver is
+ *		  is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ *		- reset line_status when interface is down
+ *
+ */
+
+#define VERSION "0.82"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops; 
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops;  // with open/relase
+static struct file_operations comx_debug_file_ops;  // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+	NULL,			/* revalidate */
+	NULL,			/* d_hash */
+	NULL, 			/* d_compare */
+	&comx_delete_dentry	/* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+	0, 4, "comx",
+	S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+	0, &comx_root_inode_ops,
+	NULL, comx_fill_inode,
+	NULL, &proc_root, NULL 
+};
+
+struct comx_debugflags_struct	comx_debugflags[] = {
+	{ "comx_rx",		DEBUG_COMX_RX		},
+	{ "comx_tx", 		DEBUG_COMX_TX		},
+	{ "hw_tx",		DEBUG_HW_TX		},
+	{ "hw_rx", 		DEBUG_HW_RX		},
+	{ "hdlc_keepalive",	DEBUG_HDLC_KEEPALIVE	},
+	{ "comxppp",		DEBUG_COMX_PPP		},
+	{ "comxlapb",		DEBUG_COMX_LAPB		},
+	{ "dlci",		DEBUG_COMX_DLCI		},
+	{ NULL,			0			} 
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct device *dev, char *fmt, ...)
+{
+	struct comx_channel *ch = dev->priv;
+	char *page,*str;
+	va_list args;
+	int len;
+
+	if (!ch->debug_area) return 0;
+
+	if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+	va_start(args, fmt);
+	len = vsprintf(str = page, fmt, args);
+	va_end(args);
+
+	if (len >= PAGE_SIZE) {
+		printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+		free_page((unsigned long)page);
+		return -EINVAL;
+	}
+
+	while (len) {
+		int to_copy;
+		int free = (ch->debug_start - ch->debug_end + ch->debug_size) 
+			% ch->debug_size;
+
+		to_copy = min( free ? free : ch->debug_size, 
+			min (ch->debug_size - ch->debug_end, len) );
+		memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+		str += to_copy;
+		len -= to_copy;
+		ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+		if (ch->debug_start == ch->debug_end) // Full ? push start away
+			ch->debug_start = (ch->debug_start + len + 1) % 
+					ch->debug_size;
+		ch->debug_file->size = (ch->debug_end - ch->debug_start +
+					ch->debug_size) % ch->debug_size;
+	} 
+
+	free_page((unsigned long)page);
+	return 0;
+}
+
+int comx_debug_skb(struct device *dev, struct sk_buff *skb, char *msg)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (!ch->debug_area) return 0;
+	if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+	if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+	return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct device *dev, unsigned char *bytes, int len, 
+		char *msg)
+{
+	int pos = 0;
+	struct comx_channel *ch = dev->priv;
+
+	if (!ch->debug_area) return 0;
+
+	comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+	while (pos != len) {
+		char line[80];
+		int i = 0;
+
+		memset(line, 0, 80);
+		sprintf(line,"%04d ", pos);
+		do {
+			sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+			sprintf(line + 60 + (pos % 16), "%c", 
+				isprint(bytes[pos]) ? bytes[pos] : '.');
+			pos++;
+		} while (pos != len && pos % 16);
+
+		while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+		line[77] = '\n';
+		line[78] = 0;
+	
+		comx_debug(dev, "%s", line);
+	}
+	comx_debug(dev, "\n");
+	return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+	struct device *dev = (struct device *)d;
+	struct comx_channel *ch = dev->priv;
+
+	ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+	ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] = 
+		ch->current_stats->tx_bytes;
+
+	ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+	mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{ 
+	struct device *dev = (struct device *)d;
+	struct comx_channel *ch = dev->priv;
+
+	if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+		if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+			ch->HW_reset(dev);
+		}
+	}
+
+	mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif                                            
+
+static int comx_open(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+	int ret=0;
+
+	if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+	if ((ret = ch->HW_open(dev))) return ret;
+	if ((ret = ch->LINE_open(dev))) { 
+		ch->HW_close(dev); 
+		return ret; 
+	};
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+		   strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+			comxdir->mode = S_IFREG | 0444;
+	}
+
+#if 0
+	ch->reset_pending = 1;
+	ch->reset_timeout = 30;
+	ch->reset_timer.function = comx_reset_timerfun;
+	ch->reset_timer.data = (unsigned long)dev;
+	ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+	add_timer(&ch->reset_timer);
+#endif
+
+	return 0;
+}
+
+static int comx_close(struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+	int ret = -ENODEV;
+
+	if (test_and_clear_bit(0, &ch->lineup_pending)) {
+		del_timer(&ch->lineup_timer);
+	}
+
+#if 0	
+	del_timer(&ch->reset_timer);
+#endif
+
+	if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+		ret = ch->LINE_close(dev);
+	}
+
+	if (ret) return ret;
+
+	if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+		ret = ch->HW_close(dev);
+	}
+	
+	ch->line_status=0;
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+		    strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+			comxdir->mode = S_IFREG | 0644;
+	}
+
+	return ret;
+}
+
+void comx_status(struct device *dev, int status)
+{
+	struct comx_channel *ch = dev->priv;
+
+#if 0
+	if(status & (PROTO_UP | PROTO_LOOP)) {
+		clear_bit(0,&ch->reset_pending);
+	}
+#endif
+
+	if (dev->flags & IFF_UP) {
+		printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+		    dev->name, status & LINE_UP ? "UP" : "DOWN", 
+		    status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ? 
+		    "UP" : "DOWN");
+	}
+	
+	ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	int rc;
+
+	if (skb->len > dev->mtu + dev->hard_header_len) {
+		printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+		(int)skb->len, dev->mtu);
+	}
+	
+	if (ch->debug_flags & DEBUG_COMX_TX) {
+		comx_debug_skb(dev, skb, "comx_xmit skb");
+	}
+	
+	rc=ch->LINE_xmit(skb, dev);
+//	if (!rc) dev_kfree_skb(skb);
+
+	return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->LINE_header) {
+		return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+	} else {
+		return 0;
+	}
+}
+
+static int comx_rebuild_header(struct sk_buff *skb) 
+{
+	struct device *dev = skb->dev;
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->LINE_rebuild_header) {
+		return(ch->LINE_rebuild_header(skb));
+	} else {
+		return 0;
+	}
+}
+
+int comx_rx(struct device *dev, struct sk_buff *skb)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->debug_flags & DEBUG_COMX_RX) {
+		comx_debug_skb(dev, skb, "comx_rx skb");
+	}
+	if (skb) {
+		netif_rx(skb);
+	}
+	return 0;
+}
+
+static struct net_device_stats *comx_stats(struct device *dev)
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+	return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+	struct device *dev = (struct device *)d;
+	struct comx_channel *ch = dev->priv;
+
+	del_timer(&ch->lineup_timer);
+	clear_bit(0, &ch->lineup_pending);
+
+	if (ch->LINE_status) {
+		ch->LINE_status(dev, ch->line_status |= LINE_UP);
+	}
+}
+
+#define LOADAVG(avg, off) (int) \
+	((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+	% ch->loadavg_size + off] -  ch->avg_bytes[(ch->loadavg_counter - 1 \
+		- ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+		% ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct device *dev, char *page)
+{
+	struct comx_channel *ch = dev->priv;
+	int len = 0;
+	int tmp;
+	int i = 0;
+	char tmpstr[20];
+	int tmpstrlen = 0;
+
+	len += sprintf(page + len, "Interface administrative status is %s, "
+		"modem status is %s, protocol is %s\n", 
+		dev->flags & IFF_UP ? "UP" : "DOWN",
+		ch->line_status & LINE_UP ? "UP" : "DOWN",
+		ch->line_status & PROTO_LOOP ? "LOOP" : 
+		ch->line_status & PROTO_UP ? "UP" : "DOWN");
+	len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+		"is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ? 
+		ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+	len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+		LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+	tmpstr[0] = 0;
+	for (i=0; i != 3; i++) {
+		char tf;
+
+		tf = ch->loadavg[i] % 60 == 0 && 
+			ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+		tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s", 
+			ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf, 
+			i == 2 ? ")\n" : "/");
+	}
+	len += sprintf(page + len, 
+		"%s              (output): %d / %d / %d bits/s (%s", tmpstr, 
+		LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size), 
+		LOADAVG(2, ch->loadavg_size), tmpstr);
+
+	len += sprintf(page + len, "Debug flags: ");
+	tmp = len; i = 0;
+	while (comx_debugflags[i].name) {
+		if (ch->debug_flags & comx_debugflags[i].value) 
+			len += sprintf(page + len, "%s ", 
+				comx_debugflags[i].name);
+		i++;
+	}
+	len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+	len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+		"aborts: %lu\n           buffer overrun: %lu, pbuffer overrun: %lu\n"
+		"TX errors: underrun: %lu\n",
+		ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors, 
+		ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors, 
+		ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+		ch->current_stats->tx_fifo_errors);
+
+	if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+		len += ch->LINE_statistics(dev, page + len);
+	} else {
+		len += sprintf(page+len, "Line status: driver not initialized\n");
+	}
+	if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+		len += ch->HW_statistics(dev, page + len);
+	} else {
+		len += sprintf(page+len, "Board status: driver not initialized\n");
+	}
+
+	return len;
+}
+
+static int comx_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->LINE_ioctl) {
+		return(ch->LINE_ioctl(dev, ifr, cmd));
+	}
+	return -EINVAL;
+}
+
+static void comx_reset_dev(struct device *dev)
+{
+	dev->open = comx_open;
+	dev->stop = comx_close;
+	dev->hard_start_xmit = comx_xmit;
+	dev->hard_header = comx_header;
+	dev->rebuild_header = comx_rebuild_header;
+	dev->get_stats = comx_stats;
+	dev->do_ioctl = comx_ioctl;
+	dev->change_mtu = NULL;
+	dev->tx_queue_len = 20;
+	dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct device *dev)
+{
+	struct comx_channel *ch;
+
+	if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(ch, 0, sizeof(struct comx_channel));
+
+	ch->loadavg[0] = 5;
+	ch->loadavg[1] = 300;
+	ch->loadavg[2] = 900;
+	ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1; 
+	if ((ch->avg_bytes = kmalloc(ch->loadavg_size * 
+		sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+
+	memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+	ch->loadavg_counter = 0;
+	ch->loadavg_timer.function = comx_loadavg_timerfun;
+	ch->loadavg_timer.data = (unsigned long)dev;
+	ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+	add_timer(&ch->loadavg_timer);
+
+	dev->priv = (void *)ch;
+	ch->dev = dev;
+	ch->line_status &= ~LINE_UP;
+
+	ch->current_stats = &ch->stats;
+
+	comx_reset_dev(dev);
+	return 0;
+}
+
+static int comx_read_proc(char *page, char **start, off_t off, int count, 
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct device *dev = file->parent->data;
+	struct comx_channel *ch=(struct comx_channel *)dev->priv;
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_STATUS) == 0) {
+		len = comx_statistics(dev, page);
+	} else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+		len = sprintf(page, "%s\n", ch->hardware ? 
+			ch->hardware->name : HWNAME_NONE);
+	} else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+		len = sprintf(page, "%s\n", ch->protocol ? 
+			ch->protocol->name : PROTONAME_NONE);
+	} else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+		len = sprintf(page, "%01d\n", ch->lineup_delay);
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return( min(count, len - off) );
+}
+
+
+static int comx_root_read_proc(char *page, char **start, off_t off, int count, 
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct comx_hardware *hw;
+	struct comx_protocol *line;
+
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+		for(hw=comx_channels;hw;hw=hw->next) 
+			len+=sprintf(page+len, "%s\n", hw->name);
+	} else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+		for(line=comx_lines;line;line=line->next)
+			len+=sprintf(page+len, "%s\n", line->name);
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+	void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct device *dev = (struct device *)entry->parent->data;
+	struct comx_channel *ch=(struct comx_channel *)dev->priv;
+	char *page;
+	struct comx_hardware *hw = comx_channels;
+	struct comx_protocol *line = comx_lines;
+	char str[30];
+	int ret=0;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (count > PAGE_SIZE) {
+		printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+		return -ENOSPC;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+	copy_from_user(page, buffer, count);
+
+	if (*(page + count - 1) == '\n') *(page + count - 1) = 0;
+
+	if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
+		int i;
+		int ret = 0;
+
+		if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+			unsigned long flags;
+
+			save_flags(flags); cli();
+			if (ch->debug_area) kfree(ch->debug_area);
+			if ((ch->debug_area = kmalloc(ch->debug_size = i, 
+				GFP_KERNEL)) == NULL) {
+				ret = -ENOMEM;
+			}
+			ch->debug_start = ch->debug_end = 0;
+			restore_flags(flags);
+			free_page((unsigned long)page);
+			return count;
+		}
+		
+		if (*page != '+' && *page != '-') {
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		while (comx_debugflags[i].value && 
+			strncmp(comx_debugflags[i].name, page + 1, 
+			strlen(comx_debugflags[i].name))) {
+			i++;
+		}
+	
+		if (comx_debugflags[i].value == 0) {
+			printk(KERN_ERR "Invalid debug option\n");
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		if (*page == '+') {
+			ch->debug_flags |= comx_debugflags[i].value;
+		} else {
+			ch->debug_flags &= ~comx_debugflags[i].value;
+		}
+	} else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+		if(strlen(page)>10) {
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		while (hw) { 
+			if (strcmp(hw->name, page) == 0) {
+				break;
+			} else {
+				hw = hw->next;
+			}
+		}
+#ifdef CONFIG_KMOD
+		if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+			sprintf(str,"comx-hw-%s",page);
+			request_module(str);
+		}		
+		hw=comx_channels;
+		while (hw) {
+			if (comx_strcasecmp(hw->name, page) == 0) {
+				break;
+			} else {
+				hw = hw->next;
+			}
+		}
+#endif
+
+		if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw)  {
+			free_page((unsigned long)page);
+			return -ENODEV;
+		}
+		if (ch->init_status & HW_OPEN) {
+			free_page((unsigned long)page);
+			return -EBUSY;
+		}
+		if (ch->hardware && ch->hardware->hw_exit && 
+		   (ret=ch->hardware->hw_exit(dev))) {
+			free_page((unsigned long)page);
+			return ret;
+		}
+		ch->hardware = hw;
+		entry->size = strlen(page) + 1;
+		if (hw && hw->hw_init) hw->hw_init(dev);
+	} else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+		if(strlen(page)>10) {
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		while (line) {
+			if (comx_strcasecmp(line->name, page) == 0) {
+				break;
+			} else {
+				line = line->next;
+			}
+		}
+#ifdef CONFIG_KMOD
+		if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+			sprintf(str,"comx-proto-%s",page);
+			request_module(str);
+		}		
+		line=comx_lines;
+		while (line) {
+			if (comx_strcasecmp(line->name, page) == 0) {
+				break;
+			} else {
+				line = line->next;
+			}
+		}
+#endif
+		
+		if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+			free_page((unsigned long)page);
+			return -ENODEV;
+		}
+		
+		if (ch->init_status & LINE_OPEN) {
+			free_page((unsigned long)page);
+			return -EBUSY;
+		}
+		
+		if (ch->protocol && ch->protocol->line_exit && 
+		    (ret=ch->protocol->line_exit(dev))) {
+			free_page((unsigned long)page);
+			return ret;
+		}
+		ch->protocol = line;
+		entry->size = strlen(page) + 1;
+		comx_reset_dev(dev);
+		if (line && line->line_init) line->line_init(dev);
+	} else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+		int i;
+
+		if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+			if (i >=0 && i < 10) { 
+				ch->lineup_delay = i; 
+			} else {
+				printk(KERN_ERR "comx: invalid lineup_delay value\n");
+			}
+		}
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+	switch(orig) {
+		case 0:	
+			file->f_pos = max(0, min(offset, 
+				file->f_dentry->d_inode->i_size));
+			return(file->f_pos);
+		case 1: 
+			file->f_pos = max(0, min(offset + file->f_pos, 
+				file->f_dentry->d_inode->i_size));
+			return(file->f_pos);
+		case 2: 
+			file->f_pos = max(0, 
+				min(offset + file->f_dentry->d_inode->i_size, 
+				file->f_dentry->d_inode->i_size));
+			return(file->f_pos);
+	}
+	return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+	if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+		return -EACCES;
+	}
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+	loff_t *ppos)
+{
+	struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+	struct device *dev = de->parent->data;
+	struct comx_channel *ch = dev->priv;
+	loff_t copied = 0;
+	unsigned long flags;
+
+	save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+				  // from irq inside read. no problem if the buffer is
+				  // large enough
+
+	while (count > 0 && ch->debug_start != ch->debug_end) {
+		int len;
+
+		len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+			%ch->debug_size, min (ch->debug_size - 
+			ch->debug_start, count));
+
+		if (len) copy_to_user(buffer + copied, 
+			ch->debug_area + ch->debug_start, len);
+		ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+		de->size -= len;
+		count -= len;
+		copied += len;
+	}
+
+	restore_flags(flags);
+	return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct proc_dir_entry *new_dir, *debug_file;
+	struct device *dev;
+	struct comx_channel *ch;
+
+	if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+	if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, 
+		&comx_root_dir)) == NULL) {
+		return -EIO;
+	}
+
+	new_dir->ops = &proc_dir_inode_operations;  // ez egy normalis /proc konyvtar
+	new_dir->nlink = 2;
+	new_dir->data = NULL; // ide jon majd a struct dev
+
+	/* Ezek kellenek */
+	if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644, 
+	    strlen(HWNAME_NONE) + 1, new_dir)) {
+		return -ENOMEM;
+	}
+	if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644, 
+	    strlen(PROTONAME_NONE) + 1, new_dir)) {
+		return -ENOMEM;
+	}
+	if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+		return -ENOMEM;
+	}
+	if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+		return -ENOMEM;
+	}
+
+	if ((debug_file = create_proc_entry(FILENAME_DEBUG, 
+	    S_IFREG | 0644, new_dir)) == NULL) {
+		return -ENOMEM;
+	}
+	debug_file->ops = &comx_debug_inode_ops;
+	debug_file->data = (void *)debug_file; 
+	debug_file->read_proc = NULL; // see below
+	debug_file->write_proc = &comx_write_proc;
+	debug_file->nlink = 1;
+
+	/* struct ppp_device is a bit larger then struct device and the
+	   syncppp driver needs it */
+	if ((dev = kmalloc(sizeof(struct ppp_device), GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(dev, 0, sizeof(struct ppp_device));
+	dev->name = (char *)new_dir->name;
+	dev->init = comx_init_dev;
+
+	if (register_netdevice(dev)) {
+		return -EIO;
+	}
+	ch=dev->priv;
+	ch->debug_file = debug_file; 
+	ch->procdir = new_dir;
+	new_dir->data = dev;
+
+	ch->debug_start = ch->debug_end = 0;
+	if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE, 
+	    GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+
+	ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+	struct device *dev = entry->data;
+	struct comx_channel *ch = dev->priv;
+	int ret;
+
+	/* Egyelore miert ne ? */
+	if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+	if (dev->flags & IFF_UP) {
+		printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+		return -EBUSY;
+	}
+
+	if (ch->protocol && ch->protocol->line_exit && 
+	    (ret=ch->protocol->line_exit(dev))) {
+		return ret;
+	}
+	if (ch->hardware && ch->hardware->hw_exit && 
+	   (ret=ch->hardware->hw_exit(dev))) { 
+		if(ch->protocol && ch->protocol->line_init) {
+			ch->protocol->line_init(dev);
+		}
+		return ret;
+	}
+	ch->protocol = NULL;
+	ch->hardware = NULL;
+
+	del_timer(&ch->loadavg_timer);
+	kfree(ch->avg_bytes);
+
+	unregister_netdev(dev);
+	if (ch->debug_area) {
+		kfree(ch->debug_area);
+	}
+	if (dev->priv) {
+		kfree(dev->priv);
+	}
+	kfree(dev);
+
+	remove_proc_entry(FILENAME_DEBUG, entry);
+	remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+	remove_proc_entry(FILENAME_STATUS, entry);
+	remove_proc_entry(FILENAME_HARDWARE, entry);
+	remove_proc_entry(FILENAME_PROTOCOL, entry);
+	remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+//	proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+	struct proc_dir_entry *de;
+	struct inode *inode = NULL;
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		return ERR_PTR(-ENOTDIR);
+	}
+
+	if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+		for (de = de->subdir ; de ; de = de->next) {
+			if ((de && de->low_ino) && 
+			    (de->namelen == dentry->d_name.len) &&
+			    (memcmp(dentry->d_name.name, de->name, 
+			    de->namelen) == 0))	{
+			 	if ((inode = proc_get_inode(dir->i_sb, 
+			 	    de->low_ino, de)) == NULL) { 
+			 		printk(KERN_ERR "COMX: lookup error\n"); 
+			 		return ERR_PTR(-EINVAL); 
+			 	}
+				break;
+			}
+		}
+	}
+	dentry->d_op = &comx_dentry_operations;
+	d_add(dentry, inode);
+	return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+	register signed char __res;
+
+	while (1) {
+		if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+			break;
+		}
+	}
+	return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+	d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir)
+{
+	struct proc_dir_entry *new_file;
+
+	if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+		new_file->ops = &comx_normal_inode_ops;
+		new_file->data = (void *)new_file;
+		new_file->read_proc = &comx_read_proc;
+		new_file->write_proc = &comx_write_proc;
+		new_file->size = size;
+		new_file->nlink = 1;
+	}
+	return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+	struct comx_hardware *hw = comx_channels;
+
+	if (!hw) {
+		comx_channels = comx_hw;
+	} else {
+		while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+			hw = hw->next;
+		}
+		if (strcmp(comx_hw->name, hw->name) == 0) {
+			return -1;
+		}
+		hw->next = comx_hw;
+	}
+
+	printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+	return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+	struct comx_hardware *hw = comx_channels;
+
+	if (!hw) {
+		return -1;
+	}
+
+	if (strcmp(hw->name, name) == 0) {
+		comx_channels = comx_channels->next;
+		return 0;
+	}
+
+	while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+		hw = hw->next;
+	}
+
+	if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+		hw->next = hw->next->next;
+		return 0;
+	}
+	return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+	struct comx_protocol *pr = comx_lines;
+
+	if (!pr) {
+		comx_lines = comx_line;
+	} else {
+		while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+			pr = pr->next;
+		}
+		if (strcmp(comx_line->name, pr->name) == 0) {
+			return -1;
+		}
+		pr->next = comx_line;
+	}
+
+	printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+	return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+	struct comx_protocol *pr = comx_lines;
+
+	if (!pr) {
+		return -1;
+	}
+
+	if (strcmp(pr->name, name) == 0) {
+		comx_lines = comx_lines->next;
+		return 0;
+	}
+
+	while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+		pr = pr->next;
+	}
+
+	if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+		pr->next = pr->next->next;
+		return 0;
+	}
+	return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+	struct proc_dir_entry *new_file;
+
+	memcpy(&comx_root_inode_ops, &proc_dir_inode_operations, 
+		sizeof(struct inode_operations));
+	comx_root_inode_ops.lookup = &comx_lookup;
+	comx_root_inode_ops.mkdir = &comx_mkdir;
+	comx_root_inode_ops.rmdir = &comx_rmdir;
+
+	memcpy(&comx_normal_inode_ops, &proc_net_inode_operations, 
+		sizeof(struct inode_operations));
+	comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+	comx_normal_inode_ops.lookup = &comx_lookup;
+
+	memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops, 
+		sizeof(struct inode_operations));
+	comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+	memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+		sizeof(struct file_operations));
+	comx_normal_file_ops.open = &comx_file_open;
+	comx_normal_file_ops.release = &comx_file_release;
+
+	memcpy(&comx_debug_file_ops, &comx_normal_file_ops, 
+		sizeof(struct file_operations));
+	comx_debug_file_ops.llseek = &comx_debug_lseek;
+	comx_debug_file_ops.read = &comx_debug_read;
+
+	if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+	if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, 
+	   S_IFREG | 0444, &comx_root_dir)) == NULL) {
+		return -ENOMEM;
+	}
+	
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->data = new_file;
+	new_file->read_proc = &comx_root_read_proc;
+	new_file->write_proc = NULL;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, 
+	   S_IFREG | 0444, &comx_root_dir)) == NULL) {
+		return -ENOMEM;
+	}
+	
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->data = new_file;
+	new_file->read_proc = &comx_root_read_proc;
+	new_file->write_proc = NULL;
+	new_file->nlink = 1;
+
+
+	printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n", 
+		VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+	comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+	comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+	comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+	comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+	comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+	comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+	comx_proto_fr_init();
+#endif
+#endif
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+	remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+	proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx.h linux.14p6/drivers/net/comx.h
--- linux.vanilla/drivers/net/comx.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comx.h	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,238 @@
+/*
+ * General definitions for the COMX driver 
+ * 
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ *
+ * net_device_stats:
+ *	rx_length_errors	rec_len < 4 || rec_len > 2000
+ *	rx_over_errors		receive overrun (OVR)
+ *	rx_crc_errors		rx crc error
+ *	rx_frame_errors		aborts rec'd (ABO)
+ *	rx_fifo_errors		status fifo overrun (PBUFOVR)
+ *	rx_missed_errors	receive buffer overrun (BUFOVR)
+ *	tx_aborted_errors	?
+ *	tx_carrier_errors	modem line status changes
+ *	tx_fifo_errors		tx underrun (locomx)
+ */
+
+struct comx_protocol {
+	char	*name;
+	char	*version;
+	unsigned short encap_type;
+	int	(*line_init)(struct device *dev);
+	int	(*line_exit)(struct device *dev);
+	struct comx_protocol *next;
+	};
+
+struct comx_hardware {
+	char *name; 
+	char *version;
+	int	(*hw_init)(struct device *dev);
+	int	(*hw_exit)(struct device *dev);
+	int	(*hw_dump)(struct device *dev);
+	struct comx_hardware *next;
+	};
+
+struct comx_channel {
+	struct device 	*dev;		// Where we belong to
+	struct device	*twin;		// On dual-port cards
+	struct proc_dir_entry *procdir;	// the directory
+
+	unsigned char	init_status;
+	unsigned char	line_status;
+
+	struct timer_list lineup_timer;	// against line jitter
+	int		lineup_pending;
+	unsigned char	lineup_delay;
+
+#if 0
+	struct timer_list reset_timer; // for board resetting
+	int		reset_pending;
+	int		reset_timeout;
+#endif
+
+	struct net_device_stats	stats;	
+	struct net_device_stats *current_stats;
+#if 0
+	unsigned long	board_resets;
+#endif
+	unsigned long 	*avg_bytes;
+	int		loadavg_counter, loadavg_size;
+	int		loadavg[3];
+	struct timer_list loadavg_timer;
+	int		debug_flags;
+	char 		*debug_area;
+	int		debug_start, debug_end, debug_size;
+	struct proc_dir_entry *debug_file;
+#ifdef	CONFIG_COMX_DEBUG_RAW
+	char		*raw;
+	int		raw_len;
+#endif
+	// LINE specific	
+	struct comx_protocol *protocol;
+	void		(*LINE_rx)(struct device *dev, struct sk_buff *skb);
+	int		(*LINE_tx)(struct device *dev);
+	void		(*LINE_status)(struct device *dev, u_short status);
+	int		(*LINE_open)(struct device *dev);
+	int		(*LINE_close)(struct device *dev);
+	int		(*LINE_xmit)(struct sk_buff *skb, struct device *dev);
+	int		(*LINE_header)(struct sk_buff *skb, struct device *dev,
+				u_short type,void *daddr, void *saddr, 
+				unsigned len);
+	int		(*LINE_rebuild_header)(struct sk_buff *skb);
+	int		(*LINE_statistics)(struct device *dev, char *page);
+	int		(*LINE_parameter_check)(struct device *dev);
+	int		(*LINE_ioctl)(struct device *dev, struct ifreq *ifr,
+				int cmd);
+	void		(*LINE_mod_use)(int);
+	void *		LINE_privdata;
+
+	// HW specific
+
+	struct comx_hardware *hardware;
+	void	(*HW_board_on)(struct device *dev);
+	void	(*HW_board_off)(struct device *dev);
+	struct device *(*HW_access_board)(struct device *dev);
+	void	(*HW_release_board)(struct device *dev, struct device *savep);
+	int	(*HW_txe)(struct device *dev);
+	int	(*HW_open)(struct device *dev);
+	int	(*HW_close)(struct device *dev);
+	int	(*HW_send_packet)(struct device *dev,struct sk_buff *skb);
+	int	(*HW_statistics)(struct device *dev, char *page);
+#if 0
+	int	(*HW_reset)(struct device *dev, char *page);
+#endif
+	int	(*HW_load_board)(struct device *dev);
+	void	(*HW_set_clock)(struct device *dev);
+	void	*HW_privdata;
+	};
+
+struct comx_debugflags_struct {
+	char *name;
+	int  value;
+	};
+
+#define	COMX_ROOT_DIR_NAME	"comx"
+
+#define	FILENAME_HARDWARE	"boardtype"
+#define FILENAME_HARDWARELIST	"boardtypes"
+#define FILENAME_PROTOCOL	"protocol"
+#define FILENAME_PROTOCOLLIST	"protocols"
+#define FILENAME_DEBUG		"debug"
+#define FILENAME_CLOCK		"clock"
+#define	FILENAME_STATUS		"status"
+#define	FILENAME_IO		"io"
+#define FILENAME_IRQ		"irq"
+#define	FILENAME_KEEPALIVE	"keepalive"
+#define FILENAME_LINEUPDELAY	"lineup_delay"
+#define FILENAME_CHANNEL	"channel"
+#define FILENAME_FIRMWARE	"firmware"
+#define FILENAME_MEMADDR	"memaddr"
+#define	FILENAME_TWIN		"twin"
+#define FILENAME_T1		"t1"
+#define FILENAME_T2		"t2"
+#define FILENAME_N2		"n2"
+#define FILENAME_WINDOW		"window"
+#define FILENAME_MODE		"mode"
+#define	FILENAME_DLCI		"dlci"
+#define	FILENAME_MASTER		"master"
+#ifdef	CONFIG_COMX_DEBUG_RAW
+#define	FILENAME_RAW		"raw"
+#endif
+
+#define PROTONAME_NONE		"none"
+#define HWNAME_NONE		"none"
+#define KEEPALIVE_OFF		"off"
+
+#define FRAME_ACCEPTED		0		/* sending and xmitter busy */
+#define FRAME_DROPPED		1
+#define FRAME_ERROR		2		/* xmitter error */
+#define	FRAME_QUEUED		3		/* sending but more can come */
+
+#define	LINE_UP			1		/* Modem UP */
+#define PROTO_UP		2
+#define PROTO_LOOP		4
+
+#define	HW_OPEN			1
+#define	LINE_OPEN		2
+#define FW_LOADED		4
+#define IRQ_ALLOCATED		8
+
+#define DEBUG_COMX_RX		2
+#define	DEBUG_COMX_TX		4
+#define	DEBUG_HW_TX		16
+#define	DEBUG_HW_RX		32
+#define	DEBUG_HDLC_KEEPALIVE	64
+#define	DEBUG_COMX_PPP		128
+#define DEBUG_COMX_LAPB		256
+#define	DEBUG_COMX_DLCI		512
+
+#define	DEBUG_PAGESIZE		3072
+#define DEFAULT_DEBUG_SIZE	4096
+#define	DEFAULT_LINEUP_DELAY	1
+#define	FILE_PAGESIZE		3072
+
+#ifndef	COMX_PPP_MAJOR
+#define	COMX_PPP_MAJOR		88
+#endif
+
+
+#ifndef min
+#define min(a,b)		((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a,b)		((a) > (b) ? (a) : (b))
+#endif
+
+
+#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv)
+
+#define TWIN(dev) (COMX_CHANNEL(dev)->twin)
+
+
+#ifndef byte
+typedef u8	byte;
+#endif
+#ifndef word
+typedef u16	word;
+#endif
+
+#ifndef	SEEK_SET
+#define	SEEK_SET	0
+#endif
+#ifndef	SEEK_CUR
+#define	SEEK_CUR	1
+#endif
+#ifndef	SEEK_END
+#define	SEEK_END	2
+#endif
+
+extern struct proc_dir_entry comx_root_dir;
+
+extern int	comx_register_hardware(struct comx_hardware *comx_hw);
+extern int	comx_unregister_hardware(char *name);
+extern int	comx_register_protocol(struct comx_protocol *comx_line);
+extern int	comx_unregister_protocol(char *name);
+
+extern int	comx_rx(struct device *dev, struct sk_buff *skb);
+extern void	comx_status(struct device *dev, int status);
+extern void	comx_lineup_func(unsigned long d);
+
+extern int	comx_debug(struct device *dev, char *fmt, ...);
+extern int	comx_debug_skb(struct device *dev, struct sk_buff *skb, char *msg);
+extern int	comx_debug_bytes(struct device *dev, unsigned char *bytes, int len,
+		char *msg);
+extern int	comx_strcasecmp(const char *cs, const char *ct);
+
+extern struct inode_operations comx_normal_inode_ops;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comxhw.h linux.14p6/drivers/net/comxhw.h
--- linux.vanilla/drivers/net/comxhw.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/comxhw.h	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,112 @@
+/*
+ * Defines for comxhw.c
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ */
+
+#define	LOCOMX_IO_EXTENT	8
+#define COMX_IO_EXTENT		4
+#define	HICOMX_IO_EXTENT	16
+
+#define COMX_MAX_TX_SIZE	1600
+
+#define COMX_JAIL_OFFSET	0xffff
+#define COMX_JAIL_VALUE		0xfe
+#define	COMX_MEMORY_SIZE	65536
+#define HICOMX_MEMORY_SIZE	16384
+#define COMX_MEM_MIN		0xa0000
+#define COMX_MEM_MAX		0xf0000
+
+#define	COMX_DEFAULT_IO		0x360
+#define	COMX_DEFAULT_IRQ	10
+#define	COMX_DEFAULT_MEMADDR	0xd0000
+#define	HICOMX_DEFAULT_IO	0x320
+#define	HICOMX_DEFAULT_IRQ	10
+#define	HICOMX_DEFAULT_MEMADDR	0xd0000
+#define	LOCOMX_DEFAULT_IO	0x368
+#define	LOCOMX_DEFAULT_IRQ	7
+
+#define MAX_CHANNELNO		2
+
+#define	COMX_CHANNEL_OFFSET	0x2000
+
+#define COMX_ENABLE_BOARD_IT    0x40
+#define COMX_BOARD_RESET       	0x20
+#define COMX_ENABLE_BOARD_MEM   0x10
+#define COMX_DISABLE_BOARD_MEM  0
+#define COMX_DISABLE_ALL	0x00
+
+#define HICOMX_DISABLE_ALL	0x00
+#define HICOMX_ENABLE_BOARD_MEM	0x02
+#define HICOMX_DISABLE_BOARD_MEM 0x0
+#define HICOMX_BOARD_RESET	0x01
+#define HICOMX_PRG_MEM		4
+#define HICOMX_DATA_MEM		0
+#define HICOMX_ID_BYTE		0x55
+
+#define CMX_ID_BYTE		0x31
+#define COMX_CLOCK_CONST	8000
+
+#define	LINKUP_READY		3
+
+#define	OFF_FW_L1_ID	0x01e	 /* ID bytes */
+#define OFF_FW_L2_ID	0x1006
+#define	FW_L1_ID_1	0xab
+#define FW_L1_ID_2_COMX		0xc0
+#define FW_L1_ID_2_HICOMX	0xc1
+#define	FW_L2_ID_1	0xab
+
+#define OFF_A_L2_CMD     0x130   /* command register for L2 */
+#define OFF_A_L2_CMDPAR  0x131   /* command parameter byte */
+#define OFF_A_L1_STATB   0x122   /* stat. block for L1 */
+#define OFF_A_L1_ABOREC  0x122   /* receive ABORT counter */
+#define OFF_A_L1_OVERRUN 0x123   /* receive overrun counter */
+#define OFF_A_L1_CRCREC  0x124   /* CRC error counter */
+#define OFF_A_L1_BUFFOVR 0x125   /* buffer overrun counter */
+#define OFF_A_L1_PBUFOVR 0x126   /* priority buffer overrun counter */
+#define OFF_A_L1_MODSTAT 0x127   /* current state of modem ctrl lines */
+#define OFF_A_L1_STATE   0x127   /* end of stat. block for L1 */
+#define OFF_A_L1_TXPC    0x128   /* Tx counter for the PC */
+#define OFF_A_L1_TXZ80   0x129   /* Tx counter for the Z80 */
+#define OFF_A_L1_RXPC    0x12a   /* Rx counter for the PC */
+#define OFF_A_L1_RXZ80   0x12b   /* Rx counter for the Z80 */
+#define OFF_A_L1_REPENA  0x12c   /* IT rep disable */
+#define OFF_A_L1_CHNR    0x12d   /* L1 channel logical number */
+#define OFF_A_L1_CLKINI  0x12e   /* Timer Const */
+#define OFF_A_L2_LINKUP	 0x132	 /* Linkup byte */
+#define OFF_A_L2_DAV	 0x134   /* Rx DAV */
+#define OFF_A_L2_RxBUFP  0x136	 /* Rx buff relative to membase */
+#define OFF_A_L2_TxEMPTY 0x138   /* Tx Empty */
+#define OFF_A_L2_TxBUFP  0x13a   /* Tx Buf */
+#define OFF_A_L2_NBUFFS	 0x144	 /* Number of buffers to fetch */
+
+#define OFF_A_L2_SABMREC 0x164	 /* LAPB no. of SABMs received */
+#define OFF_A_L2_SABMSENT 0x165	 /* LAPB no. of SABMs sent */
+#define OFF_A_L2_REJREC  0x166	 /* LAPB no. of REJs received */
+#define OFF_A_L2_REJSENT 0x167	 /* LAPB no. of REJs sent */
+#define OFF_A_L2_FRMRREC 0x168	 /* LAPB no. of FRMRs received */
+#define OFF_A_L2_FRMRSENT 0x169	 /* LAPB no. of FRMRs sent */
+#define OFF_A_L2_PROTERR 0x16A	 /* LAPB no. of protocol errors rec'd */
+#define OFF_A_L2_LONGREC 0x16B	 /* LAPB no. of long frames */
+#define OFF_A_L2_INVNR   0x16C	 /* LAPB no. of invalid N(R)s rec'd */
+#define OFF_A_L2_UNDEFFR 0x16D	 /* LAPB no. of invalid frames */
+
+#define	OFF_A_L2_T1	0x174	 /* T1 timer */
+#define	OFF_A_L2_ADDR	0x176	 /* DCE = 1, DTE = 3 */
+
+#define	COMX_CMD_INIT	1
+#define COMX_CMD_EXIT	2
+#define COMX_CMD_OPEN	16
+#define COMX_CMD_CLOSE	17
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/hamradio/Config.in linux.14p6/drivers/net/hamradio/Config.in
--- linux.vanilla/drivers/net/hamradio/Config.in	Sat Aug 14 02:27:29 1999
+++ linux.14p6/drivers/net/hamradio/Config.in	Wed Oct 27 18:50:24 1999
@@ -11,12 +11,12 @@
   bool '   support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO
 fi
     
-tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX
-tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX
-dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT
-dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT
+dep_tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX $CONFIG_AX25
+dep_tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX $CONFIG_AX25
+dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT $CONFIG_AX25
+dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT $CONFIG_AX25
 
-tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM
+dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_AX25
 if [ "$CONFIG_SOUNDMODEM" != "n" ]; then
   bool '   soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
   bool '   soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
@@ -29,4 +29,4 @@
   bool '   soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
 fi
 
-tristate 'YAM driver for AX.25' CONFIG_YAM
+dep_tristate 'YAM driver for AX.25' CONFIG_YAM $CONFIG_AX25
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/hscx.h linux.14p6/drivers/net/hscx.h
--- linux.vanilla/drivers/net/hscx.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/hscx.h	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,103 @@
+#define	HSCX_MTU	1600
+
+#define	HSCX_ISTA	0x00
+#define HSCX_MASK	0x00
+#define HSCX_STAR	0x01
+#define HSCX_CMDR	0x01
+#define HSCX_MODE	0x02
+#define HSCX_TIMR	0x03
+#define HSCX_EXIR	0x04
+#define HSCX_XAD1	0x04
+#define HSCX_RBCL	0x05
+#define HSCX_SAD2	0x05
+#define HSCX_RAH1	0x06
+#define HSCX_RSTA	0x07
+#define HSCX_RAH2	0x07
+#define HSCX_RAL1	0x08
+#define HSCX_RCHR	0x09
+#define HSCX_RAL2	0x09
+#define HSCX_XBCL	0x0a
+#define HSCX_BGR	0x0b
+#define HSCX_CCR2	0x0c
+#define HSCX_RBCH	0x0d
+#define HSCX_XBCH	0x0d
+#define HSCX_VSTR	0x0e
+#define HSCX_RLCR	0x0e
+#define HSCX_CCR1	0x0f
+#define HSCX_FIFO	0x1e
+
+#define HSCX_HSCX_CHOFFS	0x400
+#define HSCX_SEROFFS	0x1000
+
+#define HSCX_RME	0x80
+#define HSCX_RPF	0x40
+#define HSCX_RSC	0x20
+#define HSCX_XPR	0x10
+#define HSCX_TIN	0x08
+#define HSCX_ICA	0x04
+#define HSCX_EXA	0x02
+#define HSCX_EXB	0x01
+
+#define HSCX_XMR	0x80
+#define HSCX_XDU	0x40
+#define HSCX_EXE	0x40
+#define HSCX_PCE	0x20
+#define HSCX_RFO	0x10
+#define HSCX_CSC	0x08
+#define HSCX_RFS	0x04
+
+#define HSCX_XDOV	0x80
+#define HSCX_XFW	0x40
+#define HSCX_XRNR	0x20
+#define HSCX_RRNR	0x10
+#define HSCX_RLI	0x08
+#define HSCX_CEC	0x04
+#define HSCX_CTS	0x02
+#define HSCX_WFA	0x01
+
+#define HSCX_RMC	0x80
+#define HSCX_RHR	0x40
+#define HSCX_RNR	0x20
+#define HSCX_XREP	0x20
+#define HSCX_STI	0x10
+#define HSCX_XTF	0x08
+#define HSCX_XIF	0x04
+#define HSCX_XME	0x02
+#define HSCX_XRES	0x01
+
+#define HSCX_AUTO	0x00
+#define HSCX_NONAUTO	0x40
+#define HSCX_TRANS	0x80
+#define HSCX_XTRANS	0xc0
+#define HSCX_ADM16	0x20
+#define HSCX_ADM8	0x00
+#define HSCX_TMD_EXT	0x00
+#define HSCX_TMD_INT	0x10
+#define HSCX_RAC	0x08
+#define HSCX_RTS	0x04
+#define HSCX_TLP	0x01
+
+#define HSCX_VFR	0x80
+#define HSCX_RDO	0x40
+#define HSCX_CRC	0x20
+#define HSCX_RAB	0x10
+
+#define HSCX_CIE	0x04
+#define HSCX_RIE	0x02
+
+#define HSCX_DMA	0x80
+#define HSCX_NRM	0x40
+#define HSCX_CAS	0x20
+#define HSCX_XC	0x10
+
+#define HSCX_OV	0x10
+
+#define HSCX_CD	0x80
+
+#define HSCX_RC	0x80
+
+#define HSCX_PU	0x80
+#define HSCX_NRZ	0x00
+#define HSCX_NRZI	0x40
+#define HSCX_ODS	0x10
+#define HSCX_ITF	0x08
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/lance.c linux.14p6/drivers/net/lance.c
--- linux.vanilla/drivers/net/lance.c	Sat Aug 14 02:27:29 1999
+++ linux.14p6/drivers/net/lance.c	Sat Nov 13 17:41:00 1999
@@ -40,12 +40,16 @@
     8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb
     v1.12 10/27/97 Module support -djb
     v1.14  2/3/98 Module support modified, made PCI support optional -djb
+    v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed
+                  before unregister_netdev() which caused NULL pointer
+                  reference later in the chain (in rtnetlink_fill_ifinfo())
+                  -- Mika Kuoppala <miku@iki.fi>
     
     Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from
     the 2.1 version of the old driver - Alan Cox
 */
 
-static const char *version = "lance.c:v1.14ac 1998/11/20 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
+static const char *version = "lance.c:v1.15ac 1999/11/13 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -349,11 +353,11 @@
 	for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
 		struct device *dev = &dev_lance[this_dev];
 		if (dev->priv != NULL) {
-			kfree(dev->priv);
-			dev->priv = NULL;
+			unregister_netdev(dev);	
 			free_dma(dev->dma);
 			release_region(dev->base_addr, LANCE_TOTAL_SIZE);
-			unregister_netdev(dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
 		}
 	}
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/mixcom.h linux.14p6/drivers/net/mixcom.h
--- linux.vanilla/drivers/net/mixcom.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/mixcom.h	Wed Nov  3 00:18:55 1999
@@ -0,0 +1,35 @@
+/*
+ * Defines for the mixcom board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ */
+
+#define	MIXCOM_IO_EXTENT	0x20
+
+#define	MIXCOM_DEFAULT_IO	0x180
+#define	MIXCOM_DEFAULT_IRQ	5
+
+#define MIXCOM_ID		0x11
+#define MIXCOM_SERIAL_OFFSET	0x1000
+#define MIXCOM_CHANNEL_OFFSET	0x400
+#define MIXCOM_IT_OFFSET	0xc14
+#define MIXCOM_STATUS_OFFSET	0xc14
+#define MIXCOM_ID_OFFSET	0xc10
+#define MIXCOM_ON		0x1
+#define MIXCOM_OFF		0x0
+
+/* Status register bits */
+
+#define MIXCOM_CTSB		0x1
+#define MIXCOM_CTSA		0x2
+#define MIXCOM_CHANNELNO	0x20
+#define MIXCOM_POWERFAIL	0x40
+#define MIXCOM_BOOT		0x80
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/myri_sbus.c linux.14p6/drivers/net/myri_sbus.c
--- linux.vanilla/drivers/net/myri_sbus.c	Fri Mar 26 21:57:41 1999
+++ linux.14p6/drivers/net/myri_sbus.c	Tue Nov 16 15:13:27 1999
@@ -222,11 +222,11 @@
 	}
 
 	if(i == 5000)
-		printk("myricom: Chip would not reset after firmware load.\n");
+		printk(KERN_ERR "myricom: Chip would not reset after firmware load.\n");
 
 	i = myri_do_handshake(mp);
 	if(i)
-		printk("myricom: Handshake with LANAI failed.\n");
+		printk(KERN_ERR "myricom: Handshake with LANAI failed.\n");
 
 	if(mp->eeprom.cpuvers == CPUVERS_4_0)
 		mp->lregs->vers = 0;
@@ -575,7 +575,7 @@
 			return 1;
 		} else {
 			DTX(("resetting, return 0\n"));
-			printk("%s: transmit timed out, resetting\n", dev->name);
+			printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 			mp->enet_stats.tx_errors++;
 			myri_init(mp, in_interrupt());
 			dev->tbusy = 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/ncr885_debug.h linux.14p6/drivers/net/ncr885_debug.h
--- linux.vanilla/drivers/net/ncr885_debug.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/ncr885_debug.h	Sat Nov 13 15:15:45 1999
@@ -0,0 +1,54 @@
+#ifndef _H_NCR885_DEBUG
+#define _H_NCR885_DEBUG
+
+struct ncr885e_regs {
+  unsigned long tx_status;
+  unsigned long rx_status;
+  unsigned long mac_config;
+  unsigned long tx_control;
+  unsigned long rx_control;
+  unsigned long tx_cmd_ptr;
+  unsigned long rx_cmd_ptr;
+  unsigned long int_status;
+};
+
+#ifndef __KERNEL__
+
+struct ncr885e_private {
+
+  struct dbdma_cmd *head;
+  struct dbdma_cmd *tx_cmds;
+  struct dbdma_cmd *rx_cmds;
+  struct dbdma_cmd *stop_cmd;
+
+  struct sk_buff *tx_skbufs[NR_TX_RING];
+  struct sk_buff *rx_skbufs[NR_RX_RING];
+
+  int rx_current;
+  int rx_dirty;
+
+  int tx_dirty;
+  int tx_current;
+
+  unsigned short tx_status[NR_TX_RING];
+
+  unsigned char tx_fullup;
+  unsigned char tx_active;
+  
+  struct net_device_stats  stats;
+
+  struct device *dev;
+
+  struct timer_list tx_timeout;
+  int timeout_active;
+
+  spinlock_t lock;
+};
+
+#endif /* __KERNEL__ */
+
+
+#define NCR885E_GET_PRIV   _IOR('N',1,sizeof( struct ncr885e_private ))
+#define NCR885E_GET_REGS   _IOR('N',2,sizeof( struct ncr885e_regs ))
+
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/ncr885e.c linux.14p6/drivers/net/ncr885e.c
--- linux.vanilla/drivers/net/ncr885e.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/ncr885e.c	Sat Nov 13 15:15:45 1999
@@ -0,0 +1,1458 @@
+/*
+ *  An Ethernet driver for the dual-function NCR 53C885 SCSI/Ethernet
+ *  controller.
+ *
+ *
+ *  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.
+ *
+ */
+
+static const char *version =
+"ncr885e.c:v0.8 11/30/98 dan@synergymicro.com\n";
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT 
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+#include <asm/uaccess.h>
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "ncr885e.h"
+#include "ncr885_debug.h"
+
+static const char *chipname = "ncr885e";
+
+/* debugging flags */
+#if 0
+#define DEBUG_FUNC    0x0001
+#define DEBUG_PACKET  0x0002
+#define DEBUG_CMD     0x0004
+#define DEBUG_CHANNEL 0x0008
+#define DEBUG_INT     0x0010
+#define DEBUG_RX      0x0020
+#define DEBUG_TX      0x0040
+#define DEBUG_DMA     0x0080
+#define DEBUG_MAC     0x0100
+#define DEBUG_DRIVER  0x0200
+#define DEBUG_ALL     0x1fff
+#endif
+
+#ifdef DEBUG_NCR885E
+#define NCR885E_DEBUG   0
+#else
+#define NCR885E_DEBUG   0
+#endif
+
+/* The 885's Ethernet PCI device id. */
+#ifndef PCI_DEVICE_ID_NCR_53C885_ETHERNET
+#define PCI_DEVICE_ID_NCR_53C885_ETHERNET  0x0701
+#endif
+
+#define NR_RX_RING    8
+#define NR_TX_RING    8
+#define MAX_TX_ACTIVE (NR_TX_RING-1)
+#define NCMDS_TX      NR_TX_RING
+
+#define RX_BUFLEN     (ETH_FRAME_LEN + 8)
+#define TX_TIMEOUT    5*HZ
+
+#define NCR885E_TOTAL_SIZE 0xe0
+
+#define TXSR          (1<<6)   /* tx: xfer status written */
+#define TXABORT       (1<<7)   /* tx: abort */
+#define EOP           (1<<7)   /* rx: end of packet written to buffer */
+
+int ncr885e_debug = NCR885E_DEBUG;
+static int print_version = 0;
+
+struct ncr885e_private {
+
+	/* preserve a 1-1 marking with buffs */
+	struct dbdma_cmd *head;
+	struct dbdma_cmd *tx_cmds;
+	struct dbdma_cmd *rx_cmds;
+	struct dbdma_cmd *stop_cmd;
+
+	struct sk_buff *tx_skbufs[NR_TX_RING];
+	struct sk_buff *rx_skbufs[NR_RX_RING];
+
+	int rx_current;
+	int rx_dirty;
+
+	int tx_dirty;
+	int tx_current;
+
+	unsigned short tx_status[NR_TX_RING];
+
+	unsigned char tx_fullup;
+	unsigned char tx_active;
+  
+	struct net_device_stats  stats;
+
+	struct device *dev;
+
+	struct timer_list tx_timeout;
+	int timeout_active;
+
+	spinlock_t lock;
+};
+
+#ifdef MODULE
+static struct device *root_dev = NULL;
+#endif
+
+
+static int ncr885e_open( struct device *dev );
+static int ncr885e_close( struct device *dev );
+static void ncr885e_rx( struct device *dev );
+static void ncr885e_tx( struct device *dev );
+static int ncr885e_probe1( struct device *dev, unsigned long ioaddr,
+			   unsigned char irq );
+static int ncr885e_xmit_start( struct sk_buff *skb, struct device *dev );
+static struct net_device_stats *ncr885e_stats( struct device *dev );
+static void ncr885e_set_multicast( struct device *dev );
+static void ncr885e_config( struct device *dev );
+static int ncr885e_set_address( struct device *dev, void *addr );
+static void ncr885e_interrupt( int irq, void *dev_id, struct pt_regs *regs );
+static void show_dbdma_cmd( volatile struct dbdma_cmd *cmd );
+#if 0
+static int read_eeprom( unsigned int ioadddr, int location );
+#endif
+
+#ifdef NCR885E_DEBUG_MII
+static void show_mii( unsigned long ioaddr );
+static int read_mii( unsigned long ioaddr, int reg );
+static void write_mii( unsigned long ioaddr, int reg, int data );
+#endif /* NCR885E_DEBUG_MII */
+
+#define TX_RESET_FLAGS    (TX_CHANNEL_RUN|TX_CHANNEL_PAUSE|TX_CHANNEL_WAKE)
+#define RX_RESET_FLAGS    (RX_CHANNEL_RUN|RX_CHANNEL_PAUSE|RX_CHANNEL_WAKE)
+
+
+#if 0
+static int
+debug_ioctl( struct device *dev, struct ifreq *req, int cmd )
+{
+	unsigned long ioaddr = dev->base_addr;
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	struct ncr885e_private *data;
+	struct ncr885e_regs *regs;
+	unsigned long flags;
+
+	union {
+		struct ncr885e_regs dump;
+		struct ncr885e_private priv;    
+	} temp;
+
+	switch( cmd ) {
+
+		/* dump the rx ring status */
+	case NCR885E_GET_PRIV:
+
+		data = (struct ncr885e_private *) &req->ifr_data;    
+
+		if ( verify_area(VERIFY_WRITE, &req->ifr_data,
+				 sizeof( struct ncr885e_private )))
+			return -EFAULT;
+
+		memcpy((char *) &temp.priv, sp, sizeof( struct ncr885e_private ));
+		copy_to_user( data, (char *) &temp.priv, sizeof( struct ncr885e_private));
+		break;
+
+	case NCR885E_GET_REGS:
+
+		regs = (struct ncr885e_regs *) &req->ifr_data;
+  
+		if ( verify_area( VERIFY_WRITE, &req->ifr_data,
+				  sizeof( struct ncr885e_regs )))
+			return -EFAULT;
+
+		spin_lock_irqsave( &sp->lock, flags ); 
+
+		temp.dump.tx_status = inl( ioaddr + TX_CHANNEL_STATUS );
+		temp.dump.rx_status = inl( ioaddr + RX_CHANNEL_STATUS );
+		temp.dump.mac_config = inl( ioaddr + MAC_CONFIG );
+		temp.dump.tx_control = inl( ioaddr + TX_CHANNEL_CONTROL );
+		temp.dump.rx_control = inl( ioaddr + RX_CHANNEL_CONTROL );
+		temp.dump.tx_cmd_ptr = inl( ioaddr + TX_CMD_PTR_LO );
+		temp.dump.rx_cmd_ptr = inl( ioaddr + RX_CMD_PTR_LO );
+		temp.dump.int_status = inl( ioaddr + INTERRUPT_STATUS_REG );
+
+		spin_unlock_irqrestore( &sp->lock, flags );
+		copy_to_user( regs, (char *) &temp.dump, sizeof( struct ncr885e_regs ));
+
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+#endif
+
+/*  Enable interrupts on the 53C885 */
+static inline void
+ncr885e_enable( struct device *dev )
+
+{
+	unsigned long ioaddr = dev->base_addr;
+	unsigned short reg;
+
+	reg = inw(ioaddr + INTERRUPT_ENABLE);
+	outw(reg | INTERRUPT_INTE, ioaddr + INTERRUPT_ENABLE);
+}
+
+/*  Disable interrupts on the 53c885 */
+static inline void
+ncr885e_disable( struct device *dev )
+
+{
+	unsigned long ioaddr = dev->base_addr;
+	unsigned short reg;
+
+	reg = inw( ioaddr + INTERRUPT_ENABLE );
+	outw( reg & ~INTERRUPT_INTE, ioaddr + INTERRUPT_ENABLE );
+}
+
+
+static inline void
+ncr885e_reset( struct device *dev )
+
+{
+	unsigned short reg;  
+	unsigned long cntl;
+	int i;
+	unsigned long ioaddr = dev->base_addr;
+
+	if (ncr885e_debug > 1)
+		printk( KERN_INFO "%s: Resetting 53C885...\n", dev->name );
+
+	/* disable interrupts on the 53C885 */
+	ncr885e_disable( dev );
+  
+	/* disable rx in the MAC */
+	reg = inw( ioaddr + MAC_CONFIG );
+	outw( reg & ~MAC_CONFIG_RXEN, ioaddr + MAC_CONFIG );
+  
+	for( i=0; i < 100; i++ ) {
+
+		if ( !(inw( ioaddr + MAC_CONFIG ) & MAC_CONFIG_RXEN ))
+			break;
+		udelay( 10 );
+	}
+  
+	reg = inw( ioaddr + MAC_CONFIG );
+	outw( reg | MAC_CONFIG_SRST, ioaddr + MAC_CONFIG );
+	outw( reg, ioaddr + MAC_CONFIG );
+
+	/* disable both rx and tx DBDMA channels */
+	outl( TX_DBDMA_ENABLE << 16, ioaddr + TX_CHANNEL_CONTROL );
+	outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL );
+
+	for( i=0; i < 100; i++ ) {
+    
+		if ( !(inw( ioaddr + TX_CHANNEL_STATUS ) & TX_DBDMA_ENABLE ) &&
+		     !(inw( ioaddr + RX_CHANNEL_STATUS ) & RX_DBDMA_ENABLE ))
+			break;
+		udelay( 10 );
+	}
+
+	/* perform a "software reset" */
+	cntl = inl( ioaddr + DBDMA_CONTROL );
+	outl( cntl | DBDMA_SRST, ioaddr + DBDMA_CONTROL );
+
+	for( i=0; i < 100; i++ ) {
+  
+		if ( !(inl( ioaddr + DBDMA_CONTROL ) & DBDMA_SRST ))
+			break;
+		udelay( 10 );
+	}
+
+	/* books says that a software reset should be done to the MAC, as
+	   well.  This true??? */
+
+	if (ncr885e_debug > 3) 
+		printk( KERN_INFO "%s: reset complete\n", dev->name );
+
+}
+
+
+/*  configure the 53C885 chip.
+
+    The DBDMA command descriptors on the 53C885 can be programmed to
+    branch, interrupt or pause conditionally or always by using the
+    interrupt, branch and wait select registers.  */
+
+static void
+ncr885e_config( struct device *dev )
+
+{
+	unsigned long ioaddr = dev->base_addr;
+
+	if (ncr885e_debug > 3)
+		printk( KERN_INFO "%s: Configuring 53C885.\n", dev->name );
+
+	ncr885e_reset( dev );
+ 
+	/* The 53C885 can be programmed to perform conditional DBDMA
+	   branches, interrupts or waits.
+  
+	   Neither channel makes use of "wait", as it requires that the
+	   DBDMA engine to be restarted.  Don't go there.  The rx channel
+	   will branch upon the successful reception of a packet ('EOP' in
+	   the xfer_status field).  The branch address is to the STOP
+	   DBDMA command descriptor, which shuts down the rx channel until
+	   the interrupt is serviced.   */
+     
+	/* cause tx channel to stop after "status received" */
+	outl( 0, ioaddr + TX_INT_SELECT );
+	outl( (TX_WAIT_STAT_RECV << 16) | TX_WAIT_STAT_RECV, 
+	      ioaddr + TX_WAIT_SELECT );
+	outl( 0, ioaddr + TX_BRANCH_SELECT );
+
+	/* cause rx channel to branch to the STOP descriptor on "End-of-Packet" */
+#if 0
+	outl( (RX_INT_SELECT_EOP << 16) | RX_INT_SELECT_EOP,
+	      ioaddr + RX_INT_SELECT );
+#else
+	outl( 0, ioaddr + RX_INT_SELECT );
+#endif
+#if 0
+	outl( 0, ioaddr + RX_WAIT_SELECT );
+#else
+	outl( (RX_WAIT_SELECT_EOP << 16) | RX_WAIT_SELECT_EOP, 
+	      ioaddr + RX_WAIT_SELECT );
+#endif
+#if 1
+	outl( 0, ioaddr + RX_BRANCH_SELECT );
+#else
+	outl( (RX_BRANCH_SELECT_EOP << 16) | RX_BRANCH_SELECT_EOP,
+	      ioaddr + RX_BRANCH_SELECT );
+#endif
+
+	/* configure DBDMA */
+	outl( (DBDMA_BE | DBDMA_DPMRLE | DBDMA_TDPCE |
+	       DBDMA_DDPE | DBDMA_TDPE |
+	       (DBDMA_BURST_4 << DBDMA_TX_BST_SHIFT) |
+	       (DBDMA_BURST_4 << DBDMA_RX_BST_SHIFT) |
+	       (DBDMA_TX_ARBITRATION_DEFAULT) |
+	       (DBDMA_RX_ARBITRATION_DEFAULT)), ioaddr + DBDMA_CONTROL );
+
+	outl( 0, ioaddr + TX_THRESHOLD );
+
+	/* disable MAC loopback */
+	outl( (MAC_CONFIG_ITXA | MAC_CONFIG_RXEN | MAC_CONFIG_RETRYL |
+	       MAC_CONFIG_PADEN | (0x18 << 16)),
+	      ioaddr + MAC_CONFIG );
+
+	/* configure MAC */
+	outl( (MAC_CONFIG_ITXA | MAC_CONFIG_RXEN | MAC_CONFIG_RETRYL |
+	       MAC_CONFIG_PADEN | ( 0x18 << 16)), ioaddr + MAC_CONFIG );
+
+	outw( (0x1018), ioaddr + NBTOB_INTP_GAP );
+
+	/* clear and enable interrupts */
+	inw( ioaddr + INTERRUPT_CLEAR );
+	ncr885e_enable( dev );
+
+	/* and enable them in the chip */
+	outl( (INTERRUPT_INTE|INTERRUPT_TX_MASK|INTERRUPT_RX_MASK)<<16,
+	      ioaddr + INTERRUPT_ENABLE - 2);
+
+	if (ncr885e_debug > 3)
+		printk( KERN_INFO "%s: 53C885 config complete.\n", dev->name );
+
+	return;
+}
+
+
+
+/*
+   transmit interrupt  */
+
+static void
+ncr885e_tx( struct device *dev )
+
+{
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	volatile struct dbdma_cmd *cp, *dp;
+	unsigned short txbits, xfer;
+	int i;
+
+	del_timer( &sp->tx_timeout );
+
+	if (ncr885e_debug > 3)
+		printk( KERN_INFO "%s: ncr885e_tx: active=%d, dirty=%d, current=%d\n", 
+			dev->name, sp->tx_active, sp->tx_dirty, sp->tx_current );
+
+	sp->timeout_active = 0;
+
+	i = sp->tx_dirty;
+	cp = sp->tx_cmds + (i*3);
+	dp = cp+1;
+	sp->tx_active--;
+
+	xfer = inw( &dp->xfer_status );
+	txbits = inw( &sp->tx_status[i] );
+
+	if (ncr885e_debug > 4) {
+		show_dbdma_cmd( cp );
+		show_dbdma_cmd( dp );
+	}
+
+	/* get xmit result */
+	txbits = inw( &sp->tx_status[i] );
+
+	if (ncr885e_debug > 3)
+		printk( KERN_INFO "%s: tx xfer=%04x, txbits=%04x\n", dev->name,
+			xfer, txbits );
+
+	/* look for any channel status (?) */
+	if ( xfer ) {
+
+		dev_kfree_skb( sp->tx_skbufs[i] );
+		mark_bh( NET_BH );
+
+		if ( txbits & TX_STATUS_TXOK ) {
+			sp->stats.tx_packets++;
+			sp->stats.tx_bytes += inw( &cp->req_count );
+		}
+
+		/* dropped packets */
+		if ( txbits & (TX_STATUS_TDLC|TX_STATUS_TDEC) ) {
+			sp->stats.tx_dropped++;
+		}
+
+		/* add the collisions */
+		sp->stats.collisions += ( txbits & 0x04 );
+
+	}
+
+	dev->tbusy = 0;
+  
+	return;
+}
+
+/*  rx interrupt handling */
+static void
+ncr885e_rx( struct device *dev )
+
+{
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	volatile struct dbdma_cmd *cp;
+	struct sk_buff *skb;
+	int i, nb;
+	unsigned short status;
+	unsigned char *data, *stats;
+	unsigned long rxbits, ioaddr = dev->base_addr;
+
+	i = sp->rx_current;
+	cp = sp->rx_cmds + (i*2);
+
+	if (ncr885e_debug > 3)
+		printk( KERN_INFO "%s: ncr885e_rx dirty=%d, current=%d (cp@%p)\n",
+			dev->name, sp->rx_dirty, sp->rx_current, cp );
+
+	nb = inw( &cp->req_count ) - inw( &cp->res_count );
+	status = inw( &cp->xfer_status );
+
+	if (ncr885e_debug > 3)
+		printk( KERN_INFO "%s: (rx %d) bytes=%d, xfer_status=%04x\n", 
+			dev->name, i, nb, status );
+
+	if ( status ) {
+
+		skb = sp->rx_skbufs[i];
+		data = skb->data;
+		stats = data + nb - 3;
+		rxbits = (stats[0]|stats[1]<<8|stats[2]<<16);
+  
+		if (ncr885e_debug > 3)
+			printk( KERN_INFO "  rx_bits=%06lx\n", rxbits );
+
+		skb->dev = dev;
+		skb_put( skb, nb-3 );
+		skb->protocol = eth_type_trans( skb, dev );
+		netif_rx( skb );
+		sp->rx_skbufs[i] = 0;
+
+		if ( rxbits & RX_STATUS_RXOK ) {
+			sp->stats.rx_packets++;
+			sp->stats.rx_bytes += nb;
+		}
+
+		if ( rxbits & RX_STATUS_MCAST )
+			sp->stats.multicast++;
+
+	}
+
+	sp->rx_dirty = sp->rx_current;
+
+	if ( ++sp->rx_current >= NR_RX_RING )
+		sp->rx_current = 0;
+
+	/* fix up the one we just trashed */
+	cp = sp->rx_cmds + (sp->rx_dirty * 2);
+
+	skb = dev_alloc_skb( RX_BUFLEN + 2 );
+	if ( skb != 0 ) {
+		skb_reserve( skb, 2 );
+		sp->rx_skbufs[sp->rx_dirty] = skb;
+	}
+
+	if (ncr885e_debug > 2)
+		printk( KERN_INFO "%s: ncr885e_rx: using ring index %d, filling cp @ %p\n", 
+			dev->name, sp->rx_current, cp );
+  
+	outw( RX_BUFLEN, &cp->req_count );
+	outw( 0, &cp->res_count );
+	data = skb->data;
+	outl( virt_to_bus( data ), &cp->phy_addr );
+	outw( 0, &cp->xfer_status );
+
+	cp = sp->rx_cmds + (sp->rx_current * 2);
+
+	/* restart rx DMA */
+	outl( virt_to_bus( cp ), ioaddr + RX_CMD_PTR_LO );
+	outl( (RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN,
+	      ioaddr + RX_CHANNEL_CONTROL );
+
+	return;
+}
+
+static void
+ncr885e_misc_ints( struct device *dev, unsigned short status )
+
+{
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	struct dbdma_cmd *cp;
+	unsigned long ioaddr = dev->base_addr;
+
+	if (ncr885e_debug > 1)
+		printk( KERN_INFO "miscellaneous interrupt handled; status=%02x\n", 
+			status );
+
+	/* various transmit errors */
+	if ( status & 
+	     (INTERRUPT_PPET | INTERRUPT_PBFT | INTERRUPT_IIDT) ) {
+
+		/* illegal instruction in tx dma */
+		if ( status & INTERRUPT_IIDT ) {
+
+			cp = (struct dbdma_cmd *) bus_to_virt( inl( ioaddr + TX_CMD_PTR_LO ));
+			printk( KERN_INFO "%s: tx illegal insn:\n", dev->name );
+			printk( KERN_INFO " tx DBDMA - cmd = %p, status = %04x\n", 
+				cp, inw( ioaddr + TX_CHANNEL_STATUS ));
+			printk( KERN_INFO " command = %04x, phy_addr=%08x, req_count=%04x\n",
+				inw( &cp->command ), inw( &cp->phy_addr ), inw( &cp->req_count ));
+		}
+
+		if ( status & INTERRUPT_PPET )
+			printk( KERN_INFO "%s: tx PCI parity error\n", dev->name );
+ 
+		if ( status & INTERRUPT_PBFT )
+			printk( KERN_INFO "%s: tx PCI bus fault\n", dev->name );
+	}
+
+	/* look for rx errors */
+	if ( status &
+	     (INTERRUPT_PPER | INTERRUPT_PBFR | INTERRUPT_IIDR)) {
+
+		/* illegal instruction in rx dma */
+		if ( status & INTERRUPT_IIDR ) {
+#if 0
+			cmd = inl( ioaddr + RX_CMD_PTR_LO );      
+#endif
+			printk( KERN_ERR "%s: rx illegal DMA instruction:\n", dev->name );
+			printk( KERN_ERR "    channel status=%04x,\n",
+				inl( ioaddr + RX_CHANNEL_STATUS ));
+#if 0
+			show_dbdma_cmd( bus_to_virt( inl( ioaddr + RX_CMD_PTR_LO )));
+			printk( KERN_ERR "    instr (%08x) %08x %08x %08x\n",
+				(int) cmd, cmd[0], cmd[1], cmd[2] );
+#endif
+		}
+
+		/* PCI parity error */
+		if ( status & INTERRUPT_PPER )
+			printk( KERN_INFO "%s: rx PCI parity error\n", dev->name );
+
+		if ( status & INTERRUPT_PBFR )
+			printk( KERN_INFO "%s: rx PCI bus fault\n", dev->name );
+
+		sp->stats.rx_errors++;
+	}
+
+	if ( status & INTERRUPT_WI ) {
+		printk( KERN_INFO "%s: link pulse\n", dev->name );
+	}
+
+	/* bump any counters */
+  
+
+	return;
+}
+
+static void
+ncr885e_interrupt( int irq, void *dev_id, struct pt_regs *regs )
+
+{
+	struct device  *dev = (struct device *) dev_id;
+	struct ncr885e_private *sp;
+	unsigned short status;
+	int ioaddr;
+
+	if ( dev == NULL ) {
+		printk( KERN_ERR "symba: Interrupt IRQ %d for unknown device\n", irq );
+		return;
+	}
+
+	ioaddr = dev->base_addr;
+	sp = (struct ncr885e_private *) dev->priv;
+	spin_lock( &sp->lock );
+  
+	if ( dev->interrupt ) {
+		printk( KERN_ERR "%s: Re-entering interrupt handler...\n", 
+			dev->name );
+	}
+
+	dev->interrupt = 1;
+	status = inw( ioaddr + INTERRUPT_CLEAR );
+
+	if (ncr885e_debug > 2)
+		printk( KERN_INFO "%s: 53C885 interrupt 0x%02x\n", dev->name, status );
+
+	/* handle non-tx and rx interrupts first */
+	if ( status & ~(INTERRUPT_DIT|INTERRUPT_DIR))
+		ncr885e_misc_ints( dev, status );
+
+	/* look for tx interrupt: more to transmit, DBDMA stopped, or tx done */
+	if ( ( status & INTERRUPT_DIT ) ) {
+
+		if (ncr885e_debug > 2)
+			printk( KERN_INFO "%s: tx int; int=%02x, chan stat=%02x\n", 
+				dev->name, status, inw( ioaddr + TX_CHANNEL_STATUS ));
+
+		/* turn off timer */
+		del_timer( &sp->tx_timeout );
+		sp->timeout_active = 0;
+
+		/* stop DMA */
+		outl( TX_DBDMA_ENABLE << 16, ioaddr + TX_CHANNEL_CONTROL );
+
+		ncr885e_tx( dev );
+	}
+
+	if ( status & INTERRUPT_DIR ) {
+
+		if ( ncr885e_debug > 2 )
+			printk( KERN_INFO "%s: rx interrupt; int=%02x, rx channel stat=%02x\n", 
+				dev->name, status, inw( ioaddr + RX_CHANNEL_STATUS ));
+
+		/* stop DMA */
+		outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL );
+    
+		/* and handle the interrupt */
+		ncr885e_rx( dev );
+	}
+  
+	dev->interrupt = 0;
+	spin_unlock( &sp->lock );
+
+	return;
+}
+
+
+/*  doesn't set the address permanently, however... */
+static int 
+ncr885e_set_address( struct device *dev, void *addr )
+
+{
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	struct sockaddr *saddr = addr;
+	unsigned long  flags;
+	unsigned short reg[3];
+	unsigned char *ioaddr, *p;
+	int i;
+
+	memcpy( dev->dev_addr, saddr->sa_data, dev->addr_len );
+
+	p = (unsigned char *) dev->dev_addr;
+	printk( KERN_INFO "%s: setting new MAC address - ", dev->name );
+#if 0
+	for( p = (unsigned char *) dev->dev_addr, i=0; i < 6; i++, p++ ) 
+		printk("%c%2.2x", i ? ':' : ' ', *p );
+#endif
+
+
+	p = (unsigned char *) &reg;
+	for( i=0; i < 6; i++ )
+		p[i] = dev->dev_addr[i];
+
+#if 0
+	printk("%s: Setting new mac address - ", dev->name );
+	for( i=0; i < 6; i++ ) {
+		printk("%02x", i ? ':' : ' ', p[i] );
+	}
+
+	printk("\n");
+#endif
+
+	/* stop rx for the change */
+	outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL );
+  
+	spin_lock_irqsave( &sp->lock, flags );
+
+	ioaddr = (unsigned char *) dev->base_addr;
+
+	for( i = 0; i < 3; i++ ) {
+		reg[i] = ((reg[i] & 0xff) << 8) | ((reg[i] >> 8) & 0xff);
+		printk("%04x ", reg[i] );
+		outw( reg[i], ioaddr + STATION_ADDRESS_0 + (i*2));
+	}
+	printk("\n");
+
+	spin_unlock_irqrestore( &sp->lock, flags );
+
+	/* restart rx */
+	outl((RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, 
+	     ioaddr + RX_CHANNEL_CONTROL );
+
+	return 0;
+}
+
+static void 
+ncr885e_tx_timeout( unsigned long data )
+
+{
+	struct device *dev = (struct device *) data;
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	unsigned long flags, ioaddr;
+	int i;
+  
+	save_flags( flags );
+	cli();
+
+	ioaddr = dev->base_addr;
+	sp->timeout_active = 0;
+	i = sp->tx_dirty;
+
+	/* if we weren't active, bail... */
+	if ( sp->tx_active == 0 ) {
+		printk( KERN_INFO "%s: ncr885e_timeout...tx not active!\n", dev->name );
+		goto out;
+	}
+
+	printk( KERN_ERR "%s: 53C885 timed out.  Resetting...\n", dev->name );
+
+	/* disable rx and tx DMA */
+	outl( (TX_DBDMA_ENABLE << 16), ioaddr + TX_CHANNEL_CONTROL );
+	outl( (RX_DBDMA_ENABLE << 16), ioaddr + RX_CHANNEL_CONTROL );
+
+	/* reset the chip */
+	ncr885e_config( dev );
+	ncr885e_enable( dev );
+
+	/* clear the wedged skb in the tx ring */
+	sp->tx_active = 0;
+	++sp->stats.tx_errors;
+  
+	if ( sp->tx_skbufs[i] ) {
+		dev_kfree_skb( sp->tx_skbufs[i] );
+		sp->tx_skbufs[i] = 0;
+	}
+
+	/* start anew from the beginning of the ring buffer (why not?) */
+	sp->tx_current = 0;
+	dev->tbusy = 0;
+	mark_bh( NET_BH );
+
+	/* restart rx dma */
+	outl( (RX_DBDMA_ENABLE << 16) | RX_CHANNEL_RUN,
+	      ioaddr + RX_CHANNEL_CONTROL );  
+ out:
+
+	restore_flags( flags );
+}
+
+static inline void
+ncr885e_set_timeout( struct device *dev )
+
+{
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if ( sp->timeout_active )
+		del_timer( &sp->tx_timeout );
+
+	sp->tx_timeout.expires = jiffies + TX_TIMEOUT;
+	sp->tx_timeout.function = ncr885e_tx_timeout;
+	sp->tx_timeout.data = (unsigned long) dev;
+	add_timer( &sp->tx_timeout );
+	sp->timeout_active = 1;
+	restore_flags( flags );
+}
+
+
+/*
+ *  The goal is to set up DBDMA such that the rx ring contains only
+ *  one DMA descriptor per ring element and the tx ring has two (using
+ *  the cool features of branch- and wait-select.  However, I'm not sure
+ *  if it's possible.  For now, we plod through it with 3 descriptors
+ *  for tx, and two for rx.
+ */
+
+static int
+ncr885e_open( struct device *dev )
+
+{
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	struct sk_buff *skb;
+	int i, size;
+	char *data;
+	struct dbdma_cmd *cp;
+	unsigned long flags;
+
+	/* allocate enough space for the tx and rx rings and a STOP descriptor */  
+	size = (sizeof( struct dbdma_cmd ) *
+		((NR_TX_RING * 3) + (NR_RX_RING * 2) + 1));
+
+	cp = kmalloc( size, GFP_KERNEL );
+
+	if ( cp == 0 ) {
+		printk( KERN_ERR "Insufficient memory (%d bytes) for DBDMA\n", size );
+		return -ENOMEM;
+	}
+
+	spin_lock_init( &sp->lock );
+	spin_lock_irqsave( &sp->lock, flags );
+
+	memset((char *) cp, 0, size );
+	sp->head = cp;
+
+	sp->stop_cmd = cp;
+	outl( DBDMA_STOP, &cp->command );
+  
+	sp->rx_cmds = ++cp;
+
+	for( i = 0; i < NR_RX_RING; i++ ) {
+
+		cp = sp->rx_cmds + (i*2);
+		skb = dev_alloc_skb( RX_BUFLEN + 2 );
+
+		/* if there is insufficient memory, make this last ring use a 
+		   static buffer and leave the loop with that skb as final one */
+		if ( skb == 0 ) {
+			printk( KERN_ERR "%s: insufficient memory for rx ring buffer\n",
+				dev->name );
+			break;
+		}
+
+		skb_reserve( skb, 2 );
+		sp->rx_skbufs[i] = skb;
+		data = skb->data;
+
+		/* The DMA commands here are done such that an EOP is the only
+		   way that we should get an interrupt.  This means that we could
+		   fill more than one skbuff before getting the interrupt at EOP. */
+
+		/* Handle rx DMA such that it always interrupts.... */
+		outw( (INPUT_MORE|INTR_ALWAYS), &cp->command );
+		outw( RX_BUFLEN, &cp->req_count );
+		outw( 0, &cp->res_count );
+		outl( virt_to_bus( data ), &cp->phy_addr );
+		outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep );
+		outw( 0, &cp->xfer_status );
+#if 0
+		printk( KERN_INFO "rx at %p\n", cp );
+		show_dbdma_cmd( cp );
+#endif
+		++cp;
+
+		outw( DBDMA_STOP, &cp->command );
+
+	}
+
+	/* initialize to all rx buffers are available, fill limit is the end */
+	sp->rx_dirty = 0;
+	sp->rx_current = 0;
+
+	/* fill the tx ring */
+	sp->tx_cmds = cp+1;
+
+	for( i = 0; i < NR_TX_RING; i++ ) {
+
+		/* minimal setup for tx command */
+		cp = sp->tx_cmds + (i*3);
+		outw( OUTPUT_LAST, &cp->command );
+		if (ncr885e_debug > 3) {
+			printk( KERN_INFO "tx OUTPUT_LAST at %p\n", cp );
+			show_dbdma_cmd( cp );
+		}
+
+		/* full setup for the status cmd */
+		cp++;
+		outw( INPUT_LAST|INTR_ALWAYS|WAIT_IFCLR, &cp->command );
+		outl( virt_to_bus( &sp->tx_status[i] ), &cp->phy_addr );
+		outw( 2, &cp->req_count );
+		if ( ncr885e_debug > 3) {
+			printk( KERN_INFO "tx INPUT_LAST cmd at %p\n", cp );
+			show_dbdma_cmd( cp );
+		}
+
+		++cp;
+		outw( DBDMA_STOP, &cp->command );
+ 
+	}
+#if 0
+	/* chain the last tx DMA command to the STOP cmd */
+	outw((INPUT_LAST|INTR_ALWAYS|BR_ALWAYS), &cp->command );
+	outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep );
+#endif
+	sp->tx_active = 0;
+	sp->tx_current = 0;
+	sp->tx_dirty = 0;
+
+	spin_unlock_irqrestore( &sp->lock, flags );
+
+	/* the order seems important here for some reason.  If the MPIC isn't
+	   enabled before the ethernet chip is enabled, shrapnel from the
+	   bootloader causes us to receive interrupts even though we've not 
+	   yet enabled the tx channel.  Go figure.  It'd be better to configure
+	   the chip in the probe1() routine, but then we don't see interrupts
+	   at all.  Everything looks all right on the logic analyzer, but... */
+
+	ncr885e_config( dev );
+
+	/* enable ethernet interrupts */
+	if ( request_irq( dev->irq, &ncr885e_interrupt, SA_SHIRQ, chipname, dev )) {
+		printk( KERN_ERR "%s: can't get irq %d\n", dev->name, dev->irq );
+		return -EAGAIN;
+	}
+
+	(void) inw( ioaddr + INTERRUPT_CLEAR );
+
+	ncr885e_enable( dev );
+
+	/* start rx DBDMA */
+	outl( virt_to_bus( sp->rx_cmds ), ioaddr + RX_CMD_PTR_LO );
+	outl( (RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN,
+	      ioaddr + RX_CHANNEL_CONTROL );
+
+	dev->start = 1;
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int
+ncr885e_xmit_start( struct sk_buff *skb, struct device *dev )
+
+{
+	struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv;
+	volatile struct dbdma_cmd *cp, *dp;
+	unsigned long flags, ioaddr = dev->base_addr;
+	int len, next, fill, entry;
+
+	if ( ncr885e_debug > 3)
+		printk( KERN_INFO "%s: xmit_start len=%d, dirty=%d, current=%d, active=%d\n",
+			dev->name, skb->len, sp->tx_dirty, sp->tx_current, sp->tx_active );
+
+	spin_lock_irqsave( &sp->lock, flags );
+
+	/* find the free slot in the ring buffer */
+	fill = sp->tx_current;
+	next = fill + 1;
+
+	if ( next >= NR_TX_RING )
+		next = 0;
+
+	/* mark ourselves as busy, even if we have too many packets waiting */
+	dev->tbusy = 1;
+
+	/* see if it's necessary to defer this packet */
+	if ( sp->tx_active >= MAX_TX_ACTIVE ) {
+		spin_unlock_irqrestore( &sp->lock, flags );
+		return -1;
+	}
+
+	sp->tx_active++;  /* bump "active tx" count */
+	sp->tx_current = next;  /* and show that we've used this buffer */
+	sp->tx_dirty = fill;     /* and mark this one to get picked up */
+
+	len = skb->len;
+
+	if ( len > ETH_FRAME_LEN ) {
+		printk( KERN_DEBUG "%s: xmit frame too long (%d)\n", dev->name, len );
+		len = ETH_FRAME_LEN;
+	}  
+
+	/* get index into the tx DBDMA chain */
+	entry = fill * 3;
+	sp->tx_skbufs[fill] = skb;
+	cp = sp->tx_cmds + entry;
+	dp = cp + 1;
+
+	/* update the rest of the OUTPUT_MORE descriptor */
+	outw( len, &cp->req_count );
+	outl( virt_to_bus( skb->data ), &cp->phy_addr );
+	outw( 0, &cp->xfer_status );
+	outw( 0, &cp->res_count );
+
+	/* and finish off the INPUT_MORE */
+	outw( 0, &dp->xfer_status );
+	outw( 0, &dp->res_count );
+	sp->tx_status[fill] = 0;
+	outl( virt_to_bus( &sp->tx_status[fill] ), &dp->phy_addr );
+
+	if ( ncr885e_debug > 2 )
+		printk(KERN_INFO "%s: xmit_start: active %d, tx_current %d, tx_dirty %d\n",
+		       dev->name, sp->tx_active, sp->tx_current, sp->tx_dirty );
+
+	if ( ncr885e_debug > 4 ) {
+		show_dbdma_cmd( cp );
+		show_dbdma_cmd( dp );
+	}
+
+
+	/* restart the tx DMA engine */
+	outl( virt_to_bus( cp ), ioaddr + TX_CMD_PTR_LO );
+	outl( (TX_DBDMA_ENABLE << 16)|TX_CHANNEL_RUN, 
+	      ioaddr + TX_CHANNEL_CONTROL );
+
+	ncr885e_set_timeout( dev );
+
+	spin_unlock_irqrestore( &sp->lock, flags );
+	dev->trans_start = jiffies;
+
+	return 0;      
+}
+
+static int
+ncr885e_close(struct device *dev)
+
+{
+	int i;
+	struct ncr885e_private *np = (struct ncr885e_private *) dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	spin_lock( &np->lock );
+
+	printk(KERN_INFO "%s: NCR885E Ethernet closing...\n", dev->name );
+
+	if (ncr885e_debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down Ethernet chip\n", dev->name);
+
+	ncr885e_disable(dev);
+
+	del_timer(&np->tx_timeout);
+
+	/* flip off rx and tx */
+	outl( (RX_DBDMA_ENABLE << 16), ioaddr + RX_CHANNEL_CONTROL );
+	outl( (TX_DBDMA_ENABLE << 16), ioaddr + TX_CHANNEL_CONTROL );  
+
+	/* free up the IRQ */
+	free_irq( dev->irq, dev );
+
+	for( i = 0; i < NR_RX_RING; i++ ) {
+		if (np->rx_skbufs[i])
+			dev_kfree_skb( np->rx_skbufs[i] );
+		np->rx_skbufs[i] = 0;
+	}
+#if 0
+	for (i = 0; i < NR_TX_RING; i++) {
+		if (np->tx_skbufs[i])
+			dev_kfree_skb(np->tx_skbufs[i]);
+		np->tx_skbufs[i] = 0;
+	}
+#endif
+	spin_unlock( &np->lock );
+
+	kfree( np->head );
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+
+/*
+ *  multicast promiscuous mode isn't used here.  Allow code in the
+ *  IP stack to determine which multicast packets are good or bad....
+ *  (this avoids having to use the hash table registers)
+ */
+static void
+ncr885e_set_multicast( struct device *dev )
+
+{
+	int ioaddr = dev->base_addr;
+
+	if ( ncr885e_debug > 3 )
+		printk("%s: set_multicast: dev->flags = %x, AF=%04x\n", 
+		       dev->name, dev->flags, inw( ioaddr + ADDRESS_FILTER ));
+
+	if ( dev->flags & IFF_PROMISC ) {
+		printk( KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name );
+		outw( ADDRESS_RPPRO, ioaddr + ADDRESS_FILTER );
+	}
+
+	/* accept all multicast packets without checking the mc_list.  */
+	else if ( dev->flags & IFF_ALLMULTI ) {
+		printk( KERN_INFO "%s: Enabling all multicast packets.\n", 
+			dev->name );
+		outw( ADDRESS_RPPRM, ioaddr + ADDRESS_FILTER );
+	}
+
+	/* enable broadcast rx */
+	else {
+		outw( ADDRESS_RPABC, ioaddr + ADDRESS_FILTER );
+	}  
+}
+
+static struct net_device_stats *
+ncr885e_stats( struct device *dev )
+
+{
+	struct ncr885e_private *np = (struct ncr885e_private *) dev->priv;
+
+	return &np->stats;
+}
+
+/*  By this function, we're certain that we have a 885 Ethernet controller
+ *  so we finish setting it up and wrap up all the required Linux ethernet
+ *  configuration.
+ */
+
+static int
+ncr885e_probe1( struct device *dev, unsigned long ioaddr, unsigned char irq )
+
+{
+	struct ncr885e_private *sp;
+	unsigned short station_addr[3], val;
+	unsigned char *p;
+	int  i;
+
+	dev = init_etherdev( dev, 0 );
+
+	/* construct private data for the 885 ethernet */
+	dev->priv = kmalloc( sizeof( struct ncr885e_private ), GFP_KERNEL );
+
+	if ( dev->priv == NULL )
+		return -ENOMEM;
+
+	sp = (struct ncr885e_private *) dev->priv;
+	memset( sp, 0, sizeof( struct ncr885e_private ));
+
+	/* snag the station address and display it */
+	for( i = 0; i < 3; i++ ) {
+		val = inw( ioaddr + STATION_ADDRESS_0 + (i*2));
+		station_addr[i] = ((val >> 8) & 0xff) | ((val << 8) & 0xff00);
+	}
+
+	printk( KERN_INFO "%s: %s at %08lx,", dev->name, chipname, ioaddr );
+
+	p = (unsigned char *) &station_addr;
+
+	for( i=0; i < 6; i++ ) {
+		dev->dev_addr[i] = *p;
+		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i] );
+		p++;
+	}
+
+	printk(", IRQ %d.\n", irq );
+
+	request_region( ioaddr, NCR885E_TOTAL_SIZE, dev->name );
+
+	/* set up a timer */
+	init_timer( &sp->tx_timeout );
+	sp->timeout_active = 0;
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	ether_setup( dev );
+
+	/* everything else */
+	dev->open = ncr885e_open;
+	dev->stop = ncr885e_close;
+	dev->get_stats = ncr885e_stats;
+	dev->hard_start_xmit = ncr885e_xmit_start;
+	dev->set_multicast_list = ncr885e_set_multicast;
+	dev->set_mac_address = ncr885e_set_address;
+
+	return 0;
+}
+
+/*  Since the NCR 53C885 is a multi-function chip, I'm not worrying about
+ *  trying to get the the device(s) in slot order.  For our (Synergy's)
+ *  purpose, there's just a single 53C885 on the board and we don't 
+ *  worry about the rest.
+ */
+
+int __init ncr885e_probe( struct device *dev )
+{
+	struct pci_dev *pdev = NULL;
+	unsigned int ioaddr, chips = 0;
+	unsigned short cmd;
+	unsigned char irq, latency;
+
+	while(( pdev = pci_find_device( PCI_VENDOR_ID_NCR, 
+					PCI_DEVICE_ID_NCR_53C885_ETHERNET,
+					pdev )) != NULL ) {
+
+		if ( !print_version ) {
+			print_version++;
+			printk( KERN_INFO "%s", version );
+		}
+
+		/* Use I/O space */
+		pci_read_config_dword( pdev, PCI_BASE_ADDRESS_0, &ioaddr );
+		pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq );
+
+		ioaddr &= ~3;
+		/* Adjust around the Grackle... */
+#ifdef CONFIG_GEMINI
+		ioaddr |= 0xfe000000;
+#endif
+
+		if ( check_region( ioaddr, NCR885E_TOTAL_SIZE ))
+			continue;
+
+		/* finish off the probe */
+		if ( !(ncr885e_probe1( dev, ioaddr, irq ))) {
+
+			chips++;
+
+			/* Access is via I/O space, bus master enabled... */
+			pci_read_config_word( pdev, PCI_COMMAND, &cmd );
+
+			if ( !(cmd & PCI_COMMAND_MASTER) ) {
+				printk( KERN_INFO "  PCI master bit not set! Now setting.\n");
+				cmd |= PCI_COMMAND_MASTER;
+				pci_write_config_word( pdev, PCI_COMMAND, cmd );
+			}
+
+			if ( !(cmd & PCI_COMMAND_IO) ) {
+				printk( KERN_INFO "  Enabling I/O space.\n" );
+				cmd |= PCI_COMMAND_IO;
+				pci_write_config_word( pdev, PCI_COMMAND, cmd );
+			}
+
+			pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &latency );
+
+			if ( latency < 10 ) {
+				printk( KERN_INFO "  PCI latency timer (CFLT) is unreasonably"
+					" low at %d.  Setting to 255.\n", latency );
+				pci_write_config_byte( pdev, PCI_LATENCY_TIMER, 255 );
+			}
+		}
+	}
+
+	if ( !chips )
+		return -ENODEV;
+	else
+		return 0;
+}
+
+/* debugging to peek at dma descriptors */
+static void
+show_dbdma_cmd( volatile struct dbdma_cmd *cmd )
+
+{
+	printk( KERN_INFO " cmd %04x, physaddr %08x, req_count %04x\n",
+		inw( &cmd->command ), inl( &cmd->phy_addr ), inw( &cmd->req_count ));
+	printk( KERN_INFO " res_count %04x, xfer_status %04x, branch %08x\n", 
+		inw( &cmd->res_count ), inw( &cmd->xfer_status ),inl( &cmd->cmd_dep ));
+}
+
+#if 0
+static int
+read_eeprom( unsigned int ioaddr, int location )
+
+{
+	int loop;
+	unsigned char val;
+
+	outb( (location & 0xff), ioaddr + EE_WORD_ADDR );
+
+	/* take spillover from location in control reg */
+	outb(EE_CONTROL_RND_READB | (location & (0x7<<8)), ioaddr + EE_CONTROL);
+
+	loop = 1000;
+	while( (inb( ioaddr + EE_STATUS) & EE_SEB) &&
+	       (loop > 0) ) {
+		udelay( 10 );
+		loop--;
+	}
+
+	if ( inb( ioaddr + EE_STATUS ) & EE_SEE ) {
+		printk("%s: Serial EEPROM read error\n", chipname);
+		val = 0xff;
+	}
+
+	else 
+		val = inb( ioaddr + EE_READ_DATA );
+
+	return (int) val;
+}
+#endif
+
+#ifdef NCR885E_DEBUG_MII
+static void
+show_mii( unsigned long ioaddr )
+
+{
+	int   phyctrl, phystat, phyadvert, phypartner, phyexpan;
+
+	phyctrl = read_mii( ioaddr, MII_AUTO_NEGOTIATION_CONTROL );
+	phystat = read_mii( ioaddr, MII_AUTO_NEGOTIATION_STATUS );
+	phyadvert = read_mii( ioaddr, MII_AUTO_NEGOTIATION_ADVERTISEMENT );
+	phypartner = read_mii( ioaddr, MII_AUTO_NEGOTIATION_LINK_PARTNER );
+	phyexpan = read_mii( ioaddr, MII_AUTO_NEGOTIATION_EXPANSION );
+
+	printk( KERN_INFO "PHY: advert=%d %s, partner=%s %s, link=%d, %s%s\n",
+		(phyadvert & MANATECH_100BASETX_FULL_DUPLEX ? 100 : 10),
+		(phyctrl & MANC_AUTO_NEGOTIATION_ENABLE ? "auto" : "fixed"),
+		(phypartner & MANLP_ACKNOWLEDGE ?
+		 (phypartner & MANATECH_100BASETX_FULL_DUPLEX ? "100" : "10") :
+		 "?"),
+		(phyexpan & MANE_LINK_PARTNER_AUTO_ABLE ? "auto" : "fixed"),
+		(phyctrl & MANC_PHY_SPEED_100 ? 100 : 10),
+		(phystat & MANS_LINK_STATUS ? "up" : "down"),
+		(phyexpan & MANE_PARALLEL_DETECTION_FAULT ? " PD-fault" : "" ));
+	return;
+}
+
+
+static int
+read_mii( unsigned long ioaddr, int reg )
+
+{
+	int    timeout;
+
+
+	timeout = 100000;
+
+	while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) {
+
+		if ( timeout-- < 0 ) {
+			printk( KERN_INFO "Timed out waiting for MII\n" );
+			return -1;
+		}
+	}
+
+	outw( (1<<8) + reg, ioaddr + MII_ADDRESS );
+	outw( MIIM_RSTAT, ioaddr + MIIM_COMMAND );
+
+	timeout = 100000;
+	while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) {
+		if ( timeout-- < 0 ) {
+			printk( KERN_INFO "Timed out waiting for MII\n" );
+			return -1;
+		}
+	}
+
+	return( inw( ioaddr + MII_READ_DATA ));
+}
+
+static void
+write_mii( unsigned long ioaddr, int reg, int data )
+
+{
+	int timeout=100000;
+
+	printk( KERN_INFO "MII indicator: %02x\n", inw( ioaddr + MII_INDICATOR ));
+
+	while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) {
+		if ( timeout-- <= 0 ) {
+			printk( KERN_INFO "Timeout waiting to write to MII\n" );
+			return;
+		}
+		udelay( 10 );
+	}
+
+	outw( (1<<8) + reg, ioaddr + MII_ADDRESS );
+	outw( data, ioaddr + MII_WRITE_DATA );
+
+	return;
+}
+
+#endif /* NCR885E_DEBUG_MII */
+
+#ifdef MODULE
+#if defined(LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("dan@synergymicro.com");
+MODULE_DESCRIPTION("Symbios 53C885 Ethernet driver");
+MODULE_PARM(debug, "i");
+#endif 
+
+static int debug = 1;
+
+int
+init_module(void)
+{
+	if ( debug >= 0)
+		ncr885e_debug = debug;
+
+	return ncr885e_probe( NULL );
+}
+
+void
+cleanup_module(void)
+{
+	struct ncr885e_private *np;
+
+	if ( root_dev ) {
+
+		unregister_netdev( root_dev );
+		np = (struct ncr885e_private *) root_dev->priv;
+		release_region( root_dev->base_addr, NCR885E_TOTAL_SIZE );
+		kfree( root_dev->priv );
+		root_dev = NULL;
+	}  
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O6 -c symba.c"
+ * End:
+ */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/ncr885e.h linux.14p6/drivers/net/ncr885e.h
--- linux.vanilla/drivers/net/ncr885e.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/ncr885e.h	Sat Nov 13 15:15:45 1999
@@ -0,0 +1,367 @@
+#ifndef _NET_H_SYMBA
+#define _NET_H_SYMBA
+
+/* transmit status bit definitions */
+#define TX_STATUS_TXOK         (1<<13)     /* success */
+#define TX_STATUS_TDLC         (1<<12)     /* dropped for late colls */
+#define TX_STATUS_TCXSDFR      (1<<11)     /* excessive deferral */
+#define TX_STATUS_TDEC         (1<<10)     /* excessive collisions */
+#define TX_STATUS_TAUR         (1<<9)      /* abort on underrun/"jumbo" */
+#define TX_STATUS_PDFRD        (1<<8)      /* packet deferred */
+#define TX_STATUS_BCAST        (1<<7)      /* broadcast ok */
+#define TX_STATUS_MCAST        (1<<6)      /* multicast ok */
+#define TX_STATUS_CRCERR       (1<<5)      /* CRC error */
+#define TX_STATUS_LC           (1<<4)      /* late collision */
+#define TX_STATUS_CCNT_MASK    0xf         /* collision count */
+
+#define T_TXOK      (1<<13)
+#define T_TDLC      (1<<12)
+#define T_TCXSDFR   (1<<11)
+#define T_TDEC      (1<<10)
+#define T_TAUR      (1<<9)
+#define T_PDFRD     (1<<8)
+#define T_BCAST     (1<<7)
+#define T_MCAST     (1<<6)
+#define T_LC        (1<<4)
+#define T_CCNT_MASK 0xf
+
+/* receive status bit definitions */
+#define RX_STATUS_RXOVRN       (1<<23)     /* overrun */
+#define RX_STATUS_CEPS         (1<<22)     /* carrier event already seen */
+#define RX_STATUS_RXOK         (1<<21)     /* success */
+#define RX_STATUS_BCAST        (1<<20)     /* broadcast ok */
+#define RX_STATUS_MCAST        (1<<19)     /* multicast ok */
+#define RX_STATUS_CRCERR       (1<<18)     /* CRC error */
+#define RX_STATUS_DR           (1<<17)     /* dribble nibble */
+#define RX_STATUS_RCV          (1<<16)     /* rx code violation */
+#define RX_STATUS_PTL          (1<<15)     /* pkt > 1518 bytes */
+#define RX_STATUS_PTS          (1<<14)     /* pkt < 64 bytes */
+#define RX_STATUS_LEN_MASK     0x1fff      /* length mask */
+
+#define EEPROM_LENGTH          100
+
+
+/*  Serial EEPROM interface  */
+#define EE_STATUS              0xf0
+#define EE_CONTROL             0xf1
+#define EE_WORD_ADDR           0xf2
+#define EE_READ_DATA           0xf3
+#define EE_WRITE_DATA          0xf4
+#define EE_FEATURE_ENB         0xf5
+
+/*  Use on EE_STATUS  */
+#define EE_SEB                 (1<<8)
+#define EE_SEE                     1
+
+/*  Serial EEPROM commands */
+#define EE_CONTROL_SEQ_READB   (1<<4)
+#define EE_CONTROL_RND_WRITEB  (1<<5)
+#define EE_CONTROL_RND_READB   ((1<<4)|(1<<5))
+
+/*  Enable writing to serial EEPROM */
+#define EE_WRITE_ENB                1
+
+/*  The 885 configuration register */
+#define MAC_CONFIG             0xa0
+#define  MAC_CONFIG_SRST       1<<15
+#define  MAC_CONFIG_ITXA       1<<13
+#define  MAC_CONFIG_RXEN       1<<12
+#define  MAC_CONFIG_INTLB      1<<10
+#define  MAC_CONFIG_MODE_MASK  (1<<8|1<<9)
+#define  MAC_CONFIG_MODE_TP    1<<8
+#define  MAC_CONFIG_HUGEN      1<<5
+#define  MAC_CONFIG_RETRYL     1<<4
+#define  MAC_CONFIG_CRCEN      1<<3
+#define  MAC_CONFIG_PADEN      1<<2
+#define  MAC_CONFIG_FULLD      1<<1
+#define  MAC_CONFIG_NOCFR      1<<0
+
+
+
+
+
+#define TX_WAIT_SELECT         0x18
+#define RX_CHANNEL_CONTROL     0x40
+
+/* Tx channel status */
+#define TX_DBDMA_REG           0x00
+#define TX_CHANNEL_CONTROL     0x00
+#define TX_CHANNEL_STATUS      0x04
+#define  TX_STATUS_RUN         1<<15
+#define  TX_STATUS_PAUSE       1<<14
+#define  TX_STATUS_WAKE        1<<12
+#define  TX_STATUS_DEAD        1<<11
+#define  TX_STATUS_ACTIVE      1<<10
+#define  TX_STATUS_BT          1<<8
+#define  TX_STATUS_TXABORT     1<<7
+#define  TX_STATUS_TXSR        1<<6
+
+#define  TX_CHANNEL_RUN        TX_STATUS_RUN
+#define  TX_CHANNEL_PAUSE      TX_STATUS_PAUSE
+#define  TX_CHANNEL_WAKE       TX_STATUS_WAKE
+#define  TX_CHANNEL_DEAD       TX_STATUS_DEAD
+#define  TX_CHANNEL_ACTIVE     TX_STATUS_ACTIVE
+#define  TX_CHANNEL_BT         TX_STATUS_BT
+#define  TX_CHANNEL_TXABORT    TX_STATUS_TXABORT
+#define  TX_CHANNEL_TXSR       TX_STATUS_TXSR
+
+#define  TX_DBDMA_ENABLE       (TX_CHANNEL_WAKE | TX_CHANNEL_PAUSE | \
+                                TX_CHANNEL_RUN )
+
+/* Transmit command ptr lo register */
+#define TX_CMD_PTR_LO          0x0c
+
+/* Transmit interrupt select register */
+#define TX_INT_SELECT          0x10
+
+/* Transmit branch select register */
+#define TX_BRANCH_SELECT       0x14
+
+/* Transmit wait select register */
+#define TX_WAIT_SELECT         0x18
+#define  TX_WAIT_STAT_RECV     0x40
+
+/* Rx channel status */
+#define RX_DBDMA_REG           0x40
+#define RX_CHANNEL_CONTROL     0x40
+#define RX_CHANNEL_STATUS      0x44
+#define  RX_STATUS_RUN         1<<15
+#define  RX_STATUS_PAUSE       1<<14
+#define  RX_STATUS_WAKE        1<<12
+#define  RX_STATUS_DEAD        1<<11
+#define  RX_STATUS_ACTIVE      1<<10
+#define  RX_STATUS_BT          1<<8
+#define  RX_STATUS_EOP         1<<6
+
+#define  RX_CHANNEL_RUN        RX_STATUS_RUN
+#define  RX_CHANNEL_PAUSE      RX_STATUS_PAUSE
+#define  RX_CHANNEL_WAKE       RX_STATUS_WAKE
+#define  RX_CHANNEL_DEAD       RX_STATUS_DEAD
+#define  RX_CHANNEL_ACTIVE     RX_STATUS_ACTIVE
+#define  RX_CHANNEL_BT         RX_STATUS_BT
+#define  RX_CHANNEL_EOP        RX_STATUS_EOP
+
+#define  RX_DBDMA_ENABLE       (RX_CHANNEL_WAKE | RX_CHANNEL_PAUSE | \
+                                RX_CHANNEL_RUN)
+
+/*  Receive command ptr lo  */
+#define RX_CMD_PTR_LO          0x4c
+
+/*  Receive interrupt select register */
+#define RX_INT_SELECT          0x50
+#define  RX_INT_SELECT_EOP     0x40
+
+/*  Receive branch select  */
+#define RX_BRANCH_SELECT       0x54
+#define  RX_BRANCH_SELECT_EOP  0x40
+
+/*  Receive wait select  */
+#define RX_WAIT_SELECT         0x58
+#define  RX_WAIT_SELECT_EOP    0x40
+
+/*  Event status register  */
+#define EVENT_STATUS           0x80
+#define  EVENT_TXSR            1<<2
+#define  EVENT_EOP             1<<1
+#define  EVENT_TXABORT         1<<0
+
+/*  Interrupt enable register  */
+#define INTERRUPT_ENABLE       0x82
+
+/*  Interrupt clear register  */
+#define INTERRUPT_CLEAR        0x84
+
+/*  Interrupt status register  */
+#define INTERRUPT_STATUS_REG   0x86
+
+/*  bits for the above three interrupt registers */
+#define  INTERRUPT_INTE        1<<15   /* interrupt enable */
+#define  INTERRUPT_WI          1<<9    /* wakeup interrupt */
+#define  INTERRUPT_ERI         1<<8    /* early recieve interrupt */
+#define  INTERRUPT_PPET        1<<7    /* PCI Tx parity error */
+#define  INTERRUPT_PBFT        1<<6    /* PCI Tx bus fault */
+#define  INTERRUPT_IIDT        1<<5    /* illegal instruction Tx */
+#define  INTERRUPT_DIT         1<<4    /* DBDMA Tx interrupt */
+#define  INTERRUPT_PPER        1<<3    /* PCI Rx parity error */
+#define  INTERRUPT_PBFR        1<<2    /* PCI Rx bus fault */
+#define  INTERRUPT_IIDR        1<<1    /* illegal instruction Rx */
+#define  INTERRUPT_DIR         1<<0    /* DBDMA Rx interrupt */
+
+#define  INTERRUPT_TX_MASK     (INTERRUPT_PBFT|INTERRUPT_IIDT| \
+                                INTERRUPT_PPET|INTERRUPT_DIT)
+#define  INTERRUPT_RX_MASK     (INTERRUPT_PBFR|INTERRUPT_IIDR| \
+                                INTERRUPT_PPER|INTERRUPT_DIR)
+
+/*  chip revision register */
+#define CHIP_REVISION_REG      0x8c
+#define  CHIP_PCIREV_MASK      (0xf<<16)
+#define  CHIP_PCIDEV_MASK      0xff
+
+/*  Tx threshold register */
+#define TX_THRESHOLD           0x94
+
+/*  General purpose register */
+#define GEN_PURPOSE_REG        0x9e
+
+/*  General purpose pin control reg */
+#define GEN_PIN_CONTROL_REG    0x9f
+
+/*  DBDMA control register  */
+#define DBDMA_CONTROL          0x90
+#define  DBDMA_SRST            1<<31
+#define  DBDMA_TDPCE           1<<23
+#define  DBDMA_BE              1<<22
+#define  DBDMA_TAP_MASK        (1<<19|1<<20|1<<21)
+#define  DBDMA_RAP_MASK        (1<<16|1<<17|1<<18)
+#define  DBDMA_DPMRLE          1<<15
+#define  DBDMA_WIE             1<<14
+#define  DBDMA_MP              1<<13
+#define  DBDMA_SME             1<<12
+#define  DBDMA_CME             1<<11
+#define  DBDMA_DDPE            1<<10
+#define  DBDMA_TDPE            1<<9
+#define  DBDMA_EXTE            1<<8
+#define  DBDMA_BST_MASK        (1<<4|1<<5|1<<6)
+#define  DBDMA_BSR_MASK        (1<<0|1<<1|1<<2)
+
+#define  DBDMA_BURST_1         (0x00)
+#define  DBDMA_BURST_2         (0x01)
+#define  DBDMA_BURST_4         (0x02)
+#define  DBDMA_BURST_8         (0x03)
+#define  DBDMA_BURST_16        (0x04)
+#define  DBDMA_BURST_32        (0x05)
+#define  DBDMA_BURST_64        (0x06)
+#define  DBDMA_BURST_128       (0x07)
+
+#define  DBDMA_TX_BST_SHIFT    (4)
+#define  DBDMA_RX_BST_SHIFT    (0)
+
+#define  DBDMA_TX_ARBITRATION_DEFAULT ( 1 << 19 )
+#define  DBDMA_RX_ARBITRATION_DEFAULT ( 2 << 16 )
+
+
+/*  Back-to-back interpacket gap register */
+#define BTOB_INTP_GAP          0xa2
+#define  BTOB_INTP_DEFAULT     0x18
+
+/*  Non-back-to-back interpacket gap register */
+#define NBTOB_INTP_GAP         0xa4
+
+/*  MIIM command register */
+#define MIIM_COMMAND           0xa6
+#define  MIIM_SCAN             1<<1
+#define  MIIM_RSTAT            1<<0
+
+/*  MII address register */
+#define MII_ADDRESS            0xa8
+#define  MII_FIAD_MASK         (1<<8|1<<9|1<<10|1<<11|1<<12)
+#define  MII_RGAD_MASK         (1<<0|1<<1|1<<2|1<<3|1<<4)
+
+#define TPPMD_CONTROL_REG      0xa8
+#define  TPPMD_FO              1<<1
+#define  TPPMD_LB              1<<0
+
+/*  MII read and write registers */
+#define MII_WRITE_DATA         0xaa
+#define MII_READ_DATA          0xac
+
+/*  MII indicators */
+#define MII_INDICATOR          0xae
+#define  MII_NVALID            1<<2
+#define  MII_SCAN              1<<1
+#define  MII_BUSY              1<<0
+
+/*  Address filter  */
+#define ADDRESS_FILTER         0xd0
+#define  ADDRESS_RPPRM         1<<3       /* multicast promis. mode */
+#define  ADDRESS_RPPRO         1<<2       /* promiscuous mode */
+#define  ADDRESS_RPAMC         1<<1       /* accept multicasts */
+#define  ADDRESS_RPABC         1<<0       /* accept broadcasts */
+
+/*  Station addresses
+
+    Note that if the serial EEPROM is disabled, these values are all
+    zero.  If, like us, you get the chips when they're fresh, they're
+    also zero and you have to initialize the address */
+#define STATION_ADDRESS_0      0xd2
+#define STATION_ADDRESS_1      0xd4
+#define STATION_ADDRESS_2      0xd6
+
+/*  Hash tables  */
+#define HASH_TABLE_0           0xd8
+#define HASH_TABLE_1           0xda
+#define HASH_TABLE_2           0xdc
+#define HASH_TABLE_3           0xde
+
+/* PHY indentifiers */
+#define PHY_IDENTIFIER_0       0xe4
+#define PHY_IDENTIFIER_1       0xe6
+
+/*  MII Auto-negotiation register definitions  */
+
+#define MII_AUTO_NEGOTIATION_CONTROL        (0x0000)
+#define   MANC_PHY_RESET                      (0x8000)
+#define   MANC_PHY_LOOPBACK_ENABLE            (0x4000)
+#define   MANC_PHY_LOOPBACK_DISABLE           (0x0000)
+#define   MANC_PHY_SPEED_100                  (0x2000)
+#define   MANC_PHY_SPEED_10                   (0x0000)
+#define   MANC_AUTO_NEGOTIATION_ENABLE        (0x1000)
+#define   MANC_AUTO_NEGOTIATION_DISABLE       (0x0000)
+#define   MANC_PHY_POWER_DOWN                 (0x0800)
+#define   MANC_PHY_POWER_UP                   (0x0000)
+#define   MANC_ISOLATE_ENABLE                 (0x0400)
+#define   MANC_ISOLATE_DISABLE                (0x0000)
+#define   MANC_RESTART_AUTO_NEGOTIATION       (0x0200)
+#define   MANC_FULL_DUPLEX                    (0x0100)
+#define   MANC_HALF_DUPLEX                    (0x0000)
+
+#define MII_AUTO_NEGOTIATION_STATUS         (0x0001)
+#define   MANS_100BASE_T4_HALF_DUPLEX         (0x8000)
+#define   MANS_100BASE_X_FULL_DUPLEX          (0x4000)
+#define   MANS_100BASE_X_HALF_DUPLEX          (0x2000)
+#define   MANS_10MBS_FULL_DUPLEX              (0x1000)
+#define   MANS_10MBS_HALF_DUPLEX              (0x0800)
+#define   MANS_AUTO_NEGOTIATION_COMPLETE      (0x0020)
+#define   MANS_REMOTE_FAULT                   (0x0010)
+#define   MANS_AUTO_NEGOTIATION_ABILITY       (0x0008)
+#define   MANS_LINK_STATUS                    (0x0004)
+#define   MANS_JABBER_DETECT                  (0x0002)
+#define   MANS_EXTENDED_CAPABILITY            (0x0001)
+
+#define MII_PHY_IDENTIFIER_1                (0x0002)
+#define MII_PHY_IDENTIFIER_2                (0x0003)
+
+#define MII_AUTO_NEGOTIATION_ADVERTISEMENT  (0x0004)
+#define   MANA_NEXT_PAGE                      (0x8000)
+#define   MANA_REMOTE_FAULT                   (0x2000)
+#define   MANA_TECHNOLOGY_ABILITY_MASK        (0x1FE0)
+#define     MANATECH_10BASET_HALF_DUPLEX        (0x0020)
+#define     MANATECH_10BASET_FULL_DUPLEX        (0x0040)
+#define     MANATECH_100BASETX_HALF_DUPLEX      (0x0080)
+#define     MANATECH_100BASETX_FULL_DUPLEX      (0x0100)
+#define     MANATECH_100BASET4                  (0x0200)
+#define   MANA_SELECTOR_MASK                  (0x001F)
+#define     MANASELECTOR_802_3                  (0x0001)
+
+#define MII_AUTO_NEGOTIATION_LINK_PARTNER   (0x0005)
+#define   MANLP_NEXT_PAGE                     (0x8000)
+#define   MANLP_ACKNOWLEDGE                   (0x4000)
+#define   MANLP_REMOTE_FAULT                  (0x2000)
+#define   MANLP_TECHNOLOGY_ABILITY_MASK       (0x1FE0)
+#define   MANLP_SELECTOR_MASK                 (0x001F)
+
+#define MII_AUTO_NEGOTIATION_EXPANSION      (0x0006)
+#define   MANE_PARALLEL_DETECTION_FAULT       (0x0010)
+#define   MANE_LINK_PARTNER_NEXT_PAGE_ABLE    (0x0008)
+#define   MANE_NEXT_PAGE_ABLE                 (0x0004)
+#define   MANE_PAGE_RECEIVED                  (0x0002)
+#define   MANE_LINK_PARTNER_AUTO_ABLE         (0x0001)
+
+#define MII_AUTO_NEGOTIATION_NEXT_PAGE_TRANSMIT (0x0007)
+#define   MANNPT_NEXT_PAGE                    (0x8000)
+#define   MANNPT_MESSAGE_PAGE                 (0x2000)
+#define   MANNPT_ACKNOWLEDGE_2                (0x1000)
+#define   MANNPT_TOGGLE                       (0x0800)
+#define   MANNPT_MESSAGE_FIELD_MASK           (0x07FF)
+
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/pcnet32.c linux.14p6/drivers/net/pcnet32.c
--- linux.vanilla/drivers/net/pcnet32.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/net/pcnet32.c	Fri Oct 22 21:24:24 1999
@@ -13,7 +13,7 @@
  * 	This driver is for PCnet32 and PCnetPCI based ethercards
  */
 
-static const char *version = "pcnet32.c:v1.23 6.7.1999 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -39,16 +39,18 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <asm/spinlock.h>
 
 static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0};
 
 static int pcnet32_debug = 1;
+static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
 
 #ifdef MODULE
 static struct device *pcnet32_dev = NULL;
 #endif
 
-static const int max_interrupt_work = 20;
+static const int max_interrupt_work = 80;
 static const int rx_copybreak = 200;
 
 #define PORT_AUI      0x00
@@ -156,6 +158,12 @@
  *	   Michael Richard <mcr@solidum.com>)
  *         added chip id for 79c973/975 (thanks to Zach Brown <zab@zabbo.net>)
  * v1.23   fixed small bug, when manual selecting MII speed/duplex
+ * v1.24   Applied Thomas' patch to use TxStartPoint and thus decrease TxFIFO
+ *         underflows.  Added tx_start_pt module parameter. Increased
+ *         TX_RING_SIZE from 16 to 32.  Added #ifdef'd code to use DXSUFLO
+ *         for FAST[+] chipsets. <kaf@fc.hp.com>
+ * v1.24ac Added SMP spinlocking - Alan Cox <alan@redhat.com>
+ * v1.25kf Added No Interrupt on successful Tx for some Tx's <kaf@fc.hp.com>
  */
 
 
@@ -166,7 +174,7 @@
  */
 #ifndef PCNET32_LOG_TX_BUFFERS
 #define PCNET32_LOG_TX_BUFFERS 4
-#define PCNET32_LOG_RX_BUFFERS 4
+#define PCNET32_LOG_RX_BUFFERS 5
 #endif
 
 #define TX_RING_SIZE			(1 << (PCNET32_LOG_TX_BUFFERS))
@@ -255,12 +263,17 @@
     struct sk_buff *rx_skbuff[RX_RING_SIZE];
     struct pcnet32_access a;
     void *origmem;
-    int cur_rx, cur_tx;			/* The next free ring entry */
-    int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */
+    spinlock_t lock;				/* Guard lock */
+    unsigned int cur_rx, cur_tx;		/* The next free ring entry */
+    unsigned int dirty_rx, dirty_tx;		/* The ring entries to be free()ed. */
     struct net_device_stats stats;
     char tx_full;
     int  options;
     int  shared_irq:1,                      /* shared irq possible */
+	 ltint:1,
+#ifdef DO_DXSUFLO
+	 dxsuflo:1,			    /* disable transmit stop on uflo */
+#endif
          full_duplex:1,                     /* full duplex possible */
          mii:1;                             /* mii port available */
 #ifdef MODULE
@@ -299,6 +312,10 @@
 	PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0, 0,
 	PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
 	pcnet32_probe1},
+    { "AMD PCnetPCI series (IBM)",
+	PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0x1014, 0x2000,
+	PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
+	pcnet32_probe1},
     { "AMD PCnetHome series",
 	PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PCNETHOME, 0, 0,
 	PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
@@ -448,8 +465,8 @@
 	    int chip_idx;
 	    u16 sdid,svid;
 
-	    pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &sdid);
-	    pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &svid);
+	    pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &svid);
+	    pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdid);
 	    for (chip_idx = 0; pcnet32_tbl[chip_idx].vendor_id; chip_idx++)
 		if ((pdev->vendor == pcnet32_tbl[chip_idx].vendor_id) &&
 		    (pdev->device == pcnet32_tbl[chip_idx].device_id) &&
@@ -514,6 +531,10 @@
 {
     struct pcnet32_private *lp;
     int i,media,fdx = 0, mii = 0;
+#ifdef DO_DXSUFLO
+    int dxsuflo = 0;
+#endif
+    int ltint = 0;
     int chip_version;
     char *chipname;
     char *priv;
@@ -532,12 +553,14 @@
 	    return ENODEV;
     }
 
+
     chip_version = a->read_csr (ioaddr, 88) | (a->read_csr (ioaddr,89) << 16);
     if (pcnet32_debug > 2)
 	printk("  PCnet chip version is %#x.\n", chip_version);
     if ((chip_version & 0xfff) != 0x003)
 	return ENODEV;
     chip_version = (chip_version >> 12) & 0xffff;
+
     switch (chip_version) {
      case 0x2420:
 	chipname = "PCnet/PCI 79C970";
@@ -554,11 +577,33 @@
 	break;
      case 0x2623:
 	chipname = "PCnet/FAST 79C971";
+	/* To prevent Tx FIFO underflows ... (may increase Tx latency) */
+	/* Set BCR18:NOUFLO to not start Tx until reach Tx start point */
+	/* Looks like EEPROM sets BCR18:5/6 for BurstWrite/Read */
+        a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800));
+	/* Set CSR80:XMTSP, Tx start point = 20|64|128|248 bytes or size of frame */
+        i = a->read_csr(ioaddr, 80) & ~0x0C00; /* Clear bits we are touching */
+        a->write_csr(ioaddr, 80, i | (tx_start << 10));
 	fdx = 1; mii = 1;
+#ifdef DO_DXSUFLO
+	dxsuflo = 1;
+#endif
+	ltint = 1;
 	break;
      case 0x2624:
 	chipname = "PCnet/FAST+ 79C972";
+	/* To prevent Tx FIFO underflows ... (may increase Tx latency) */
+	/* Set BCR18:NOUFLO to not start Tx until reach Tx start point */
+	/* Looks like EEPROM sets BCR18:5/6 for BurstWrite/Read */
+        a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800));
+	/* Set CSR80:XMTSP, Tx start point = 20|64|128|220 bytes or size of frame */
+        i = a->read_csr(ioaddr, 80) & ~0x0C00; /* Clear bits we are touching */
+        a->write_csr(ioaddr, 80, i | (tx_start << 10));
 	fdx = 1; mii = 1;
+#ifdef DO_DXSUFLO
+	dxsuflo = 1;
+#endif
+	ltint = 1;
 	break;
      case 0x2625:
 	chipname = "PCnet/FAST III 79C973";
@@ -602,6 +647,29 @@
     for (i = 0; i < 6; i++)
       printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
 
+    if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */
+        i = a->read_csr(ioaddr, 80) & 0x0C00;  /* Check tx_start_pt */
+	printk("\n    tx_start_pt(0x%04x):",i);
+	switch(i>>10) {
+	    case 0: printk("  20 bytes,"); break;
+	    case 1: printk("  64 bytes,"); break;
+	    case 2: printk(" 128 bytes,"); break;
+	    case 3: printk("~220 bytes,"); break;
+	}
+        i = a->read_bcr(ioaddr, 18);  /* Check Burst/Bus control */
+        printk(" BCR18(%x):",i&0xffff);
+	if (i & (1<<5)) printk("BurstWrEn ");
+	if (i & (1<<6)) printk("BurstRdEn ");
+	if (i & (1<<7)) printk("DWordIO ");
+	if (i & (1<<11)) printk("NoUFlow ");
+        i = a->read_bcr(ioaddr, 25);
+        printk("\n    SRAMSIZE=0x%04x,",i<<8);
+        i = a->read_bcr(ioaddr, 26);
+        printk(" SRAM_BND=0x%04x,",i<<8);
+        i = a->read_bcr(ioaddr, 27);
+	if (i & (1<<14)) printk("LowLatRx,");
+    }
+
     dev->base_addr = ioaddr;
     request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname);
     
@@ -615,10 +683,17 @@
     lp = (struct pcnet32_private *)(((unsigned long)priv+15) & ~15);
       
     memset(lp, 0, sizeof(*lp));
+    
+    spin_lock_init(&lp->lock);
+    
     dev->priv = lp;
     lp->name = chipname;
     lp->shared_irq = shared;
     lp->full_duplex = fdx;
+#ifdef DO_DXSUFLO
+    lp->dxsuflo = dxsuflo;
+#endif
+    lp->ltint = ltint;
     lp->mii = mii;
     if (options[card_idx] > sizeof (options_mapping))
 	lp->options = PORT_ASEL;
@@ -746,7 +821,7 @@
 	lp->a.write_bcr (ioaddr, 9, val);
     }
     
-    /* set/reset GPSI bit in test register */
+    /* NOOP ??? set/reset GPSI bit in test register */
     val = lp->a.read_csr (ioaddr, 124) & ~0x10;
     if ((lp->options & PORT_PORTSEL) == PORT_GPSI)
 	val |= 0x10;
@@ -760,6 +835,19 @@
 	    val |= 0x08;
 	lp->a.write_bcr (ioaddr, 32, val);
     }
+
+#ifdef DO_DXSUFLO 
+    if (lp->dxsuflo) { /* Disable transmit stop on underflow */
+        val = lp->a.read_csr (ioaddr, 3);
+	val |= 0x40;
+        lp->a.write_csr (ioaddr, 3, val);
+    }
+#endif
+    if (lp->ltint) { /* Enable TxDone-intr inhibitor */
+        val = lp->a.read_csr (ioaddr, 5);
+	val |= (1<<14);
+        lp->a.write_csr (ioaddr, 5, val);
+    }
     
     lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
     lp->init_block.filter[0] = 0x00000000;
@@ -890,6 +978,7 @@
 {
     struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
     unsigned int ioaddr = dev->base_addr;
+    u16 status;
     int entry;
     unsigned long flags;
 
@@ -937,8 +1026,23 @@
 	return 1;
     }
 
-    save_flags (flags);
-    cli ();
+    spin_lock_irqsave(&lp->lock, flags);
+
+    /* Default status -- will not enable Successful-TxDone
+     * interrupt when that option is available to us.
+     */
+    status = 0x8300;
+    if ((lp->ltint) &&
+	((lp->cur_tx - lp->dirty_tx == TX_RING_SIZE/2) ||
+	 (lp->cur_tx - lp->dirty_tx >= TX_RING_SIZE-2)))
+    {
+	/* Enable Successful-TxDone interrupt if we have
+	 * 1/2 of, or nearly all of, our ring buffer Tx'd
+	 * but not yet cleaned up.  Thus, most of the time,
+	 * we will not enable Successful-TxDone interrupts.
+	 */
+	status = 0x9300;
+    }
 
     /* Fill in a Tx ring entry */
 
@@ -954,7 +1058,8 @@
 
     lp->tx_skbuff[entry] = skb;
     lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data));
-    lp->tx_ring[entry].status = le16_to_cpu(0x8300);
+
+    lp->tx_ring[entry].status = le16_to_cpu(status);
 
     lp->cur_tx++;
     lp->stats.tx_bytes += skb->len;
@@ -968,7 +1073,7 @@
 	clear_bit (0, (void *)&dev->tbusy);
     else
 	lp->tx_full = 1;
-    restore_flags(flags);
+    spin_unlock_irqrestore(&lp->lock, flags);
     return 0;
 }
 
@@ -990,6 +1095,9 @@
 
     ioaddr = dev->base_addr;
     lp = (struct pcnet32_private *)dev->priv;
+
+    spin_lock(&lp->lock);
+
     if (dev->interrupt)
 	printk("%s: Re-entering the interrupt handler.\n", dev->name);
 
@@ -1010,7 +1118,7 @@
 	    pcnet32_rx(dev);
 
 	if (csr0 & 0x0200) {		/* Tx-done interrupt */
-	    int dirty_tx = lp->dirty_tx;
+	    unsigned int dirty_tx = lp->dirty_tx;
 
 	    while (dirty_tx < lp->cur_tx) {
 		int entry = dirty_tx & TX_RING_MOD_MASK;
@@ -1028,14 +1136,27 @@
 		    if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
 		    if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
 		    if (err_status & 0x10000000) lp->stats.tx_window_errors++;
+#ifndef DO_DXSUFLO
 		    if (err_status & 0x40000000) {
-			/* Ackk!  On FIFO errors the Tx unit is turned off! */
 			lp->stats.tx_fifo_errors++;
+			/* Ackk!  On FIFO errors the Tx unit is turned off! */
 			/* Remove this verbosity later! */
-			printk("%s: Tx FIFO error! Status %4.4x.\n",
-			       dev->name, csr0);
+			printk("%s: Tx FIFO error! CSR0=%4.4x\n",
+					    dev->name, csr0);
 			must_restart = 1;
 					}
+#else
+		    if (err_status & 0x40000000) {
+			lp->stats.tx_fifo_errors++;
+			if (! lp->dxsuflo) {  /* If controller doesn't recover ... */
+			    /* Ackk!  On FIFO errors the Tx unit is turned off! */
+			    /* Remove this verbosity later! */
+			    printk("%s: Tx FIFO error! CSR0=%4.4x\n",
+					   	dev->name, csr0);
+			    must_restart = 1;
+			}
+		    }
+#endif
 		} else {
 		    if (status & 0x1800)
 			lp->stats.collisions++;
@@ -1104,6 +1225,8 @@
 	       dev->name, lp->a.read_csr (ioaddr, 0));
 
     dev->interrupt = 0;
+
+    spin_unlock(&lp->lock);
     return;
 }
 
@@ -1252,12 +1375,11 @@
     u16 saved_addr;
     unsigned long flags;
 
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&lp->lock, flags);
     saved_addr = lp->a.read_rap(ioaddr);
     lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112);
     lp->a.write_rap(ioaddr, saved_addr);
-    restore_flags(flags);
+    spin_unlock_irqrestore(&lp->lock, flags);
 
     return &lp->stats;
 }
@@ -1371,18 +1493,22 @@
 MODULE_PARM(debug, "i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(tx_start_pt, "i");
 MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 					     
 
 /* An additional parameter that may be passed in... */
 static int debug = -1;
+static int tx_start_pt = -1;
 
 int
 init_module(void)
 {
     if (debug > 0)
 	pcnet32_debug = debug;
+    if ((tx_start_pt >= 0) && (tx_start_pt <= 3))
+	tx_start = tx_start_pt;
     
     pcnet32_dev = NULL;
     return pcnet32_probe(NULL);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/shaper.c linux.14p6/drivers/net/shaper.c
--- linux.vanilla/drivers/net/shaper.c	Sat Aug 14 02:27:29 1999
+++ linux.14p6/drivers/net/shaper.c	Mon Nov 15 19:15:18 1999
@@ -557,6 +557,13 @@
 {
 	struct shaperconf *ss= (struct shaperconf *)&ifr->ifr_data;
 	struct shaper *sh=dev->priv;
+	
+	if(ss->ss_cmd == SHAPER_SET_DEV || ss->ss_cmd == SHAPER_SET_SPEED)
+	{
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+	}
+	
 	switch(ss->ss_cmd)
 	{
 		case SHAPER_SET_DEV:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sis900.c linux.14p6/drivers/net/sis900.c
--- linux.vanilla/drivers/net/sis900.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/net/sis900.c	Thu Nov  4 23:46:11 1999
@@ -1,58 +1,29 @@
-/*****************************************************************************/
-/*      sis900.c: A SiS 900 PCI Fast Ethernet driver for Linux.              */
-/*                                                                           */
-/*                Silicon Integrated System Corporation                      */ 
-/*                Revision:	1.05	Aug 7 1999                           */
-/*                                                                           */
-/*****************************************************************************/
-
-/*                                                                            
-      Modified from the driver which is originally written by Donald Becker. 
-
-      This software may be used and distributed according to the terms
-      of the GNU Public License (GPL), incorporated herein by reference.
-      Drivers based on this skeleton fall under the GPL and must retain
-      the authorship (implicit copyright) notice.
-
-      The author may be reached as becker@tidalwave.net, or
-      Donald Becker
-      312 Severn Ave. #W302
-      Annapolis MD 21403
-
-      Support and updates [to the original skeleton] available at
-      http://www.tidalwave.net/~becker/pci-skeleton.html
+/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
+   Silicon Integrated System Corporation 
+   Revision:	1.05	Aug 7 1999
+   
+   Modified from the driver which is originally written by Donald Becker. 
+   
+   This software may be used and distributed according to the terms
+   of the GNU Public License (GPL), incorporated herein by reference.
+   Drivers based on this skeleton fall under the GPL and must retain
+   the authorship (implicit copyright) notice.
+   
+   References:
+   SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
+   preliminary Rev. 1.0 Jan. 14, 1998
+   SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
+   preliminary Rev. 1.0 Nov. 10, 1998
+   SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
+   preliminary Rev. 1.0 Jan. 18, 1998
+   http://www.sis.com.tw/support/databook.htm
+   
+   Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release
+   Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx  
+   Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support
+   Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release
 */
 
-static const char *version =
-"sis900.c:v1.05  8/07/99\n";
-
-static int max_interrupt_work = 20;
-#define sis900_debug debug
-static int sis900_debug = 0;
-
-static int multicast_filter_limit = 128;
-
-#define MAX_UNITS 8             /* More are supported, limit only on options */
-static int speeds[MAX_UNITS] = {100, 100, 100, 100, 100, 100, 100, 100};
-static int full_duplex[MAX_UNITS] = {1, 1, 1, 1, 1, 1, 1, 1};
-
-#define TX_BUF_SIZE     1536
-#define RX_BUF_SIZE     1536
-
-#define TX_DMA_BURST    0
-#define RX_DMA_BURST    0
-#define TX_FIFO_THRESH  16
-#define TxDRNT_100      (1536>>5)
-#define TxDRNT_10       16 
-#define RxDRNT_100      8
-#define RxDRNT_10       8 
-#define TRUE            1
-#define FALSE           0
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  (4*HZ)
-
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
@@ -65,680 +36,320 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/netdevice.h>
+
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <asm/processor.h>      /* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
-
-#define RUN_AT(x) (jiffies + (x))
-
 #include <linux/delay.h>
 
-#if LINUX_VERSION_CODE < 0x20123
-#define test_and_set_bit(val, addr) set_bit(val, addr)
-#endif
-#if LINUX_VERSION_CODE <= 0x20139
-#define net_device_stats enet_statistics
-#else
-#define NETSTATS_VER2
-#endif
-#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
-/* Grrrr, the PCI code changed, but did not consider CardBus... */
-#include <linux/bios32.h>
-#define PCI_SUPPORT_VER1
-#else
-#define PCI_SUPPORT_VER2
-#endif
-#if LINUX_VERSION_CODE < 0x20159
-#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
-#else
-#define dev_free_skb(skb) dev_kfree_skb(skb);
-#endif
+#include "sis900.h"
 
-/* The I/O extent. */
-#define SIS900_TOTAL_SIZE 0x100
+static const char *version =
+"sis900.c: v1.06  11/04/99\n";
 
-/* This table drives the PCI probe routines.  It's mostly boilerplate in all
-   of the drivers, and will likely be provided by some future kernel.
-   Note the matching code -- the first table entry matchs all 56** cards but
-   second only the 1234 card.
-*/
+static int max_interrupt_work = 20;
+#define sis900_debug debug
+static int sis900_debug = 0;
 
-enum pci_flags_bit {
-        PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
-};
+static int multicast_filter_limit = 128;
 
-struct pci_id_info {
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (4*HZ)
+
+struct mac_chip_info {
         const char *name;
-        u16     vendor_id, device_id, device_id_mask, flags;
-        int io_size;
-        struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
-                         long ioaddr, int irq, int chip_idx, int fnd_cnt);
+        u16 	vendor_id, device_id, flags;
+        int 	io_size;
+        struct device *(*probe) (struct mac_chip_info *mac, struct pci_dev * pci_dev, 
+				 struct device * net_dev);
 };
+static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev,
+					 struct device * net_dev);
 
-static struct device * sis900_probe1(int pci_bus, int pci_devfn,
-                                  struct device *dev, long ioaddr,
-                                  int irq, int chp_idx, int fnd_cnt);
-
-static struct pci_id_info pci_tbl[] =
-{{ "SiS 900 PCI Fast Ethernet",
-   0x1039, 0x0900, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1},
-  { "SiS 7016 PCI Fast Ethernet",
-   0x1039, 0x7016, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1},
- {0,},                                          /* 0 terminated list. */
+static struct mac_chip_info  mac_chip_table[] = {
+	{ "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900,
+	  PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
+	{ "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016,
+	  PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
+	{0,},                                          /* 0 terminated list. */
 };
 
-/* The capability table matches the chip table above. */
-enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04};
-static int sis_cap_tbl[] = {
-        HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG,
-        HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG,
+static void sis900_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex);
+static void amd79c901_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex);
+
+static struct mii_chip_info {
+	const char * name;
+	u16 phy_id0;
+	u16 phy_id1;
+	void (*read_mode) (struct device *net_dev, int phy_addr, int *speed, int *duplex);
+} mii_chip_table[] = {
+	{"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
+	{"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
+	{"AMD 79C901 10BASE-T PHY",  0x0000, 0x35b9, amd79c901_read_mode},
+	{"AMD 79C901 HomePNA PHY",   0x0000, 0x35c8, amd79c901_read_mode},
+	{0,},
 };
 
-/* The rest of these values should never change. */
-#define NUM_TX_DESC     16      /* Number of Tx descriptor registers. */
-#define NUM_RX_DESC     8       /* Number of Rx descriptor registers. */
-
-/* Symbolic offsets to registers. */
-enum SIS900_registers {
-        cr=0x0,                 //Command Register
-        cfg=0x4,                //Configuration Register
-        mear=0x8,               //EEPROM Access Register
-        ptscr=0xc,              //PCI Test Control Register
-        isr=0x10,               //Interrupt Status Register
-        imr=0x14,               //Interrupt Mask Register
-        ier=0x18,               //Interrupt Enable Register
-        epar=0x18,              //Enhanced PHY Access Register
-        txdp=0x20,              //Transmit Descriptor Pointer Register
-        txcfg=0x24,             //Transmit Configuration Register
-        rxdp=0x30,              //Receive Descriptor Pointer Register
-        rxcfg=0x34,             //Receive Configuration Register
-        flctrl=0x38,            //Flow Control Register
-        rxlen=0x3c,             //Receive Packet Length Register
-        rfcr=0x48,              //Receive Filter Control Register
-        rfdr=0x4C,              //Receive Filter Data Register
-        pmctrl=0xB0,            //Power Management Control Register
-        pmer=0xB4               //Power Management Wake-up Event Register
+struct mii_phy {
+	struct mii_phy * next;
+	struct mii_chip_info * chip_info;
+	int phy_addr;
+	u16 status;
 };
 
-#define RESET           0x00000100
-#define SWI             0x00000080
-#define RxRESET         0x00000020
-#define TxRESET         0x00000010
-#define RxDIS           0x00000008
-#define RxENA           0x00000004
-#define TxDIS           0x00000002
-#define TxENA           0x00000001
-
-#define BISE            0x80000000
-#define EUPHCOM         0x00000100
-#define REQALG          0x00000080
-#define SB              0x00000040
-#define POW             0x00000020
-#define EXD             0x00000010
-#define PESEL           0x00000008
-#define LPM             0x00000004
-#define BEM             0x00000001
-
-/* Interrupt register bits, using my own meaningful names. */
-#define WKEVT           0x10000000
-#define TxPAUSEEND      0x08000000
-#define TxPAUSE         0x04000000
-#define TxRCMP          0x02000000
-#define RxRCMP          0x01000000
-#define DPERR           0x00800000
-#define SSERR           0x00400000
-#define RMABT           0x00200000
-#define RTABT           0x00100000
-#define RxSOVR          0x00010000
-#define HIBERR          0x00008000
-#define SWINT           0x00001000
-#define MIBINT          0x00000800
-#define TxURN           0x00000400
-#define TxIDLE          0x00000200
-#define TxERR           0x00000100
-#define TxDESC          0x00000080
-#define TxOK            0x00000040
-#define RxORN           0x00000020
-#define RxIDLE          0x00000010
-#define RxEARLY         0x00000008
-#define RxERR           0x00000004
-#define RxDESC          0x00000002
-#define RxOK            0x00000001
-
-#define IE              0x00000001
-
-#define TxCSI           0x80000000
-#define TxHBI           0x40000000
-#define TxMLB           0x20000000
-#define TxATP           0x10000000
-#define TxIFG           0x0C000000
-#define TxMXF           0x03800000
-#define TxMXF_shift     0x23
-#define TxMXDMA         0x00700000
-#define TxMXDMA_shift   20
-#define TxRTCNT         0x000F0000
-#define TxRTCNT_shift   16
-#define TxFILLT         0x00007F00
-#define TxFILLT_shift   8
-#define TxDRNT          0x0000007F
-
-#define RxAEP           0x80000000
-#define RxARP           0x40000000
-#define RxATP           0x10000000
-#define RxAJAB          0x08000000
-#define RxMXF           0x03800000
-#define RxMXF_shift     23
-#define RxMXDMA         0x00700000
-#define RxMXDMA_shift   20
-#define RxDRNT          0x0000007F
-
-#define RFEN            0x80000000
-#define RFAAB           0x40000000
-#define RFAAM           0x20000000
-#define RFAAP           0x10000000
-#define RFPromiscuous   (RFAAB|RFAAM|RFAAP)
-#define RFAA_shift      28
-#define RFEP            0x00070000
-#define RFEP_shift      16
-
-#define RFDAT           0x0000FFFF
-
-#define OWN             0x80000000
-#define MORE            0x40000000
-#define INTR            0x20000000
-#define OK              0x08000000
-#define DSIZE           0x00000FFF
-
-#define SUPCRC          0x10000000
-#define ABORT           0x04000000
-#define UNDERRUN        0x02000000
-#define NOCARRIER       0x01000000
-#define DEFERD          0x00800000
-#define EXCDEFER        0x00400000
-#define OWCOLL          0x00200000
-#define EXCCOLL         0x00100000
-#define COLCNT          0x000F0000
-
-#define INCCRC          0x10000000
-//      ABORT           0x04000000
-#define OVERRUN         0x02000000
-#define DEST            0x01800000
-#define BCAST           0x01800000
-#define MCAST           0x01000000
-#define UNIMATCH        0x00800000
-#define TOOLONG         0x00400000
-#define RUNT            0x00200000
-#define RXISERR         0x00100000
-#define CRCERR          0x00080000
-#define FAERR           0x00040000
-#define LOOPBK          0x00020000
-#define RXCOL           0x00010000
-
-#define EuphLiteEEMACAddr               0x08
-#define EuphLiteEEVendorID              0x02
-#define EuphLiteEEDeviceID              0x03
-#define EuphLiteEECardTypeRev           0x0b
-#define EuphLiteEEPlexusRev             0x0c
-#define EuphLiteEEChecksum              0x0f
-
-#define RXSTS_shift     18
-#define OWN             0x80000000
-#define MORE            0x40000000
-#define INTR            0x20000000
-#define OK              0x08000000
-#define DSIZE           0x00000FFF
-/* MII register offsets */
-#define MII_CONTROL             0x0000
-#define MII_STATUS              0x0001
-#define MII_PHY_ID0             0x0002
-#define MII_PHY_ID1             0x0003
-#define MII_ANAR                0x0004
-#define MII_ANLPAR              0x0005
-#define MII_ANER                0x0006
-/* MII Control register bit definitions. */
-#define MIICNTL_FDX             0x0100
-#define MIICNTL_RST_AUTO        0x0200
-#define MIICNTL_ISOLATE         0x0400
-#define MIICNTL_PWRDWN          0x0800
-#define MIICNTL_AUTO            0x1000
-#define MIICNTL_SPEED           0x2000
-#define MIICNTL_LPBK            0x4000
-#define MIICNTL_RESET           0x8000
-/* MII Status register bit significance. */
-#define MIISTAT_EXT             0x0001
-#define MIISTAT_JAB             0x0002
-#define MIISTAT_LINK            0x0004
-#define MIISTAT_CAN_AUTO        0x0008
-#define MIISTAT_FAULT           0x0010
-#define MIISTAT_AUTO_DONE       0x0020
-#define MIISTAT_CAN_T           0x0800
-#define MIISTAT_CAN_T_FDX       0x1000
-#define MIISTAT_CAN_TX          0x2000
-#define MIISTAT_CAN_TX_FDX      0x4000
-#define MIISTAT_CAN_T4          0x8000
-/* MII NWAY Register Bits ...
-** valid for the ANAR (Auto-Negotiation Advertisement) and
-** ANLPAR (Auto-Negotiation Link Partner) registers */
-#define MII_NWAY_NODE_SEL       0x001f
-#define MII_NWAY_CSMA_CD        0x0001
-#define MII_NWAY_T              0x0020
-#define MII_NWAY_T_FDX          0x0040
-#define MII_NWAY_TX             0x0080
-#define MII_NWAY_TX_FDX         0x0100
-#define MII_NWAY_T4             0x0200
-#define MII_NWAY_RF             0x2000
-#define MII_NWAY_ACK            0x4000
-#define MII_NWAY_NP             0x8000
-
-/* MII Auto-Negotiation Expansion Register Bits */
-#define MII_ANER_PDF            0x0010
-#define MII_ANER_LP_NP_ABLE     0x0008
-#define MII_ANER_NP_ABLE        0x0004
-#define MII_ANER_RX_PAGE        0x0002
-#define MII_ANER_LP_AN_ABLE     0x0001
-#define HALF_DUPLEX                     1
-#define FDX_CAPABLE_DUPLEX_UNKNOWN      2
-#define FDX_CAPABLE_HALF_SELECTED       3
-#define FDX_CAPABLE_FULL_SELECTED       4
-#define HW_SPEED_UNCONFIG       0
-#define HW_SPEED_10_MBPS        10
-#define HW_SPEED_100_MBPS       100
-#define HW_SPEED_DEFAULT        (HW_SPEED_10_MBPS)
-
-#define ACCEPT_ALL_PHYS         0x01
-#define ACCEPT_ALL_MCASTS       0x02
-#define ACCEPT_ALL_BCASTS       0x04
-#define ACCEPT_ALL_ERRORS       0x08
-#define ACCEPT_CAM_QUALIFIED    0x10
-#define MAC_LOOPBACK            0x20
-//#define FDX_CAPABLE_FULL_SELECTED     4
-#define CRC_SIZE                4
-#define MAC_HEADER_SIZE         14
-
-typedef struct _EuphLiteDesc {
-        u32     llink;
-        unsigned char*  buf;
-        u32     physAddr;
-        /* Hardware sees the physical address of descriptor */
-        u32     plink;
+typedef struct _BufferDesc {
+        u32     link;
         u32     cmdsts;
-        u32     bufPhys;
-} EuphLiteDesc;
+        u32     bufptr;
+} BufferDesc;
 
 struct sis900_private {
-        char devname[8];                /* Used only for kernel debugging. */
-        const char *product_name;
         struct device *next_module;
-        int chip_id;
-        int chip_revision;
-        unsigned char pci_bus, pci_devfn;
-#if LINUX_VERSION_CODE > 0x20139
         struct net_device_stats stats;
-#else
-        struct enet_statistics stats;
-#endif
-        struct timer_list timer;        /* Media selection timer. */
-        unsigned int cur_rx;    /* Index into the Rx buffer of next Rx pkt. */
-        unsigned int cur_tx, dirty_tx, tx_flag;
-
-        /* The saved address of a sent-in-place packet/buffer, for skfree(). */
-        struct sk_buff* tx_skbuff[NUM_TX_DESC];
-        EuphLiteDesc tx_buf[NUM_TX_DESC];       /* Tx bounce buffers */
-        EuphLiteDesc rx_buf[NUM_RX_DESC];
-        unsigned char *rx_bufs;
-        unsigned char *tx_bufs;                 /* Tx bounce buffer region. */
-        char phys[4];                           /* MII device addresses.    */
-        int phy_idx;                            /* Support Max 4 PHY        */
-        u16 pmd_status;
-        unsigned int tx_full;                   /* The Tx queue is full.    */
-	int MediaSpeed;                         /* user force speed         */
-	int MediaDuplex;                        /* user force duplex        */
-        int full_duplex;                        /* Full/Half-duplex.        */
-        int speeds;                             /* 100/10 Mbps.             */
-        u16 LinkOn;
-        u16 LinkChange;
+	struct pci_dev * pci_dev;
+
+	struct mac_chip_info * mac;
+	struct mii_phy * mii;
+	unsigned int cur_phy;
+
+        struct timer_list timer;        		/* Link status detection timer. */
+        unsigned int cur_rx, dirty_rx;		
+        unsigned int cur_tx, dirty_tx;
+
+        /* The saved address of a sent/receive-in-place packet buffer */
+        struct sk_buff *tx_skbuff[NUM_TX_DESC];
+	struct sk_buff *rx_skbuff[NUM_RX_DESC];
+	BufferDesc tx_ring[NUM_TX_DESC];	
+        BufferDesc rx_ring[NUM_RX_DESC];
+        unsigned int tx_full;			/* The Tx queue is full.    */
+
+        int LinkOn;
 };
 
 #ifdef MODULE
 #if LINUX_VERSION_CODE > 0x20115
 MODULE_AUTHOR("Jim Huang <cmhuang@sis.com.tw>");
 MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver");
-MODULE_PARM(speeds, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM(multicast_filter_limit, "i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(debug, "i");
 #endif
 #endif
 
-static int sis900_open(struct device *dev);
+static int sis900_open(struct device *net_dev);
+static int sis900_mii_probe (struct device * net_dev);
+static void sis900_init_rxfilter (struct device * net_dev);
 static u16 read_eeprom(long ioaddr, int location);
-static int mdio_read(struct device *dev, int phy_id, int location);
-static void mdio_write(struct device *dev, int phy_id, int location, int val);
+static u16 mdio_read(struct device *net_dev, int phy_id, int location);
+static void mdio_write(struct device *net_dev, int phy_id, int location, int val);
 static void sis900_timer(unsigned long data);
-static void sis900_tx_timeout(struct device *dev);
-static void sis900_init_ring(struct device *dev);
-static int sis900_start_xmit(struct sk_buff *skb, struct device *dev);
-static int sis900_rx(struct device *dev);
+static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy);
+static void sis900_tx_timeout(struct device *net_dev);
+static void sis900_init_tx_ring(struct device *net_dev);
+static void sis900_init_rx_ring(struct device *net_dev);
+static int sis900_start_xmit(struct sk_buff *skb, struct device *net_dev);
+static int sis900_rx(struct device *net_dev);
+static void sis900_finish_xmit (struct device *net_dev);
 static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int sis900_close(struct device *dev);
-static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
-static struct enet_statistics *sis900_get_stats(struct device *dev);
-static void set_rx_mode(struct device *dev);
-static void sis900_reset(struct device *dev);
-static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed);
-static void elSetCapability(struct device *dev, int phy_id, int duplex, int speed);
-static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex);
-static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value);
-static void elSetMediaType(struct device *dev, int speed, int duplex);
+static int sis900_close(struct device *net_dev);
+static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd);
+static struct enet_statistics *sis900_get_stats(struct device *net_dev);
+static u16 sis900_compute_hashtable_index(u8 *addr);
+static void set_rx_mode(struct device *net_dev);
+static void sis900_reset(struct device *net_dev);
 
 /* A list of all installed SiS900 devices, for removing the driver module. */
 static struct device *root_sis900_dev = NULL;
 
-/* Ideally we would detect all network cards in slot order.  That would
-   be best done a central PCI probe dispatch, which wouldn't work
-   well when dynamically adding drivers.  So instead we detect just the
-   SiS 900 cards in slot order. */
-
-int sis900_probe(struct device *dev)
-{
-        int cards_found = 0;
-        int pci_index = 0;
-        unsigned char pci_bus, pci_device_fn;
-
-        if ( ! pcibios_present())
-                return -ENODEV;
-
-        for (;pci_index < 0xff; pci_index++) {
-                u16 vendor, device, pci_command, new_command;
-                int chip_idx, irq;
-                long ioaddr;
-
-                if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
-                                                pci_index,
-                                                &pci_bus, &pci_device_fn)
-                                != PCIBIOS_SUCCESSFUL) {
-                        break;
-                }
-                pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
-                                                        &vendor);
-                pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
-                                                        &device);
-
-                for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
-                        if (vendor == pci_tbl[chip_idx].vendor_id &&
-                                (device & pci_tbl[chip_idx].device_id_mask) ==
-                                pci_tbl[chip_idx].device_id)
-                                break;
-                if (pci_tbl[chip_idx].vendor_id == 0)   /* Compiled out! */
-                        continue;
-
-        {
-#if defined(PCI_SUPPORT_VER2)
-                struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
-                ioaddr = pdev->base_address[0] & ~3;
-                irq = pdev->irq;
-#else
-                u32 pci_ioaddr;
-                u8 pci_irq_line;
-                pcibios_read_config_byte(pci_bus, pci_device_fn,
-                                         PCI_INTERRUPT_LINE, &pci_irq_line);
-                pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                         PCI_BASE_ADDRESS_0, &pci_ioaddr);
-                ioaddr = pci_ioaddr & ~3;
-                irq = pci_irq_line;
-#endif
-        }
+/* walk through every ethernet PCI devices to see if some of them are matched with our card list*/
+int sis900_probe (struct device * net_dev)
+{
+	int found = 0;
+	struct pci_dev * pci_dev = NULL;
+		
+	if (!pci_present())
+		return -ENODEV;
+		
+	while ((pci_dev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_dev)) != NULL) {
+		/* pci_dev contains all ethernet devices */
+		u32 pci_io_base;
+		struct mac_chip_info * mac;
+
+		for (mac = mac_chip_table; mac->vendor_id; mac++) {
+			/* try to match our card list */
+			if (pci_dev->vendor == mac->vendor_id &&
+			    pci_dev->device == mac->device_id)
+				break;
+		}
+		
+		if (mac->vendor_id == 0)
+			/* pci_dev does not match any of our cards */
+			continue;
+		
+		/* now, pci_dev should be either 900 or 7016 */
+		pci_io_base = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+		if ((mac->flags & PCI_COMMAND_IO ) && 
+		    check_region(pci_io_base, mac->io_size))
+			continue;
+		
+		/* setup various bits in PCI command register */
+		pci_set_master(pci_dev);
+
+		/* do the real low level jobs */
+		net_dev = mac->probe(mac, pci_dev, net_dev);
+		
+		if (net_dev != NULL) {
+			found++;
+		}
+		net_dev = NULL;
+	}
+	return found ? 0 : -ENODEV;
+}
 
-                if ((pci_tbl[chip_idx].flags & PCI_USES_IO) &&
-                        check_region(ioaddr, pci_tbl[chip_idx].io_size))
-                        continue;
-
-                /* Activate the card: fix for brain-damaged Win98 BIOSes. */
-                pcibios_read_config_word(pci_bus, pci_device_fn,
-                                         PCI_COMMAND, &pci_command);
-                new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
-                if (pci_command != new_command) {
-                        printk(KERN_INFO "  The PCI BIOS has not enabled the"
-                                   " device at %d/%d!"
-                                   "Updating PCI command %4.4x->%4.4x.\n",
-                                        pci_bus, pci_device_fn,
-                                        pci_command, new_command);
+static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, 
+					 struct device * net_dev)
+{
+	struct sis900_private *sis_priv;
+	long ioaddr = pci_dev->base_address[0] & ~3;
+	int irq = pci_dev->irq;
+	static int did_version = 0;
+	u16 signature;
+	int i;
+
+	if (did_version++ == 0)
+		printk(KERN_INFO "%s", version);
+
+	/* check to see if we have sane EEPROM */
+	signature = (u16) read_eeprom(ioaddr, EEPROMSignature);    
+	if (signature == 0xffff || signature == 0x0000) {
+		printk (KERN_INFO "%s: Error EERPOM read %x\n", 
+			net_dev->name, signature);
+		return NULL;
+	}
 
-                        pcibios_write_config_word(pci_bus, pci_device_fn,
-                                                  PCI_COMMAND, new_command);
-                }
+	if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
+		return NULL;
 
-                dev = pci_tbl[chip_idx].probe1(pci_bus,
-                                                pci_device_fn,
-                                                dev,
-                                                ioaddr,
-                                                irq,
-                                                chip_idx,
-                                                cards_found);
-
-                if (dev  && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
-                        u8 pci_latency;
-
-                        pcibios_read_config_byte(pci_bus, pci_device_fn,
-                                        PCI_LATENCY_TIMER, &pci_latency);
-
-                        if (pci_latency < 32) {
-                           printk(KERN_NOTICE "  PCI latency timer (CFLT) is "
-                           "unreasonably low at %d.  Setting to 64 clocks.\n",
-                                           pci_latency);
-                           pcibios_write_config_byte(pci_bus, pci_device_fn,
-                                          PCI_LATENCY_TIMER, 64);
-                        }
-                }
-                dev = 0;
-                cards_found++;
-        }
-        return cards_found ? 0 : -ENODEV;
-}
+	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name,
+	       ioaddr, irq);
 
-static struct device * sis900_probe1(   int pci_bus,
-                                        int pci_devfn,
-                                        struct device *dev,
-                                        long ioaddr,
-                                        int irq,
-                                        int chip_idx,
-                                        int found_cnt)
-{
-        static int did_version = 0;     /* Already printed version info. */
-        struct sis900_private *tp;
-	u16    status;
-        int    duplex = found_cnt < MAX_UNITS ? full_duplex[found_cnt] : 0 ;
-        int    speed  = found_cnt < MAX_UNITS ? speeds[found_cnt] : 0 ;
-        int    phy=0, phy_idx=0, i;
-
-        if (did_version++ == 0)
-                printk(KERN_INFO "%s", version);
-
-        dev = init_etherdev(dev, 0);
-        
-        if(dev==NULL)
-        	return NULL;
-
-        printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
-                   dev->name, pci_tbl[chip_idx].name, ioaddr, irq);
-
-        if ((u16)read_eeprom(ioaddr, EuphLiteEEVendorID) != 0xffff) {
-                for (i = 0; i < 3; i++)
-                        ((u16 *)(dev->dev_addr))[i] =
-                                        read_eeprom(ioaddr,i+EuphLiteEEMACAddr);
-                for (i = 0; i < 5; i++)
-                        printk("%2.2x:", (u8)dev->dev_addr[i]);
-                printk("%2.2x.\n", dev->dev_addr[i]);
-        } else
-                printk(KERN_INFO "Error EEPROM read\n");
-
-        /* We do a request_region() to register /proc/ioports info. */
-        request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
-
-        dev->base_addr = ioaddr;
-        dev->irq = irq;
-
-        /* Some data structures must be quadword aligned. */
-        tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
-        if(tp==NULL)
-        {
-        	release_region(ioaddr, pci_tbl[chip_idx].io_size);
-        	return NULL;
-        }
-        memset(tp, 0, sizeof(*tp));
-        dev->priv = tp;
+	/* get MAC address from EEPROM */
+	for (i = 0; i < 3; i++)
+		((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
+	for (i = 0; i < 5; i++)
+		printk("%2.2x:", (u8)net_dev->dev_addr[i]);
+	printk("%2.2x.\n", net_dev->dev_addr[i]);
+
+	if ((net_dev->priv = kmalloc(sizeof(struct sis900_private), GFP_KERNEL)) == NULL) {
+		unregister_netdevice(net_dev);
+		return NULL;
+	}
 
-        tp->next_module = root_sis900_dev;
-        root_sis900_dev = dev;
+	sis_priv = net_dev->priv;
+	memset(sis_priv, 0, sizeof(struct sis900_private));
 
-        tp->chip_id = chip_idx;
-        tp->pci_bus = pci_bus;
-        tp->pci_devfn = pci_devfn;
-
-        /* Find the connected MII xcvrs.
-           Doing this in open() would allow detecting external xcvrs later, but
-           takes too much time. */
-        if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) {
-                for (phy = 0, phy_idx = 0;
-                        phy < 32 && phy_idx < sizeof(tp->phys); phy++)
-                {
-                        int mii_status ;
-			mii_status = mdio_read(dev, phy, MII_STATUS);
-
-                        if (mii_status != 0xffff && mii_status != 0x0000) {
-                                tp->phy_idx = phy_idx;
-                                tp->phys[phy_idx++] = phy;
-                                tp->pmd_status=mdio_read(dev, phy, MII_STATUS);
-                                printk(KERN_INFO "%s: MII transceiver found "
-                                                 "at address %d.\n",
-                                                 dev->name, phy);
-                                break;
-                        }
-                }
+	/* We do a request_region() to register /proc/ioports info. */
+	request_region(ioaddr, mac->io_size, net_dev->name);
+	net_dev->base_addr = ioaddr;
+	net_dev->irq = irq;
+	sis_priv->pci_dev = pci_dev;
+	sis_priv->mac = mac;
+
+	/* probe for mii transciver */
+	if (sis900_mii_probe(net_dev) == 0) {
+		unregister_netdev(net_dev);
+		kfree(sis_priv);
+		release_region(ioaddr, mac->io_size);
+		return NULL;
+	}
 
-                if (phy_idx == 0) {
-                        printk(KERN_INFO "%s: No MII transceivers found!\n",
-                                        dev->name);
-                        tp->phys[0] = -1;
-			tp->pmd_status = 0;
-                }
-        } else {
-                        tp->phys[0] = -1;
-			tp->pmd_status = 0;
-        }
+	sis_priv->next_module = root_sis900_dev;
+	root_sis900_dev = net_dev;
 
-        if ((tp->pmd_status > 0) && (phy_idx > 0)) {
-		if (sis900_debug > 1) {
-			printk(KERN_INFO "duplex=%d, speed=%d\n",
-						duplex, speed);
-		}
-		if (!duplex && !speed) {  
-			// auto-config media type
-			// Set full capability
-			if (sis900_debug > 1) {
-				printk(KERN_INFO "Auto Config ...\n");
-			}
-			elSetCapability(dev, tp->phys[tp->phy_idx], 1, 100);
-            		tp->pmd_status=elAutoNegotiate(dev,
-               	    				       tp->phys[tp->phy_idx],
-			                  	       &tp->full_duplex,
-						       &tp->speeds);
-		} else {
-			tp->MediaSpeed = speed;
-			tp->MediaDuplex = duplex;
-			elSetCapability(dev, tp->phys[tp->phy_idx],
-					duplex, speed);
-            		elAutoNegotiate(dev, tp->phys[tp->phy_idx],
-			                &tp->full_duplex,
-					&tp->speeds);
-			status = mdio_read(dev, phy, MII_ANLPAR);
-			if ( !(status & (MII_NWAY_T  | MII_NWAY_T_FDX |
-					 MII_NWAY_TX | MII_NWAY_TX_FDX )))
-			{
-				u16 cmd=0;
-				cmd |= ( speed == 100 ?
-					 MIICNTL_SPEED : 0 );
-				cmd |= ( duplex ? MIICNTL_FDX : 0 );
-        			mdio_write(dev, phy, MII_CONTROL, cmd);
-				elSetMediaType(dev, speed==100 ? 
-						    HW_SPEED_100_MBPS :
-						    HW_SPEED_10_MBPS,
-						    duplex ?
-						    FDX_CAPABLE_FULL_SELECTED:
-						    FDX_CAPABLE_HALF_SELECTED);
-        			elMIIpollBit(dev, phy, MII_STATUS,
-						MIISTAT_LINK, TRUE, &status);
-			} else {
-				status = mdio_read(dev, phy, MII_STATUS);
-			}
-		}
+	/* The SiS900-specific entries in the device structure. */
+	net_dev->open = &sis900_open;
+	net_dev->hard_start_xmit = &sis900_start_xmit;
+	net_dev->stop = &sis900_close;
+	net_dev->get_stats = &sis900_get_stats;
+	net_dev->set_multicast_list = &set_rx_mode;
+	net_dev->do_ioctl = &mii_ioctl;
 
-                if (tp->pmd_status & MIISTAT_LINK) 
-               	        tp->LinkOn = TRUE;
-	        else
-                        tp->LinkOn = FALSE;
+	return net_dev;
+}
 
-		tp->LinkChange = FALSE;
+static int sis900_mii_probe (struct device * net_dev)
+{
+	struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv;
+	int phy_addr;
 	
-        }
+	sis_priv->mii = NULL;
+	
+	/* search for total of 32 possible mii phy addresses */
+	for (phy_addr = 0; phy_addr < 32; phy_addr++) {
+		u16 mii_status;
+		u16 phy_id0, phy_id1;
+		int i;
+		
+		mii_status = mdio_read(net_dev, phy_addr, MII_STATUS);
+		if (mii_status == 0xffff || mii_status == 0x0000)
+			/* the mii is not accessable, try next one */
+			continue;
+		
+		phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0);
+		phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);
+		
+		/* search our mii table for the current mii */ 
+		for (i = 0; mii_chip_table[i].phy_id1; i++)
+			if (phy_id0 == mii_chip_table[i].phy_id0) {
+				struct mii_phy * mii_phy;
+				
+				printk(KERN_INFO 
+				       "%s: %s transceiver found at address %d.\n",
+				       net_dev->name, mii_chip_table[i].name, 
+				       phy_addr);;
+				if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) {
+					mii_phy->chip_info = mii_chip_table+i;
+					mii_phy->phy_addr = phy_addr;
+					mii_phy->status = mdio_read(net_dev, phy_addr, 
+								    MII_STATUS);
+					mii_phy->next = sis_priv->mii;
+					sis_priv->mii = mii_phy;
+				}
+				/* the current mii is on our mii_info_table, 
+				   try next address */
+				break;
+			}
+	}
+	
+	if (sis_priv->mii == NULL) {
+		printk(KERN_INFO "%s: No MII transceivers found!\n", 
+		       net_dev->name);
+		return 0;
+	}
 
-	if (sis900_debug > 1) {
-        	if (tp->full_duplex == FDX_CAPABLE_FULL_SELECTED) {
-                	printk(KERN_INFO "%s: Media type is Full Duplex.\n",
-						dev->name);
-        	} else {
-                	printk(KERN_INFO "%s: Media type is Half Duplex.\n",
-						dev->name);
-        	}
-        	if (tp->speeds == HW_SPEED_100_MBPS) {
-                	printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name);
-        	} else {
-                	printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name);
-        	}
-	}
-
-        /* The SiS900-specific entries in the device structure. */
-        dev->open = &sis900_open;
-        dev->hard_start_xmit = &sis900_start_xmit;
-        dev->stop = &sis900_close;
-        dev->get_stats = &sis900_get_stats;
-        dev->set_multicast_list = &set_rx_mode;
-        dev->do_ioctl = &mii_ioctl;
-
-        return dev;
-}
-
-/* Serial EEPROM section. */
-
-/*  EEPROM_Ctrl bits. */
-#define EECLK           0x00000004      /* EEPROM shift clock. */
-#define EECS            0x00000008      /* EEPROM chip select. */
-#define EEDO            0x00000002      /* EEPROM chip data out. */
-#define EEDI            0x00000001      /* EEPROM chip data in. */
+	/* arbitrary choose that last PHY and current PHY */
+	sis_priv->cur_phy = sis_priv->mii->phy_addr;
+	printk(KERN_INFO "%s: Using %s as default\n", net_dev->name,
+	       sis_priv->mii->chip_info->name);
 
-/* Delay between EEPROM clock transitions.
-   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
- */
+	if (sis_priv->mii->status & MII_STAT_LINK) 
+		sis_priv->LinkOn = TRUE;
+	else
+		sis_priv->LinkOn = FALSE;
 
-#define eeprom_delay()  inl(ee_addr)
+	return 1;
+}
 
-/* The EEPROM commands include the alway-set leading bit. */
-#define EEread          0x0180
-#define EEwrite         0x0140
-#define EEerase         0x01C0
-#define EEwriteEnable   0x0130
-#define EEwriteDisable  0x0100
-#define EEeraseAll      0x0120
-#define EEwriteAll      0x0110
-#define EEaddrMask      0x013F
-#define EEcmdShift      16
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay()  inl(ee_addr)
 
+/* Read Serial EEPROM through EEPROM Access Register, Note that location is 
+   in word (16 bits) unit */
 static u16 read_eeprom(long ioaddr, int location)
 {
-        int i;
+	int i;
         u16 retval = 0;
         long ee_addr = ioaddr + mear;
         u32 read_cmd = location | EEread;
@@ -748,7 +359,7 @@
         outl(EECLK, ee_addr);
         eeprom_delay();
 
-        /* Shift the read command bits out. */
+        /* Shift the read command (9) bits out. */
         for (i = 8; i >= 0; i--) {
                 u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
                 outl(dataval, ee_addr);
@@ -759,6 +370,7 @@
         outb(EECS, ee_addr);
         eeprom_delay();
 
+	/* read the 16-bits data in */
         for (i = 16; i > 0; i--) {
                 outl(EECS, ee_addr);
                 eeprom_delay();
@@ -767,39 +379,20 @@
                 retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
                 eeprom_delay();
         }
-
+		
         /* Terminate the EEPROM access. */
         outl(0, ee_addr);
         eeprom_delay();
         outl(EECLK, ee_addr);
+
         return (retval);
 }
 
-/* MII serial management: mostly bogus for now. */
 /* Read and write the MII management registers using software-generated
-   serial MDIO protocol.
-   The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
-   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
-   "overclocking" issues. */
-
+   serial MDIO protocol. Note that the command bits and data bits are
+   send out seperately */
 #define mdio_delay()    inl(mdio_addr)
 
-#define MIIread         0x6000
-#define MIIwrite        0x6002
-#define MIIpmdMask      0x0F80
-#define MIIpmdShift     7
-#define MIIregMask      0x007C
-#define MIIregShift     2
-#define MIIturnaroundBits       2
-#define MIIcmdLen       16
-#define MIIcmdShift     16
-#define MIIreset        0xFFFFFFFF
-#define MIIwrLen        32
-
-#define MDC             0x00000040
-#define MDDIR           0x00000020
-#define MDIO            0x00000010
-
 static void mdio_idle(long mdio_addr)
 {
         outl(MDIO | MDDIR, mdio_addr);
@@ -821,11 +414,11 @@
         return;
 }
 
-static int mdio_read(struct device *dev, int phy_id, int location)
+static u16 mdio_read(struct device *net_dev, int phy_id, int location)
 {
-        long mdio_addr = dev->base_addr + mear;
+        long mdio_addr = net_dev->base_addr + mear;
         int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
-        int retval = 0;
+        u16 retval = 0;
         int i;
 
         mdio_reset(mdio_addr);
@@ -834,13 +427,15 @@
         for (i = 15; i >= 0; i--) {
                 int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
                 outl(dataval, mdio_addr);
+		mdio_delay();
                 outl(dataval | MDC, mdio_addr);
+		mdio_delay();
         }
 
-        /* Read the two transition, 16 data, and wire-idle bits. */
+        /* Read the 16 data bits. */
         for (i = 16; i > 0; i--) {
                 outl(0, mdio_addr);
-                //mdio_delay();
+                mdio_delay();
                 retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
                 outl(MDC, mdio_addr);
                 mdio_delay();
@@ -848,9 +443,9 @@
         return retval;
 }
 
-static void mdio_write(struct device *dev, int phy_id, int location, int value)
+static void mdio_write(struct device *net_dev, int phy_id, int location, int value)
 {
-        long mdio_addr = dev->base_addr + mear;
+        long mdio_addr = net_dev->base_addr + mear;
         int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
         int i;
 
@@ -858,7 +453,7 @@
         mdio_idle(mdio_addr);
 
         /* Shift the command bits out. */
-        for (i = 31; i >= 0; i--) {
+        for (i = 15; i >= 0; i--) {
                 int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
                 outb(dataval, mdio_addr);
                 mdio_delay();
@@ -866,6 +461,17 @@
                 mdio_delay();
         }
         mdio_delay();
+
+	/* Shift the value bits out. */
+	for (i = 15; i >= 0; i--) {
+		int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR;
+	        outl(dataval, mdio_addr);
+	        mdio_delay();
+	        outl(dataval | MDC, mdio_addr);
+	       	mdio_delay();
+	}
+	mdio_delay();
+	
         /* Clear out extra bits. */
         for (i = 2; i > 0; i--) {
                 outb(0, mdio_addr);
@@ -877,354 +483,366 @@
 }
 
 static int
-sis900_open(struct device *dev)
+sis900_open(struct device *net_dev)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
-
-        if (sis900_debug > 0)
-                printk(KERN_INFO "%s sis900_open, IO Addr=%x, Irq=%x\n",
-                                dev->name, (unsigned int)ioaddr, dev->irq);
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
 
         /* Soft reset the chip. */
-        outl(0, ioaddr + imr);
-        outl(0, ioaddr + ier);
-        outl(0, ioaddr + rfcr);
-        outl(RESET | RxRESET | TxRESET, ioaddr + cr);
+	sis900_reset(net_dev);
 
-        if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev))
-        {
+        if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {
                 return -EAGAIN;
         }
 
-        MOD_INC_USE_COUNT;      
+        MOD_INC_USE_COUNT;
 
-        tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL);
-        tp->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL);
-        if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) {
-                if (tp->tx_bufs)
-                        kfree(tp->tx_bufs);
-                if (tp->rx_bufs)
-                        kfree(tp->rx_bufs);
-		if (!tp->tx_bufs) {
-	              printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n",
-                                   dev->name, TX_BUF_SIZE * NUM_TX_DESC);
-		}
-		if (!tp->rx_bufs) {
-	              printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n",
-                                   dev->name, RX_BUF_SIZE * NUM_RX_DESC);
-		}
-                return -ENOMEM;
-        }
-
-        {
-                u32 rfcrSave;
-                u32 w;
-                u32 i;
-
-                rfcrSave = inl(rfcr);
-                outl(rfcrSave & ~RFEN, rfcr);
-                for (i=0 ; i<3 ; i++) {
-                        w = (u16)*((u16*)(dev->dev_addr)+i);
-                        outl((((u32) i) << RFEP_shift), ioaddr + rfcr);
-                        outl((u32)w, ioaddr + rfdr);
-                        if (sis900_debug > 4) {
-                                printk(KERN_INFO "Filter Addr[%d]=%x\n",
-                                        i, inl(ioaddr + rfdr));
-                        }
-                }
-                outl(rfcrSave, rfcr);
-        }
-
-        sis900_init_ring(dev);
-        outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp);
-        outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp);
-
-        if (sis900_debug > 4)
-                printk(KERN_INFO "txdp:%8.8x\n", inl(ioaddr + txdp));
-
-        /* Check that the chip has finished the reset. */
-        {
-                u32 status;
-                int j=0;
-                status = TxRCMP | RxRCMP;
-                while (status && (j++ < 30000)) {
-                        status ^= (inl(isr) & status);
-                }
-        }
+	sis900_init_rxfilter(net_dev);
 
-        outl(PESEL, ioaddr + cfg);
+	sis900_init_tx_ring(net_dev);
+	sis900_init_rx_ring(net_dev);
 
-        /* Must enable Tx/Rx before setting transfer thresholds! */
-        /*
-         *      #define TX_DMA_BURST    0
-         *      #define RX_DMA_BURST    0
-         *      #define TX_FIFO_THRESH  16
-         *      #define TxDRNT_100      (1536>>5)
-         *      #define TxDRNT_10       (1536>>5)
-         *      #define RxDRNT_100      (1536>>5)
-         *      #define RxDRNT_10       (1536>>5)
-         */
-        outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg);
-        outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10,
-                                                ioaddr + txcfg);
-        if (sis900_debug > 1)
-        {
-        	if (tp->LinkOn) {
-        		printk(KERN_INFO"%s: Media Type %s%s-duplex.\n",
-                                dev->name,
-                                tp->speeds==HW_SPEED_100_MBPS ?
-					"100mbps " : "10mbps ",
-                                tp->full_duplex== FDX_CAPABLE_FULL_SELECTED ?
-					"full" : "half");
-		}
-		else
-		{
-			printk(KERN_INFO"%s: Media Link Off\n", dev->name);
-		}
-	}
-        set_rx_mode(dev);
+        set_rx_mode(net_dev);
 
-        dev->tbusy = 0;
-        dev->interrupt = 0;
-        dev->start = 1;
+        net_dev->tbusy = 0;
+        net_dev->interrupt = 0;
+        net_dev->start = 1;
 
         /* Enable all known interrupts by setting the interrupt mask. */
-        outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr);
+	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
         outl(RxENA, ioaddr + cr);
         outl(IE, ioaddr + ier);
 
-        if (sis900_debug > 3)
-                printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n",
-                           dev->name, ioaddr, dev->irq);
+	sis900_check_mode(net_dev, sis_priv->mii);
 
         /* Set the timer to switch to check for link beat and perhaps switch
            to an alternate media type. */
-        init_timer(&tp->timer);
-        tp->timer.expires = RUN_AT((24*HZ)/10);         /* 2.4 sec. */
-        tp->timer.data = (unsigned long)dev;
-        tp->timer.function = &sis900_timer;             /* timer handler */
-        add_timer(&tp->timer);
+        init_timer(&sis_priv->timer);
+        sis_priv->timer.expires = jiffies + HZ;
+        sis_priv->timer.data = (unsigned long)net_dev;
+        sis_priv->timer.function = &sis900_timer;
+        add_timer(&sis_priv->timer);
 
         return 0;
 }
 
-static void sis900_timer(unsigned long data)
+/* set receive filter address to our MAC address */
+static void
+sis900_init_rxfilter (struct device * net_dev)
 {
-        struct device *dev = (struct device *)data;
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        int next_tick = 0;
-        u16 status;
+	long ioaddr = net_dev->base_addr;
+	u32 rfcrSave;
+	u32 i;
+	
+	rfcrSave = inl(rfcr + ioaddr);
 
-        if (!tp->LinkOn) {
-                status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS);
-		if (status & MIISTAT_LINK) {
-                	elPMDreadMode(dev, tp->phys[tp->phy_idx],
-                                        &tp->speeds, &tp->full_duplex);
-			tp->LinkOn = TRUE;
-                        printk(KERN_INFO "%s: Media Link On %s%s-duplex ",
-                                   dev->name,
-                                   tp->speeds == HW_SPEED_100_MBPS ?
-						"100mbps " : "10mbps ",
-                                   tp->full_duplex==FDX_CAPABLE_FULL_SELECTED ?
-						"full" : "half");
-		}
-        } else { // previous link on
-                status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS);
-		if (!(status & MIISTAT_LINK)) {
-			tp->LinkOn = FALSE;
-                        printk(KERN_INFO "%s: Media Link Off\n", dev->name);
+	/* disable packet filtering before setting filter */
+	outl(rfcrSave & ~RFEN, rfcr);
+
+	/* load MAC addr to filter data register */
+	for (i = 0 ; i < 3 ; i++) {
+		u32 w;
+
+		w = (u32) *((u16 *)(net_dev->dev_addr)+i);
+		outl((i << RFADDR_shift), ioaddr + rfcr);
+		outl(w, ioaddr + rfdr);
+
+		if (sis900_debug > 2) {
+			printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n",
+			       net_dev->name, i, inl(ioaddr + rfdr));
 		}
-        }
-        next_tick = 2*HZ;
+	}
 
-        if (next_tick) {
-                tp->timer.expires = RUN_AT(next_tick);
-                add_timer(&tp->timer);
-        }
+	/* enable packet filitering */
+	outl(rfcrSave | RFEN, rfcr + ioaddr);
 }
 
-static void sis900_tx_timeout(struct device *dev)
+/* Initialize the Tx ring. */
+static void
+sis900_init_tx_ring(struct device *net_dev)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr; 
         int i;
+	
+        sis_priv->tx_full = 0;
+        sis_priv->dirty_tx = sis_priv->cur_tx = 0;
+	
+        for (i = 0; i < NUM_TX_DESC; i++) {
+		sis_priv->tx_skbuff[i] = NULL;
 
-        if (sis900_debug > 0)
-                printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n",
-                           dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
-
-        /* Disable interrupts by clearing the interrupt mask. */
-        outl(0x0000, ioaddr + imr);
+		sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]);
+                sis_priv->tx_ring[i].cmdsts = 0;
+		sis_priv->tx_ring[i].bufptr = 0;
+        }
+	sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]);
+
+	/* load Transmit Descriptor Register */
+        outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp); 
+	if (sis900_debug > 2)
+                printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", 
+		       net_dev->name, inl(ioaddr + txdp));
+}
+
+/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */ 
+static void 
+sis900_init_rx_ring(struct device *net_dev) 
+{ 
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; 
+        long ioaddr = net_dev->base_addr; 
+        int i;
+ 
+        sis_priv->cur_rx = 0; 
+	sis_priv->dirty_rx = 0;
+
+	/* init RX descriptor */
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		sis_priv->rx_skbuff[i] = NULL;
+
+		sis_priv->rx_ring[i].link = (u32) virt_to_bus(&sis_priv->rx_ring[i+1]);
+		sis_priv->rx_ring[i].cmdsts = 0;
+		sis_priv->rx_ring[i].bufptr = 0;
+	}
+	sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]);
 
-        /* Emit info to figure out what went wrong. */
-	if (sis900_debug > 1) {
-	        printk(KERN_INFO "%s:Tx queue start entry %d dirty entry %d.\n",
-                		   dev->name, tp->cur_tx, tp->dirty_tx);
-        	for (i = 0; i < NUM_TX_DESC; i++) 
-                	printk(KERN_INFO "%s:  Tx descriptor %d is %8.8x.%s\n",
-                        	dev->name, i, (unsigned int)&tp->tx_buf[i],
-                     		i == tp->dirty_tx % NUM_TX_DESC ?
-                      		" (queue head)" : "");
+        /* allocate sock buffers */
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		struct sk_buff *skb;
+
+		if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
+			/* not enough memory for skbuff, this makes a "hole"
+			   on the buffer ring, it is not clear how the 
+			   hardware will react to this kind of degenerated 
+			   buffer */
+			break;
+		}
+		skb->dev = net_dev;
+		sis_priv->rx_skbuff[i] = skb;
+		sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;
+		sis_priv->rx_ring[i].bufptr = virt_to_bus(skb->tail);
 	}
+	sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC);
 
-        /* Soft reset the chip. */
-        //outb(RESET, ioaddr + cr);
-        /* Check that the chip has finished the reset. */
-        /*
-        for (i = 1000; i > 0; i--)
-                if ((inb(ioaddr + cr) & RESET) == 0)
-                        break;
-        */
+	/* load Receive Descriptor Register */
+        outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp);
+	if (sis900_debug > 2)
+                printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", 
+		       net_dev->name, inl(ioaddr + rxdp));
+}
+/* on each timer ticks we check two things, Link Status (ON/OFF) and 
+   Link Mode (10/100/Full/Half)
+ */
+static void sis900_timer(unsigned long data)
+{
+        struct device *net_dev = (struct device *)data;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	struct mii_phy *mii_phy = sis_priv->mii;
+        static int next_tick = 5*HZ;
+        u16 status;
 
-        tp->cur_rx = 0; 
-        /* Must enable Tx/Rx before setting transfer thresholds! */
-        /*
-        set_rx_mode(dev);
-        */
-        {       /* Save the unsent Tx packets. */
-                struct sk_buff *saved_skb[NUM_TX_DESC], *skb;
-                int j;
-                for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++)
-                        saved_skb[j]=tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC];
-                tp->dirty_tx = tp->cur_tx = 0;
-
-                for (i = 0; i < j; i++) {
-                        skb = tp->tx_skbuff[i] = saved_skb[i];
-                        /* Always alignment */
-                        memcpy((unsigned char*)(tp->tx_buf[i].buf),
-                                                skb->data, skb->len);
-                        tp->tx_buf[i].cmdsts = OWN | skb->len;
-                        /* Note: the chip doesn't have auto-pad! */
-                        /*
-                        outl(tp->tx_flag|(skb->len>=ETH_ZLEN?skb->len:ETH_ZLEN),
-                                 ioaddr + TxStatus0 + i*4);
-                        */
-                }
-                outl(TxENA, ioaddr + cr);
-                tp->cur_tx = i;
-                while (i < NUM_TX_DESC)
-                        tp->tx_skbuff[i++] = 0;
-                if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
-                        dev->tbusy = 0;
-                        tp->tx_full = 0;
-                } else {
-                        tp->tx_full = 1;
-                }
-        }
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
 
-        dev->trans_start = jiffies;
-        tp->stats.tx_errors++;
-        /* Enable all known interrupts by setting the interrupt mask. */
-        outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr);
-        return;
-}
+	/* current mii phy is failed to link, try another one */
+	while (!(status & MII_STAT_LINK)) {		
+		if (mii_phy->next == NULL) { 
+			if (sis_priv->LinkOn) {
+				/* link stat change from ON to OFF */
+				next_tick = HZ;
+				sis_priv->LinkOn = FALSE;
+				printk(KERN_INFO "%s: Media Link Off\n",
+				       net_dev->name);
+			}
+			sis_priv->timer.expires = jiffies + next_tick;
+			add_timer(&sis_priv->timer);
+			return;
+		}
+		mii_phy = mii_phy->next;
+		status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS);
+	}
 
+	if (!sis_priv->LinkOn) {
+		/* link stat change forn OFF to ON, read and report link mode */
+		sis_priv->LinkOn = TRUE;
+		next_tick = 5*HZ;
+		/* change what cur_phy means */
+		if (mii_phy->phy_addr != sis_priv->cur_phy) {
+			printk(KERN_INFO "%s: Changing transceiver to %s\n", net_dev->name,
+			       mii_phy->chip_info->name);
+			status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
+			mdio_write(net_dev, sis_priv->cur_phy, 
+				   MII_CONTROL, status | MII_CNTL_ISOLATE);
+			status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL);
+			mdio_write(net_dev, mii_phy->phy_addr, 
+				   MII_CONTROL, status & ~MII_CNTL_ISOLATE);
+			sis_priv->cur_phy = mii_phy->phy_addr;
+		}
+		sis900_check_mode(net_dev, mii_phy);
+	}
 
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-sis900_init_ring(struct device *dev)
+	sis_priv->timer.expires = jiffies + next_tick;
+	add_timer(&sis_priv->timer);
+}
+static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        int i;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
+	int speed, duplex;
+	u32 tx_flags = 0, rx_flags = 0;
 
-        tp->tx_full = 0;
-        tp->cur_rx = 0;
-        tp->dirty_tx = tp->cur_tx = 0;
+	mii_phy->chip_info->read_mode(net_dev, sis_priv->cur_phy, &speed, &duplex);
 
-        /* Tx Buffer */
-        for (i = 0; i < NUM_TX_DESC; i++) {
-                tp->tx_skbuff[i] = 0;
-                tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE];
-                tp->tx_buf[i].bufPhys =
-                                virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]);
-        }
+	tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+	rx_flags = RX_DMA_BURST << RxMXDMA_shift;
 
-        /* Tx Descriptor */
-        for (i = 0; i< NUM_TX_DESC; i++) {
-                tp->tx_buf[i].llink = (u32)
-                        &(tp->tx_buf[((i+1) < NUM_TX_DESC) ? (i+1) : 0]);
-                tp->tx_buf[i].plink = (u32)
-                        virt_to_bus(&(tp->tx_buf[((i+1) < NUM_TX_DESC) ?
-                                (i+1) : 0].plink));
-                tp->tx_buf[i].physAddr=
-                                virt_to_bus(&(tp->tx_buf[i].plink));
-                tp->tx_buf[i].cmdsts=0;
-        }
+	if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) {
+		rx_flags |= (RxDRNT_10 << RxDRNT_shift);
+		tx_flags |= (TxDRNT_10 << TxDRNT_shift);
+	}
+	else {
+		rx_flags |= (RxDRNT_100 << RxDRNT_shift);
+		tx_flags |= (TxDRNT_100 << TxDRNT_shift);
+	}
 
-        /* Rx Buffer */
-        for (i = 0; i < NUM_RX_DESC; i++) {
-                tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE];
-                tp->rx_buf[i].bufPhys =
-                                virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]);
-        }
+	if (duplex == FDX_CAPABLE_FULL_SELECTED) {
+		tx_flags |= (TxCSI | TxHBI);
+		rx_flags |= RxATX;
+	}
 
-        /* Rx Descriptor */
-        for (i = 0; i< NUM_RX_DESC; i++) {
-                tp->rx_buf[i].llink = (u32)
-                        &(tp->rx_buf[((i+1) < NUM_RX_DESC) ? (i+1) : 0]);
-                tp->rx_buf[i].plink = (u32)
-                        virt_to_bus(&(tp->rx_buf[((i+1) < NUM_RX_DESC) ?
-                                (i+1) : 0].plink));
-                tp->rx_buf[i].physAddr=
-                                virt_to_bus(&(tp->rx_buf[i].plink));
-                tp->rx_buf[i].cmdsts=RX_BUF_SIZE;
-        }
+	outl (tx_flags, ioaddr + txcfg);
+	outl (rx_flags, ioaddr + rxcfg);
 }
+static void sis900_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex)
+{
+	int i = 0;
+	u32 status;
+	
+	/* STSOUT register is Latched on Transition, read operation updates it */
+	while (i++ < 2)
+		status = mdio_read(net_dev, phy_addr, MII_STSOUT);
+
+	if (status & MII_STSOUT_SPD)
+		*speed = HW_SPEED_100_MBPS;
+	else
+		*speed = HW_SPEED_10_MBPS;
+
+	if (status & MII_STSOUT_DPLX)
+		*duplex = FDX_CAPABLE_FULL_SELECTED;
+	else
+		*duplex = FDX_CAPABLE_HALF_SELECTED;
+
+	if (status & MII_STSOUT_LINK_FAIL)
+		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+	else
+		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", 
+		       net_dev->name,
+		       *speed == HW_SPEED_100_MBPS ? 
+		       "100mbps" : "10mbps",
+		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
+		       "full" : "half");
+}
+static void amd79c901_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex)
+{
+	int i;
+	u16 status;
+	
+	for (i = 0; i < 2; i++)
+		status = mdio_read(net_dev, phy_addr, MII_STATUS);
 
-static int
-sis900_start_xmit(struct sk_buff *skb, struct device *dev)
+	if (status & MII_STAT_CAN_AUTO) {
+		/* 10BASE-T PHY */
+		for (i = 0; i < 2; i++)
+			status = mdio_read(net_dev, phy_addr, MII_STATUS_SUMMARY);
+		if (status & MII_STSSUM_SPD)
+			*speed = HW_SPEED_100_MBPS;
+		else
+			*speed = HW_SPEED_10_MBPS;
+		if (status & MII_STSSUM_DPLX)
+			*duplex = FDX_CAPABLE_FULL_SELECTED;
+		else
+			*duplex = FDX_CAPABLE_HALF_SELECTED;
+
+		if (status & MII_STSSUM_LINK)
+			printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", 
+			       net_dev->name,
+			       *speed == HW_SPEED_100_MBPS ? 
+			       "100mbps" : "10mbps",
+			       *duplex == FDX_CAPABLE_FULL_SELECTED ?
+			       "full" : "half");
+		else
+			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+			
+	}
+	else {
+		/* HomePNA */
+		*speed = HW_SPEED_HOME;
+		*duplex = FDX_CAPABLE_HALF_SELECTED;
+		if (status & MII_STAT_LINK)
+			printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n", 
+			       net_dev->name);
+		else
+			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+	}
+}
+static void sis900_tx_timeout(struct device *net_dev)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
-        int entry;
-
-        /* Block a timer-based transmit from overlapping.  This could better be
-           done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
-        if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
-                if (jiffies - dev->trans_start < TX_TIMEOUT)
-                        return 1;
-                sis900_tx_timeout(dev);
-                return 1;
-        }
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
 
-        /* Calculate the next Tx descriptor entry. ????? */
-        entry = tp->cur_tx % NUM_TX_DESC;
+	printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",
+	       net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
+	
+        /* Disable interrupts by clearing the interrupt mask. */
+        outl(0x0000, ioaddr + imr);
 
-        tp->tx_skbuff[entry] = skb;
+        sis_priv->cur_rx = 0;
+        net_dev->trans_start = jiffies;
+        sis_priv->stats.tx_errors++;
 
-        if (sis900_debug > 5) {
-                int i;
-                printk(KERN_INFO "%s: SKB Tx Frame contents:(len=%d)",
-                                                dev->name,skb->len);
-
-                for (i = 0; i < skb->len; i++) {
-                        printk("%2.2x ",
-                        (u8)skb->data[i]);
-                }
-                printk(".\n");
-        }
+	/* FIXME: Should we restart the transmission thread here  ?? */
 
-        memcpy(tp->tx_buf[entry].buf,
-                                skb->data, skb->len);
+        /* Enable all known interrupts by setting the interrupt mask. */
+        outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
+        return;
+}
 
-        tp->tx_buf[entry].cmdsts=(OWN | skb->len);
+static int
+sis900_start_xmit(struct sk_buff *skb, struct device *net_dev)
+{
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
+        unsigned int  entry;
+
+	/* test tbusy to see if we have timeout situation then set it */
+        if (test_and_set_bit(0, (void*)&net_dev->tbusy) != 0) {
+                if (jiffies - net_dev->trans_start > TX_TIMEOUT)
+			sis900_tx_timeout(net_dev);
+                return 1;
+        }
 
-        //tp->tx_buf[entry].plink = 0;
+        /* Calculate the next Tx descriptor entry. */
+        entry = sis_priv->cur_tx % NUM_TX_DESC;
+        sis_priv->tx_skbuff[entry] = skb;
+
+	/* set the transmit buffer descriptor and enable Transmit State Machine */
+	sis_priv->tx_ring[entry].bufptr = virt_to_bus(skb->data);
+        sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);
         outl(TxENA, ioaddr + cr);
-        if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
-                clear_bit(0, (void*)&dev->tbusy);
+
+        if (++sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC) {
+		/* Typical path, clear tbusy to indicate more 
+		   transmission is possible */
+                clear_bit(0, (void*)&net_dev->tbusy);
         } else {
-                tp->tx_full = 1;
+		/* no more transmit descriptor avaiable, tbusy remain set */
+                sis_priv->tx_full = 1;
         }
 
-        /* Note: the chip doesn't have auto-pad! */
+        net_dev->trans_start = jiffies;
 
-        dev->trans_start = jiffies;
-        if (sis900_debug > 4)
-                printk(KERN_INFO "%s: Queued Tx packet at "
-                                "%p size %d to slot %d.\n",
-                           dev->name, skb->data, (int)skb->len, entry);
+        if (sis900_debug > 3)
+                printk(KERN_INFO "%s: Queued Tx packet at %p size %d "
+		       "to slot %d.\n",
+		       net_dev->name, skb->data, (int)skb->len, entry);
 
         return 0;
 }
@@ -1233,340 +851,264 @@
    after the Tx thread. */
 static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-        struct device *dev = (struct device *)dev_instance;
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        struct device *net_dev = (struct device *)dev_instance;
         int boguscnt = max_interrupt_work;
-        int status;
-        long ioaddr = dev->base_addr;
+	long ioaddr = net_dev->base_addr;
+	u32 status;
 
 #if defined(__i386__)
         /* A lock to prevent simultaneous entry bug on Intel SMP machines. */
-        if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+        if (test_and_set_bit(0, (void*)&net_dev->interrupt)) {
                 printk(KERN_INFO "%s: SMP simultaneous entry of "
-                                "an interrupt handler.\n", dev->name);
-                dev->interrupt = 0;     /* Avoid halting machine. */
+		       "an interrupt handler.\n", net_dev->name);
+                net_dev->interrupt = 0;     /* Avoid halting machine. */
                 return;
         }
 #else
-        if (dev->interrupt) {
-                printk(KERN_INFO "%s: Re-entering the "
-                                "interrupt handler.\n", dev->name);
+        if (net_dev->interrupt) {
+                printk(KERN_INFO "%s: Re-entering the interrupt handler.\n", 
+		       net_dev->name);
                 return;
         }
-        dev->interrupt = 1;
+        net_dev->interrupt = 1;
 #endif
 
         do {
                 status = inl(ioaddr + isr);
-                /* Acknowledge all of the current interrupt sources ASAP. */
-                outl(status, ioaddr + isr); // ?????
-
-                if (sis900_debug > 4)
-                        printk(KERN_INFO "%s: interrupt  status=%#4.4x "
-                                "new intstat=%#4.4x.\n",
-                                dev->name, status, inl(ioaddr + isr));
+		
+                if (sis900_debug > 3)
+			printk(KERN_INFO "%s: entering interrupt, "
+			       "original status = %#8.8x, "
+			       "new status = %#8.8x.\n",
+			       net_dev->name, status, inl(ioaddr + isr));
 
-                if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) {
+                if ((status & (HIBERR|TxURN|TxERR|TxOK|RxORN|RxERR|RxOK)) == 0)
+			/* nothing intresting happened */
                         break;
-                }
-
-                if (status & (RxOK|RxORN|RxERR)) /* Rx interrupt */
-                        sis900_rx(dev);
 
-                if (status & (TxOK | TxERR)) {
-                        unsigned int dirty_tx;
-
-                        if (sis900_debug > 5) {
-                                printk(KERN_INFO "TxOK:tp->cur_tx:%d,"
-                                                "tp->dirty_tx:%x\n",
-                                        tp->cur_tx, tp->dirty_tx);
-                        }
-                        for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx;
-                                dirty_tx++)
-                        {
-                                int i;
-                                int entry = dirty_tx % NUM_TX_DESC;
-                                int txstatus = tp->tx_buf[entry].cmdsts;
-
-                                if (sis900_debug > 4) {
-                                        printk(KERN_INFO "%s:     Tx Frame contents:"
-                                                "(len=%d)",
-                                                dev->name, (txstatus & DSIZE));
-
-                                        for (i = 0; i < (txstatus & DSIZE) ;
-                                                                        i++) {
-                                                printk("%2.2x ",
-                                                (u8)(tp->tx_buf[entry].buf[i]));
-                                        }
-                                        printk(".\n");
-                                }
-                                if ( ! (txstatus & (OK | UNDERRUN)))
-                                {
-                                        if (sis900_debug > 1)
-                                                printk(KERN_INFO "Tx NOT (OK,"
-                                                        "UnderRun)\n");
-                                        break;  /* It still hasn't been Txed */
-                                }
-
-                                /* Note: TxCarrierLost is always asserted
-                                                at 100mbps.                 */
-                                if (txstatus & (OWCOLL | ABORT)) {
-                                        /* There was an major error, log it. */
-                                        if (sis900_debug > 1)
-                                                printk(KERN_INFO "Tx Out of "
-                                                        " Window,Abort\n");
-#ifndef final_version
-                                        if (sis900_debug > 1)
-                                                printk(KERN_INFO "%s: Transmit "
-                                                    "error, Tx status %8.8x.\n",
-                                                           dev->name, txstatus);
-#endif
-                                        tp->stats.tx_errors++;
-                                        if (txstatus & ABORT) {
-                                                tp->stats.tx_aborted_errors++;
-                                        }
-                                        if (txstatus & NOCARRIER)
-                                                tp->stats.tx_carrier_errors++;
-                                        if (txstatus & OWCOLL)
-                                                tp->stats.tx_window_errors++;
-#ifdef ETHER_STATS
-                                        if ((txstatus & COLCNT)==COLCNT)
-                                                tp->stats.collisions16++;
-#endif
-                                } else {
-#ifdef ETHER_STATS
-                                        /* No count for tp->stats.tx_deferred */
-#endif
-                                        if (txstatus & UNDERRUN) {
-                                           if (sis900_debug > 2)
-                                             printk(KERN_INFO "Tx UnderRun\n");
-                                        }
-                                        tp->stats.collisions +=
-                                                        (txstatus >> 16) & 0xF;
-#if LINUX_VERSION_CODE > 0x20119
-                                        tp->stats.tx_bytes += txstatus & DSIZE;
-#endif
-                                        if (sis900_debug > 2)
-                                           printk(KERN_INFO "Tx Transmit OK\n");
-                                        tp->stats.tx_packets++;
-                                }
-
-                                /* Free the original skb. */
-                                if (sis900_debug > 2)
-                                        printk(KERN_INFO "Free original skb\n");
-                                dev_free_skb(tp->tx_skbuff[entry]);
-                                tp->tx_skbuff[entry] = 0;
-                        } // for dirty
-
-#ifndef final_version
-                        if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
-                                printk(KERN_INFO"%s: Out-of-sync dirty pointer,"
-                                                " %d vs. %d, full=%d.\n",
-                                                dev->name, dirty_tx,
-                                                tp->cur_tx, tp->tx_full);
-                                dirty_tx += NUM_TX_DESC;
-                        }
-#endif
-
-                        if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) {
-                                /* The ring is no longer full, clear tbusy. */
-				if (sis900_debug > 3)
-                                   printk(KERN_INFO "Tx Ring NO LONGER Full\n");
-                                tp->tx_full = 0;
-                                dev->tbusy = 0;
-                                mark_bh(NET_BH);
-                        }
-
-                        tp->dirty_tx = dirty_tx;
-                        if (sis900_debug > 2)
-                           printk(KERN_INFO "TxOK,tp->cur_tx:%d,tp->dirty:%d\n",
-                                                tp->cur_tx, tp->dirty_tx);
-                } // if (TxOK | TxERR)
-
-                /* Check uncommon events with one test. */
-                if (status & (RxORN | TxERR | RxERR)) {
-                        if (sis900_debug > 2)
-                                printk(KERN_INFO "%s: Abnormal interrupt,"
-                                        "status %8.8x.\n", dev->name, status);
-
-                        if (status == 0xffffffff)
-                                break;
-                        if (status & (RxORN | RxERR))
-                                tp->stats.rx_errors++;
-
-
-                        if (status & RxORN) {
-                                tp->stats.rx_over_errors++;
-                        }
-                }
+		/* why dow't we break after Tx/Rx case ?? keyword: full-duplex */
+                if (status & (RxORN | RxERR | RxOK))
+			/* Rx interrupt */
+                        sis900_rx(net_dev);
+
+                if (status & (TxURN | TxERR | TxOK))
+			/* Tx interrupt */
+			sis900_finish_xmit(net_dev);
+
+                /* something strange happened !!! */
+                if (status & HIBERR) {
+			printk(KERN_INFO "%s: Abnormal interrupt,"
+			       "status %#8.8x.\n", net_dev->name, status);
+			break;
+		}
                 if (--boguscnt < 0) {
                         printk(KERN_INFO "%s: Too much work at interrupt, "
-                                   "IntrStatus=0x%4.4x.\n",
-                                   dev->name, status);
+			       "interrupt status = %#8.8x.\n",
+			       net_dev->name, status);
                         break;
                 }
         } while (1);
-
+	
         if (sis900_debug > 3)
-                printk(KERN_INFO "%s: exiting interrupt, intr_status=%#4.4x.\n",
-                           dev->name, inl(ioaddr + isr));
-
+                printk(KERN_INFO "%s: exiting interrupt, "
+		       "interrupt status = 0x%#8.8x.\n",
+		       net_dev->name, inl(ioaddr + isr));
+	
 #if defined(__i386__)
-        clear_bit(0, (void*)&dev->interrupt);
+        clear_bit(0, (void*)&net_dev->interrupt);
 #else
-        dev->interrupt = 0;
+        net_dev->interrupt = 0;
 #endif
         return;
 }
 
-/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
-   field alignments and semantics. */
-static int sis900_rx(struct device *dev)
-{
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
-        u16 cur_rx = tp->cur_rx % NUM_RX_DESC;
-        int rx_status=tp->rx_buf[cur_rx].cmdsts;
-
-        if (sis900_debug > 4)
-                printk(KERN_INFO "%s: sis900_rx, current %4.4x,"
-                                " rx status=%8.8x\n",
-                                dev->name, cur_rx,
-                                rx_status);
-
+/* Process receive interrupt events, put buffer to higher layer and refill buffer pool 
+   Note: This fucntion is called by interrupt handler, don't do "too much" work here */
+static int sis900_rx(struct device *net_dev)
+{
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
+	unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;
+        u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
+	
+        if (sis900_debug > 3)
+                printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "
+		       "status:0x%8.8x\n",
+		       sis_priv->cur_rx, sis_priv->dirty_rx,rx_status);
+	
         while (rx_status & OWN) {
-                int rx_size = rx_status & DSIZE;
-                rx_size -= CRC_SIZE;
-
-                if (sis900_debug > 4) {
-                        int i;
-                        printk(KERN_INFO "%s:  sis900_rx, rx status %8.8x,"
-                                        " size %4.4x, cur %4.4x.\n",
-                                   dev->name, rx_status, rx_size, cur_rx);
-                        printk(KERN_INFO "%s: Rx Frame contents:", dev->name);
-
-                        for (i = 0; i < rx_size; i++) {
-                                printk("%2.2x ",
-                                (u8)(tp->rx_buf[cur_rx].buf[i]));
-                        }
-
-                        printk(".\n");
-                }
-                if (rx_status & TOOLONG) {
-                        if (sis900_debug > 1)
-                                printk(KERN_INFO "%s: Oversized Ethernet frame,"
-                                                " status %4.4x!\n",
-                                           dev->name, rx_status);
-                        tp->stats.rx_length_errors++;
-                } else if (rx_status & (RXISERR | RUNT | CRCERR | FAERR)) {
-                        if (sis900_debug > 1)
-                                printk(KERN_INFO"%s: Ethernet frame had errors,"
-                                        " status %4.4x.\n",
-                                        dev->name, rx_status);
-                        tp->stats.rx_errors++;
+                unsigned int rx_size;
+		
+		rx_size = (rx_status & DSIZE) - CRC_SIZE;
+		
+		if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
+			/* corrupted packet received */
+                        if (sis900_debug > 3)
+                                printk(KERN_INFO "%s: Corrupted packet "
+				       "received, buffer status = 0x%8.8x.\n",
+				       net_dev->name, rx_status);
+                        sis_priv->stats.rx_errors++;
+			if (rx_status & OVERRUN)
+				sis_priv->stats.rx_over_errors++;
+			if (rx_status & (TOOLONG|RUNT))
+                                sis_priv->stats.rx_length_errors++;
                         if (rx_status & (RXISERR | FAERR))
-                                tp->stats.rx_frame_errors++;
-                        if (rx_status & (RUNT | TOOLONG))
-                                tp->stats.rx_length_errors++;
-                        if (rx_status & CRCERR) tp->stats.rx_crc_errors++;
+                                sis_priv->stats.rx_frame_errors++;
+			if (rx_status & CRCERR) 
+				sis_priv->stats.rx_crc_errors++;
+			/* reset buffer descriptor state */
+			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
                 } else {
-                        /* Malloc up new buffer, compatible with net-2e. */
-                        /* Omit the four octet CRC from the length. */
-                        struct sk_buff *skb;
-
-                        skb = dev_alloc_skb(rx_size + 2);
-                        if (skb == NULL) {
-                                printk(KERN_INFO "%s: Memory squeeze,"
-                                                "deferring packet.\n",
-                                                dev->name);
-                                /* We should check that some rx space is free.
-                                   If not,
-                                   free one and mark stats->rx_dropped++. */
-                                tp->stats.rx_dropped++;
-                                tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE;
-                                break;
-                        }
-                        skb->dev = dev;
-                        skb_reserve(skb, 2); /* 16 byte align the IP fields. */
-                        if (rx_size+CRC_SIZE > RX_BUF_SIZE) {
-                                /*
-                                int semi_count = RX_BUF_LEN - ring_offset - 4;
-                                memcpy(skb_put(skb, semi_count),
-                                        &rx_bufs[ring_offset + 4], semi_count);
-                                memcpy(skb_put(skb, rx_size-semi_count),
-                                        rx_bufs, rx_size - semi_count);
-                                if (sis900_debug > 4) {
-                                        int i;
-                                        printk(KERN_DEBUG"%s:  Frame wrap @%d",
-                                                   dev->name, semi_count);
-                                        for (i = 0; i < 16; i++)
-                                                printk(" %2.2x", rx_bufs[i]);
-                                        printk(".\n");
-                                        memset(rx_bufs, 0xcc, 16);
-                                }
-                                */
-                        } else {
-#if 0  /* USE_IP_COPYSUM */
-                                eth_copy_and_sum(skb,
-                                   tp->rx_buf[cur_rx].buf, rx_size, 0);
-                                skb_put(skb, rx_size);
-#else
-                                memcpy(skb_put(skb, rx_size),
-                                        tp->rx_buf[cur_rx].buf, rx_size);
-#endif
-                        }
-                        skb->protocol = eth_type_trans(skb, dev);
+			struct sk_buff * skb;
+			
+			if (sis_priv->rx_skbuff[entry] == NULL) {
+				printk(KERN_INFO "%s: NULL pointer " 
+				       "encountered in Rx ring, skipping\n",
+				       net_dev->name);
+				break;			
+			}
+			skb = sis_priv->rx_skbuff[entry];
+			sis_priv->rx_skbuff[entry] = NULL;
+			/* reset buffer descriptor state */
+			sis_priv->rx_ring[entry].cmdsts = 0;
+			sis_priv->rx_ring[entry].bufptr = 0;
+			
+			skb_put(skb, rx_size);
+                        skb->protocol = eth_type_trans(skb, net_dev);
                         netif_rx(skb);
-#if LINUX_VERSION_CODE > 0x20119
-                        tp->stats.rx_bytes += rx_size;
-#endif
-                        tp->stats.rx_packets++;
+			
+			if ((rx_status & BCAST) == MCAST)
+				sis_priv->stats.multicast++;
+			net_dev->last_rx = jiffies;
+                        sis_priv->stats.rx_bytes += rx_size;
+                        sis_priv->stats.rx_packets++;
                 }
-                tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE;
-
-                cur_rx = ((cur_rx+1) % NUM_RX_DESC);
-                rx_status = tp->rx_buf[cur_rx].cmdsts;
+                sis_priv->cur_rx++;
+		entry = sis_priv->cur_rx % NUM_RX_DESC;
+                rx_status = sis_priv->rx_ring[entry].cmdsts;
         } // while
-        if (sis900_debug > 4)
-                printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x "
-                                "Cmd %2.2x.\n",
-                           dev->name, cur_rx,
-                           inb(ioaddr + cr));
-        tp->cur_rx = cur_rx;
+	
+	/* refill the Rx buffer, what if the rate of refilling is slower than 
+	   consuming ?? */
+	for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) {
+		struct sk_buff *skb;
+		
+		entry = sis_priv->dirty_rx % NUM_RX_DESC;
+		
+		if (sis_priv->rx_skbuff[entry] == NULL) {
+			if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
+				/* not enough memory for skbuff, this makes a "hole"
+				   on the buffer ring, it is not clear how the 
+				   hardware will react to this kind of degenerated 
+				   buffer */
+				printk(KERN_INFO "%s: Memory squeeze,"
+				       "deferring packet.\n",
+				       net_dev->name);
+				sis_priv->stats.rx_dropped++;
+				break;
+			}
+			skb->dev = net_dev;
+			sis_priv->rx_skbuff[entry] = skb;
+			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+			sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail);
+		}
+	}
+	/* re-enable the potentially idle receive state matchine */
+	outl(RxENA , ioaddr + cr );
+	
         return 0;
 }
 
+/* finish up transmission of packets, check for error condition and free skbuff etc.
+   Note: This fucntion is called by interrupt handler, don't do "too much" work here */
+static void sis900_finish_xmit (struct device *net_dev)
+{
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	
+	for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) {
+		unsigned int entry;
+		u32 tx_status;
+		
+		entry = sis_priv->dirty_tx % NUM_TX_DESC;
+	        tx_status = sis_priv->tx_ring[entry].cmdsts;
+		
+		if (tx_status & OWN) {
+			/* The packet is not transmited yet (owned by hardware) ! */
+			break;
+		}
+		
+		if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
+			/* packet unsuccessfully transmited */
+			if (sis900_debug > 3)
+				printk(KERN_INFO "%s: Transmit "
+				       "error, Tx status %8.8x.\n",
+				       net_dev->name, tx_status);
+			sis_priv->stats.tx_errors++;
+			if (tx_status & UNDERRUN)
+				sis_priv->stats.tx_fifo_errors++;
+			if (tx_status & ABORT)
+				sis_priv->stats.tx_aborted_errors++;
+			if (tx_status & NOCARRIER)
+				sis_priv->stats.tx_carrier_errors++;
+			if (tx_status & OWCOLL)
+				sis_priv->stats.tx_window_errors++;
+		} else {
+			/* packet successfully transmited */
+			if (sis900_debug > 3)
+				printk(KERN_INFO "Tx Transmit OK\n");
+			sis_priv->stats.collisions += (tx_status & COLCNT) >> 16;
+			sis_priv->stats.tx_bytes += tx_status & DSIZE;
+			sis_priv->stats.tx_packets++;
+		}
+		/* Free the original skb. */
+		dev_kfree_skb(sis_priv->tx_skbuff[entry]);
+		sis_priv->tx_skbuff[entry] = NULL;
+		sis_priv->tx_ring[entry].bufptr = 0;
+		sis_priv->tx_ring[entry].cmdsts = 0;
+	}
+	
+	if (sis_priv->tx_full && net_dev->tbusy && 
+	    sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) {
+		/* The ring is no longer full, clear tbusy, tx_full and schedule 
+		   more transmission by marking NET_BH */
+		sis_priv->tx_full = 0;
+		clear_bit(0, (void *)&net_dev->tbusy);
+		mark_bh(NET_BH);
+	}
+}
+
 static int
-sis900_close(struct device *dev)
+sis900_close(struct device *net_dev)
 {
-        long ioaddr = dev->base_addr;
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = net_dev->base_addr;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
         int i;
-
-        dev->start = 0;
-        dev->tbusy = 1;
-
-        if (sis900_debug > 1)
-                printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n",
-                           dev->name, inl(ioaddr + isr));
+	
+        net_dev->start = 0;
+        net_dev->tbusy = 1;
 
         /* Disable interrupts by clearing the interrupt mask. */
         outl(0x0000, ioaddr + imr);
+	outl(0x0000, ioaddr + ier);
 
-        /* Stop the chip's Tx and Rx DMA processes. */
-        outl(0x00, ioaddr + cr);
+        /* Stop the chip's Tx and Rx Status Machine */
+        outl(RxDIS | TxDIS, ioaddr + cr);
 
-        del_timer(&tp->timer);
+        del_timer(&sis_priv->timer);
 
-        free_irq(dev->irq, dev);
+        free_irq(net_dev->irq, net_dev);
 
+	/* Free Tx and RX skbuff */
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		if (sis_priv->rx_skbuff[i] != NULL)
+			dev_kfree_skb(sis_priv->rx_skbuff[i]);
+		sis_priv->rx_skbuff[i] = 0;
+	}
         for (i = 0; i < NUM_TX_DESC; i++) {
-                if (tp->tx_skbuff[i])
-                        dev_free_skb(tp->tx_skbuff[i]);
-                tp->tx_skbuff[i] = 0;
+                if (sis_priv->tx_skbuff[i] != NULL)
+                        dev_kfree_skb(sis_priv->tx_skbuff[i]);
+                sis_priv->tx_skbuff[i] = 0;
         }
-        kfree(tp->rx_bufs);
-        kfree(tp->tx_bufs);
 
         /* Green! Put the chip in low-power mode. */
 
@@ -1575,22 +1117,22 @@
         return 0;
 }
 
-static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
         u16 *data = (u16 *)&rq->ifr_data;
 
         switch(cmd) {
-        case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
-                data[0] = tp->phys[tp->phy_idx];
+        case SIOCDEVPRIVATE:            	/* Get the address of the PHY in use. */
+                data[0] = sis_priv->mii->phy_addr;
                 /* Fall Through */
-        case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
-                data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+        case SIOCDEVPRIVATE+1:          	/* Read the specified MII register. */
+                data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
                 return 0;
-        case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+        case SIOCDEVPRIVATE+2:          	/* Write the specified MII register */
                 if (!suser())
                         return -EPERM;
-                mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+                mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
                 return 0;
         default:
                 return -EOPNOTSUPP;
@@ -1598,326 +1140,140 @@
 }
 
 static struct enet_statistics *
-sis900_get_stats(struct device *dev)
-{
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-
-        return &tp->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
-   This routine is not state sensitive and need not be SMP locked. */
-
-static u16 elComputeHashTableIndex(u8 *addr)
-{
-#define POLYNOMIAL 0x04C11DB6L
-    u32      crc = 0xffffffff, msb;
-    int      i, j;
-    u8       byte;
-
-    for( i=0; i<6; i++ ) {
-        byte = *addr++;
-        for( j=0; j<8; j++ ) {
-            msb = crc >> 31;
-            crc <<= 1;
-            if( msb ^ ( byte & 1 )) {
-                crc ^= POLYNOMIAL;
-                crc |= 1;
-            }
-            byte >>= 1;
-        }
-    }
-    // 7 bit crc for 128 bit hash table
-    return( (int)(crc >> 25) );
-}
-
-static u16 elMIIpollBit(struct device *dev,
-                         int phy_id,
-                         int location,
-                         u16 mask,
-                         u16 polarity,
-                         u16 *value)
-{
-        u32 i;
-        i=0;
-        while (1) {
-                *value = mdio_read(dev, phy_id, location);
-                if (polarity) {
-                        if (mask & *value) return(TRUE);
-                } else {
-                        if (mask & ~(*value)) return(TRUE);
-                }
-                if (++i == 1200) break;
-        }
-        return(FALSE);
-}
-
-static u16 elPMDreadMode(struct device *dev,
-                         int phy_id,
-                         int *speed,
-                         int *duplex)
-{
-        u16 status, OurCap;
-
-        *speed = HW_SPEED_10_MBPS;
-        *duplex = FDX_CAPABLE_HALF_SELECTED;
-
-        status = mdio_read(dev, phy_id, MII_ANLPAR);
-        OurCap = mdio_read(dev, phy_id, MII_ANAR);
-	if (sis900_debug > 1) {
-		printk(KERN_INFO "Link Part Status %4X\n", status);
-		printk(KERN_INFO "Our Status %4X\n", OurCap);
-		printk(KERN_INFO "Status Reg %4X\n",
-					mdio_read(dev, phy_id, MII_STATUS));
-	}
-	status &= OurCap;
-
-        if ( !( status &
-                (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) {
-		if (sis900_debug > 1) {
-			printk(KERN_INFO "The other end NOT support NWAY...\n");
-		}
-                while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ;
-                while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ;
-                if (status & 0x80)
-                        *speed = HW_SPEED_100_MBPS;
-                if (status & 0x40)
-                        *duplex = FDX_CAPABLE_FULL_SELECTED;
-                if (sis900_debug > 3) {
-                        printk(KERN_INFO"%s: Setting %s%s-duplex.\n",
-                                dev->name,
-                                *speed == HW_SPEED_100_MBPS ?
-                                        "100mbps " : "10mbps ",
-                                *duplex == FDX_CAPABLE_FULL_SELECTED ?
-                                        "full" : "half");
-                }
-        } else {
-		if (sis900_debug > 1) {
-			printk(KERN_INFO "The other end support NWAY...\n");
-		}
-
-                if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) {
-                        *duplex = FDX_CAPABLE_FULL_SELECTED;
-                }
-                if (status & (MII_NWAY_TX_FDX | MII_NWAY_TX)) {
-                        *speed = HW_SPEED_100_MBPS;
-                }
-                if (sis900_debug > 3) {
-                        printk(KERN_INFO"%s: Setting %s%s-duplex based on"
-                                " auto-negotiated partner ability.\n",
-                                dev->name,
-                                *speed == HW_SPEED_100_MBPS ?
-                                        "100mbps " : "10mbps ",
-                                *duplex == FDX_CAPABLE_FULL_SELECTED ?
-                                        "full" : "half");
-                }
-        }
-        return (status);
-}
-
-static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed)
+sis900_get_stats(struct device *net_dev)
 {
-        u16 status, retnVal;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
 
-	if (sis900_debug > 1) {
-		printk(KERN_INFO "AutoNegotiate...\n");
-	}
-        mdio_write(dev, phy_id, MII_CONTROL, 0);
-        mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | MIICNTL_RST_AUTO);
-        retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO,
-				FALSE,&status);
-	if (!retnVal) {
-		printk(KERN_INFO "Not wait for Reset Complete\n");
-	}
-        retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE,
-				TRUE, &status);
-	if (!retnVal) {
-		printk(KERN_INFO "Not wait for AutoNego Complete\n");
-	}
-        retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK,
-				TRUE, &status);
-	if (!retnVal) {
-		printk(KERN_INFO "Not wait for Link Complete\n");
-	}
-        if (status & MIISTAT_LINK) {
-                elPMDreadMode(dev, phy_id, speed, duplex);
-                elSetMediaType(dev, *speed, *duplex);
-        }
-        return(status);
+        return &sis_priv->stats;
 }
 
-static void elSetCapability(struct device *dev, int phy_id,
-			    int duplex, int speed)
+/* SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast hash table, which makes
+   this function a little bit different from other drivers */
+static u16 sis900_compute_hashtable_index(u8 *addr)
 {
-        u16 cap = ( MII_NWAY_T  | MII_NWAY_T_FDX  |
-		    MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_CSMA_CD );
 
-	if (speed != 100) {
-		cap &= ~( MII_NWAY_TX | MII_NWAY_TX_FDX );
-		if (sis900_debug > 1) {
-			printk(KERN_INFO "UNSET 100Mbps\n");
-		}
-	}
-
-	if (!duplex) {
-		cap &= ~( MII_NWAY_T_FDX | MII_NWAY_TX_FDX );
-		if (sis900_debug > 1) {
-			printk(KERN_INFO "UNSET full-duplex\n");
+/* what is the correct value of the POLYNOMIAL ??
+   Donald Becker use 0x04C11DB7U */
+#define POLYNOMIAL 0x04C11DB6L
+	u32 crc = 0xffffffff, msb;
+	int  i, j;
+	u8  byte;
+
+	for (i = 0; i < 6; i++) {
+		byte = *addr++;
+		for (j = 0; j < 8; j++) {
+			msb = crc >> 31;
+			crc <<= 1;
+			if (msb ^ (byte & 1)) {
+				crc ^= POLYNOMIAL;
+				crc |= 1;
+			}
+			byte >>= 1;
 		}
 	}
-
-        mdio_write(dev, phy_id, MII_ANAR, cap);
+	/* leave 7 most siginifant bits */ 
+	return ((int)(crc >> 25));
 }
 
-static void elSetMediaType(struct device *dev, int speed, int duplex)
+static void set_rx_mode(struct device *net_dev)
 {
-        long ioaddr = dev->base_addr;
-        u32     txCfgOn = 0, txCfgOff = TxDRNT;
-        u32     rxCfgOn = 0, rxCfgOff = 0;
-
-        if (speed == HW_SPEED_100_MBPS) {
-                txCfgOn |= (TxDRNT_100 | TxHBI);
-        } else {
-                txCfgOn |= TxDRNT_10;
-        }
-
-        if (duplex == FDX_CAPABLE_FULL_SELECTED) {
-                txCfgOn |= (TxCSI | TxHBI);
-                rxCfgOn |= RxATP;
-        } else {
-                txCfgOff |= (TxCSI | TxHBI);
-                rxCfgOff |= RxATP;
-        }
-        outl( (inl(ioaddr + txcfg) & ~txCfgOff) | txCfgOn, ioaddr + txcfg);
-        outl( (inl(ioaddr + rxcfg) & ~rxCfgOff) | rxCfgOn, ioaddr + rxcfg);
-}
-
-static void set_rx_mode(struct device *dev)
-{
-        long ioaddr = dev->base_addr;
-        u16 mc_filter[8];
+        long ioaddr = net_dev->base_addr;
+        u16 mc_filter[8];			/* 128 bits multicast hash table */
         int i;
-        int rx_mode;
-        u32 rxCfgOn = 0, rxCfgOff = 0;
-        u32 txCfgOn = 0, txCfgOff = 0;
+        u32 rx_mode;
 
-        if (sis900_debug > 3)
-                printk(KERN_INFO "%s: set_rx_mode (%4.4x) done--"
-                                "RxCfg %8.8x.\n",
-                                dev->name, dev->flags, inl(ioaddr + rxcfg));
-
-        /* Note: do not reorder, GCC is clever about common statements. */
-        if (dev->flags & IFF_PROMISC) {
-                printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name);
-                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
-                                ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_PHYS;
-                for (i=0 ; i<8 ; i++)
-                        mc_filter[i]=0xffff;
-        } else if ((dev->mc_count > multicast_filter_limit)
-                           ||  (dev->flags & IFF_ALLMULTI)) {
-                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
-                                ACCEPT_CAM_QUALIFIED;
-                for (i=0 ; i<8 ; i++)
-                        mc_filter[i]=0xffff;
+        if (net_dev->flags & IFF_PROMISC) {
+		/* Accept any kinds of packets */
+		rx_mode = RFPromiscuous;
+                for (i = 0; i < 8; i++)
+                        mc_filter[i] = 0xffff;
+        } else if ((net_dev->mc_count > multicast_filter_limit) ||
+		   (net_dev->flags & IFF_ALLMULTI)) {
+		/* too many multicast addresses or accept all multicast packet */
+                rx_mode = RFAAB | RFAAM;
+                for (i = 0; i < 8; i++)
+                        mc_filter[i] = 0xffff;
         } else {
+		/* Accept Broadcast packet, destination address matchs our MAC address,
+		   use Receive Filter to reject unwanted MCAST packet */
                 struct dev_mc_list *mclist;
-                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
-                                ACCEPT_CAM_QUALIFIED;
-                for (i=0 ; i<8 ; i++)
+                rx_mode = RFAAB;
+                for (i = 0; i < 8; i++)
                         mc_filter[i]=0;
-                for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-                         i++, mclist = mclist->next)
-                        set_bit(elComputeHashTableIndex(mclist->dmi_addr),
-                                                mc_filter);
+                for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count;
+		     i++, mclist = mclist->next)
+                        set_bit(sis900_compute_hashtable_index(mclist->dmi_addr),
+				mc_filter);
         }
 
-        for (i=0 ; i<8 ; i++) {
-                outl((u32)(0x00000004+i) << 16, ioaddr + rfcr);
+	/* update Multicast Hash Table in Receive Filter */
+        for (i = 0; i < 8; i++) {
+		/* why plus 0x04 ??, I don't know, UNDOCUMENT FEATURE ?? */
+                outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
                 outl(mc_filter[i], ioaddr + rfdr);
         }
-        /* We can safely update without stopping the chip. */
-        //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS | ACCEPT_ALL_PHYS;
-        //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS;
-        outl(RFEN | ((rx_mode & (ACCEPT_ALL_MCASTS | ACCEPT_ALL_BCASTS |
-                          ACCEPT_ALL_PHYS)) << RFAA_shift), ioaddr + rfcr);
-
-        if (rx_mode & ACCEPT_ALL_ERRORS) {
-                rxCfgOn = RxAEP | RxARP | RxAJAB;
-        } else {
-                rxCfgOff = RxAEP | RxARP | RxAJAB;
-        }
-        if (rx_mode & MAC_LOOPBACK) {
-                rxCfgOn |= RxATP;
-                txCfgOn |= TxMLB;
-        } else {
-                if (!(( (struct sis900_private *)(dev->priv) )->full_duplex))
-                        rxCfgOff |= RxATP;
-                txCfgOff |= TxMLB;
-        }
 
-        if (sis900_debug > 2) {
-                printk(KERN_INFO "Before Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
-                printk(KERN_INFO "Before Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
-        }
+        outl(RFEN | rx_mode, ioaddr + rfcr);
 
-        outl((inl(ioaddr + rxcfg) | rxCfgOn) & ~rxCfgOff, ioaddr + rxcfg);
-        outl((inl(ioaddr + txcfg) | txCfgOn) & ~txCfgOff, ioaddr + txcfg);
+	/* sis900 is capatable of looping back packet at MAC level for debugging purpose */
+	if (net_dev->flags & IFF_LOOPBACK) {
+		u32 cr_saved;
+		/* We must disable Tx/Rx before setting loopback mode */
+		cr_saved = inl(ioaddr + cr);
+		outl(cr_saved | TxDIS | RxDIS, ioaddr + cr);
+		/* enable loopback */
+		outl(inl(ioaddr + txcfg) | TxMLB, ioaddr + txcfg);
+		outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg);
+		/* restore cr */
+		outl(cr_saved, ioaddr + cr);
+	}		
 
-        if (sis900_debug > 2) {
-                printk(KERN_INFO "After Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
-                printk(KERN_INFO "After Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
-                printk(KERN_INFO "Receive Filter Register:%8.8x\n",
-                                                        inl(ioaddr + rfcr));
-        }
         return;
 }
 
-static void sis900_reset(struct device *dev)
+static void sis900_reset(struct device *net_dev)
 {
-        long ioaddr = dev->base_addr;
+        long ioaddr = net_dev->base_addr;
+	int i = 0;
+	u32 status = TxRCMP | RxRCMP;
 
         outl(0, ioaddr + ier);
         outl(0, ioaddr + imr);
         outl(0, ioaddr + rfcr);
 
         outl(RxRESET | TxRESET | RESET, ioaddr + cr);
-        outl(PESEL, ioaddr + cfg);
+	
+	/* Check that the chip has finished the reset. */
+	while (status && (i++ < 1000)) {
+		status ^= (inl(isr + ioaddr) & status);
+        }
 
-        set_rx_mode(dev);
+        outl(PESEL, ioaddr + cfg);
 }
 
 #ifdef MODULE
 int init_module(void)
 {
-        return sis900_probe(0);
+        return sis900_probe(NULL);
 }
 
 void
 cleanup_module(void)
 {
-        struct device *next_dev;
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (root_sis900_dev) {
+		struct sis900_private *sis_priv =
+			(struct sis900_private *)root_sis900_dev->priv;
+		struct device *next_dev = sis_priv->next_module;
+		
+		unregister_netdev(root_sis900_dev);
+		release_region(root_sis900_dev->base_addr,
+			       sis_priv->mac->io_size);
+		kfree(sis_priv);
+		kfree(root_sis900_dev);
 
-        /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-        while (root_sis900_dev) {
-                struct sis900_private *tp =
-                        (struct sis900_private *)root_sis900_dev->priv;
-                next_dev = tp->next_module;
-                unregister_netdev(root_sis900_dev);
-                release_region(root_sis900_dev->base_addr,
-                                           pci_tbl[tp->chip_id].io_size);
-                kfree(tp);
-                kfree(root_sis900_dev);
-                root_sis900_dev = next_dev;
+		root_sis900_dev = next_dev;
         }
 }
 
 #endif  /* MODULE */
-/*
- * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 4
- * End:
- */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sis900.h linux.14p6/drivers/net/sis900.h
--- linux.vanilla/drivers/net/sis900.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/net/sis900.h	Thu Nov  4 23:46:11 1999
@@ -0,0 +1,248 @@
+/* sis900.h Definitions for SiS ethernet controllers including 7014/7016 and 900 
+ * Copyright 1999 Silicon Integrated System Corporation
+ * References:
+ *   SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
+ *	preliminary Rev. 1.0 Jan. 14, 1998
+ *   SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
+ *	preliminary Rev. 1.0 Nov. 10, 1998
+ *   SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
+ *	preliminary Rev. 1.0 Jan. 18, 1998
+ *   http://www.sis.com.tw/support/databook.htm
+ */
+
+/* MAC operationl registers of SiS 7016 and SiS 900 ehternet controller */
+/* The I/O extent, SiS 900 needs 256 bytes of io address */
+#define SIS900_TOTAL_SIZE 0x100
+
+/* Symbolic offsets to registers. */
+enum sis900_registers {
+	cr=0x0,                 //Command Register
+	cfg=0x4,                //Configuration Register
+	mear=0x8,               //EEPROM Access Register
+	ptscr=0xc,              //PCI Test Control Register
+	isr=0x10,               //Interrupt Status Register
+	imr=0x14,               //Interrupt Mask Register
+	ier=0x18,               //Interrupt Enable Register
+	epar=0x18,              //Enhanced PHY Access Register
+	txdp=0x20,              //Transmit Descriptor Pointer Register
+        txcfg=0x24,             //Transmit Configuration Register
+        rxdp=0x30,              //Receive Descriptor Pointer Register
+        rxcfg=0x34,             //Receive Configuration Register
+        flctrl=0x38,            //Flow Control Register
+        rxlen=0x3c,             //Receive Packet Length Register
+        rfcr=0x48,              //Receive Filter Control Register
+        rfdr=0x4C,              //Receive Filter Data Register
+        pmctrl=0xB0,            //Power Management Control Register
+        pmer=0xB4               //Power Management Wake-up Event Register
+};
+
+/* Symbolic names for bits in various registers */
+enum sis900_command_register_bits {
+	RESET   = 0x00000100, SWI = 0x00000080, RxRESET = 0x00000020,
+	TxRESET = 0x00000010, RxDIS = 0x00000008, RxENA = 0x00000004,
+	TxDIS   = 0x00000002, TxENA = 0x00000001
+};
+
+enum sis900_configuration_register_bits {
+	DESCRFMT = 0x00000100 /* 7016 specific */, REQALG = 0x00000080,
+	SB    = 0x00000040, POW = 0x00000020, EXD = 0x00000010, 
+	PESEL = 0x00000008, LPM = 0x00000004, BEM = 0x00000001
+};
+
+enum sis900_eeprom_access_reigster_bits {
+	MDC  = 0x00000040, MDDIR = 0x00000020, MDIO = 0x00000010, /* 7016 specific */ 
+	EECS = 0x00000008, EECLK = 0x00000004, EEDO = 0x00000002,
+	EEDI = 0x00000001
+};
+
+enum sis900_interrupt_register_bits {
+	WKEVT  = 0x10000000, TxPAUSEEND = 0x08000000, TxPAUSE = 0x04000000,
+	TxRCMP = 0x02000000, RxRCMP = 0x01000000, DPERR = 0x00800000,
+	SSERR  = 0x00400000, RMABT  = 0x00200000, RTABT = 0x00100000,
+	RxSOVR = 0x00010000, HIBERR = 0x00008000, SWINT = 0x00001000,
+	MIBINT = 0x00000800, TxURN  = 0x00000400, TxIDLE  = 0x00000200,
+	TxERR  = 0x00000100, TxDESC = 0x00000080, TxOK  = 0x00000040,
+	RxORN  = 0x00000020, RxIDLE = 0x00000010, RxEARLY = 0x00000008,
+	RxERR  = 0x00000004, RxDESC = 0x00000002, RxOK  = 0x00000001
+};
+
+enum sis900_interrupt_enable_reigster_bits {
+	IE = 0x00000001
+};
+
+/* maximum dma burst fro transmission and receive*/
+#define MAX_DMA_RANGE	7	/* actually 0 means MAXIMUM !! */
+#define TxMXDMA_shift   	20
+#define RxMXDMA_shift    20
+#define TX_DMA_BURST    	0
+#define RX_DMA_BURST    	0
+
+/* transmit FIFO threshholds */
+#define TX_FILL_THRESH   16	/* 1/4 FIFO size */
+#define TxFILLT_shift   	8
+#define TxDRNT_shift    	0
+#define TxDRNT_100      	48	/* 3/4 FIFO size */
+#define TxDRNT_10		16 	/* 1/2 FIFO size */
+
+enum sis900_transmit_config_register_bits {
+	TxCSI = 0x80000000, TxHBI = 0x40000000, TxMLB = 0x20000000,
+	TxATP = 0x10000000, TxIFG = 0x0C000000, TxFILLT = 0x00003F00,
+	TxDRNT = 0x0000003F
+};
+
+/* recevie FIFO thresholds */
+#define RxDRNT_shift     1
+#define RxDRNT_100	24	/* 3/4 FIFO size */
+#define RxDRNT_10		16 	/* 1/2 FIFO size */
+
+enum sis900_reveive_config_register_bits {
+	RxAEP  = 0x80000000, RxARP = 0x40000000, RxATX = 0x10000000,
+	RxAJAB = 0x08000000, RxDRNT = 0x0000007F
+};
+
+#define RFAA_shift      28
+#define RFADDR_shift    16
+
+enum sis900_receive_filter_control_register_bits {
+	RFEN  = 0x80000000, RFAAB = 0x40000000, RFAAM = 0x20000000,
+	RFAAP = 0x10000000, RFPromiscuous = (RFAAB|RFAAM|RFAAP)
+};
+
+enum sis900_reveive_filter_data_mask {
+	RFDAT =  0x0000FFFF
+};
+
+/* EEPROM Addresses */
+enum sis900_eeprom_address {
+	EEPROMSignature = 0x00, EEPROMVendorID = 0x02, EEPROMDeviceID = 0x03,
+	EEPROMMACAddr   = 0x08, EEPROMChecksum = 0x0b
+};
+
+/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */
+enum sis900_eeprom_command {
+	EEread     = 0x0180, EEwrite    = 0x0140, EEerase = 0x01C0, 
+	EEwriteEnable = 0x0130, EEwriteDisable = 0x0100,
+	EEeraseAll = 0x0120, EEwriteAll = 0x0110, 
+	EEaddrMask = 0x013F, EEcmdShift = 16
+};
+
+/* Manamgement Data I/O (mdio) frame */
+#define MIIread         0x6000
+#define MIIwrite        0x5002
+#define MIIpmdShift     7
+#define MIIregShift     2
+#define MIIcmdLen       16
+#define MIIcmdShift     16
+
+/* Buffer Descriptor Status*/
+enum sis900_buffer_status {
+	OWN    = 0x80000000, MORE   = 0x40000000, INTR = 0x20000000,
+	SUPCRC = 0x10000000, INCCRC = 0x10000000,
+	OK     = 0x08000000, DSIZE  = 0x00000FFF
+};
+/* Status for TX Buffers */
+enum sis900_tx_buffer_status {
+	ABORT   = 0x04000000, UNDERRUN = 0x02000000, NOCARRIER = 0x01000000,
+	DEFERD  = 0x00800000, EXCDEFER = 0x00400000, OWCOLL    = 0x00200000,
+	EXCCOLL = 0x00100000, COLCNT   = 0x000F0000
+};
+
+enum sis900_rx_bufer_status {
+	OVERRUN = 0x02000000, DEST = 0x00800000,     BCAST = 0x01800000,
+	MCAST   = 0x01000000, UNIMATCH = 0x00800000, TOOLONG = 0x00400000,
+	RUNT    = 0x00200000, RXISERR  = 0x00100000, CRCERR  = 0x00080000,
+	FAERR   = 0x00040000, LOOPBK   = 0x00020000, RXCOL   = 0x00010000
+};
+
+/* MII register offsets */
+enum mii_registers {
+	MII_CONTROL = 0x0000, MII_STATUS = 0x0001, MII_PHY_ID0 = 0x0002,
+	MII_PHY_ID1 = 0x0003, MII_ANADV  = 0x0004, MII_ANLPAR  = 0x0005,
+	MII_ANEXT   = 0x0006
+};
+
+/* mii registers specific to SiS 900 */
+enum sis_mii_registers {
+	MII_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012,
+	MII_MASK    = 0x0013
+};
+
+/* mii registers specific to AMD 79C901 */
+enum amd_mii_registers {
+	MII_STATUS_SUMMARY = 0x0018
+};
+
+/* MII Control register bit definitions. */
+enum mii_control_register_bits {
+	MII_CNTL_FDX     = 0x0100, MII_CNTL_RST_AUTO = 0x0200, 
+	MII_CNTL_ISOLATE = 0x0400, MII_CNTL_PWRDWN   = 0x0800,
+	MII_CNTL_AUTO    = 0x1000, MII_CNTL_SPEED    = 0x2000,
+	MII_CNTL_LPBK    = 0x4000, MII_CNTL_RESET    = 0x8000
+};
+
+/* MII Status register bit  */
+enum mii_status_register_bits {
+	MII_STAT_EXT    = 0x0001, MII_STAT_JAB        = 0x0002, 
+	MII_STAT_LINK   = 0x0004, MII_STAT_CAN_AUTO   = 0x0008, 
+	MII_STAT_FAULT  = 0x0010, MII_STAT_AUTO_DONE  = 0x0020,
+	MII_STAT_CAN_T  = 0x0800, MII_STAT_CAN_T_FDX  = 0x1000,
+	MII_STAT_CAN_TX = 0x2000, MII_STAT_CAN_TX_FDX = 0x4000,
+	MII_STAT_CAN_T4 = 0x8000
+};
+
+#define		MII_ID1_OUI_LO		0xFC00	/* low bits of OUI mask */
+#define		MII_ID1_MODEL		0x03F0	/* model number */
+#define		MII_ID1_REV		0x000F	/* model number */
+
+/* MII NWAY Register Bits ...
+   valid for the ANAR (Auto-Negotiation Advertisement) and
+   ANLPAR (Auto-Negotiation Link Partner) registers */
+enum mii_nway_register_bits {
+	MII_NWAY_NODE_SEL = 0x001f, MII_NWAY_CSMA_CD = 0x0001,
+	MII_NWAY_T	  = 0x0020, MII_NWAY_T_FDX   = 0x0040,
+	MII_NWAY_TX       = 0x0080, MII_NWAY_TX_FDX  = 0x0100,
+	MII_NWAY_T4       = 0x0200, MII_NWAY_PAUSE   = 0x0400,
+	MII_NWAY_RF       = 0x2000, MII_NWAY_ACK     = 0x4000,
+	MII_NWAY_NP       = 0x8000
+};
+
+enum mii_stsout_register_bits {
+	MII_STSOUT_LINK_FAIL = 0x4000,
+	MII_STSOUT_SPD       = 0x0080, MII_STSOUT_DPLX = 0x0040
+};
+
+enum mii_stssum_register_bits {
+	MII_STSSUM_LINK = 0x0008, MII_STSSUM_DPLX = 0x0004,
+	MII_STSSUM_AUTO = 0x0002, MII_STSSUM_SPD  = 0x0001
+};
+
+#define FDX_CAPABLE_DUPLEX_UNKNOWN      0
+#define FDX_CAPABLE_HALF_SELECTED       1
+#define FDX_CAPABLE_FULL_SELECTED       2
+
+#define HW_SPEED_UNCONFIG		0
+#define HW_SPEED_HOME		1
+#define HW_SPEED_10_MBPS        	10
+#define HW_SPEED_100_MBPS       	100
+#define HW_SPEED_DEFAULT        	(HW_SPEED_100_MBPS)
+
+#define CRC_SIZE                4
+#define MAC_HEADER_SIZE         14
+
+#define TX_BUF_SIZE     1536
+#define RX_BUF_SIZE     1536
+
+#define NUM_TX_DESC     16      	/* Number of Tx descriptor registers. */
+#define NUM_RX_DESC     16       	/* Number of Rx descriptor registers. */
+
+#define TRUE            1
+#define FALSE           0
+
+/* PCI stuff, should be move to pic.h */
+#define PCI_DEVICE_ID_SI_900	0x900   
+#define PCI_DEVICE_ID_SI_7016	0x7016  
+
+/* ioctl for accessing MII transveiver */
+#define SIOCGMIIPHY (SIOCDEVPRIVATE)		/* Get the PHY in use. */
+#define SIOCGMIIREG (SIOCDEVPRIVATE+1)		/* Read a PHY register. */
+#define SIOCSMIIREG (SIOCDEVPRIVATE+2)		/* Write a PHY register */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sunbmac.c linux.14p6/drivers/net/sunbmac.c
--- linux.vanilla/drivers/net/sunbmac.c	Tue Mar 16 00:11:30 1999
+++ linux.14p6/drivers/net/sunbmac.c	Tue Nov 16 15:13:27 1999
@@ -85,7 +85,7 @@
 	}
 	if(tries)
 		return 0;
-	printk("BigMAC: Cannot reset the QEC.\n");
+	printk(KERN_ERR "BigMAC: Cannot reset the QEC.\n");
 	return -1;
 }
 
@@ -133,8 +133,8 @@
 		udelay(20);
 
 	if(!tries) {
-		printk("BIGMAC: Transmitter will not reset.\n");
-		printk("BIGMAC: tx_cfg is %08x\n", bregs->tx_cfg);
+		printk(KERN_ERR "BIGMAC: Transmitter will not reset.\n");
+		printk(KERN_ERR "BIGMAC: tx_cfg is %08x\n", bregs->tx_cfg);
 	}
 }
 
@@ -147,8 +147,8 @@
 		udelay(20);
 
 	if(!tries) {
-		printk("BIGMAC: Receiver will not reset.\n");
-		printk("BIGMAC: rx_cfg is %08x\n", bregs->rx_cfg);
+		printk(KERN_ERR "BIGMAC: Receiver will not reset.\n");
+		printk(KERN_ERR "BIGMAC: rx_cfg is %08x\n", bregs->rx_cfg);
 	}
 }
 
@@ -292,7 +292,7 @@
 					 MGMT_PAL_DCLOCK);
 		garbage = tregs->mgmt_pal;
 	} else {
-		printk("write_tcvr_bit: No transceiver type known!\n");
+		printk(KERN_ERR "write_tcvr_bit: No transceiver type known!\n");
 	}
 }
 
@@ -314,7 +314,7 @@
 		garbage = tregs->mgmt_pal;
 		retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2;
 	} else {
-		printk("read_tcvr_bit: No transceiver type known!\n");
+		printk(KERN_ERR "read_tcvr_bit: No transceiver type known!\n");
 	}
 	return retval;
 }
@@ -337,7 +337,7 @@
 		tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK);
 		garbage = tregs->mgmt_pal;
 	} else {
-		printk("read_tcvr_bit2: No transceiver type known!\n");
+		printk(KERN_ERR "read_tcvr_bit2: No transceiver type known!\n");
 	}
 	return retval;
 }
@@ -367,7 +367,7 @@
 		break;
 
 	default:
-		printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n");
+		printk(KERN_ERR "bigmac_tcvr_read: Whoops, no known transceiver type.\n");
 		return;
 	};
 
@@ -406,7 +406,7 @@
 		break;
 
 	default:
-		printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n");
+		printk(KERN_ERR "bigmac_tcvr_read: Whoops, no known transceiver type.\n");
 		return 0xffff;
 	};
 
@@ -493,9 +493,9 @@
 				    TCVR_PAL_LTENABLE);
 		garbage = tregs->tcvr_pal;
 	} else {
-		printk("BIGMAC: AIEEE, neither internal nor "
+		printk(KERN_ERR "BIGMAC: AIEEE, neither internal nor "
 		       "external MDIO available!\n");
-		printk("BIGMAC: mgmt_pal[%08x] tcvr_pal[%08x]\n",
+		printk(KERN_ERR "BIGMAC: mgmt_pal[%08x] tcvr_pal[%08x]\n",
 		       tregs->mgmt_pal, tregs->tcvr_pal);
 	}
 }
@@ -521,7 +521,7 @@
 			udelay(20);
 		}
 		if(timeout == 0)
-			printk("%s: PHY reset failed.\n", bp->dev->name);
+			printk(KERN_ERR "%s: PHY reset failed.\n", bp->dev->name);
 
 		bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
 
@@ -546,7 +546,7 @@
 		bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR);
 		bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
 		if(bp->sw_bmsr & BMSR_LSTATUS) {
-			printk("%s: Link is now up at %s.\n",
+			printk(KERN_INFO "%s: Link is now up at %s.\n",
 			       bp->dev->name,
 			       (bp->sw_bmcr & BMCR_SPEED100) ?
 			       "100baseT" : "10baseT");
@@ -558,11 +558,11 @@
 
 				ret = try_next_permutation(bp, tregs);
 				if(ret == -1) {
-					printk("%s: Link down, cable problem?\n",
+					printk(KERN_ERR "%s: Link down, cable problem?\n",
 					       bp->dev->name);
 					ret = bigmac_init(bp, 0);
 					if(ret) {
-						printk("%s: Error, cannot re-init the "
+						printk(KERN_ERR "%s: Error, cannot re-init the "
 						       "BigMAC.\n", bp->dev->name);
 					}
 					return;
@@ -575,7 +575,7 @@
 		}
 	} else {
 		/* Can't happens.... */
-		printk("%s: Aieee, link timer is asleep but we got one anyways!\n",
+		printk(KERN_ERR "%s: Aieee, link timer is asleep but we got one anyways!\n",
 		       bp->dev->name);
 		restart_timer = 0;
 		bp->timer_ticks = 0;
@@ -614,7 +614,7 @@
 		udelay(20);
 	}
 	if(timeout == 0)
-		printk("%s: PHY reset failed.\n", bp->dev->name);
+		printk(KERN_ERR "%s: PHY reset failed.\n", bp->dev->name);
 
 	bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
 
@@ -724,7 +724,7 @@
 				  unsigned int qec_status,
 				  unsigned int bmac_status)
 {
-	printk("bigmac_is_medium_rare: ");
+	printk(KERN_ERR "bigmac_is_medium_rare: ");
 	if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) {
 		if(qec_status & GLOB_STAT_ER)
 			printk("QEC_ERROR, ");
@@ -838,7 +838,7 @@
 #ifdef NEED_DMA_SYNCHRONIZATION
 #ifdef __sparc_v9__
 		if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) {
-			printk("sunbmac: Bogus DMA buffer address "
+			printk(KERN_CRIT "sunbmac: Bogus DMA buffer address "
 			       "[%016lx]\n", ((unsigned long) skb->data));
 			panic("DMA address too large, tell DaveM");
 		}
@@ -895,7 +895,7 @@
 	}
 	bp->rx_new = elem;
 	if(drops)
-		printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name);
+		printk(KERN_NOTICE "%s: Memory squeeze, deferring packet.\n", bp->dev->name);
 }
 
 #ifndef __sparc_v9__
@@ -948,7 +948,7 @@
 	}
 	bp->rx_new = elem;
 	if(drops)
-		printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name);
+		printk(KERN_NOTICE "%s: Memory squeeze, deferring packet.\n", bp->dev->name);
 }
 #endif
 
@@ -1024,14 +1024,14 @@
 	if(sparc_cpu_model == sun4c) {
 		if(request_irq(dev->irq, &sun4c_bigmac_interrupt,
 			       SA_SHIRQ, "BIG MAC", (void *) bp)) {
-			printk("BIGMAC: Can't order irq %d to go.\n", dev->irq);
+			printk(KERN_ERR "BIGMAC: Can't order irq %d to go.\n", dev->irq);
 			return -EAGAIN;
 		}
 	} else
 #endif
 	if(request_irq(dev->irq, &bigmac_interrupt,
 		       SA_SHIRQ, "BIG MAC", (void *) bp)) {
-		printk("BIGMAC: Can't order irq %d to go.\n", dev->irq);
+		printk(KERN_ERR "BIGMAC: Can't order irq %d to go.\n", dev->irq);
 		return -EAGAIN;
 	}
 	init_timer(&bp->bigmac_timer);
@@ -1069,7 +1069,7 @@
 		if (tickssofar < 40) {
 			return 1;
 		} else {
-			printk ("%s: transmit timed out, resetting\n", dev->name);
+			printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 			bp->enet_stats.tx_errors++;
 			bigmac_init(bp, 0);
 			dev->tbusy = 0;
@@ -1080,7 +1080,7 @@
 	}
 
 	if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
+		printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
 		return 1;
 	}
 
@@ -1138,7 +1138,7 @@
 		if (tickssofar < 40) {
 			return 1;
 		} else {
-			printk ("%s: transmit timed out, resetting\n", dev->name);
+			printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 			bp->enet_stats.tx_errors++;
 			bigmac_init(bp, 0);
 			dev->tbusy = 0;
@@ -1148,7 +1148,7 @@
 	}
 
 	if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
+		printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
 		return 1;
 	}
 
@@ -1266,10 +1266,10 @@
 	dev = init_etherdev(0, sizeof(struct bigmac));
 
 	if(version_printed++ == 0)
-		printk(version);
+		printk(KERN_INFO "%s", version);
 
 	/* Report what we have found to the user. */
-	printk("%s: BigMAC 100baseT Ethernet ", dev->name);
+	printk(KERN_INFO "%s: BigMAC 100baseT Ethernet ", dev->name);
 	dev->base_addr = (long) qec_sdev;
 	for(i = 0; i < 6; i++)
 		printk("%2.2x%c", dev->dev_addr[i] = idprom->id_ethaddr[i],
@@ -1287,10 +1287,10 @@
 	/* Verify the registers we expect, are actually there. */
 	if((bp->bigmac_sbus_dev->num_registers != 3) ||
 	   (bp->qec_sbus_dev->num_registers != 2)) {
-		printk("BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n",
+		printk(KERN_ERR "BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n",
 		       bp->qec_sbus_dev->num_registers,
 		       bp->bigmac_sbus_dev->num_registers);
-		printk("BIGMAC: Would you like that for here or to go?\n");
+		printk(KERN_ERR "BIGMAC: Would you like that for here or to go?\n");
 		goto fail_and_cleanup;
 	}
 
@@ -1309,7 +1309,7 @@
 				   qranges[k].ot_child_space)
 					break;
 			if(k >= num_qranges) {
-				printk("BigMAC: Aieee, bogus QEC range for space %08x\n",
+				printk(KERN_ERR "BigMAC: Aieee, bogus QEC range for space %08x\n",
 				       bp->bigmac_sbus_dev->reg_addrs[j].which_io);
 				goto fail_and_cleanup;
 			}
@@ -1338,13 +1338,13 @@
 				   bp->qec_sbus_dev->reg_addrs[0].which_io,
 				   0);
 	if(!bp->gregs) {
-		printk("BIGMAC: Cannot map QEC global registers.\n");
+		printk(KERN_ERR "BIGMAC: Cannot map QEC global registers.\n");
 		goto fail_and_cleanup;
 	}
 
 	/* Make sure QEC is in BigMAC mode. */
 	if((bp->gregs->ctrl & 0xf0000000) != GLOB_CTRL_BMODE) {
-		printk("BigMAC: AIEEE, QEC is not in BigMAC mode!\n");
+		printk(KERN_ERR "BigMAC: AIEEE, QEC is not in BigMAC mode!\n");
 		goto fail_and_cleanup;
 	}
 
@@ -1380,7 +1380,7 @@
 				  bp->bigmac_sbus_dev->reg_addrs[0].which_io,
 				  0);
 	if(!bp->creg) {
-		printk("BIGMAC: Cannot map QEC channel registers.\n");
+		printk(KERN_ERR "BIGMAC: Cannot map QEC channel registers.\n");
 		goto fail_and_cleanup;
 	}
 
@@ -1392,7 +1392,7 @@
 				   bp->bigmac_sbus_dev->reg_addrs[1].which_io,
 				   0);
 	if(!bp->bregs) {
-		printk("BIGMAC: Cannot map BigMAC primary registers.\n");
+		printk(KERN_ERR "BIGMAC: Cannot map BigMAC primary registers.\n");
 		goto fail_and_cleanup;
 	}
 
@@ -1406,7 +1406,7 @@
 				   bp->bigmac_sbus_dev->reg_addrs[2].which_io,
 				   0);
 	if(!bp->tregs) {
-		printk("BIGMAC: Cannot map BigMAC transceiver registers.\n");
+		printk(KERN_ERR "BIGMAC: Cannot map BigMAC transceiver registers.\n");
 		goto fail_and_cleanup;
 	}
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sunhme.c linux.14p6/drivers/net/sunhme.c
--- linux.vanilla/drivers/net/sunhme.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/net/sunhme.c	Tue Nov 16 15:13:27 1999
@@ -297,7 +297,7 @@
 	while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries)
 		udelay(20);
 	if(!tries) {
-		printk("happy meal: Aieee, transceiver MIF read bolixed\n");
+		printk(KERN_ERR "happy meal: Aieee, transceiver MIF read bolixed\n");
 		return TCVR_FAILURE;
 	}
 	retval = hme_read32(hp, &tregs->frame) & 0xffff;
@@ -328,7 +328,7 @@
 
 	/* Anything else? */
 	if(!tries)
-		printk("happy meal: Aieee, transceiver MIF write bolixed\n");
+		printk(KERN_ERR "happy meal: Aieee, transceiver MIF write bolixed\n");
 
 	/* Fifty-two cents is your change, have a nice day. */
 }
@@ -391,7 +391,7 @@
 
 static void display_link_mode(struct happy_meal *hp, struct hmeal_tcvregs *tregs)
 {
-	printk("%s: Link is up using ", hp->dev->name);
+	printk(KERN_INFO "%s: Link is up using ", hp->dev->name);
 	if(hp->tcvr_type == external)
 		printk("external ");
 	else
@@ -413,7 +413,7 @@
 
 static void display_forced_link_mode(struct happy_meal *hp, struct hmeal_tcvregs *tregs)
 {
-	printk("%s: Link has been forced up using ", hp->dev->name);
+	printk(KERN_INFO "%s: Link has been forced up using ", hp->dev->name);
 	if(hp->tcvr_type == external)
 		printk("external ");
 	else
@@ -492,6 +492,25 @@
 
 static int happy_meal_init(struct happy_meal *hp, int from_irq);
 
+static int is_lucent_phy(struct happy_meal *hp)
+{
+	struct hmeal_tcvregs *tregs = hp->tcvregs;
+	unsigned short mr2, mr3;
+	int ret = 0;
+
+	mr2 = happy_meal_tcvr_read(hp, tregs, 2);
+	mr3 = happy_meal_tcvr_read(hp, tregs, 3);
+	if ((mr2 & 0xffff) == 0x0180 &&
+	    ((mr3 & 0xffff) >> 10) == 0x1d) {
+#if 0
+		printk("HMEDEBUG: Lucent PHY detected.\n");
+#endif
+		ret = 1;
+	}
+
+	return ret;
+}
+
 static void happy_meal_timer(unsigned long data)
 {
 	struct happy_meal *hp = (struct happy_meal *) data;
@@ -508,19 +527,22 @@
 			/* Enter force mode. */
 	do_force_mode:
 			hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
-			printk("%s: Auto-Negotiation unsuccessful, trying force link mode\n",
+			printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful, trying force link mode\n",
 			       hp->dev->name);
 			hp->sw_bmcr = BMCR_SPEED100;
 			happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
 
-			/* OK, seems we need do disable the transceiver for the first
-			 * tick to make sure we get an accurate link state at the
-			 * second tick.
-			 */
-			hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
-			hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
-			happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
-
+			if (!is_lucent_phy(hp)) {
+				/* OK, seems we need do disable the transceiver for
+				 * the first tick to make sure we get an accurate
+				 * link state at the second tick.
+				 */
+				hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
+								       DP83840_CSCONFIG);
+				hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+				happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG,
+						      hp->sw_csconfig);
+			}
 			hp->timer_state = ltrywait;
 			hp->timer_ticks = 0;
 			restart_timer = 1;
@@ -567,7 +589,7 @@
 			restart_timer = 0;
 		} else {
 			if(hp->timer_ticks >= 10) {
-				printk("%s: Auto negotiation successful, link still "
+				printk(KERN_NOTICE "%s: Auto negotiation successful, link still "
 				       "not completely up.\n", hp->dev->name);
 				hp->timer_ticks = 0;
 				restart_timer = 1;
@@ -587,15 +609,24 @@
 		hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
 		if(hp->timer_ticks == 1) {
 			/* Re-enable transceiver, we'll re-enable the transceiver next
-			 * tick, then check link state on the following tick. */
-			hp->sw_csconfig |= CSCONFIG_TCVDISAB;
-			happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+			 * tick, then check link state on the following tick.
+			 * XXX But dont do this on Lucent PHY -DaveM
+			 */
+			if (!is_lucent_phy(hp)) {
+				hp->sw_csconfig |= CSCONFIG_TCVDISAB;
+				happy_meal_tcvr_write(hp, tregs,
+						      DP83840_CSCONFIG, hp->sw_csconfig);
+			}
 			restart_timer = 1;
 			break;
 		}
 		if(hp->timer_ticks == 2) {
-			hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
-			happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+			/* XXX See above about Lucent PHY -DaveM */
+			if (!is_lucent_phy(hp)) {
+				hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+				happy_meal_tcvr_write(hp, tregs,
+						      DP83840_CSCONFIG, hp->sw_csconfig);
+			}
 			restart_timer = 1;
 			break;
 		}
@@ -616,20 +647,25 @@
 					 */
 
 					/* Let the user know... */
-					printk("%s: Link down, cable problem?\n",
+					printk(KERN_NOTICE "%s: Link down, cable problem?\n",
 					       hp->dev->name);
 
 					ret = happy_meal_init(hp, 0);
 					if(ret) {
 						/* ho hum... */
-						printk("%s: Error, cannot re-init the "
+						printk(KERN_ERR "%s: Error, cannot re-init the "
 						       "Happy Meal.\n", hp->dev->name);
 					}
 					return;
 				}
-				hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
-				hp->sw_csconfig |= CSCONFIG_TCVDISAB;
-				happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+				hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
+								       DP83840_CSCONFIG);
+				if (!is_lucent_phy(hp)) {
+					hp->sw_csconfig |= CSCONFIG_TCVDISAB;
+					happy_meal_tcvr_write(hp, tregs,
+							      DP83840_CSCONFIG,
+							      hp->sw_csconfig);
+				}
 				hp->timer_ticks = 0;
 				restart_timer = 1;
 			} else {
@@ -641,7 +677,7 @@
 	case asleep:
 	default:
 		/* Can't happens.... */
-		printk("%s: Aieee, link timer is asleep but we got one anyways!\n",
+		printk(KERN_ERR "%s: Aieee, link timer is asleep but we got one anyways!\n",
 		       hp->dev->name);
 		restart_timer = 0;
 		hp->timer_ticks = 0;
@@ -672,7 +708,7 @@
 
 	/* Lettuce, tomato, buggy hardware (no extra charge)? */
 	if(!tries)
-		printk("happy meal: Transceiver BigMac ATTACK!");
+		printk(KERN_ERR "happy meal: Transceiver BigMac ATTACK!");
 
 	/* Take care. */
 	HMD(("done\n"));
@@ -692,7 +728,7 @@
 
 	/* Will that be all? */
 	if(!tries)
-		printk("happy meal: Receiver BigMac ATTACK!");
+		printk(KERN_ERR "happy meal: Receiver BigMac ATTACK!");
 
 	/* Don't forget your vik_1137125_wa.  Have a nice day. */
 	HMD(("done\n"));
@@ -714,7 +750,7 @@
 
 	/* Come back next week when we are "Sun Microelectronics". */
 	if(!tries)
-		printk("happy meal: Fry guys.");
+		printk(KERN_ERR "happy meal: Fry guys.");
 
 	/* Remember: "Different name, same old buggy as shit hardware." */
 	HMD(("done\n"));
@@ -910,8 +946,11 @@
 		return -1;
 	}
 	ASD((" SUCCESS and CSCONFIG_DFBYPASS\n"));
-	result = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
-	happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, (result | CSCONFIG_DFBYPASS));
+	if (!is_lucent_phy(hp)) {
+		result = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
+		happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG,
+				      (result | CSCONFIG_DFBYPASS));
+	}
 	return 0;
 }
 
@@ -972,7 +1011,7 @@
 				hp->tcvr_type = internal;
 				ASD(("<internal>\n"));
 			} else {
-				printk("happy meal: Transceiver and a coke please.");
+				printk(KERN_ERR "happy meal: Transceiver and a coke please.");
 				hp->tcvr_type = none; /* Grrr... */
 				ASD(("<none>\n"));
 			}
@@ -1201,9 +1240,9 @@
 			udelay(10);
 		}
 		if(!timeout) {
-			printk("%s: Happy Meal would not start auto negotiation "
+			printk(KERN_ERR "%s: Happy Meal would not start auto negotiation "
 			       "BMCR=0x%04x\n", hp->dev->name, hp->sw_bmcr);
-			printk("%s: Performing force link detection.\n",
+			printk(KERN_NOTICE "%s: Performing force link detection.\n",
 			       hp->dev->name);
 			goto force_link;
 		} else {
@@ -1230,17 +1269,17 @@
 				hp->sw_bmcr |= BMCR_FULLDPLX;
 		}
 		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
-
-		/* OK, seems we need do disable the transceiver for the first
-		 * tick to make sure we get an accurate link state at the
-		 * second tick.
-		 */
-		hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
-						       DP83840_CSCONFIG);
-		hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
-		happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG,
-				      hp->sw_csconfig);
-
+		if (!is_lucent_phy(hp)) {
+			/* OK, seems we need do disable the transceiver for the first
+			 * tick to make sure we get an accurate link state at the
+			 * second tick.
+			 */
+			hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
+							       DP83840_CSCONFIG);
+			hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+			happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG,
+					      hp->sw_csconfig);
+		}
 		hp->timer_state = ltrywait;
 	}
 
@@ -1479,8 +1518,8 @@
 	regtmp = hme_read32(hp, &erxregs->cfg);
 	hme_write32(hp, &erxregs->cfg, ERX_CFG_DEFAULT(RX_OFFSET));
 	if(hme_read32(hp, &erxregs->cfg) != ERX_CFG_DEFAULT(RX_OFFSET)) {
-		printk("happy meal: Eieee, rx config register gets greasy fries.\n");
-		printk("happy meal: Trying to set %08x, reread gives %08lx\n",
+		printk(KERN_ERR "happy meal: Eieee, rx config register gets greasy fries.\n");
+		printk(KERN_ERR "happy meal: Trying to set %08x, reread gives %08lx\n",
 		       ERX_CFG_DEFAULT(RX_OFFSET), regtmp);
 		/* XXX Should return failure here... */
 	}
@@ -1605,24 +1644,24 @@
 		     GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR |
 		     GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR |
 		     GREG_STAT_SLVPERR))
-		printk("%s: Error interrupt for happy meal, status = %08lx\n",
+		printk(KERN_ERR "%s: Error interrupt for happy meal, status = %08lx\n",
 		       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);
+		printk(KERN_ERR "%s: Happy Meal receive FIFO overflow.\n", hp->dev->name);
 		reset = 1;
 	}
 
 	if(status & GREG_STAT_STSTERR) {
 		/* BigMAC SQE link test failed. */
-		printk("%s: Happy Meal BigMAC SQE test failed.\n", hp->dev->name);
+		printk(KERN_ERR "%s: Happy Meal BigMAC SQE test failed.\n", hp->dev->name);
 		reset = 1;
 	}
 
 	if(status & GREG_STAT_TFIFO_UND) {
 		/* Transmit FIFO underrun, again DMA error likely. */
-		printk("%s: Happy Meal transmitter FIFO underrun, DMA error.\n",
+		printk(KERN_ERR "%s: Happy Meal transmitter FIFO underrun, DMA error.\n",
 		       hp->dev->name);
 		reset = 1;
 	}
@@ -1631,7 +1670,7 @@
 		/* Driver error, tried to transmit something larger
 		 * than ethernet max mtu.
 		 */
-		printk("%s: Happy Meal MAX Packet size error.\n", hp->dev->name);
+		printk(KERN_ERR "%s: Happy Meal MAX Packet size error.\n", hp->dev->name);
 		reset = 1;
 	}
 
@@ -1648,7 +1687,7 @@
 
 	if(status & (GREG_STAT_RXERR|GREG_STAT_RXPERR|GREG_STAT_RXTERR)) {
 		/* All sorts of DMA receive errors. */
-		printk("%s: Happy Meal rx DMA errors [ ", hp->dev->name);
+		printk(KERN_ERR "%s: Happy Meal rx DMA errors [ ", hp->dev->name);
 		if(status & GREG_STAT_RXERR)
 			printk("GenericError ");
 		if(status & GREG_STAT_RXPERR)
@@ -1663,20 +1702,20 @@
 		/* Driver bug, didn't set EOP bit in tx descriptor given
 		 * to the happy meal.
 		 */
-		printk("%s: EOP not set in happy meal transmit descriptor!\n",
+		printk(KERN_ERR "%s: EOP not set in happy meal transmit descriptor!\n",
 		       hp->dev->name);
 		reset = 1;
 	}
 
 	if(status & GREG_STAT_MIFIRQ) {
 		/* MIF signalled an interrupt, were we polling it? */
-		printk("%s: Happy Meal MIF interrupt.\n", hp->dev->name);
+		printk(KERN_ERR "%s: Happy Meal MIF interrupt.\n", hp->dev->name);
 	}
 
 	if(status &
 	   (GREG_STAT_TXEACK|GREG_STAT_TXLERR|GREG_STAT_TXPERR|GREG_STAT_TXTERR)) {
 		/* All sorts of transmit DMA errors. */
-		printk("%s: Happy Meal tx DMA errors [ ", hp->dev->name);
+		printk(KERN_ERR "%s: Happy Meal tx DMA errors [ ", hp->dev->name);
 		if(status & GREG_STAT_TXEACK)
 			printk("GenericError ");
 		if(status & GREG_STAT_TXLERR)
@@ -1693,14 +1732,14 @@
 		/* Bus or parity error when cpu accessed happy meal registers
 		 * or it's internal FIFO's.  Should never see this.
 		 */
-		printk("%s: Happy Meal register access SBUS slave (%s) error.\n",
+		printk(KERN_ERR "%s: Happy Meal register access SBUS slave (%s) error.\n",
 		       hp->dev->name,
 		       (status & GREG_STAT_SLVPERR) ? "parity" : "generic");
 		reset = 1;
 	}
 
 	if(reset) {
-		printk("%s: Resetting...\n", hp->dev->name);
+		printk(KERN_NOTICE "%s: Resetting...\n", hp->dev->name);
 		happy_meal_init(hp, 1);
 		return 1;
 	}
@@ -1711,22 +1750,22 @@
 					    struct hmeal_gregs *gregs,
 					    struct hmeal_tcvregs *tregs)
 {
-	printk("%s: Link status change.\n", hp->dev->name);
+	printk(KERN_INFO "%s: Link status change.\n", hp->dev->name);
 	hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
 	hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, DP83840_LPA);
 
 	/* Use the fastest transmission protocol possible. */
 	if(hp->sw_lpa & LPA_100FULL) {
-		printk("%s: Switching to 100Mbps at full duplex.", hp->dev->name);
+		printk(KERN_INFO "%s: Switching to 100Mbps at full duplex.", hp->dev->name);
 		hp->sw_bmcr |= (BMCR_FULLDPLX | BMCR_SPEED100);
 	} else if(hp->sw_lpa & LPA_100HALF) {
-		printk("%s: Switching to 100MBps at half duplex.", hp->dev->name);
+		printk(KERN_INFO "%s: Switching to 100MBps at half duplex.", hp->dev->name);
 		hp->sw_bmcr |= BMCR_SPEED100;
 	} else if(hp->sw_lpa & LPA_10FULL) {
-		printk("%s: Switching to 10MBps at full duplex.", hp->dev->name);
+		printk(KERN_INFO "%s: Switching to 10MBps at full duplex.", hp->dev->name);
 		hp->sw_bmcr |= BMCR_FULLDPLX;
 	} else {
-		printk("%s: Using 10Mbps at half duplex.", hp->dev->name);
+		printk(KERN_INFO "%s: Using 10Mbps at half duplex.", hp->dev->name);
 	}
 	happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
 
@@ -1942,7 +1981,7 @@
 	}
 	hp->rx_new = elem;
 	if(drops)
-		printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name);
+		printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name);
 	RXD((">"));
 }
 
@@ -2061,7 +2100,7 @@
 	}
 	hp->rx_new = elem;
 	if(drops)
-		printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name);
+		printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name);
 	RXD((">"));
 }
 #endif
@@ -2126,7 +2165,7 @@
 	}
 	hp->rx_new = elem;
 	if(drops)
-		printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name);
+		printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name);
 	RXD((">"));
 }
 
@@ -2235,7 +2274,7 @@
 	}
 	hp->rx_new = elem;
 	if(drops)
-		printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name);
+		printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name);
 	RXD((">"));
 }
 #endif
@@ -2487,14 +2526,14 @@
 		if(request_irq(dev->irq, &sun4c_happy_meal_interrupt,
 			       SA_SHIRQ, "HAPPY MEAL", (void *) dev)) {
 			HMD(("EAGAIN\n"));
-			printk("happy meal: Can't order irq %d to go.\n", dev->irq);
+			printk(KERN_ERR "happy meal: Can't order irq %d to go.\n", dev->irq);
 			return -EAGAIN;
 		}
 	} else if (sparc_cpu_model == sun4d) {
 		if(request_irq(dev->irq, &sun4d_happy_meal_interrupt,
 			       SA_SHIRQ, "HAPPY MEAL", (void *) dev)) {
 			HMD(("EAGAIN\n"));
-			printk("happy_meal(SBUS): Can't order irq %s to go.\n",
+			printk(KERN_ERR "happy_meal(SBUS): Can't order irq %s to go.\n",
 			       __irq_itoa(dev->irq));
 			return -EAGAIN;
 		}
@@ -2505,7 +2544,7 @@
 		if(request_irq(dev->irq, &pci_happy_meal_interrupt,
 			       SA_SHIRQ, "HAPPY MEAL (PCI)", dev)) {
 		HMD(("EAGAIN\n"));
-		printk("happy_meal(PCI: Can't order irq %s to go.\n",
+		printk(KERN_ERR "happy_meal(PCI: Can't order irq %s to go.\n",
 		       __irq_itoa(dev->irq));
 			return -EAGAIN;
 		}
@@ -2514,7 +2553,7 @@
 	if(request_irq(dev->irq, &happy_meal_interrupt,
 		       SA_SHIRQ, "HAPPY MEAL", (void *)dev)) {
 		HMD(("EAGAIN\n"));
-		printk("happy_meal(SBUS): Can't order irq %s to go.\n",
+		printk(KERN_ERR "happy_meal(SBUS): Can't order irq %s to go.\n",
 		       __irq_itoa(dev->irq));
 		return -EAGAIN;
 	}
@@ -2568,10 +2607,10 @@
 		int tickssofar = jiffies - dev->trans_start;
 	    
 		if (tickssofar >= 40) {
-			printk ("%s: transmit timed out, resetting\n", dev->name);
+			printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 			hp->net_stats.tx_errors++;
 			tx_dump_log();
-			printk ("%s: Happy Status %08x TX[%08x:%08x]\n", dev->name,
+			printk (KERN_ERR "%s: Happy Status %08x TX[%08x:%08x]\n", dev->name,
 				hme_read32(hp, &hp->gregs->stat),
 				hme_read32(hp, &hp->etxregs->cfg),
 				hme_read32(hp, &hp->bigmacregs->tx_cfg));
@@ -2633,7 +2672,7 @@
 		if (tickssofar >= 40) {
 			unsigned long flags;
 
-			printk ("%s: transmit timed out, resetting\n", dev->name);
+			printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 
 			save_and_cli(flags);
 			tx_dump_log();
@@ -2690,7 +2729,7 @@
 		if (tickssofar < 40) {
 			return 1;
 		} else {
-			printk ("%s: transmit timed out, resetting\n", dev->name);
+			printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 			hp->net_stats.tx_errors++;
 			happy_meal_init(hp, 0);
 			dev->tbusy = 0;
@@ -2700,7 +2739,7 @@
 	}
 
 	if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
-		printk("happy meal: Transmitter access conflict.\n");
+		printk(KERN_ERR "happy meal: Transmitter access conflict.\n");
 		return 1;
 	}
 
@@ -2742,10 +2781,10 @@
 		int tickssofar = jiffies - dev->trans_start;
 	    
 		if (tickssofar >= 40) {
-			printk ("%s: transmit timed out, resetting\n", dev->name);
+			printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 			hp->net_stats.tx_errors++;
 			tx_dump_log();
-			printk ("%s: Happy Status %08x TX[%08x:%08x]\n", dev->name,
+			printk (KERN_ERR "%s: Happy Status %08x TX[%08x:%08x]\n", dev->name,
 				hme_read32(hp, &hp->gregs->stat),
 				hme_read32(hp, &hp->etxregs->cfg),
 				hme_read32(hp, &hp->bigmacregs->tx_cfg));
@@ -3085,7 +3124,7 @@
 				  SA_SHIRQ, "Quattro",
 				  qp);
 		if(err != 0) {
-			printk("Quattro: Fatal IRQ registery error %d.\n", err);
+			printk(KERN_ERR "Quattro: Fatal IRQ registery error %d.\n", err);
 			panic("QFE request irq");
 		}
 	}
@@ -3117,13 +3156,13 @@
 			return -ENOMEM;
 	}
 	if(hme_version_printed++ == 0)
-		printk(version);
+		printk(KERN_INFO "%s", version);
 
 	if(qfe_slot != -1)
-		printk("%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ",
+		printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ",
 		       dev->name, qfe_slot);
 	else
-		printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
+		printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
 		       dev->name);
 
 	dev->base_addr = (long) sdev;
@@ -3148,9 +3187,9 @@
 #endif
 
 	if(sdev->num_registers != 5) {
-		printk("happymeal: Device does not have 5 regs, it has %d.\n",
+		printk(KERN_ERR "happymeal: Device does not have 5 regs, it has %d.\n",
 		       sdev->num_registers);
-		printk("happymeal: Would you like that for here or to go?\n");
+		printk(KERN_ERR "happymeal: Would you like that for here or to go?\n");
 		return ENODEV;
 	}
 
@@ -3168,7 +3207,7 @@
 				   "Happy Meal Global Regs",
 				   sdev->reg_addrs[0].which_io, 0);
 	if(!hp->gregs) {
-		printk("happymeal: Cannot map Happy Meal global registers.\n");
+		printk(KERN_ERR "happymeal: Cannot map Happy Meal global registers.\n");
 		return ENODEV;
 	}
 
@@ -3177,7 +3216,7 @@
 				     "Happy Meal MAC TX Regs",
 				     sdev->reg_addrs[1].which_io, 0);
 	if(!hp->etxregs) {
-		printk("happymeal: Cannot map Happy Meal MAC Transmit registers.\n");
+		printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Transmit registers.\n");
 		return ENODEV;
 	}
 
@@ -3186,7 +3225,7 @@
 				     "Happy Meal MAC RX Regs",
 				     sdev->reg_addrs[2].which_io, 0);
 	if(!hp->erxregs) {
-		printk("happymeal: Cannot map Happy Meal MAC Receive registers.\n");
+		printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Receive registers.\n");
 		return ENODEV;
 	}
 
@@ -3195,7 +3234,7 @@
 					"Happy Meal BIGMAC Regs",
 					sdev->reg_addrs[3].which_io, 0);
 	if(!hp->bigmacregs) {
-		printk("happymeal: Cannot map Happy Meal BIGMAC registers.\n");
+		printk(KERN_ERR "happymeal: Cannot map Happy Meal BIGMAC registers.\n");
 		return ENODEV;
 	}
 
@@ -3204,7 +3243,7 @@
 				     "Happy Meal Tranceiver Regs",
 				     sdev->reg_addrs[4].which_io, 0);
 	if(!hp->tcvregs) {
-		printk("happymeal: Cannot map Happy Meal Tranceiver registers.\n");
+		printk(KERN_ERR "happymeal: Cannot map Happy Meal Tranceiver registers.\n");
 		return ENODEV;
 	}
 
@@ -3299,7 +3338,7 @@
 	/* Now make sure pci_dev cookie is there. */
 	pcp = pdev->sysdata;
 	if(pcp == NULL || pcp->prom_node == -1) {
-		printk("happymeal(PCI): Some PCI device info missing\n");
+		printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n");
 		return ENODEV;
 	}
 	node = pcp->prom_node;
@@ -3323,7 +3362,7 @@
 			return -ENOMEM;
 	}
 	if(hme_version_printed++ == 0)
-		printk(version);
+		printk(KERN_INFO "%s", version);
 
 	if (!qfe_slot) {
 		prom_name[0] = 0;
@@ -3331,7 +3370,7 @@
 			int i = simple_strtoul(dev->name + 3, NULL, 10);
 			sprintf(prom_name, "-%d", i + 3);
 		}
-		printk("%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name);
+		printk(KERN_INFO "%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name);
 		if (qp->quattro_pci_dev->vendor == PCI_VENDOR_ID_DEC &&
 		    qp->quattro_pci_dev->device == PCI_DEVICE_ID_DEC_21153)
 			printk("DEC 21153 PCI Bridge\n");
@@ -3340,10 +3379,10 @@
 				qp->quattro_pci_dev->vendor, qp->quattro_pci_dev->device);
 	}
 	if(qfe_slot != -1)
-		printk("%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ",
+		printk(KERN_INFO "%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ",
 		       dev->name, qfe_slot);
 	else
-		printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
+		printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
 		       dev->name);
 
 	dev->base_addr = (long) pdev;
@@ -3362,7 +3401,7 @@
 
 	hpreg_base = pdev->base_address[0];
 	if((hpreg_base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
-		printk("happymeal(PCI): Cannot find proper PCI device base address.\n");
+		printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n");
 		return ENODEV;
 	}
 	hpreg_base &= PCI_BASE_ADDRESS_MEM_MASK;
@@ -3404,7 +3443,7 @@
 
 	hp->happy_block = (struct hmeal_init_block *) get_free_page(GFP_DMA);
 	if(!hp->happy_block) {
-		printk("happymeal(PCI): Cannot get hme init block.\n");
+		printk(KERN_ERR "happymeal(PCI): Cannot get hme init block.\n");
 		return ENODEV;
 	}
 
@@ -3443,6 +3482,9 @@
 	 * PROM leaves it at zero.
 	 */
 	{
+#if 1
+		unsigned char latency_timer = 64;
+#else
 		unsigned char min_gnt, latency_timer;
 
 		pci_read_config_byte(pdev, PCI_MIN_GNT, &min_gnt);
@@ -3450,6 +3492,7 @@
 			latency_timer = 64;
 		else
 			latency_timer = ((min_gnt << 3) & 0xff);
+#endif
 		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
 	}
 #ifdef __sparc_v9__
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sunlance.c linux.14p6/drivers/net/sunlance.c
--- linux.vanilla/drivers/net/sunlance.c	Sun Mar 21 15:22:00 1999
+++ linux.14p6/drivers/net/sunlance.c	Tue Nov 16 15:13:27 1999
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.85 1999/03/21 05:22:05 davem Exp $
+/* $Id: sunlance.c,v 1.85.2.1 1999/10/09 06:03:38 davem Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -428,7 +428,7 @@
 	for (i = 0; (i < 100) && !(ll->rdp & (LE_C0_ERR | LE_C0_IDON)); i++)
 		barrier();
 	if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
-		printk ("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp);
+		printk (KERN_ERR "LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp);
 		if (lp->ledma)
 			printk ("dcsr=%8.8x\n",
 				(unsigned int) lp->ledma->regs->cond_reg);
@@ -489,7 +489,7 @@
 			skb = dev_alloc_skb (len+2);
 
 			if (skb == 0) {
-				printk ("%s: Memory squeeze, deferring packet.\n",
+				printk (KERN_INFO "%s: Memory squeeze, deferring packet.\n",
 					dev->name);
 				lp->stats.rx_dropped++;
 				rd->mblength = 0;
@@ -547,7 +547,7 @@
 				lp->stats.tx_carrier_errors++;
 				if (lp->auto_select) {
 					lp->tpe = 1 - lp->tpe;
-					printk("%s: Carrier Lost, trying %s\n",
+					printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n",
 					       dev->name, lp->tpe?"TPE":"AUI");
 					/* Stop the lance */
 					ll->rap = LE_CSR0;
@@ -565,7 +565,7 @@
 			if (status & (LE_T3_BUF|LE_T3_UFL)) {
 				lp->stats.tx_fifo_errors++;
 
-				printk ("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
+				printk (KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
 					dev->name);
 				/* Stop the lance */
 				ll->rap = LE_CSR0;
@@ -606,7 +606,7 @@
 	int csr0;
     
 	if (dev->interrupt)
-		printk ("%s: again", dev->name);
+		printk (KERN_ERR "%s: again", dev->name);
     
 	dev->interrupt = 1;
 
@@ -643,7 +643,7 @@
 		struct sparc_dma_registers *dregs = lp->ledma->regs;
 		unsigned long tst = (unsigned long)dregs->st_addr;
 
-		printk ("%s: Memory error, status %04x, addr %06lx\n",
+		printk (KERN_ERR "%s: Memory error, status %04x, addr %06lx\n",
 			dev->name, csr0, tst & 0xffffff);
 
 		ll->rdp = LE_C0_STOP;
@@ -674,7 +674,7 @@
 
 	if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ,
 			 lancestr, (void *) dev)) {
-		printk ("Lance: Can't get irq %s\n", __irq_itoa(dev->irq));
+		printk (KERN_ERR "Lance: Can't get irq %s\n", __irq_itoa(dev->irq));
 		return -EAGAIN;
 	}
 
@@ -775,7 +775,7 @@
 
 	/* On the 4m, reset the dma too */
 	if (lp->ledma) {
-		printk ("resetting ledma\n");
+		printk (KERN_NOTICE "resetting ledma\n");
 		lp->ledma->regs->cond_reg |= DMA_RST_ENET;
 		udelay (200);
 		lp->ledma->regs->cond_reg &= ~DMA_RST_ENET;
@@ -789,7 +789,7 @@
 	dev->tbusy = 0;
 	status = init_restart_lance (lp);
 #ifdef DEBUG_DRIVER
-	printk ("Lance restart=%d\n", status);
+	printk (KERN_DEBUG "Lance restart=%d\n", status);
 #endif
 	return status;
 }
@@ -809,7 +809,7 @@
 		if (tickssofar < 100)
 			return 1;
 
-		printk ("%s: transmit timed out, status %04x, reset\n",
+		printk (KERN_ERR "%s: transmit timed out, status %04x, reset\n",
 			dev->name, ll->rdp);
 		lp->stats.tx_errors++;
 		lance_reset (dev);
@@ -986,9 +986,9 @@
 		memset(dev->priv, 0, sizeof (struct lance_private) + 8);
 	}
 	if (sparc_lance_debug && version_printed++ == 0)
-		printk (version);
+		printk (KERN_INFO "%s", version);
 
-	printk ("%s: LANCE ", dev->name);
+	printk (KERN_INFO "%s: LANCE ", dev->name);
 	/* Fill the dev fields */
 	dev->base_addr = (long) sdev;
 
@@ -1061,7 +1061,7 @@
 		if (prop[0] == 0) {
 			int topnd, nd;
 
-			printk("%s: using auto-carrier-detection.\n",
+			printk(KERN_INFO "%s: using auto-carrier-detection.\n",
 			       dev->name);
 
 			/* Is this found at /options .attributes in all
@@ -1081,9 +1081,9 @@
 				       sizeof(prop));
 
 			if (strcmp(prop, "true")) {
-				printk("%s: warning: overriding option "
+				printk(KERN_NOTICE "%s: warning: overriding option "
 				       "'tpe-link-test?'\n", dev->name);
-				printk("%s: warning: mail any problems "
+				printk(KERN_NOTICE "%s: warning: mail any problems "
 				       "to ecd@skynet.be\n", dev->name);
 				set_auxio(AUXIO_LINK_TEST, 0);
 			}
@@ -1106,7 +1106,7 @@
 
 	/* This should never happen. */
 	if ((unsigned long)(lp->init_block->brx_ring) & 0x07) {
-		printk("%s: ERROR: Rx and Tx rings not on even boundary.\n",
+		printk(KERN_ERR "%s: ERROR: Rx and Tx rings not on even boundary.\n",
 		       dev->name);
 		return ENODEV;
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sunqe.c linux.14p6/drivers/net/sunqe.c
--- linux.vanilla/drivers/net/sunqe.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/net/sunqe.c	Tue Nov 16 15:13:27 1999
@@ -67,7 +67,7 @@
 	}
 	if(tries)
 		return 0;
-	printk("QuadEther: AIEEE cannot reset the QEC!\n");
+	printk(KERN_ERR "QuadEther: AIEEE cannot reset the QEC!\n");
 	return -1;
 }
 
@@ -91,7 +91,7 @@
 		break;
 	}
 	if(!tries) {
-		printk("QuadEther: AIEEE cannot reset the MACE!\n");
+		printk(KERN_ERR "QuadEther: AIEEE cannot reset the MACE!\n");
 		return -1;
 	}
 
@@ -105,7 +105,7 @@
 		break;
 	}
 	if(!tries) {
-		printk("QuadEther: Cannot reset QE channel!\n");
+		printk(KERN_ERR "QuadEther: Cannot reset QE channel!\n");
 		return -1;
 	}
 	return 0;
@@ -216,7 +216,7 @@
 				break;
 		}
 		if (tries == 0)
-			printk("%s: Warning, link state is down.\n", qep->dev->name);
+			printk(KERN_NOTICE "%s: Warning, link state is down.\n", qep->dev->name);
 	}
 
 	/* Missed packet counter is cleared on a read. */
@@ -240,41 +240,41 @@
 	int mace_hwbug_workaround = 0;
 
 	if(qe_status & CREG_STAT_EDEFER) {
-		printk("%s: Excessive transmit defers.\n", dev->name);
+		printk(KERN_ERR "%s: Excessive transmit defers.\n", dev->name);
 		qep->net_stats.tx_errors++;
 	}
 
 	if(qe_status & CREG_STAT_CLOSS) {
-		printk("%s: Carrier lost, link down?\n", dev->name);
+		printk(KERN_ERR "%s: Carrier lost, link down?\n", dev->name);
 		qep->net_stats.tx_errors++;
 		qep->net_stats.tx_carrier_errors++;
 	}
 
 	if(qe_status & CREG_STAT_ERETRIES) {
-		printk("%s: Excessive transmit retries (more than 16).\n", dev->name);
+		printk(KERN_ERR "%s: Excessive transmit retries (more than 16).\n", dev->name);
 		qep->net_stats.tx_errors++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_LCOLL) {
-		printk("%s: Late transmit collision.\n", dev->name);
+		printk(KERN_ERR "%s: Late transmit collision.\n", dev->name);
 		qep->net_stats.tx_errors++;
 		qep->net_stats.collisions++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_FUFLOW) {
-		printk("%s: Transmit fifo underflow, driver bug.\n", dev->name);
+		printk(KERN_ERR "%s: Transmit fifo underflow, driver bug.\n", dev->name);
 		qep->net_stats.tx_errors++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_JERROR) {
-		printk("%s: Jabber error.\n", dev->name);
+		printk(KERN_ERR "%s: Jabber error.\n", dev->name);
 	}
 
 	if(qe_status & CREG_STAT_BERROR) {
-		printk("%s: Babble error.\n", dev->name);
+		printk(KERN_ERR "%s: Babble error.\n", dev->name);
 	}
 
 	if(qe_status & CREG_STAT_CCOFLOW) {
@@ -283,27 +283,27 @@
 	}
 
 	if(qe_status & CREG_STAT_TXDERROR) {
-		printk("%s: Transmit descriptor is bogus, driver bug.\n", dev->name);
+		printk(KERN_ERR "%s: Transmit descriptor is bogus, driver bug.\n", dev->name);
 		qep->net_stats.tx_errors++;
 		qep->net_stats.tx_aborted_errors++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_TXLERR) {
-		printk("%s: Transmit late error.\n", dev->name);
+		printk(KERN_ERR "%s: Transmit late error.\n", dev->name);
 		qep->net_stats.tx_errors++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_TXPERR) {
-		printk("%s: Transmit DMA parity error.\n", dev->name);
+		printk(KERN_ERR "%s: Transmit DMA parity error.\n", dev->name);
 		qep->net_stats.tx_errors++;
 		qep->net_stats.tx_aborted_errors++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_TXSERR) {
-		printk("%s: Transmit DMA sbus error ack.\n", dev->name);
+		printk(KERN_ERR "%s: Transmit DMA sbus error ack.\n", dev->name);
 		qep->net_stats.tx_errors++;
 		qep->net_stats.tx_aborted_errors++;
 		mace_hwbug_workaround = 1;
@@ -325,13 +325,13 @@
 	}
 
 	if(qe_status & CREG_STAT_RXFOFLOW) {
-		printk("%s: Receive fifo overflow.\n", dev->name);
+		printk(KERN_ERR "%s: Receive fifo overflow.\n", dev->name);
 		qep->net_stats.rx_errors++;
 		qep->net_stats.rx_over_errors++;
 	}
 
 	if(qe_status & CREG_STAT_RLCOLL) {
-		printk("%s: Late receive collision.\n", dev->name);
+		printk(KERN_ERR "%s: Late receive collision.\n", dev->name);
 		qep->net_stats.rx_errors++;
 		qep->net_stats.collisions++;
 	}
@@ -347,33 +347,33 @@
 	}
 
 	if(qe_status & CREG_STAT_RXDROP) {
-		printk("%s: Receive packet dropped.\n", dev->name);
+		printk(KERN_INFO "%s: Receive packet dropped.\n", dev->name);
 		qep->net_stats.rx_errors++;
 		qep->net_stats.rx_dropped++;
 		qep->net_stats.rx_missed_errors++;
 	}
 
 	if(qe_status & CREG_STAT_RXSMALL) {
-		printk("%s: Receive buffer too small, driver bug.\n", dev->name);
+		printk(KERN_ERR "%s: Receive buffer too small, driver bug.\n", dev->name);
 		qep->net_stats.rx_errors++;
 		qep->net_stats.rx_length_errors++;
 	}
 
 	if(qe_status & CREG_STAT_RXLERR) {
-		printk("%s: Receive late error.\n", dev->name);
+		printk(KERN_ERR "%s: Receive late error.\n", dev->name);
 		qep->net_stats.rx_errors++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_RXPERR) {
-		printk("%s: Receive DMA parity error.\n", dev->name);
+		printk(KERN_ERR "%s: Receive DMA parity error.\n", dev->name);
 		qep->net_stats.rx_errors++;
 		qep->net_stats.rx_missed_errors++;
 		mace_hwbug_workaround = 1;
 	}
 
 	if(qe_status & CREG_STAT_RXSERR) {
-		printk("%s: Receive DMA sbus error ack.\n", dev->name);
+		printk(KERN_ERR "%s: Receive DMA sbus error ack.\n", dev->name);
 		qep->net_stats.rx_errors++;
 		qep->net_stats.rx_missed_errors++;
 		mace_hwbug_workaround = 1;
@@ -437,7 +437,7 @@
 	}
 	qep->rx_new = elem;
 	if(drops)
-		printk("%s: Memory squeeze, deferring packet.\n", qep->dev->name);
+		printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", qep->dev->name);
 }
 
 /* Interrupts for all QE's get filtered out via the QEC master controller,
@@ -538,7 +538,7 @@
 		long tickssofar = jiffies - dev->trans_start;
 
 		if (tickssofar >= 40) {
-			printk("%s: transmit timed out, resetting\n", dev->name);
+			printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name);
 			qe_init(qep, 1);
 			dev->tbusy = 0;
 			dev->trans_start = jiffies;
@@ -711,7 +711,7 @@
 		qe_devs[0]->dev_addr[j] = idprom->id_ethaddr[j];
 
 	if(version_printed++ == 0)
-		printk(version);
+		printk(KERN_INFO "%s", version);
 
 	qe_devs[1] = qe_devs[2] = qe_devs[3] = NULL;
 	for(i = 1; i < 4; i++) {
@@ -777,7 +777,7 @@
 					   qranges[k].ot_child_space)
 						break;
 				if(k >= num_qranges)
-					printk("QuadEther: Aieee, bogus QEC range for "
+					printk(KERN_ERR "QuadEther: Aieee, bogus QEC range for "
 					       "space %08x\n",qesdevs[i]->reg_addrs[j].which_io);
 				qesdevs[i]->reg_addrs[j].which_io = qranges[k].ot_parent_space;
 				qesdevs[i]->reg_addrs[j].phys_addr += qranges[k].ot_parent_base;
@@ -796,14 +796,14 @@
 				     "QEC Global Registers",
 				     sdev->reg_addrs[0].which_io, 0);
 	if(!qecp->gregs) {
-		printk("QuadEther: Cannot map QEC global registers.\n");
+		printk(KERN_ERR "QuadEther: Cannot map QEC global registers.\n");
 		res = ENODEV;
 		goto qec_free_devs;
 	}
 
 	/* Make sure the QEC is in MACE mode. */
 	if((qecp->gregs->ctrl & 0xf0000000) != GLOB_CTRL_MMODE) {
-		printk("QuadEther: AIEEE, QEC is not in MACE mode!\n");
+		printk(KERN_ERR "QuadEther: AIEEE, QEC is not in MACE mode!\n");
 		res = ENODEV;
 		goto qec_free_devs;
 	}
@@ -841,7 +841,7 @@
 						 "QEC Per-Channel Registers",
 						 qesdevs[i]->reg_addrs[0].which_io, 0);
 		if(!qeps[i]->qcregs) {
-			printk("QuadEther: Cannot map QE %d's channel registers.\n", i);
+			printk(KERN_ERR "QuadEther: Cannot map QE %d's channel registers.\n", i);
 			res = ENODEV;
 			goto qec_free_devs;
 		}
@@ -852,7 +852,7 @@
 						"QE MACE Registers",
 						qesdevs[i]->reg_addrs[1].which_io, 0);
 		if(!qeps[i]->mregs) {
-			printk("QuadEther: Cannot map QE %d's MACE registers.\n", i);
+			printk(KERN_ERR "QuadEther: Cannot map QE %d's MACE registers.\n", i);
 			res = ENODEV;
 			goto qec_free_devs;
 		}
@@ -888,14 +888,14 @@
 	 */
 	if(request_irq(sdev->irqs[0], &qec_interrupt,
 		       SA_SHIRQ, "QuadEther", (void *) qecp)) {
-		printk("QuadEther: Can't register QEC master irq handler.\n");
+		printk(KERN_ERR "QuadEther: Can't register QEC master irq handler.\n");
 		res = EAGAIN;
 		goto qec_free_devs;
 	}
 
 	/* Report the QE channels. */
 	for(i = 0; i < 4; i++) {
-		printk("%s: QuadEthernet channel[%d] ", qe_devs[i]->name, i);
+		printk(KERN_INFO "%s: QuadEthernet channel[%d] ", qe_devs[i]->name, i);
 		for(j = 0; j < 6; j++)
 			printk ("%2.2x%c",
 				qe_devs[i]->dev_addr[j],
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/syncppp.c linux.14p6/drivers/net/syncppp.c
--- linux.vanilla/drivers/net/syncppp.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/net/syncppp.c	Wed Nov  3 00:18:55 1999
@@ -151,7 +151,9 @@
 
 static void if_down(struct device *dev)
 {
-	;
+	struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+
+	sp->pp_link_state=SPPP_LINK_DOWN;
 }
 
 /*
@@ -192,7 +194,7 @@
 	skb->dev=dev;
 	skb->mac.raw=skb->data;
 	
-	if (dev->flags & IFF_UP)
+	if (dev->flags & IFF_RUNNING)
 	{
 		/* Count received bytes, add FCS and one flag */
 		sp->ibytes+= skb->len + 3;
@@ -368,7 +370,7 @@
 
 		/* Keepalive mode disabled or channel down? */
 		if (! (sp->pp_flags & PP_KEEPALIVE) ||
-		    ! (dev->flags & IFF_RUNNING))
+		    ! (dev->flags & IFF_UP))
 			continue;
 
 		/* No keepalive in PPP mode if LCP not opened yet. */
@@ -528,10 +530,10 @@
 		if (h->ident != sp->lcp.confid)
 			break;
 		sppp_clear_timeout (sp);
-		if (! (dev->flags & IFF_UP) &&
-		    (dev->flags & IFF_RUNNING)) {
+		if ((sp->pp_link_state != SPPP_LINK_UP) &&
+		    (dev->flags & IFF_UP)) {
 			/* Coming out of loopback mode. */
-			dev->flags |= IFF_UP;
+			sp->pp_link_state=SPPP_LINK_UP;
 			printk (KERN_INFO "%s: up\n", dev->name);
 		}
 		switch (sp->lcp.state) {
@@ -696,9 +698,9 @@
 			break;
 		}
 		sp->pp_loopcnt = 0;
-		if (! (dev->flags & IFF_UP) &&
-		    (dev->flags & IFF_RUNNING)) {
-			dev->flags |= IFF_UP;
+		if (sp->pp_link_state==SPPP_LINK_DOWN &&
+		    (dev->flags & IFF_UP)) {
+			sp->pp_link_state=SPPP_LINK_UP;
 			printk (KERN_INFO "%s: up\n", dev->name);
 		}
 		break;
@@ -823,7 +825,7 @@
 int sppp_close (struct device *dev)
 {
 	struct sppp *sp = &((struct ppp_device *)dev)->sppp;
-	dev->flags &= ~IFF_RUNNING;
+	sp->pp_link_state = SPPP_LINK_DOWN;
 	sp->lcp.state = LCP_STATE_CLOSED;
 	sp->ipcp.state = IPCP_STATE_CLOSED;
 	sppp_clear_timeout (sp);
@@ -837,9 +839,10 @@
 {
 	struct sppp *sp = &((struct ppp_device *)dev)->sppp;
 	sppp_close(dev);
-	dev->flags |= IFF_RUNNING;
-	if (!(sp->pp_flags & PP_CISCO))
+	if (!(sp->pp_flags & PP_CISCO)) {
 		sppp_lcp_open (sp);
+	}
+	sp->pp_link_state = SPPP_LINK_DOWN;
 	return 0;
 }
 
@@ -849,7 +852,6 @@
 {
 	struct sppp *sp = &((struct ppp_device *)dev)->sppp;
 	sppp_close(dev);
-	dev->flags |= IFF_RUNNING;
 	if (!(sp->pp_flags & PP_CISCO))
 	{
 		sp->lcp.magic = jiffies;
@@ -858,7 +860,8 @@
 		sp->ipcp.state = IPCP_STATE_CLOSED;
 		/* Give it a moment for the line to settle then go */
 		sppp_set_timeout (sp, 1);
-	}
+	} 
+	sp->pp_link_state=SPPP_LINK_DOWN;
 	return 0;
 }
 
@@ -1176,7 +1179,7 @@
 	cli();
 
 	sp->pp_flags &= ~PP_TIMO;
-	if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
+	if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
 		restore_flags(flags);
 		return;
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/syncppp.h linux.14p6/drivers/net/syncppp.h
--- linux.vanilla/drivers/net/syncppp.h	Sat Feb  6 20:46:21 1999
+++ linux.14p6/drivers/net/syncppp.h	Wed Nov  3 00:18:55 1999
@@ -47,6 +47,7 @@
 	u32		ipkts,opkts;	/* Packets in/out */
 	struct timer_list	pp_timer;
 	struct device	*pp_if;
+	char		pp_link_state;
 };
 
 struct ppp_device
@@ -71,6 +72,9 @@
 #define IPCP_STATE_ACK_RCVD     1       /* IPCP state: conf-ack received */
 #define IPCP_STATE_ACK_SENT     2       /* IPCP state: conf-ack sent */
 #define IPCP_STATE_OPENED       3       /* IPCP state: opened */
+
+#define SPPP_LINK_DOWN		0	/* link down - no keepalive */
+#define SPPP_LINK_UP		1	/* link is up - keepalive ok */
 
 void sppp_attach (struct ppp_device *pd);
 void sppp_detach (struct device *dev);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/tulip.c linux.14p6/drivers/net/tulip.c
--- linux.vanilla/drivers/net/tulip.c	Sat Aug 14 02:27:30 1999
+++ linux.14p6/drivers/net/tulip.c	Fri Oct 29 23:35:05 1999
@@ -1,13 +1,13 @@
 /* tulip.c: A DEC 21040-family ethernet driver for Linux. */
 /*
-	Written 1994-1998 by Donald Becker.
+	Written/copyright 1994-1999 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
 
-	This driver is for the Digital "Tulip" ethernet adapter interface.
+	This driver is for the Digital "Tulip" Ethernet adapter interface.
 	It should work with most DEC 21*4*-based chips/ethercards, as well as
-	PNIC and MXIC chips.
+	with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
 
 	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
 	Center of Excellence in Space Data and Information Sciences
@@ -18,7 +18,7 @@
 */
 
 #define SMP_CHECK
-static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n";
+static const char version[] = "tulip.c:v0.91g-ppc 7/16/99 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
@@ -60,80 +60,134 @@
 static int rx_copybreak = 100;
 #endif
 
+/*
+  Set the bus performance register.
+	Typical: Set 16 longword cache alignment, no burst limit.
+	Cache alignment bits 15:14	     Burst length 13:8
+		0000	No alignment  0x00000000 unlimited		0800 8 longwords
+		4000	8  longwords		0100 1 longword		1000 16 longwords
+		8000	16 longwords		0200 2 longwords	2000 32 longwords
+		C000	32  longwords		0400 4 longwords
+	Warning: many older 486 systems are broken and require setting 0x00A04800
+	   8 longword cache alignment, 8 longword burst.
+	ToDo: Non-Intel setting could be better.
+*/
+
+#if defined(__alpha__)
+static int csr0 = 0x01A00000 | 0xE000;
+#elif defined(__i386__) || defined(__powerpc__) || defined(__sparc__)
+static int csr0 = 0x01A00000 | 0x8000;
+#else
+#warning Processor architecture undefined!
+static int csr0 = 0x00A00000 | 0x4800;
+#endif
+
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (4*HZ)
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+   to support a pre-NWay full-duplex signaling mechanism using short frames.
+   No one knows what it should be, but if left at its default value some
+   10base2(!) packets trigger a full-duplex-request interrupt. */
+#define FULL_DUPLEX_MAGIC	0x6969
+
+#if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
 
+#include <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-
-#include <asm/processor.h>		/* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
 
-/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
    This is only in the support-all-kernels source code. */
-#include <linux/version.h>		/* Evil, but neccessary */
 
-#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
-#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
-#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
-#define virt_to_bus(addr)  ((unsigned long)addr)
-#define bus_to_virt(addr) ((void*)addr)
+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(reverse_probe, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(csr0, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+#endif
 
-#else  /* 1.3.0 and later */
 #define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
-#endif
 
-#if (LINUX_VERSION_CODE >= 0x10344)
-#define NEW_MULTICAST
-#include <linux/delay.h>
-#endif
-#ifdef SA_SHIRQ
-#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
-#else
-#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#if (LINUX_VERSION_CODE >= 0x20100)
+static char kernel_version[] = UTS_RELEASE;
 #endif
 
-#if (LINUX_VERSION_CODE < 0x20123)
+#if LINUX_VERSION_CODE < 0x20123
 #define hard_smp_processor_id() smp_processor_id()
 #define test_and_set_bit(val, addr) set_bit(val, addr)
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#define cpu_to_le32(val) (val)
 #endif
-
-/* This my implementation of shared IRQs, now only used for 1.2.13. */
-#ifdef HAVE_SHARED_IRQ
-#define USE_SHARED_IRQ
-#include <linux/shared_irq.h>
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#else
+#define NETSTATS_VER2
 #endif
-
-/* The total size is unusually large: The 21040 aligns each of its 16
-   longword-wide registers on a quadword boundary. */
-#define TULIP_TOTAL_SIZE 0x80
-
-#ifdef HAVE_DEVLIST
-struct netdev_entry tulip_drv =
-{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#if LINUX_VERSION_CODE < 0x20155
+/* Grrrr, the PCI code changed, but did not consider CardBus... */
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb);
+#endif
+#if ! defined(CAP_NET_ADMIN)
+#define capable(CAP_XXX) (suser())
 #endif
+#if ! defined(HAS_NETIF_QUEUE)
+#define netif_wake_queue(dev)  mark_bh(NET_BH);
+#endif
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
 
+#define tulip_debug debug
 #ifdef TULIP_DEBUG
-int tulip_debug = TULIP_DEBUG;
+static int tulip_debug = TULIP_DEBUG;
 #else
-int tulip_debug = 1;
+static int tulip_debug = 1;
 #endif
 
 /*
@@ -143,17 +197,27 @@
 
 This device driver is designed for the DECchip "Tulip", Digital's
 single-chip ethernet controllers for PCI.  Supported members of the family
-are the 21040, 21041, 21140, 21140A, 21142, and 21143.  These chips are used on
-many PCI boards including the SMC EtherPower series.
-
+are the 21040, 21041, 21140, 21140A, 21142, and 21143.  Similar work-alike
+chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
+supported. 
+
+These chips are used on at least 140 unique PCI board designs.  The great
+number of chips and board designs supported is the reason for the
+driver size and complexity.  Almost of the increasing complexity is in the
+board configuration and media selection code.  There is very little
+increasing in the operational critical path length.
 
 II. Board-specific settings
 
 PCI bus devices are configured by the system at boot time, so no jumpers
 need to be set on the board.  The system BIOS preferably should assign the
 PCI INTA signal to an otherwise unused system IRQ line.
-Note: Kernel versions earlier than 1.3.73 do not support shared PCI
-interrupt lines.
+
+Some boards have EEPROMs tables with default media entry.  The factory default
+is usually "autoselect".  This should only be overridden when using
+transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!)
+for forcing full-duplex when used with old link partners that do not do
+autonegotiation. 
 
 III. Driver operation
 
@@ -201,111 +265,159 @@
 
 IV. Notes
 
-Thanks to Duke Kamstra of SMC for providing an EtherPower board.
+Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board.
+Greg LaPolla at Linksys provided PNIC and other Linksys boards.
+Znyx provided a four-port card for testing.
 
 IVb. References
 
 http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
 http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")
-http://www.national.com/pf/DP/DP83840.html
+http://www.national.com/pf/DP/DP83840A.html
+http://www.asix.com.tw/pmac.htm
+http://www.admtek.com.tw/
 
 IVc. Errata
 
-The DEC databook doesn't document which Rx filter settings accept broadcast
-packets.  Nor does it document how to configure the part to configure the
-serial subsystem for normal (vs. loopback) operation or how to have it
-autoswitch between internal 10baseT, SIA and AUI transceivers.
-
+The old DEC databooks were light on details.
 The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written.  Hmmm, now how is that possible?  */
-
-
-/* A few values that may be tweaked. */
-#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+register of the set CSR12-15 written.  Hmmm, now how is that possible?
 
-/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
-   to support a pre-NWay full-duplex signaling mechanism using short frames.
-   No one knows what it should be, but if left at its default value some
-   10base2(!) packets trigger a full-duplex-request interrupt. */
-#define FULL_DUPLEX_MAGIC	0x6969
+The DEC SROM format is very badly designed not precisely defined, leading to
+part of the media selection junkheap below.  Some boards do not have EEPROM
+media tables and need to be patched up.  Worse, other boards use the DEC
+design kit media table when it isn't correct for their board.
 
-#ifndef PCI_VENDOR_ID_DEC		/* Now defined in linux/pci.h */
-#define PCI_VENDOR_ID_DEC			0x1011
-#define PCI_DEVICE_ID_TULIP			0x0002		/* 21040. */
-#define PCI_DEVICE_ID_TULIP_FAST	0x0009		/* 21140. */
-#endif
+We cannot use MII interrupts because there is no defined GPIO pin to attach
+them.  The MII transceiver status is polled using an kernel timer.
 
-#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
-#define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014		/* 21041. */
-#endif
-#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
-#define PCI_DEVICE_ID_DEC_TULIP_21142	0x0019
-#endif
+*/
 
-#ifndef PCI_VENDOR_ID_LITEON
-#define PCI_VENDOR_ID_LITEON	0x11AD
-#endif
+static struct device *
+tulip_probe1(int pci_bus, int pci_devfn, struct device *dev, long ioaddr,
+			 int irq, int chip_idx, int board_idx);
+
+/* This table drives the PCI probe routines.  It's mostly boilerplate in all
+   of the drivers, and will likely be provided by some future kernel.
+   Note the matching code -- the first table entry matchs all 56** cards but
+   second only the 1234 card.
+*/
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+#define PCI_ADDR0_IO (PCI_USES_IO|PCI_ADDR0)
 
-#ifndef PCI_VENDOR_ID_MXIC
-#define	PCI_VENDOR_ID_MXIC		0x10d9
-#define PCI_DEVICE_ID_MX98713	0x0512
-#define PCI_DEVICE_ID_MX98715	0x0531
-#define PCI_DEVICE_ID_MX98725	0x0531
-#endif
+struct pci_id_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, flags;
+	int io_size, min_latency;
+	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
+};
+#ifndef CARDBUS
+static struct pci_id_info pci_tbl[] = {
+  { "Digital DC21040 Tulip",
+	0x1011, 0x0002, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Digital DC21041 Tulip",
+	0x1011, 0x0014, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Digital DS21140 Tulip",
+	0x1011, 0x0009, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Digital DS21143 Tulip",
+	0x1011, 0x0019, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Lite-On 82c168 PNIC",
+	0x11AD, 0x0002, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "Macronix 98713 PMAC",
+	0x10d9, 0x0512, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "Macronix 98715 PMAC",
+	0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "Macronix 98725 PMAC",
+	0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "ASIX AX88140",
+	0x125B, 0x1400, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Lite-On LC82C115 PNIC-II",
+	0x11AD, 0xc115, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "ADMtek AN981 Comet",
+	0x1317, 0x0981, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "Compex RL100-TX",
+	0x11F6, 0x9881, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Intel 21145 Tulip",
+	0x8086, 0x0039, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Xircom Tulip clone",
+	0x115d, 0x0003, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  {0},
+};
+#endif /* !CARD_BUS */
 
-/* The rest of these values should never change. */
+/* This table use during operation for capabilities and media timer. */
 
 static void tulip_timer(unsigned long data);
 static void t21142_timer(unsigned long data);
 static void mxic_timer(unsigned long data);
 static void pnic_timer(unsigned long data);
+static void comet_timer(unsigned long data);
 
-/* A table describing the chip types. */
-enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,};
+enum tbl_flag {
+	HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
+	HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
+	HAS_PNICNWAY=0x80, HAS_NWAY143=0x40,	/* Uses internal NWay xcvr. */
+	HAS_8023X=0x100,
+};
 static struct tulip_chip_table {
-	int vendor_id, device_id;
 	char *chip_name;
 	int io_size;
 	int valid_intrs;			/* CSR7 interrupt enable settings */
 	int flags;
 	void (*media_timer)(unsigned long data);
 } tulip_tbl[] = {
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP,
-	"Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS,
-	"Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST,
-	"Digital DS21140 Tulip", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM,
-	tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142,
-	"Digital DS21142/3 Tulip", 128, 0x0801fbff,
-	HAS_MII | HAS_MEDIA_TABLE, t21142_timer },
-  { PCI_VENDOR_ID_LITEON, 0x0002,
-	"Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713,
-	"Macronix 98713 PMAC", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715,
-	"Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725,
-	"Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
-  { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff,
+  { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
+  { "Digital DC21041 Tulip", 128, 0x0001ebff, HAS_MEDIA_TABLE, tulip_timer },
+  { "Digital DS21140 Tulip", 128, 0x0001ebef,
 	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
-  {0, 0, 0, 0},
+  { "Digital DS21143 Tulip", 128, 0x0801fbff,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+	t21142_timer },
+  { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
+	HAS_MII | HAS_PNICNWAY, pnic_timer },
+  { "Macronix 98713 PMAC", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
+  { "Macronix 98715 PMAC", 256, 0x0001ebef,
+	HAS_MEDIA_TABLE, mxic_timer },
+  { "Macronix 98725 PMAC", 256, 0x0001ebef,
+	HAS_MEDIA_TABLE, mxic_timer },
+  { "ASIX AX88140", 128, 0x0001fbff,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer },
+  { "Lite-On PNIC-II", 256, 0x0801fbff,
+	HAS_MII | HAS_NWAY143 | HAS_8023X, t21142_timer },
+  { "ADMtek Comet", 256, 0x0001abef,
+	MC_HASH_ONLY, comet_timer },
+  { "Compex 9881 PMAC", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
+  { "Intel DS21145 Tulip", 128, 0x0801fbff,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+	t21142_timer },
+  { "Xircom tulip work-alike", 128, 0x0801fbff,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+	t21142_timer },
+  {0},
+};
+/* This matches the table above.  Note 21142 == 21143. */
+enum chips {
+	DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
+	LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881,
+	I21145,
 };
-/* This matches the table above. */
-enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
-			 LC82C168, MX98713, MX98715, MX98725};
 
 /* A full-duplex map for media types. */
-enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
-		  MediaIs100=16};
+enum MediaIs {
+	MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
+	MediaIs100=16};
 static const char media_cap[] =
 {0,0,0,16,  3,19,16,24,  27,4,7,5, 0,20,23,20 };
+static u8 t21040_csr13[] = {2,0x0C,8,4,  4,0,0,0, 0,0,0,0, 4,0,0,0};
 /* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
-static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
-static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
 static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
 static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
@@ -327,10 +439,6 @@
 	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
 };
 
-enum desc_status_bits {
-	DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
-};
-        
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
 	s32 status;
@@ -344,6 +452,22 @@
 	u32 buffer1, buffer2;				/* We use only buffer 1.  */
 };
 
+enum desc_status_bits {
+	DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
+};
+
+/* Ring-wrap flag in length field, use for last ring entry.
+	0x01000000 means chain on buffer2 address,
+	0x02000000 means use the ring start address in CSR2/3.
+   Note: Some work-alike chips do not function correctly in chained mode.
+   The ASIX chip works only in chained mode.
+   Thus we indicates ring mode, but always write the 'next' field for
+   chained mode as well.
+*/
+#define DESC_RING_WRAP 0x02000000
+
+#define EEPROM_SIZE 128 	/* 2 << EEPROM_ADDRLEN */
+
 struct medialeaf {
 	u8 type;
 	u8 media;
@@ -353,7 +477,8 @@
 struct mediatable {
 	u16 defaultmedia;
 	u8 leafcount, csr12dir;				/* General purpose pin directions. */
-	unsigned has_mii:1, has_nonmii:1;
+	unsigned has_mii:1, has_nonmii:1, has_reset:6;
+	u32 csr15dir, csr15val;				/* 21143 NWay setting. */
 	struct medialeaf mleaf[0];
 };
 
@@ -375,19 +500,13 @@
 	/* The addresses of receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
 	char *rx_buffs;				/* Address of temporary Rx buffers. */
-	u32 setup_frame[48];		/* Pseudo-Tx frame to init address table. */
+	u16 setup_frame[96];		/* Pseudo-Tx frame to init address table. */
 	int chip_id;
 	int revision;
-#if LINUX_VERSION_CODE > 0x20139
+	int flags;
 	struct net_device_stats stats;
-#else
-	struct enet_statistics stats;
-#endif
 	struct timer_list timer;	/* Media selection timer. */
 	int interrupt;				/* In-interrupt flag. */
-#ifdef SMP_CHECK
-	int smp_proc_id;			/* Which processor in IRQ handler. */
-#endif
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
@@ -398,134 +517,130 @@
 	unsigned int media2:4;				/* Secondary monitored media port. */
 	unsigned int medialock:1;			/* Don't sense media type. */
 	unsigned int mediasense:1;			/* Media sensing in progress. */
+	unsigned int nway:1, nwayset:1;		/* 21143 internal NWay. */
+	unsigned int csr0;					/* CSR0 setting. */
 	unsigned int csr6;					/* Current CSR6 control settings. */
-	unsigned char eeprom[128];			/* Serial EEPROM contents. */
+	unsigned char eeprom[EEPROM_SIZE];	/* Serial EEPROM contents. */
+	void (*link_change)(struct device *dev, int csr5);
 	u16 to_advertise;					/* NWay capabilities advertised.  */
+	u16 lpar;							/* 21143 Link partner ability. */
 	u16 advertising[4];
 	signed char phys[4], mii_cnt;		/* MII device addresses. */
 	struct mediatable *mtable;
 	int cur_index;						/* Current media index. */
-	unsigned char pci_bus, pci_dev_fn;
+	int saved_if_port;
+	unsigned char pci_bus, pci_devfn;
 	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
-static struct device *tulip_probe1(int pci_bus, int pci_devfn,
-								   struct device *dev,
-								   int chip_id, int options);
 static void parse_eeprom(struct device *dev);
-static int read_eeprom(long ioaddr, int location);
+static int read_eeprom(long ioaddr, int location, int addr_len);
 static int mdio_read(struct device *dev, int phy_id, int location);
 static void mdio_write(struct device *dev, int phy_id, int location, int value);
 static void select_media(struct device *dev, int startup);
 static int tulip_open(struct device *dev);
-static void tulip_timer(unsigned long data);
+/* Chip-specific media selection (timer functions prototyped above). */
+static void t21142_lnk_change(struct device *dev, int csr5);
+static void t21142_start_nway(struct device *dev);
+static void pnic_lnk_change(struct device *dev, int csr5);
+static void pnic_do_nway(struct device *dev);
+
 static void tulip_tx_timeout(struct device *dev);
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
 static int tulip_rx(struct device *dev);
-static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
-static struct enet_statistics *tulip_get_stats(struct device *dev);
+static struct net_device_stats *tulip_get_stats(struct device *dev);
 #ifdef HAVE_PRIVATE_IOCTL
 static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd);
 #endif
-#ifdef NEW_MULTICAST
 static void set_rx_mode(struct device *dev);
-#else
-static void set_rx_mode(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 
 
-/* A list of all installed Tulip devices, for removing the driver module. */
+/* A list of all installed Tulip devices. */
 static struct device *root_tulip_dev = NULL;
 
-/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
-   but now receives directly into full-sized skbuffs that are allocated
-   at open() time.
-   This allows the probe routine to use the old driver initialization
-   interface. */
-
+#ifndef CARDBUS
 int tulip_probe(struct device *dev)
 {
 	int cards_found = 0;
-	static int pci_index = 0;	/* Static, for multiple probe calls. */
+	int pci_index = 0;
 	unsigned char pci_bus, pci_device_fn;
 
-	/* Ideally we would detect all network cards in slot order.  That would
-	   be best done a central PCI probe dispatch, which wouldn't work
-	   well with the current structure.  So instead we detect just the
-	   Tulip cards in slot order. */
-
-#if LINUX_VERSION_CODE >= 0x20155
-	if (! pci_present())
-		return -ENODEV;
-#else
-	if (! pcibios_present())
+	if ( ! pcibios_present())
 		return -ENODEV;
-#endif
+
 	for (;pci_index < 0xff; pci_index++) {
 		u16 vendor, device, pci_command, new_command;
-		unsigned long pci_ioaddr = 0;
-		int chip_idx = 0;
+		int chip_idx;
+		int irq;
+		long ioaddr;
 
 		if (pcibios_find_class
 			(PCI_CLASS_NETWORK_ETHERNET << 8,
 			 reverse_probe ? 0xfe - pci_index : pci_index,
-			 &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+			 &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) {
 			if (reverse_probe)
 				continue;
 			else
 				break;
+		}
 		pcibios_read_config_word(pci_bus, pci_device_fn,
 								 PCI_VENDOR_ID, &vendor);
 		pcibios_read_config_word(pci_bus, pci_device_fn,
 								 PCI_DEVICE_ID, &device);
 
-		for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
-			if (vendor == tulip_tbl[chip_idx].vendor_id  &&
-				device == tulip_tbl[chip_idx].device_id)
+		for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+			if (vendor == pci_tbl[chip_idx].vendor_id
+				&& (device & pci_tbl[chip_idx].device_id_mask) ==
+				pci_tbl[chip_idx].device_id)
 				break;
-		if (tulip_tbl[chip_idx].chip_name == 0) {
-			if (vendor == PCI_VENDOR_ID_DEC  ||
-				vendor == PCI_VENDOR_ID_LITEON)
-				printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type"
-					   " %4.4x %4.4x"" detected: not configured.\n",
-					   vendor, device);
+		if (pci_tbl[chip_idx].vendor_id == 0)
 			continue;
-		}
-#if LINUX_VERSION_CODE >= 0x20155
-		pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0];
-#else
-		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-								  &pci_ioaddr);
+
+		{
+#if defined(PCI_SUPPORT_VER2)
+			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+			ioaddr = pdev->base_address[0] & ~3;
+			irq = pdev->irq;
+#else
+			u32 pci_ioaddr;
+			u8 pci_irq_line;
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			ioaddr = pci_ioaddr & ~3;
+			irq = pci_irq_line;
 #endif
-		/* Remove I/O space marker in bit 0. */
-		pci_ioaddr &= ~3;
+		}
 
-		if (tulip_debug > 2)
-			printk(KERN_DEBUG "Found %s at I/O %#lx.\n",
-				   tulip_tbl[chip_idx].chip_name, pci_ioaddr);
+		if (debug > 2)
+			printk(KERN_INFO "Found %s at PCI I/O address %#lx.\n",
+				   pci_tbl[chip_idx].name, ioaddr);
 
-		if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
+		if (check_region(ioaddr, pci_tbl[chip_idx].io_size))
 			continue;
 
 		pcibios_read_config_word(pci_bus, pci_device_fn,
 								 PCI_COMMAND, &pci_command);
 		new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
 		if (pci_command != new_command) {
-			printk(KERN_INFO "  The PCI BIOS has not enabled this"
-				   " device!  Updating PCI command %4.4x->%4.4x.\n",
-				   pci_command, new_command);
+			printk(KERN_INFO "  The PCI BIOS has not enabled the"
+				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_bus, pci_device_fn, pci_command, new_command);
 			pcibios_write_config_word(pci_bus, pci_device_fn,
 									  PCI_COMMAND, new_command);
 		}
 
-		dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found);
+		dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr,
+									   irq, chip_idx, cards_found);
 
 		/* Get and check the bus-master and latency values. */
 		if (dev) {
-			unsigned char pci_latency;
+			u8 pci_latency;
 			pcibios_read_config_byte(pci_bus, pci_device_fn,
 									 PCI_LATENCY_TIMER, &pci_latency);
 			if (pci_latency < 10) {
@@ -534,75 +649,62 @@
 					   pci_latency);
 				pcibios_write_config_byte(pci_bus, pci_device_fn,
 										  PCI_LATENCY_TIMER, 64);
-			} else if (tulip_debug > 1)
-				printk(KERN_INFO "  PCI latency timer (CFLT) is %#x, "
-					   " PCI command is %4.4x.\n",
-					   pci_latency, new_command);
-			/* Bring the 21143 out power-down mode. */
-			if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
-				pcibios_write_config_dword(pci_bus, pci_device_fn,
-										   0x40, 0x40000000);
-			dev = 0;
-			cards_found++;
+			}
 		}
+		dev = 0;
+		cards_found++;
 	}
 
 	return cards_found ? 0 : -ENODEV;
 }
+#endif  /* not CARDBUS */
 
-static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
-								   struct device *dev,
-								   int chip_id, int board_idx)
+static struct device *tulip_probe1(int pci_bus, int pci_devfn,
+								   struct device *dev, long ioaddr, int irq,
+								   int chip_idx, int board_idx)
 {
 	static int did_version = 0;			/* Already printed version info. */
 	struct tulip_private *tp;
-	long ioaddr;
-	int irq;
 	/* See note below on the multiport cards. */
 	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
 	static int last_irq = 0;
 	static int multiport_cnt = 0;		/* For four-port boards w/one EEPROM */
+	u8 chip_rev;
 	int i;
 	unsigned short sum;
+	u8 ee_data[EEPROM_SIZE];
 
 	if (tulip_debug > 0  &&  did_version++ == 0)
 		printk(KERN_INFO "%s", version);
 
 	dev = init_etherdev(dev, 0);
 
-#if LINUX_VERSION_CODE >= 0x20155
-	irq = pci_find_slot(pci_bus, pci_device_fn)->irq;
-	ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0];
-#else
-	{
-		u8 pci_irq_line;
-		u32 pci_ioaddr;
-		pcibios_read_config_byte(pci_bus, pci_device_fn,
-								 PCI_INTERRUPT_LINE, &pci_irq_line);
-		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-								  &pci_ioaddr);
-		irq = pci_irq_line;
-		ioaddr = pci_ioaddr;
-	}
-#endif
-	/* Remove I/O space marker in bit 0. */
-	ioaddr &= ~3;
+	/* Make certain the data structures are quadword aligned. */
+	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
+	memset(tp, 0, sizeof(*tp));
+	dev->priv = tp;
+
+	tp->next_module = root_tulip_dev;
+	root_tulip_dev = dev;
+
+	pcibios_read_config_byte(pci_bus, pci_devfn, PCI_REVISION_ID, &chip_rev);
+
+	/* Bring the 21041/21143 out of sleep mode.
+	   Caution: Snooze mode does not work with some boards! */
+	if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
+		pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x00000000);
 
-	printk(KERN_INFO "%s: %s at %#3lx,",
-		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
+	printk(KERN_INFO "%s: %s rev %d at %#3lx,",
+		   dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
 
 	/* Stop the chip's Tx and Rx processes. */
 	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
 	/* Clear the missed-packet counter. */
 	(volatile int)inl(ioaddr + CSR8);
 
-	if (chip_id == DC21041) {
-		if (inl(ioaddr + CSR9) & 0x8000) {
-			printk(" 21040 compatible mode,");
-			chip_id = DC21040;
-		} else {
-			printk(" 21041 mode,");
-		}
+	if (chip_idx == DC21041  &&  inl(ioaddr + CSR9) & 0x8000) {
+		printk(" 21040 compatible mode,");
+		chip_idx = DC21040;
 	}
 
 	/* The station address ROM is read byte serially.  The register must
@@ -610,7 +712,7 @@
 	   EEPROM.
 	   */
 	sum = 0;
-	if (chip_id == DC21040) {
+	if (chip_idx == DC21040) {
 		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
 		for (i = 0; i < 6; i++) {
 			int value, boguscnt = 100000;
@@ -620,28 +722,34 @@
 			dev->dev_addr[i] = value;
 			sum += value & 0xff;
 		}
-	} else if (chip_id == LC82C168) {
+	} else if (chip_idx == LC82C168) {
 		for (i = 0; i < 3; i++) {
 			int value, boguscnt = 100000;
 			outl(0x600 | i, ioaddr + 0x98);
 			do
 				value = inl(ioaddr + CSR9);
 			while (value < 0  && --boguscnt > 0);
-			((u16*)dev->dev_addr)[i] = value;
+			put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i);
 			sum += value & 0xffff;
 		}
-	} else {	/* Must be a new chip, with a serial EEPROM interface. */
-		/* We read the whole EEPROM, and sort it out later.  DEC has a
-		   specification _Digital Semiconductor 21X4 Serial ROM Format_
-		   but early vendor boards just put the address in the first six
-		   EEPROM locations. */
-		unsigned char ee_data[128];
+	} else if (chip_idx == COMET) {
+		/* No need to read the EEPROM. */
+		put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr);
+		put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4));
+		for (i = 0; i < 6; i ++)
+			sum += dev->dev_addr[i];
+	} else {
+		/* A serial EEPROM interface, we read now and sort it out later. */
 		int sa_offset = 0;
+		int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6;
 
 		for (i = 0; i < sizeof(ee_data)/2; i++)
-			((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
+			((u16 *)ee_data)[i] =
+				le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size));
 
-		/* Detect the simple EEPROM format by the duplicated station addr. */
+		/* DEC now has a specification (see Notes) but early board makers
+		   just put the address in the first EEPROM locations. */
+		/* This does  memcmp(eedata, eedata+16, 8) */
 		for (i = 0; i < 8; i ++)
 			if (ee_data[i] != ee_data[16+i])
 				sa_offset = 20;
@@ -655,7 +763,8 @@
 		}
 	}
 	/* Lite-On boards have the address byte-swapped. */
-	if (dev->dev_addr[0] == 0xA0  &&  dev->dev_addr[1] == 0x00)
+	if ((dev->dev_addr[0] == 0xA0  ||  dev->dev_addr[0] == 0xC0)
+		&&  dev->dev_addr[1] == 0x00)
 		for (i = 0; i < 6; i+=2) {
 			char tmp = dev->dev_addr[i];
 			dev->dev_addr[i] = dev->dev_addr[i+1];
@@ -671,34 +780,37 @@
 		for (i = 0; i < 5; i++)
 			dev->dev_addr[i] = last_phys_addr[i];
 		dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__i386__)		/* This BIOS bug doesn't exist on Alphas. */
-		irq = last_irq;
+#if defined(__i386__)		/* Patch up x86 BIOS bug. */
+		if (last_irq)
+			irq = last_irq;
 #endif
 	}
 
 	for (i = 0; i < 6; i++)
-		printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
+		printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
 	printk(", IRQ %d.\n", irq);
 	last_irq = irq;
 
 	/* We do a request_region() only to register /proc/ioports info. */
-	/* Note that proper size is tulip_tbl[chip_id].chip_name, but... */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name);
+	/* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
+	request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name);
 
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
 
-	/* Make certain the data structures are quadword aligned. */
-	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
-	memset(tp, 0, sizeof(*tp));
-	dev->priv = tp;
-
-	tp->next_module = root_tulip_dev;
-	root_tulip_dev = dev;
-
 	tp->pci_bus = pci_bus;
-	tp->pci_dev_fn = pci_device_fn;
-	tp->chip_id = chip_id;
+	tp->pci_devfn = pci_devfn;
+	tp->chip_id = chip_idx;
+	tp->revision = chip_rev;
+	tp->flags = tulip_tbl[chip_idx].flags;
+	tp->csr0 = csr0;
+
+	/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
+	   And the ASIX must have a burst limit or horrible things happen. */
+	if (chip_idx == DC21143  &&  chip_rev == 65)
+		tp->csr0 &= ~0x01000000;
+	else if (chip_idx == AX88140)
+		tp->csr0 |= 0x2000;
 
 #ifdef TULIP_FULL_DUPLEX
 	tp->full_duplex = 1;
@@ -729,37 +841,58 @@
 	if (tp->full_duplex)
 		tp->full_duplex_lock = 1;
 
-	/* This is logically part of probe1(), but too complex to write inline. */
-	if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE)
-		parse_eeprom(dev);
-
 	if (media_cap[tp->default_port] & MediaIsMII) {
 		u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
 		tp->to_advertise = media2advert[tp->default_port - 9];
-	} else
-		tp->to_advertise = 0x03e1;
+	} else if (tp->flags & HAS_8023X)
+		tp->to_advertise = 0x05e1;
+	else 
+		tp->to_advertise = 0x01e1;
+
+	/* This is logically part of probe1(), but too complex to write inline. */
+	if (tp->flags & HAS_MEDIA_TABLE) {
+		memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom));
+		parse_eeprom(dev);
+	}
 
-	if ((tp->mtable  &&  tp->mtable->has_mii) ||
-		( ! tp->mtable  &&  (tulip_tbl[tp->chip_id].flags & HAS_MII))) {
+	if ((tp->flags & ALWAYS_CHECK_MII) ||
+		(tp->mtable  &&  tp->mtable->has_mii) ||
+		( ! tp->mtable  &&  (tp->flags & HAS_MII))) {
 		int phy, phy_idx;
+		if (tp->mtable  &&  tp->mtable->has_mii) {
+			for (i = 0; i < tp->mtable->leafcount; i++)
+				if (tp->mtable->mleaf[i].media == 11) {
+					tp->cur_index = i;
+					tp->saved_if_port = dev->if_port;
+					select_media(dev, 1);
+					dev->if_port = tp->saved_if_port;
+					break;
+				}
+		}
 		/* Find the connected MII xcvrs.
 		   Doing this in open() would allow detecting external xcvrs later,
 		   but takes much time. */
 		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
 			 phy++) {
 			int mii_status = mdio_read(dev, phy, 1);
-			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+			if ((mii_status & 0x8301) == 0x8001 ||
+				((mii_status & 0x8000) == 0  && (mii_status & 0x7800) != 0)) {
 				int mii_reg0 = mdio_read(dev, phy, 0);
+				int mii_advert = mdio_read(dev, phy, 4);
 				int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
 				tp->phys[phy_idx] = phy;
 				tp->advertising[phy_idx++] = reg4;
-				printk(KERN_INFO "%s:  MII transceiver found at MDIO address "
-					   "%d, config %4.4x status %4.4x.\n",
-					   dev->name, phy, mii_reg0, mii_status);
-				if (1 || (media_cap[tp->default_port] & MediaIsMII)) {
+				printk(KERN_INFO "%s:  MII transceiver #%d "
+					   "config %4.4x status %4.4x advertising %4.4x.\n",
+					   dev->name, phy, mii_reg0, mii_status, mii_advert);
+				/* Fixup for DLink with miswired PHY. */
+				if (mii_advert != reg4) {
 					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
 						   " previously advertising %4.4x.\n",
-						   dev->name, reg4, phy, mdio_read(dev, phy, 4));
+						   dev->name, reg4, phy, mii_advert);
+					printk(KERN_DEBUG "%s:  Advertising %4.4x (to advertise"
+						   " is %4.4x).\n",
+						   dev->name, reg4, tp->to_advertise);
 					mdio_write(dev, phy, 4, reg4);
 				}
 				/* Enable autonegotiation: some boards default to off. */
@@ -788,9 +921,15 @@
 	dev->set_multicast_list = &set_rx_mode;
 #endif
 
+	if ((tp->flags & HAS_NWAY143)  || tp->chip_id == DC21041)
+		tp->link_change = t21142_lnk_change;
+	else if (tp->flags & HAS_PNICNWAY)
+		tp->link_change = pnic_lnk_change;
+
 	/* Reset the xcvr interface and turn on heartbeat. */
-	switch (chip_id) {
+	switch (chip_idx) {
 	case DC21041:
+		tp->to_advertise = 0x0061;
 		outl(0x00000000, ioaddr + CSR13);
 		outl(0xFFFFFFFF, ioaddr + CSR14);
 		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
@@ -806,28 +945,43 @@
 			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
 		break;
 	case DC21142:
-		outl(0x82420200, ioaddr + CSR6);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x0003FFFF, ioaddr + CSR14);
-		outl(0x0008, ioaddr + CSR15);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+	case PNIC2:
+		if (tp->mii_cnt  ||  media_cap[dev->if_port] & MediaIsMII) {
+			outl(0x82020000, ioaddr + CSR6);
+			outl(0x0000, ioaddr + CSR13);
+			outl(0x0000, ioaddr + CSR14);
+			outl(0x820E0000, ioaddr + CSR6);
+		} else
+			t21142_start_nway(dev);
 		break;
 	case LC82C168:
 		if ( ! tp->mii_cnt) {
+			tp->nway = 1;
+			tp->nwayset = 0;
 			outl(0x00420000, ioaddr + CSR6);
 			outl(0x30, ioaddr + CSR12);
 			outl(0x0001F078, ioaddr + 0xB8);
 			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
 		}
 		break;
-	case MX98713: case MX98715: case MX98725:
+	case MX98713: case COMPEX9881:
 		outl(0x00000000, ioaddr + CSR6);
 		outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
 		outl(0x00000001, ioaddr + CSR13);
 		break;
+	case MX98715: case MX98725:
+		outl(0x01a80000, ioaddr + CSR6);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00001000, ioaddr + CSR12);
+		break;
+	case COMET:
+		/* No initialization necessary. */
+		break;
 	}
 
+	if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
+		pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x40000000);
+
 	return dev;
 }
 
@@ -846,29 +1000,37 @@
 } eeprom_fixups[] = {
   {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
 						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
-  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
+  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f,
 							   0x0000, 0x009E, /* 10baseT */
-							   0x0903, 0x006D, /* 100baseTx */ }},
-  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
+							   0x0004, 0x009E, /* 10baseT-FD */
+							   0x0903, 0x006D, /* 100baseTx */
+							   0x0905, 0x006D, /* 100baseTx-FD */ }},
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f,
 								 0x0107, 0x8021, /* 100baseFx */
 								 0x0108, 0x8021, /* 100baseFx-FD */
-								 0x0103, 0x006D, /* 100baseTx */ }},
-  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
+								 0x0100, 0x009E, /* 10baseT */
+								 0x0104, 0x009E, /* 10baseT-FD */
+								 0x0103, 0x006D, /* 100baseTx */
+								 0x0105, 0x006D, /* 100baseTx-FD */ }},
+  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513,
 							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
 							   0x0000, 0x009E, /* 10baseT */
-							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
-  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
-							0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
-							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ 
-							0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+							   0x0004, 0x009E, /* 10baseT-FD */
+							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */
+							   0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}},
+  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F,
+								  0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
+								  0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+								  0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */
+								  0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
+								  0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */
    }},
   {0, 0, 0, 0, {}}};
 
 static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
  "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
 
-#define EEPROM_SIZE 128
-#if defined(__i386__)
+#if defined(__i386__)			/* AKA get_unaligned() */
 #define get_u16(ptr) (*(u16 *)(ptr))
 #else
 #define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
@@ -881,14 +1043,10 @@
 	static unsigned char *last_ee_data = NULL;
 	static int controller_index = 0;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
 	unsigned char *ee_data = tp->eeprom;
 	int i;
 
 	tp->mtable = 0;
-	for (i = 0; i < EEPROM_SIZE/2; i++)
-		((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
-
 	/* Detect an old-style (SA only) EEPROM layout:
 	   memcmp(eedata, eedata+16, 8). */
 	for (i = 0; i < 8; i ++)
@@ -925,20 +1083,13 @@
 		}
 	  }
 	  if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
-		printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n",
+		  printk(KERN_INFO "%s: Old style EEPROM with no media selection "
+				 "information.\n",
 			   dev->name);
 		return;
 	  }
 	}
-	if (tulip_debug > 1) {
-	  printk(KERN_DEBUG "read_eeprom:");
-	  for (i = 0; i < 64; i++) {
-		printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ",
-			   read_eeprom(ioaddr, i));
-	  }
-	  printk("\n");
-	}
-	
+
 	controller_index = 0;
 	if (ee_data[19] > 1) {		/* Multiport board. */
 		last_ee_data = ee_data;
@@ -948,42 +1099,29 @@
 	if (ee_data[27] == 0) {		/* No valid media table. */
 	} else if (tp->chip_id == DC21041) {
 		unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
-		short media;
-		int count;
-
-		media = get_u16(p);
-		p += 2;
-		count = *p++;
+		int media = get_u16(p);
+		int count = p[2];
+		p += 3;
 
-		printk(KERN_INFO "%s:21041 Media information at %d, default media "
-			   "%4.4x (%s).\n", dev->name, ee_data[27], media,
+		printk(KERN_INFO "%s: 21041 Media table, default media %4.4x (%s).\n",
+			   dev->name, media,
 			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
 		for (i = 0; i < count; i++) {
 			unsigned char media_code = *p++;
-			u16 csrvals[3];
-			int idx;
-			for (idx = 0; idx < 3; idx++) {
-				csrvals[idx] = get_u16(p);
-				p += 2;
-			}
-			if (media_code & 0x40) {
-				printk(KERN_INFO "%s:  21041 media %2.2x (%s),"
-					   " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
-					   dev->name, media_code & 15, medianame[media_code & 15],
-					   csrvals[0], csrvals[1], csrvals[2]);
-			} else
-				printk(KERN_INFO "%s:  21041 media #%d, %s.\n",
-					   dev->name, media_code & 15, medianame[media_code & 15]);
+			if (media_code & 0x40)
+				p += 6;
+			printk(KERN_INFO "%s:  21041 media #%d, %s.\n",
+				   dev->name, media_code & 15, medianame[media_code & 15]);
 		}
 	} else {
 		unsigned char *p = (void *)ee_data + ee_data[27];
 		unsigned char csr12dir = 0;
-		int count;
+		int count, new_advertise = 0;
 		struct mediatable *mtable;
 		u16 media = get_u16(p);
 
 		p += 2;
-		if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
+		if (tp->flags & CSR12_IN_SROM)
 			csr12dir = *p++;
 		count = *p++;
 		mtable = (struct mediatable *)
@@ -995,13 +1133,14 @@
 		mtable->defaultmedia = media;
 		mtable->leafcount = count;
 		mtable->csr12dir = csr12dir;
-		mtable->has_nonmii = mtable->has_mii = 0;
+		mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
+		mtable->csr15dir = mtable->csr15val = 0;
 
 		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
 			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
 		for (i = 0; i < count; i++) {
 			struct medialeaf *leaf = &mtable->mleaf[i];
-			
+
 			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
 				leaf->type = 0;
 				leaf->media = p[0] & 0x3f;
@@ -1011,12 +1150,34 @@
 				p += 4;
 			} else {
 				leaf->type = p[1];
-				if (p[1] & 1) {
+				if (p[1] == 0x05) {
+					mtable->has_reset = i;
+					leaf->media = p[2] & 0x0f;
+				} else if (p[1] & 1) {
 					mtable->has_mii = 1;
 					leaf->media = 11;
 				} else {
 					mtable->has_nonmii = 1;
 					leaf->media = p[2] & 0x0f;
+					switch (leaf->media) {
+					case 0: new_advertise |= 0x0020; break;
+					case 4: new_advertise |= 0x0040; break;
+					case 3: new_advertise |= 0x0080; break;
+					case 5: new_advertise |= 0x0100; break;
+					case 6: new_advertise |= 0x0200; break;
+					}
+					if (p[1] == 2  &&  leaf->media == 0) {
+						if (p[2] & 0x40) {
+							u32 base15 = get_unaligned((u16*)&p[7]);
+							mtable->csr15dir =
+								(get_unaligned((u16*)&p[9])<<16) + base15;
+							mtable->csr15val =
+								(get_unaligned((u16*)&p[11])<<16) + base15;
+						} else {
+							mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
+							mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
+						}
+					}
 				}
 				leaf->leafdata = p + 2;
 				p += (p[0] & 0x3f) + 1;
@@ -1025,7 +1186,7 @@
 				unsigned char *bp = leaf->leafdata;
 				printk(KERN_INFO "%s:  MII interface PHY %d, setup/reset "
 					   "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
-					   dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
+					   dev->name, bp[0], bp[1], bp[2 + bp[1]*2],
 					   bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
 			}
 			printk(KERN_INFO "%s:  Index #%d - Media %s (#%d) described "
@@ -1033,6 +1194,8 @@
 				   dev->name, i, medianame[leaf->media], leaf->media,
 				   block_name[leaf->type], leaf->type);
 		}
+		if (new_advertise)
+			tp->to_advertise = new_advertise;
 	}
 }
 /* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
@@ -1040,54 +1203,48 @@
 /*  EEPROM_Ctrl bits. */
 #define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
 #define EE_CS			0x01	/* EEPROM chip select. */
-#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_DATA_WRITE	0x04	/* Data from the Tulip to EEPROM. */
 #define EE_WRITE_0		0x01
 #define EE_WRITE_1		0x05
-#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_DATA_READ	0x08	/* Data from the EEPROM chip. */
 #define EE_ENB			(0x4800 | EE_CS)
 
 /* Delay between EEPROM clock transitions.
-   The 1.2 code is a "nasty" timing loop, but PC compatible machines are
-   *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro.  */
-#ifdef _LINUX_DELAY_H
-#define eeprom_delay(nanosec)	udelay((nanosec + 999)/1000)
-#else
-#define eeprom_delay(nanosec)	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
-#endif
+   Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
+   We add a bus turn-around to insure that this remains true. */
+#define eeprom_delay()	inl(ee_addr)
 
 /* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD	(5 << 6)
-#define EE_READ_CMD		(6 << 6)
-#define EE_ERASE_CMD	(7 << 6)
+#define EE_READ_CMD		(6)
 
-static int read_eeprom(long ioaddr, int location)
+/* Note: this routine returns extra data bits for size detection. */
+static int read_eeprom(long ioaddr, int location, int addr_len)
 {
 	int i;
-	unsigned short retval = 0;
+	unsigned retval = 0;
 	long ee_addr = ioaddr + CSR9;
-	int read_cmd = location | EE_READ_CMD;
-	
+	int read_cmd = location | (EE_READ_CMD << addr_len);
+
 	outl(EE_ENB & ~EE_CS, ee_addr);
 	outl(EE_ENB, ee_addr);
-	
+
 	/* Shift the read command bits out. */
-	for (i = 10; i >= 0; i--) {
+	for (i = 4 + addr_len; i >= 0; i--) {
 		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
 		outl(EE_ENB | dataval, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(150);
-		outl(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
-		eeprom_delay(250);
+		eeprom_delay();
+		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
 	}
 	outl(EE_ENB, ee_addr);
-	
+
 	for (i = 16; i > 0; i--) {
 		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
 		outl(EE_ENB, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 	}
 
 	/* Terminate the EEPROM access. */
@@ -1121,19 +1278,33 @@
 	int i;
 	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
 	int retval = 0;
-	long mdio_addr = dev->base_addr + CSR9;
+	long ioaddr = dev->base_addr;
+	long mdio_addr = ioaddr + CSR9;
 
 	if (tp->chip_id == LC82C168) {
-		long ioaddr = dev->base_addr;
 		int i = 1000;
 		outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
+		inl(ioaddr + 0xA0);
+		inl(ioaddr + 0xA0);
 		while (--i > 0)
 			if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
 				return retval & 0xffff;
 		return 0xffff;
 	}
 
-	/* Establish sync by sending at least 32 logic ones. */ 
+	if (tp->chip_id == COMET) {
+		if (phy_id == 1) {
+			if (location < 7)
+				return inl(ioaddr + 0xB4 + (location<<2));
+			else if (location == 17)
+				return inl(ioaddr + 0xD0);
+			else if (location >= 29 && location <= 31)
+				return inl(ioaddr + 0xD4 + ((location-29)<<2));
+		}
+		return 0xffff;
+	}
+
+	/* Establish sync by sending at least 32 logic ones. */
 	for (i = 32; i >= 0; i--) {
 		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
 		mdio_delay();
@@ -1165,10 +1336,10 @@
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
 	int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
-	long mdio_addr = dev->base_addr + CSR9;
+	long ioaddr = dev->base_addr;
+	long mdio_addr = ioaddr + CSR9;
 
 	if (tp->chip_id == LC82C168) {
-		long ioaddr = dev->base_addr;
 		int i = 1000;
 		outl(cmd, ioaddr + 0xA0);
 		do
@@ -1178,7 +1349,19 @@
 		return;
 	}
 
-	/* Establish sync by sending 32 logic ones. */ 
+	if (tp->chip_id == COMET) {
+		if (phy_id != 1)
+			return;
+		if (location < 7)
+			outl(value, ioaddr + 0xB4 + (location<<2));
+		else if (location == 17)
+			outl(value, ioaddr + 0xD0);
+		else if (location >= 29 && location <= 31)
+			outl(value, ioaddr + 0xD4 + ((location-29)<<2));
+		return;
+	}
+
+	/* Establish sync by sending 32 logic ones. */
 	for (i = 32; i >= 0; i--) {
 		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
 		mdio_delay();
@@ -1209,7 +1392,12 @@
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
-	int i = 0;
+	int next_tick = 3*HZ;
+	int i;
+
+	/* Wake the chip from sleep/snooze mode. */
+	if (tp->flags & HAS_PWRDWN)
+		pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0);
 
 	/* On some chip revs we must set the MII/SYM port before the reset!? */
 	if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii))
@@ -1217,86 +1405,61 @@
 
 	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
 	outl(0x00000001, ioaddr + CSR0);
-#ifdef _LINUX_DELAY_H
-	udelay(2);
-#else
-	SLOW_DOWN_IO;
-#endif
+
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+	MOD_INC_USE_COUNT;
+
 	/* Deassert reset.
-	   486: Set 8 longword cache alignment, 8 longword burst.
-	   586: Set 16 longword cache alignment, no burst limit.
-	   Cache alignment bits 15:14	     Burst length 13:8
-		0000	No alignment  0x00000000 unlimited		0800 8 longwords
-		4000	8  longwords		0100 1 longword		1000 16 longwords
-		8000	16 longwords		0200 2 longwords	2000 32 longwords
-		C000	32  longwords		0400 4 longwords
 	   Wait the specified 50 PCI cycles after a reset by initializing
 	   Tx and Rx queues and the address filter list. */
-#if defined(__alpha__)
-	/* ToDo: Alpha setting could be better. */
-	outl(0x01A00000 | 0xE000, ioaddr + CSR0);
-#elif defined(__powerpc__)
-	outl(0x01A00080 | 0x8000, ioaddr + CSR0);
-#elif defined(__i386__)
-#if defined(MODULE)
-	/* When a module we don't have 'x86' to check. */
-	outl(0x01A00000 | 0x4800, ioaddr + CSR0);
-#else
-#if (LINUX_VERSION_CODE > 0x2014c)
-#define x86 boot_cpu_data.x86
-#endif
-	outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
-	if (x86 <= 4)
-	  printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
-			 "alignment to %x.\n", dev->name,
-			 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000));
-#endif
-#else
-	outl(0x01A00000 | 0x4800, ioaddr + CSR0);
-#warning Processor architecture undefined!
-#endif
-
-#ifdef SA_SHIRQ
-	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) {
-		return -EAGAIN;
-	}
-#else
-	if (irq2dev_map[dev->irq] != NULL
-		|| (irq2dev_map[dev->irq] = dev) == NULL
-		|| dev->irq == 0
-		|| request_irq(dev->irq, &tulip_interrupt, 0,
-					   tulip_tbl[tp->chip_id].chip_name)) {
-		return -EAGAIN;
-	}
-#endif
+	outl(tp->csr0, ioaddr + CSR0);
 
 	if (tulip_debug > 1)
 		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
 
-	MOD_INC_USE_COUNT;
-
 	tulip_init_ring(dev);
 
-	/* This is set_rx_mode(), but without starting the transmitter. */
-	/* Fill the whole address filter table with our physical address. */
-	{
+#if 0
+	if (tp->chip_id == PNIC2) {
+		u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
+		u32 addr_high = cpu_to_le16(get_unaligned((u16 *)(dev->dev_addr+4)));
+		addr_high = (dev->dev_addr[4]<<8) + (dev->dev_addr[5]<<0);
+		outl((dev->dev_addr[0]<<8) + dev->dev_addr[1] +
+			 (dev->dev_addr[2]<<24) + (dev->dev_addr[3]<<16),
+			 ioaddr + 0xB0);
+		outl(addr_high + (addr_high<<16), ioaddr + 0xB8);
+	}
+#endif
+	if (tp->flags & MC_HASH_ONLY) {
+		u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
+		u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4)));
+		if (tp->chip_id == AX88140) {
+			outl(0, ioaddr + CSR13);
+			outl(addr_low,  ioaddr + CSR14);
+			outl(1, ioaddr + CSR13);
+			outl(addr_high, ioaddr + CSR14);
+		} else if (tp->chip_id == COMET) {
+			outl(addr_low,  ioaddr + 0xA4);
+			outl(addr_high, ioaddr + 0xA8);
+			outl(0, ioaddr + 0xAC);
+			outl(0, ioaddr + 0xB0);
+		}
+	} else {
+		/* This is set_rx_mode(), but without starting the transmitter. */
 		u16 *eaddrs = (u16 *)dev->dev_addr;
-		u32 *setup_frm = tp->setup_frame, i;
+		u16 *setup_frm = &tp->setup_frame[15*6];
 
-		/* You must add the broadcast address when doing perfect filtering! */
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
-		/* Fill the rest of the accept table with our physical address. */
-		for (i = 1; i < 16; i++) {
-			*setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2];
-		}
+		/* 21140 bug: you must add the broadcast address. */
+		memset(tp->setup_frame, 0xff, sizeof(tp->setup_frame));
+		/* Fill the final entry of the table with our physical address. */
+		*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+		*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+		*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
 		/* Put the setup frame on the Tx list. */
-		tp->tx_ring[0].length = 0x08000000 | 192;
-		tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
-		tp->tx_ring[0].status = 0x80000000;
+		tp->tx_ring[0].length = cpu_to_le32(0x08000000 | 192);
+		tp->tx_ring[0].buffer1 = virt_to_le32desc(tp->setup_frame);
+		tp->tx_ring[0].status = cpu_to_le32(DescOwned);
 
 		tp->cur_tx++;
 	}
@@ -1304,13 +1467,12 @@
 	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
 	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
 
+	tp->saved_if_port = dev->if_port;
 	if (dev->if_port == 0)
 		dev->if_port = tp->default_port;
-	if (tp->chip_id == DC21041  &&  dev->if_port > 4)
-		/* Invalid: Select initial TP, autosense, autonegotiate.  */
-		dev->if_port = 4;
 
 	/* Allow selecting a default media. */
+	i = 0;
 	if (tp->mtable == NULL)
 		goto media_picked;
 	if (dev->if_port) {
@@ -1323,31 +1485,80 @@
 				goto media_picked;
 			}
 	}
-	if ((tp->mtable->defaultmedia & 0x0800) == 0)
+	if ((tp->mtable->defaultmedia & 0x0800) == 0) {
+		int looking_for = tp->mtable->defaultmedia & 15;
 		for (i = 0; i < tp->mtable->leafcount; i++)
-		  if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) {
-			printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
-				   dev->name, medianame[tp->mtable->mleaf[i].media]);
-			goto media_picked;
-		  }
+			if (tp->mtable->mleaf[i].media == looking_for) {
+				printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
+					   dev->name, medianame[looking_for]);
+				goto media_picked;
+			}
+	}
 	/* Start sensing first non-full-duplex media. */
 	for (i = tp->mtable->leafcount - 1;
 		 (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
-	  ;
+		;
 media_picked:
 
 	tp->csr6 = 0;
 	tp->cur_index = i;
+	tp->nwayset = 0;
+	if (dev->if_port == 0  && tp->chip_id == DC21041) {
+		tp->nway = 1;
+	}
 	if (dev->if_port == 0  &&  tp->chip_id == DC21142) {
-		tp->csr6 = 0x82420200;
-		outl(0x0003FFFF, ioaddr + CSR14);
+		if (tp->mii_cnt) {
+			select_media(dev, 1);
+			if (tulip_debug > 1)
+				printk(KERN_INFO "%s: Using MII transceiver %d, status "
+					   "%4.4x.\n",
+					   dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
+			outl(0x82020000, ioaddr + CSR6);
+			tp->csr6 = 0x820E0000;
+			dev->if_port = 11;
+			outl(0x0000, ioaddr + CSR13);
+			outl(0x0000, ioaddr + CSR14);
+		} else
+			t21142_start_nway(dev);
+	} else if (tp->chip_id == PNIC2) {
+		t21142_start_nway(dev);
+	} else if (tp->chip_id == LC82C168  &&  ! tp->medialock) {
+		if (tp->mii_cnt) {
+			dev->if_port = 11;
+			tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
+			outl(0x0001, ioaddr + CSR15);
+		} else if (inl(ioaddr + CSR5) & TPLnkPass)
+			pnic_do_nway(dev);
+		else {
+			/* Start with 10mbps to do autonegotiation. */
+			outl(0x32, ioaddr + CSR12);
+			tp->csr6 = 0x00420000;
+			outl(0x0001B078, ioaddr + 0xB8);
+			outl(0x0201B078, ioaddr + 0xB8);
+			next_tick = 1*HZ;
+		}
+	} else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881)
+			   && ! tp->medialock) {
+		dev->if_port = 0;
+		tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
+		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+	} else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) {
+		/* Provided by BOLO, Macronix - 12/10/1998. */
+		dev->if_port = 0;
+		tp->csr6 = 0x01a80200;
+		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+		outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
+	} else if (tp->chip_id == DC21143  &&
+			   media_cap[dev->if_port] & MediaIsMII) {
+		/* We must reset the media CSRs when we force-select MII mode. */
+		outl(0x0000, ioaddr + CSR13);
+		outl(0x0000, ioaddr + CSR14);
 		outl(0x0008, ioaddr + CSR15);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x1301, ioaddr + CSR12);
-	} else if (tp->chip_id == LC82C168  &&  tp->mii_cnt && ! tp->medialock) {
-		dev->if_port = 11;
-		tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0);
-		outl(0x0001, ioaddr + CSR15);
+	} else if (tp->chip_id == COMET) {
+		dev->if_port = 0;
+		tp->csr6 = 0x00040000;
+	} else if (tp->chip_id == AX88140) {
+		tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100;
 	} else
 		select_media(dev, 1);
 
@@ -1373,7 +1584,7 @@
 	/* Set the timer to switch to check for link beat and perhaps switch
 	   to an alternate media type. */
 	init_timer(&tp->timer);
-	tp->timer.expires = RUN_AT(5*HZ);
+	tp->timer.expires = RUN_AT(next_tick);
 	tp->timer.data = (unsigned long)dev;
 	tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
 	add_timer(&tp->timer);
@@ -1388,7 +1599,7 @@
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	struct mediatable *mtable = tp->mtable;
 	u32 new_csr6;
-	int check_mii =0, i;
+	int i;
 
 	if (mtable) {
 		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
@@ -1406,33 +1617,56 @@
 			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
 			break;
 		case 2: case 4: {
-			u16 setup[3];
-			for (i = 0; i < 3; i++)
+			u16 setup[5];
+			u32 csr13val, csr14val, csr15dir, csr15val;
+			for (i = 0; i < 5; i++)
 				setup[i] = get_u16(&p[i*2 + 1]);
 
 			dev->if_port = p[0] & 15;
+			if (media_cap[dev->if_port] & MediaAlwaysFD)
+				tp->full_duplex = 1;
+
+			if (startup && mtable->has_reset) {
+				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
+				unsigned char *rst = rleaf->leafdata;
+				if (tulip_debug > 1)
+					printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
+						   dev->name);
+				for (i = 0; i < rst[0]; i++)
+					outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
+			}
 			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
+				printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control "
+					   "%4.4x/%4.4x.\n",
 					   dev->name, medianame[dev->if_port], setup[0], setup[1]);
 			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
+				csr13val = setup[0];
+				csr14val = setup[1];
+				csr15dir = (setup[3]<<16) | setup[2];
+				csr15val = (setup[4]<<16) | setup[2];
 				outl(0, ioaddr + CSR13);
-				outl(setup[1], ioaddr + CSR14);
-				outl(setup[2], ioaddr + CSR15);
-				outl(setup[0], ioaddr + CSR13);
-				for (i = 0; i < 3; i++)			/* Re-fill setup[]  */
-					setup[i] = get_u16(&p[i*2 + 7]);
-			} else if (dev->if_port <= 4) {
-				outl(0, ioaddr + CSR13);
-				outl(t21142_csr14[dev->if_port], ioaddr + CSR14);
-				outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
-				outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+				outl(csr14val, ioaddr + CSR14);
+				outl(csr15dir, ioaddr + CSR15);	/* Direction */
+				outl(csr15val, ioaddr + CSR15);	/* Data */
+				outl(csr13val, ioaddr + CSR13);
 			} else {
-				outl(0, ioaddr + CSR14);
-				outl(8, ioaddr + CSR15);
-				outl(0, ioaddr + CSR13);
+				csr13val = 1;
+				csr14val = 0x0003FF7F;
+				csr15dir = (setup[0]<<16) | 0x0008;
+				csr15val = (setup[1]<<16) | 0x0008;
+				if (dev->if_port <= 4)
+					csr14val = t21142_csr14[dev->if_port];
+				if (startup) {
+					outl(0, ioaddr + CSR13);
+					outl(csr14val, ioaddr + CSR14);
+				}
+				outl(csr15dir, ioaddr + CSR15);	/* Direction */
+				outl(csr15val, ioaddr + CSR15);	/* Data */
+				if (startup) outl(csr13val, ioaddr + CSR13);
 			}
-			outl(setup[0]<<16, ioaddr + CSR15);	/* Direction */
-			outl(setup[1]<<16, ioaddr + CSR15);	/* Data */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s:  Setting CSR15 to %8.8x/%8.8x.\n",
+					   dev->name, csr15dir, csr15val);
 			if (mleaf->type == 4)
 				new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
 			else
@@ -1446,7 +1680,6 @@
 			u16 to_advertise;
 
 			dev->if_port = 11;
-			check_mii = 1;
 			new_csr6 = 0x020E0000;
 			if (mleaf->type == 3) {	/* 21142 */
 				u16 *init_sequence = (u16*)(p+2);
@@ -1473,7 +1706,7 @@
 			}
 			to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
 			tp->advertising[phy_num] = to_advertise;
-			if (tulip_debug > 1 || 1)
+			if (tulip_debug > 1)
 				printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d (%d).\n",
 					   dev->name, to_advertise, phy_num, tp->phys[phy_num]);
 			/* Bogus: put in by a committee?  */
@@ -1481,32 +1714,33 @@
 			break;
 		}
 		default:
-		  new_csr6 = 0x020E0000;
+			printk(KERN_DEBUG "%s:  Invalid media table selection %d.\n",
+					   dev->name, mleaf->type);
+			new_csr6 = 0x020E0000;
 		}
 		if (tulip_debug > 1)
 			printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
 				   dev->name, medianame[dev->if_port],
 				   inl(ioaddr + CSR12) & 0xff);
 	} else if (tp->chip_id == DC21041) {
+		int port = dev->if_port <= 4 ? dev->if_port : 0;
 		if (tulip_debug > 1)
 			printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
-				   dev->name, medianame[dev->if_port & 15],
-				   inl(ioaddr + CSR12) & 0xffff);
+				   dev->name, medianame[port == 3 ? 12: port],
+				   inl(ioaddr + CSR12));
 		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
-		outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
-		outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
-		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+		outl(t21041_csr14[port], ioaddr + CSR14);
+		outl(t21041_csr15[port], ioaddr + CSR15);
+		outl(t21041_csr13[port], ioaddr + CSR13);
 		new_csr6 = 0x80020000;
 	} else if (tp->chip_id == LC82C168) {
 		if (startup && ! tp->medialock)
 			dev->if_port = tp->mii_cnt ? 11 : 0;
 		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x,"
-				   " media %s.\n",
-				   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
-				   medianame[dev->if_port]);
+			printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, media %s.\n",
+				   dev->name, inl(ioaddr + 0xB8), medianame[dev->if_port]);
 		if (tp->mii_cnt) {
-			new_csr6 = 0x812C0000;
+			new_csr6 = 0x810C0000;
 			outl(0x0001, ioaddr + CSR15);
 			outl(0x0201B07A, ioaddr + 0xB8);
 		} else if (startup) {
@@ -1518,10 +1752,8 @@
 		} else if (dev->if_port == 3  ||  dev->if_port == 5) {
 			outl(0x33, ioaddr + CSR12);
 			new_csr6 = 0x01860000;
-			if (startup)
-				outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
-			else
-				outl(0x1F868, ioaddr + 0xB8);
+			/* Trigger autonegotiation. */
+			outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8);
 		} else {
 			outl(0x32, ioaddr + CSR12);
 			new_csr6 = 0x00420000;
@@ -1532,18 +1764,24 @@
 		int csr12 = inl(ioaddr + CSR12);
 		if (tulip_debug > 1)
 			printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
-				   dev->name, dev->if_port ? "AUI" : "10baseT", csr12);
-		new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000);
+				   dev->name, medianame[dev->if_port], csr12);
+		if (media_cap[dev->if_port] & MediaAlwaysFD)
+			tp->full_duplex = 1;
+		new_csr6 = 0x20000;
 		/* Set the full duplux match frame. */
 		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
 		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
-		outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+		if (t21040_csr13[dev->if_port] & 8) {
+			outl(0x0705, ioaddr + CSR14);
+			outl(0x0006, ioaddr + CSR15);
+		} else {
+			outl(0xffff, ioaddr + CSR14);
+			outl(0x0000, ioaddr + CSR15);
+		}
+		outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13);
 	} else {					/* Unknown chip type with no media table. */
 		if (tp->default_port == 0)
-			if (tp->mii_cnt) {
-				dev->if_port = 11;
-			} else
-				dev->if_port = 3;
+			dev->if_port = tp->mii_cnt ? 11 : 3;
 		if (media_cap[dev->if_port] & MediaIsMII) {
 			new_csr6 = 0x020E0000;
 		} else if (media_cap[dev->if_port] & MediaIsFx) {
@@ -1561,28 +1799,80 @@
 	return;
 }
 
-static void tulip_timer(unsigned long data)
+/*
+  Check the MII negotiated duplex, and change the CSR6 setting if
+  required.
+  Return 0 if everything is OK.
+  Return < 0 if the transceiver is missing or has no link beat.
+  */
+static int check_duplex(struct device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int mii_reg1, mii_reg5, negotiated, duplex;
+
+	if (tp->full_duplex_lock)
+		return 0;
+	mii_reg1 = mdio_read(dev, tp->phys[0], 1);
+	mii_reg5 = mdio_read(dev, tp->phys[0], 5);
+	if (tulip_debug > 1)
+		printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
+			   "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
+	if (mii_reg1 == 0xffff)
+		return -2;
+	if ((mii_reg1 & 0x0004) == 0) {
+		int new_reg1 = mdio_read(dev, tp->phys[0], 1);
+		if ((new_reg1 & 0x0004) == 0) {
+			if (tulip_debug  > 1)
+				printk(KERN_INFO "%s: No link beat on the MII interface,"
+					   " status %4.4x.\n", dev->name, new_reg1);
+			return -1;
+		}
+	}
+	negotiated = mii_reg5 & tp->advertising[0];
+	duplex = ((negotiated & 0x0300) == 0x0100
+			  || (negotiated & 0x00C0) == 0x0040);
+	/* 100baseTx-FD  or  10T-FD, but not 100-HD */
+	if (tp->full_duplex != duplex) {
+		tp->full_duplex = duplex;
+		if (negotiated & 0x038)	/* 100mbps. */
+			tp->csr6 &= ~0x00400000;
+		if (tp->full_duplex) tp->csr6 |= 0x0200;
+		else				 tp->csr6 &= ~0x0200;
+		outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		if (tulip_debug > 0)
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+				   "#%d link partner capability of %4.4x.\n",
+				   dev->name, tp->full_duplex ? "full" : "half",
+				   tp->phys[0], mii_reg5);
+		return 1;
+	}
+	return 0;
+}
+
+static void tulip_timer(unsigned long data)
 {
 	struct device *dev = (struct device *)data;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	u32 csr12 = inl(ioaddr + CSR12);
-	int next_tick = 0;
+	int next_tick = 2*HZ;
 
-	if (tulip_debug > 3) {
-		printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
-			   "SIA %8.8x %8.8x %8.8x %8.8x.\n",
-			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
-			   csr12, inl(ioaddr + CSR13),
+	if (tulip_debug > 2) {
+		printk(KERN_DEBUG "%s: Media selection tick, %s, status %8.8x mode"
+			   " %8.8x SIA %8.8x %8.8x %8.8x %8.8x.\n",
+			   dev->name, medianame[dev->if_port], inl(ioaddr + CSR5),
+			   inl(ioaddr + CSR6), csr12, inl(ioaddr + CSR13),
 			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
 	}
 	switch (tp->chip_id) {
 	case DC21040:
-		if (csr12 & 0x0002) { /* Network error */
-			printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n",
-				   dev->name, dev->if_port ? "10baseT" : "AUI");
-			dev->if_port ^= 1;
-			outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+		if (!tp->medialock  &&  csr12 & 0x0002) { /* Network error */
+			printk(KERN_INFO "%s: No link beat found.\n",
+				   dev->name);
+			dev->if_port = (dev->if_port == 2 ? 0 : 2);
+			select_media(dev, 0);
 			dev->trans_start = jiffies;
 		}
 		break;
@@ -1590,6 +1880,7 @@
 		if (tulip_debug > 2)
 			printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n",
 				   dev->name, csr12);
+		if (tp->medialock) break;
 		switch (dev->if_port) {
 		case 0: case 3: case 4:
 		  if (csr12 & 0x0004) { /*LnkFail */
@@ -1611,25 +1902,26 @@
 		  break;
 		case 1:					/* 10base2 */
 		case 2:					/* AUI */
-		  if (csr12 & 0x0100) {
-			next_tick = (30*HZ);			/* 30 sec. */
-			tp->mediasense = 0;
-		  } else if ((csr12 & 0x0004) == 0) {
-			printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name);
-			dev->if_port = 0;
-			select_media(dev, 0);
-			next_tick = (24*HZ)/10;				/* 2.4 sec. */
-		  } else if (tp->mediasense || (csr12 & 0x0002)) {
-			dev->if_port = 3 - dev->if_port; /* Swap ports. */
-			select_media(dev, 0);
-			next_tick = 20*HZ;
-		  } else {
-			next_tick = 20*HZ;
-		  }
-		  break;
+			if (csr12 & 0x0100) {
+				next_tick = (30*HZ);			/* 30 sec. */
+				tp->mediasense = 0;
+			} else if ((csr12 & 0x0004) == 0) {
+				printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n",
+					   dev->name);
+				dev->if_port = 0;
+				select_media(dev, 0);
+				next_tick = (24*HZ)/10;				/* 2.4 sec. */
+			} else if (tp->mediasense || (csr12 & 0x0002)) {
+				dev->if_port = 3 - dev->if_port; /* Swap ports. */
+				select_media(dev, 0);
+				next_tick = 20*HZ;
+			} else {
+				next_tick = 20*HZ;
+			}
+			break;
 		}
 		break;
-	case DC21140:  case DC21142: case MX98713: default: {
+	case DC21140:  case DC21142: case MX98713: case COMPEX9881: default: {
 		struct medialeaf *mleaf;
 		unsigned char *p;
 		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
@@ -1698,53 +1990,11 @@
 			next_tick = (24*HZ)/10;
 			break;
 		}
-		case 1:  case 3: {		/* 21140, 21142 MII */
-			int mii_reg1, mii_reg5;
+		case 1:  case 3:		/* 21140, 21142 MII */
 		actually_mii:
-			mii_reg1 = mdio_read(dev, tp->phys[0], 1);
-			mii_reg5 = mdio_read(dev, tp->phys[0], 5);
-			if (tulip_debug > 1)
-				printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
-					   "%4.4x, CSR12 %2.2x, %cD.\n",
-					   dev->name, mii_reg1, mii_reg5, csr12,
-					   tp->full_duplex ? 'F' : 'H');
-			if (mii_reg1 != 0xffff  &&  (mii_reg1 & 0x0004) == 0) {
-				int new_reg1 = mdio_read(dev, tp->phys[0], 1);
-				if ((new_reg1 & 0x0004) == 0) {
-					printk(KERN_INFO "%s: No link beat on the MII interface,"
-						   " status then %4.4x now %4.4x.\n",
-						   dev->name, mii_reg1, new_reg1);
-					if (tp->mtable  &&  tp->mtable->has_nonmii)
-						goto select_next_media;
-				}
-			}
-			if (mii_reg5 == 0xffff  ||  mii_reg5 == 0x0000)
-				;				/* No MII device or no link partner report */
-			else if (tp->full_duplex_lock)
-				;
-			else {
-				int negotiated = mii_reg5 & tp->advertising[0];
-				int duplex = ((negotiated & 0x0100) != 0
-							  || (negotiated & 0x00C0) == 0x0040);
-				/* 100baseTx-FD  or  10T-FD, but not 100-HD */
-				if (tp->full_duplex != duplex) {
-					tp->full_duplex = duplex;
-					if (tp->full_duplex)
-						tp->csr6 |= 0x0200;
-					else
-						tp->csr6 &= ~0x0200;
-					outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-					outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-					if (tulip_debug > 0) /* Gurppp, should be >1 */
-						printk(KERN_INFO "%s: Setting %s-duplex based on MII"
-							   " Xcvr #%d parter capability of %4.4x.\n",
-							   dev->name, tp->full_duplex ? "full" : "half",
-							   tp->phys[0], mii_reg5);
-				}
-			}
+			check_duplex(dev);
 			next_tick = 60*HZ;
 			break;
-		}
 		case 2:					/* 21142 serial block has no link beat. */
 		default:
 			break;
@@ -1752,10 +2002,8 @@
 	}
 	break;
 	}
-	if (next_tick) {
-		tp->timer.expires = RUN_AT(next_tick);
-		add_timer(&tp->timer);
-	}
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
 }
 
 /* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
@@ -1769,49 +2017,50 @@
 	int next_tick = 60*HZ;
 	int new_csr6 = 0;
 
-	if (tulip_debug > 1)
-		printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n",
+	if (tulip_debug > 2)
+		printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
 			   dev->name, csr12, medianame[dev->if_port]);
-	if (dev->if_port == 3) {
-		if (csr12 & 2) {		/* No 100mbps link beat, revert to 10mbps. */
-			new_csr6 = 0x82420200;
-			outl(new_csr6, ioaddr + CSR6);
-			outl(0x0000, ioaddr + CSR13);
-			outl(0x0003FFFF, ioaddr + CSR14);
-			outl(0x0008, ioaddr + CSR15);
-			outl(0x0001, ioaddr + CSR13);
-			outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		check_duplex(dev);
+		next_tick = 60*HZ;
+	} else if (tp->nwayset) {
+		/* Don't screw up a negotiated session! */
+		if (tulip_debug > 1)
+			printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n",
+				   dev->name, medianame[dev->if_port], csr12);
+	} else if (tp->medialock) {
+			;
+	} else if (dev->if_port == 3) {
+		if (csr12 & 2) {	/* No 100mbps link beat, revert to 10mbps. */
+			if (tulip_debug > 1)
+				printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, "
+					   "trying NWay.\n", dev->name, csr12);
+			t21142_start_nway(dev);
+			next_tick = 3*HZ;
 		}
 	} else if ((csr12 & 0x7000) != 0x5000) {
 		/* Negotiation failed.  Search media types. */
 		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n",
+			printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
 				   dev->name, csr12);
 		if (!(csr12 & 4)) {		/* 10mbps link beat good. */
 			new_csr6 = 0x82420000;
 			dev->if_port = 0;
 			outl(0, ioaddr + CSR13);
 			outl(0x0003FFFF, ioaddr + CSR14);
-			outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
+			outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
 			outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
-		} else if (csr12 & 0x100) {
-			new_csr6 = 0x82420200;
-			dev->if_port = 2;
-			outl(0, ioaddr + CSR13);
-			outl(0x0003FFFF, ioaddr + CSR14);
-			outl(0x0008, ioaddr + CSR15);
-			outl(0x0001, ioaddr + CSR13);
 		} else {
 			/* Select 100mbps port to check for link beat. */
 			new_csr6 = 0x83860000;
 			dev->if_port = 3;
 			outl(0, ioaddr + CSR13);
 			outl(0x0003FF7F, ioaddr + CSR14);
-			outl(8, ioaddr + CSR15);
+			outw(8, ioaddr + CSR15);
 			outl(1, ioaddr + CSR13);
 		}
 		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: Testing new 21142 media %s.\n",
+			printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
 				   dev->name, medianame[dev->if_port]);
 		if (new_csr6 != (tp->csr6 & ~0x00D5)) {
 			tp->csr6 &= 0x00D5;
@@ -1820,51 +2069,147 @@
 			outl(tp->csr6 | 0x0002, ioaddr + CSR6);
 			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 		}
+		next_tick = 3*HZ;
+	}
+	if (tp->cur_tx - tp->dirty_tx > 0  &&
+		jiffies - dev->trans_start > TX_TIMEOUT) {
+		printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n",
+			   dev->name, tp->cur_tx, tp->dirty_tx);
+		tulip_tx_timeout(dev);
 	}
+
 	tp->timer.expires = RUN_AT(next_tick);
 	add_timer(&tp->timer);
 }
 
-static void t21142_lnk_change( struct device *dev)
+static void t21142_start_nway(struct device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int csr14 = ((tp->to_advertise & 0x0780) << 9)  |
+		((tp->to_advertise&0x0020)<<1) | 0xffbf;
+
+	dev->if_port = 0;
+	tp->nway = tp->mediasense = 1;
+	tp->nwayset = tp->lpar = 0;
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n",
+			   dev->name, csr14);
+	outl(0x0001, ioaddr + CSR13);
+	outl(csr14, ioaddr + CSR14);
+	tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
+	outl(tp->csr6, ioaddr + CSR6);
+	if (tp->mtable  &&  tp->mtable->csr15dir) {
+		outl(tp->mtable->csr15dir, ioaddr + CSR15);
+		outl(tp->mtable->csr15val, ioaddr + CSR15);
+	} else
+		outw(0x0008, ioaddr + CSR15);
+	outl(0x1301, ioaddr + CSR12); 		/* Trigger NWAY. */
+}
+
+static void t21142_lnk_change(struct device *dev, int csr5)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	int csr12 = inl(ioaddr + CSR12);
 
 	if (tulip_debug > 1)
-		printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n",
-			   dev->name, csr12, inl(ioaddr + CSR5));
+		printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
+			   "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
 
-	if ((csr12 & 0x7000) == 0x5000) {
-		if (csr12 & 0x01800000) {
-			/* Switch to 100mbps mode. */
-			outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-			if (csr12 & 0x01000000) {
-				dev->if_port = 5;
-				tp->csr6 = 0x83860200;
-			} else {
+	/* If NWay finished and we have a negotiated partner capability. */
+	if (tp->nway  &&  !tp->nwayset  &&  (csr12 & 0x7000) == 0x5000) {
+		int setup_done = 0;
+		int negotiated = tp->to_advertise & (csr12 >> 16);
+		tp->lpar = csr12 >> 16;
+		tp->nwayset = 1;
+		if (negotiated & 0x0100)		dev->if_port = 5;
+		else if (negotiated & 0x0080)	dev->if_port = 3;
+		else if (negotiated & 0x0040)	dev->if_port = 4;
+		else if (negotiated & 0x0020)	dev->if_port = 0;
+		else {
+			tp->nwayset = 0;
+			if ((csr12 & 2) == 0  &&  (tp->to_advertise & 0x0180))
 				dev->if_port = 3;
-				tp->csr6 = 0x83860000;
-			}
-			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-		} /* Else 10baseT-FD is handled automatically. */
-	} else if (dev->if_port == 3) {
-		if (!(csr12 & 2))
-			printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n",
-				   dev->name);
-		else
-			dev->if_port = 0;
-	} else if (dev->if_port == 0) {
-		if (!(csr12 & 4))
-			printk(KERN_INFO"%s: 21142 10baseT link beat good.\n",
+		}
+		tp->full_duplex = (media_cap[dev->if_port] & MediaAlwaysFD) ? 1:0;
+
+		if (tulip_debug > 1) {
+			if (tp->nwayset)
+				printk(KERN_INFO "%s: Switching to %s based on link "
+					   "negotiation %4.4x & %4.4x = %4.4x.\n",
+					   dev->name, medianame[dev->if_port], tp->to_advertise,
+					   tp->lpar, negotiated);
+			else
+				printk(KERN_INFO "%s: Autonegotiation failed, using %s,"
+					   " link beat status %4.4x.\n",
+					   dev->name, medianame[dev->if_port], csr12);
+		}
+
+		if (tp->mtable) {
+			int i;
+			for (i = 0; i < tp->mtable->leafcount; i++)
+				if (tp->mtable->mleaf[i].media == dev->if_port) {
+					tp->cur_index = i;
+					select_media(dev, 0);
+					setup_done = 1;
+					break;
+				}
+		}
+		if ( ! setup_done) {
+			tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000;
+			if (tp->full_duplex)
+				tp->csr6 |= 0x0200;
+			outl(1, ioaddr + CSR13);
+		}
+#if 0							/* Restart shouldn't be needed. */
+		outl(tp->csr6 | 0x0000, ioaddr + CSR6);
+		if (debug > 2)
+			printk(KERN_DEBUG "%s:  Restarting Tx and Rx, CSR5 is %8.8x.\n",
+				   dev->name, inl(ioaddr + CSR5));
+#endif
+		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		if (debug > 2)
+			printk(KERN_DEBUG "%s:  Setting CSR6 %8.8x/%x CSR12 %8.8x.\n",
+				   dev->name, tp->csr6, inl(ioaddr + CSR6),
+				   inl(ioaddr + CSR12));
+	} else if ((tp->nwayset  &&  (csr5 & 0x08000000)
+				&& (dev->if_port == 3  ||  dev->if_port == 5)
+				&& (csr12 & 2) == 2) ||
+			   (tp->nway && (csr5 & (TPLnkFail)))) {
+		/* Link blew? Maybe restart NWay. */
+		del_timer(&tp->timer);
+		t21142_start_nway(dev);
+		tp->timer.expires = RUN_AT(3*HZ);
+		add_timer(&tp->timer);
+	} else if (dev->if_port == 3  ||  dev->if_port == 5) {
+		if (tulip_debug > 1)
+			printk(KERN_INFO"%s: 21143 %s link beat %s.\n",
+				   dev->name, medianame[dev->if_port],
+				   (csr12 & 2) ? "failed" : "good");
+		if ((csr12 & 2)  &&  ! tp->medialock) {
+			del_timer(&tp->timer);
+			t21142_start_nway(dev);
+			tp->timer.expires = RUN_AT(3*HZ);
+			add_timer(&tp->timer);
+		}
+	} else if (dev->if_port == 0  ||  dev->if_port == 4) {
+		if ((csr12 & 4) == 0)
+			printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
 				   dev->name);
 	} else if (!(csr12 & 4)) {		/* 10mbps link beat good. */
-			printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n",
+		if (tulip_debug)
+			printk(KERN_INFO"%s: 21143 10mbps sensed media.\n",
+				   dev->name);
+		dev->if_port = 0;
+	} else if (tp->nwayset) {
+		if (tulip_debug)
+			printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n",
+				   dev->name, medianame[dev->if_port], tp->csr6);
+	} else {		/* 100mbps link beat good. */
+		if (tulip_debug)
+			printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
 				   dev->name);
-			dev->if_port = 0;
-	} else  {		/* 100mbps link beat good. */
-		printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n",
-			   dev->name);
 		dev->if_port = 3;
 		tp->csr6 = 0x83860000;
 		outl(0x0003FF7F, ioaddr + CSR14);
@@ -1873,7 +2218,6 @@
 		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 	}
 }
-	
 
 static void mxic_timer(unsigned long data)
 {
@@ -1892,57 +2236,98 @@
 	}
 }
 
+static void pnic_do_nway(struct device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	u32 phy_reg = inl(ioaddr + 0xB8);
+	u32 new_csr6 = tp->csr6 & ~0x40C40200;
+
+	if (phy_reg & 0x78000000) { /* Ignore baseT4 */
+		if (phy_reg & 0x20000000)		dev->if_port = 5;
+		else if (phy_reg & 0x40000000)	dev->if_port = 3;
+		else if (phy_reg & 0x10000000)	dev->if_port = 4;
+		else if (phy_reg & 0x08000000)	dev->if_port = 0;
+		tp->nwayset = 1;
+		new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000;
+		outl(0x32 | (dev->if_port & 1), ioaddr + CSR12);
+		if (dev->if_port & 1)
+			outl(0x1F868, ioaddr + 0xB8);
+		if (phy_reg & 0x30000000) {
+			tp->full_duplex = 1;
+			new_csr6 |= 0x00000200;
+		}
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: PNIC autonegotiated status %8.8x, %s.\n",
+				   dev->name, phy_reg, medianame[dev->if_port]);
+		if (tp->csr6 != new_csr6) {
+			tp->csr6 = new_csr6;
+			outl(tp->csr6 | 0x0002, ioaddr + CSR6);	/* Restart Tx */
+			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			dev->trans_start = jiffies;
+		}
+	}
+}
+static void pnic_lnk_change(struct device *dev, int csr5)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int phy_reg = inl(ioaddr + 0xB8);
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: PNIC link changed state %8.8x, CSR5 %8.8x.\n",
+			   dev->name, phy_reg, csr5);
+	if (inl(ioaddr + CSR5) & TPLnkFail) {
+		outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7);
+		if (! tp->nwayset  ||  jiffies - dev->trans_start > 1*HZ) {
+			tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff);
+			outl(tp->csr6, ioaddr + CSR6);
+			outl(0x30, ioaddr + CSR12);
+			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+			dev->trans_start = jiffies;
+		}
+	} else if (inl(ioaddr + CSR5) & TPLnkPass) {
+		pnic_do_nway(dev);
+		outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7);
+	}
+}
 static void pnic_timer(unsigned long data)
 {
 	struct device *dev = (struct device *)data;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
-	int csr12 = inl(ioaddr + CSR12);
 	int next_tick = 60*HZ;
-	int new_csr6 = tp->csr6 & ~0x40C40200;
 
 	if (media_cap[dev->if_port] & MediaIsMII) {
-		int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0];
-
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: LC82C168 negotiated capability %8.8x, "
-				   "CSR5 %8.8x.\n",
-				   dev->name, negotiated, inl(ioaddr + CSR5));
-
-		if (negotiated & 0x0380) 				/* 10 vs 100mbps */
-			new_csr6 |= 0x812E0000;
-		else
-			new_csr6 |= 0x816E0000;
-		if (((negotiated & 0x0300) == 0x0100)			/* Duplex */
-			|| (negotiated & 0x00C0) == 0x0040
-			|| tp->full_duplex_lock) {
-			tp->full_duplex = 1;
-			new_csr6 |= 0x0200;
-		}
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: LC82C168 MII PHY status %4.4x, Link "
-				   "partner report %4.4x, csr6 %8.8x/%8.8x.\n",
-			   dev->name, mdio_read(dev, tp->phys[0], 1), negotiated,
-				   tp->csr6, inl(ioaddr + CSR6));
+		if (check_duplex(dev) > 0)
+			next_tick = 3*HZ;
 	} else {
+		int csr12 = inl(ioaddr + CSR12);
+		int new_csr6 = tp->csr6 & ~0x40C40200;
 		int phy_reg = inl(ioaddr + 0xB8);
 		int csr5 = inl(ioaddr + CSR5);
 
 		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
-				   dev->name, phy_reg, csr5);
-
+			printk(KERN_DEBUG "%s: PNIC timer PHY status %8.8x, %s "
+				   "CSR5 %8.8x.\n",
+				   dev->name, phy_reg, medianame[dev->if_port], csr5);
 		if (phy_reg & 0x04000000) {	/* Remote link fault */
-			/*outl(0x0201F078, ioaddr + 0xB8);*/
-			next_tick = 3*HZ;
-		}
-		if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+			outl(0x0201F078, ioaddr + 0xB8);
+			next_tick = 1*HZ;
+			tp->nwayset = 0;
+		} else if (phy_reg & 0x78000000) { /* Ignore baseT4 */
+			pnic_do_nway(dev);
+			next_tick = 60*HZ;
+		} else if (csr5 & TPLnkFail) { /* 100baseTx link beat */
 			if (tulip_debug > 1)
 				printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
 					   "CSR5 %8.8x, PHY %3.3x.\n",
 					   dev->name, medianame[dev->if_port], csr12,
 					   inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
+			next_tick = 3*HZ;
 			if (tp->medialock) {
+			} else if (tp->nwayset  &&  (dev->if_port & 1)) {
+				next_tick = 1*HZ;
 			} else if (dev->if_port == 0) {
 				dev->if_port = 3;
 				outl(0x33, ioaddr + CSR12);
@@ -1954,121 +2339,144 @@
 				new_csr6 = 0x00420000;
 				outl(0x1F078, ioaddr + 0xB8);
 			}
-			new_csr6 |= (tp->csr6 & 0xfdff);
-			next_tick = 3*HZ;
-		} else
-			new_csr6 = tp->csr6;
-		if (tp->full_duplex_lock  ||  (phy_reg & 0x30000000) != 0) {
-			tp->full_duplex = 1;
-			new_csr6 |= 0x00000200;
+			if (tp->csr6 != new_csr6) {
+				tp->csr6 = new_csr6;
+				outl(tp->csr6 | 0x0002, ioaddr + CSR6);	/* Restart Tx */
+				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+				dev->trans_start = jiffies;
+				if (tulip_debug > 1)
+					printk(KERN_INFO "%s: Changing PNIC configuration to %s "
+						   "%s-duplex, CSR6 %8.8x.\n",
+						   dev->name, medianame[dev->if_port],
+						   tp->full_duplex ? "full" : "half", new_csr6);
+			}
 		}
 	}
-	if (tp->csr6 != new_csr6) {
-		tp->csr6 = new_csr6;
-		outl(tp->csr6 | 0x0002, ioaddr + CSR6);	/* Restart Tx */
-		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-		dev->trans_start = jiffies;
-		if (tulip_debug > 0) /* Gurppp, should be >1 */
-			printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
-				   "CSR6 %8.8x.\n",
-				   dev->name, tp->full_duplex ? "full" : "half", new_csr6);
-	}
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
+}
+
+static void comet_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int next_tick = 60*HZ;
+
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability "
+			   "%4.4x.\n",
+			   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8));
 	tp->timer.expires = RUN_AT(next_tick);
 	add_timer(&tp->timer);
 }
 
 static void tulip_tx_timeout(struct device *dev)
 {
-  struct tulip_private *tp = (struct tulip_private *)dev->priv;
-  long ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 
-  if (media_cap[dev->if_port] & MediaIsMII) {
-	  /* Do nothing -- the media monitor should handle this. */
-	  if (tulip_debug > 1)
-		  printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
-				 dev->name);
-	  dev->trans_start = jiffies;
-	  return;
-  } else if (tp->chip_id == DC21040) {
-	  if (inl(ioaddr + CSR12) & 0x0002) {
-		  printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n",
-				 dev->name, dev->if_port ? "10baseT" : "AUI");
-		  dev->if_port ^= 1;
-		  outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
-	  }
-	  dev->trans_start = jiffies;
-	  return;
-  } else if (tp->chip_id == DC21041) {
-	u32 csr12 = inl(ioaddr + CSR12);
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		/* Do nothing -- the media monitor should handle this. */
+		if (tulip_debug > 1)
+			printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
+				   dev->name);
+	} else if (tp->chip_id == DC21040) {
+		if ( !tp->medialock  &&  inl(ioaddr + CSR12) & 0x0002) {
+			dev->if_port = (dev->if_port == 2 ? 0 : 2);
+			printk(KERN_INFO "%s: transmit timed out, switching to "
+				   "%s.\n",
+				   dev->name, medianame[dev->if_port]);
+			select_media(dev, 0);
+		}
+		dev->trans_start = jiffies;
+		return;
+	} else if (tp->chip_id == DC21041) {
+		int csr12 = inl(ioaddr + CSR12);
+
+		printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, "
+			   "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), csr12,
+			   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
+		tp->mediasense = 1;
+		if ( ! tp->medialock) {
+			if (dev->if_port == 1 || dev->if_port == 2)
+				if (csr12 & 0x0004) {
+					dev->if_port = 2 - dev->if_port;
+				} else
+					dev->if_port = 0;
+			else
+				dev->if_port = 1;
+			select_media(dev, 0);
+		}
+	} else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
+			   || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) {
+		printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
+			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+			   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+		if ( ! tp->medialock  &&  tp->mtable) {
+			do
+				--tp->cur_index;
+			while (tp->cur_index >= 0
+				   && (media_cap[tp->mtable->mleaf[tp->cur_index].media]
+					   & MediaIsFD));
+			if (--tp->cur_index < 0) {
+				/* We start again, but should instead look for default. */
+				tp->cur_index = tp->mtable->leafcount - 1;
+			}
+			select_media(dev, 0);
+			printk(KERN_WARNING "%s: transmit timed out, switching to %s "
+				   "media.\n", dev->name, medianame[dev->if_port]);
+		}
+	} else {
+		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 "
+			   "%8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
+		dev->if_port = 0;
+	}
+
+#if defined(way_too_many_messages)
+	if (tulip_debug > 3) {
+		int i;
+		for (i = 0; i < RX_RING_SIZE; i++) {
+			u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
+			int j;
+			printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x  "
+				   "%2.2x %2.2x %2.2x.\n",
+				   i, (unsigned int)tp->rx_ring[i].status,
+				   (unsigned int)tp->rx_ring[i].length,
+				   (unsigned int)tp->rx_ring[i].buffer1,
+				   (unsigned int)tp->rx_ring[i].buffer2,
+				   buf[0], buf[1], buf[2]);
+			for (j = 0; buf[j] != 0xee && j < 1600; j++)
+				if (j < 100) printk(" %2.2x", buf[j]);
+			printk(" j=%d.\n", j);
+		}
+		printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)tp->rx_ring);
+		for (i = 0; i < RX_RING_SIZE; i++)
+			printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+		printk("\n" KERN_DEBUG "  Tx ring %8.8x: ", (int)tp->tx_ring);
+		for (i = 0; i < TX_RING_SIZE; i++)
+			printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+		printk("\n");
+	}
+#endif
+
+	/* Stop and restart the chip's Tx processes . */
+	outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	/* Trigger an immediate transmit demand. */
+	outl(0, ioaddr + CSR1);
 
-	printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x,"
-		   " CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
-		   dev->name, inl(ioaddr + CSR5), csr12,
-		   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
-	tp->mediasense = 1;
-	if (dev->if_port == 1 || dev->if_port == 2)
-		if (csr12 & 0x0004) {
-			dev->if_port = 2 - dev->if_port;
-		} else
-			dev->if_port = 0;
-	else
-		dev->if_port = 1;
-	select_media(dev, 0);
-	tp->stats.tx_errors++;
 	dev->trans_start = jiffies;
+	tp->stats.tx_errors++;
 	return;
-  } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
-			 || tp->chip_id == MX98713) {
-	  /* Stop the transmit process. */
-	  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-	  printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
-			 "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-			 dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
-			 inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
-	  if (tp->mtable) {
-		  if (--tp->cur_index < 0) {
-			  /* We start again, but should instead look for default. */
-			  tp->cur_index = tp->mtable->leafcount - 1;
-		  }
-		  select_media(dev, 0);
-		  printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n",
-				 dev->name, dev->if_port ? "100baseTx" : "10baseT");
-	  }
-	  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-	  tp->stats.tx_errors++;
-	  dev->trans_start = jiffies;
-	  return;
-  } else
-	printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
-		   " resetting...\n",
-		   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
-#ifdef way_too_many_messages
-  printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
-  for (i = 0; i < RX_RING_SIZE; i++)
-	printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
-  printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
-  for (i = 0; i < TX_RING_SIZE; i++)
-	printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
-  printk("\n");
-#endif
-
-  /* Perhaps we should reinitialize the hardware here. */
-  dev->if_port = 0;
-  /* Stop and restart the chip's Tx processes . */
-  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-  /* Trigger an immediate transmit demand. */
-  outl(0, ioaddr + CSR1);
-
-  dev->trans_start = jiffies;
-  tp->stats.tx_errors++;
-  return;
 }
 
 
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-tulip_init_ring(struct device *dev)
+static void tulip_init_ring(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
@@ -2078,38 +2486,37 @@
 	tp->dirty_rx = tp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = 0x80000000;	/* Owned by Tulip chip */
-		tp->rx_ring[i].length = PKT_BUF_SZ;
-		{
-			/* Note the receive buffer must be longword aligned.
-			   dev_alloc_skb() provides 16 byte alignment.  But do *not*
-			   use skb_reserve() to align the IP header! */
-			struct sk_buff *skb;
-			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
-			tp->rx_skbuff[i] = skb;
-			if (skb == NULL)
-				break;			/* Bad news!  */
-			skb->dev = dev;			/* Mark as being used by this device. */
-#if LINUX_VERSION_CODE > 0x10300
-			tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
-#else
-			tp->rx_ring[i].buffer1 = virt_to_bus(skb->data);
-#endif
-		}
-		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
+		tp->rx_ring[i].status = 0x00000000;
+		tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ);
+		tp->rx_ring[i].buffer2 = virt_to_le32desc(&tp->rx_ring[i+1]);
+		tp->rx_skbuff[i] = NULL;
 	}
 	/* Mark the last entry as wrapping the ring. */
-	tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
-	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
+	tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP);
+	tp->rx_ring[i-1].buffer2 = virt_to_le32desc(&tp->rx_ring[0]);
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		/* Note the receive buffer must be longword aligned.
+		   dev_alloc_skb() provides 16 byte alignment.  But do *not*
+		   use skb_reserve() to align the IP header! */
+		struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
+		tp->rx_skbuff[i] = skb;
+		if (skb == NULL)
+			break;
+		skb->dev = dev;			/* Mark as being used by this device. */
+		tp->rx_ring[i].status = cpu_to_le32(DescOwned);	/* Owned by Tulip chip */
+		tp->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail);
+	}
+	tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 
 	/* The Tx buffer descriptor is filled in as needed, but we
 	   do need to clear the ownership bit. */
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		tp->tx_skbuff[i] = 0;
 		tp->tx_ring[i].status = 0x00000000;
-		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
+		tp->tx_ring[i].buffer2 = virt_to_le32desc(&tp->tx_ring[i+1]);
 	}
-	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
+	tp->tx_ring[i-1].buffer2 = virt_to_le32desc(&tp->tx_ring[0]);
 }
 
 static int
@@ -2128,78 +2535,64 @@
 		return 1;
 	}
 
-	/* Caution: the write order is important here, set the base address
-	   with the "ownership" bits last. */
+	/* Caution: the write order is important here, set the field
+	   with the ownership bits last. */
 
 	/* Calculate the next Tx descriptor entry. */
 	entry = tp->cur_tx % TX_RING_SIZE;
 
 	tp->tx_skbuff[entry] = skb;
-	tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
+	tp->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data);
 
 	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
-	  flag = 0x60000000; /* No interrupt */
-	  dev->tbusy = 0;
+		flag = 0x60000000; /* No interrupt */
 	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
-	  flag = 0xe0000000; /* Tx-done intr. */
-	  dev->tbusy = 0;
+		flag = 0xe0000000; /* Tx-done intr. */
 	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
-	  flag = 0x60000000; /* No Tx-done intr. */
-	  dev->tbusy = 0;
-	} else {
-	  /* Leave room for set_rx_mode() to fill entries. */
-	  flag = 0xe0000000; /* Tx-done intr. */
-	  tp->tx_full = 1;
+		flag = 0x60000000; /* No Tx-done intr. */
+	} else {		/* Leave room for set_rx_mode() to fill entries. */
+		tp->tx_full = 1;
+		flag = 0xe0000000; /* Tx-done intr. */
 	}
 	if (entry == TX_RING_SIZE-1)
-		flag |= 0xe2000000;
+		flag = 0xe0000000 | DESC_RING_WRAP;
 
-	tp->tx_ring[entry].length = skb->len | flag;
-	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
+	tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag);
+	tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
 	tp->cur_tx++;
-	/* Trigger an immediate transmit demand. */
-	outl(0, dev->base_addr + CSR1);
+	if ( ! tp->tx_full)
+		clear_bit(0, (void*)&dev->tbusy);
 
 	dev->trans_start = jiffies;
+	/* Trigger an immediate transmit demand. */
+	outl(0, dev->base_addr + CSR1);
 
 	return 0;
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
 	struct device *dev = (struct device *)dev_instance;
-#else
-	struct device *dev = (struct device *)(irq2dev_map[irq]);
-#endif
-
-	struct tulip_private *tp;
-	long ioaddr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 	int csr5, work_budget = max_interrupt_work;
 
-	if (dev == NULL) {
-		printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n",
-				irq);
+#if defined(__i386__) && defined(SMP_CHECK)
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+		printk(KERN_ERR "%s: Duplicate entry of the interrupt handler by "
+			   "processor %d.\n",
+			   dev->name, hard_smp_processor_id());
+		dev->interrupt = 0;
 		return;
 	}
-
-	ioaddr = dev->base_addr;
-	tp = (struct tulip_private *)dev->priv;
-	if (test_and_set_bit(0, (void*)&tp->interrupt)) {
-#ifdef SMP_CHECK
-		printk(KERN_ERR "%s: Re-entering the interrupt handler with proc %d,"
-			   " proc %d already handling.\n", dev->name,
-			   tp->smp_proc_id, hard_smp_processor_id());
 #else
+	if (dev->interrupt) {
 		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
-#endif
 		return;
 	}
 	dev->interrupt = 1;
-#ifdef SMP_CHECK
-	tp->smp_proc_id = hard_smp_processor_id();
 #endif
 
 	do {
@@ -2223,10 +2616,10 @@
 			for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
 				 dirty_tx++) {
 				int entry = dirty_tx % TX_RING_SIZE;
-				int status = tp->tx_ring[entry].status;
+				int status = le32_to_cpu(tp->tx_ring[entry].status);
 
 				if (status < 0)
-					break;			/* It still hasn't been Txed */
+					break;			/* It still has not been Txed */
 				/* Check for Rx filter setup frames. */
 				if (tp->tx_skbuff[entry] == NULL)
 				  continue;
@@ -2253,18 +2646,14 @@
 					if (status & 0x0001) tp->stats.tx_deferred++;
 #endif
 #if LINUX_VERSION_CODE > 0x20127
-					tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
+					tp->stats.tx_bytes += tp->tx_skbuff[entry]->len;
 #endif
 					tp->stats.collisions += (status >> 3) & 15;
 					tp->stats.tx_packets++;
 				}
 
 				/* Free the original skb. */
-#if (LINUX_VERSION_CODE > 0x20155)
-				dev_kfree_skb(tp->tx_skbuff[entry]);
-#else
-				dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE);
-#endif
+				dev_free_skb(tp->tx_skbuff[entry]);
 				tp->tx_skbuff[entry] = 0;
 			}
 
@@ -2281,15 +2670,15 @@
 				/* The ring is no longer full, clear tbusy. */
 				tp->tx_full = 0;
 				dev->tbusy = 0;
-				mark_bh(NET_BH);
+				netif_wake_queue(dev);
 			}
 
 			tp->dirty_tx = dirty_tx;
 			if (csr5 & TxDied) {
-				if (tulip_debug > 1)
-					printk(KERN_WARNING "%s: The transmitter stopped!"
-						   "  CSR5 is %x, CSR6 %x.\n",
-						   dev->name, csr5, inl(ioaddr + CSR6));
+				if (tulip_debug > 2)
+					printk(KERN_WARNING "%s: The transmitter stopped."
+						   "  CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
+						   dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
 				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
 				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 			}
@@ -2297,6 +2686,8 @@
 
 		/* Log errors. */
 		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */
+			if (csr5 == 0xffffffff)
+				break;
 			if (csr5 & TxJabber) tp->stats.tx_errors++;
 			if (csr5 & TxFIFOUnderflow) {
 				if ((tp->csr6 & 0xC000) != 0xC000)
@@ -2310,47 +2701,48 @@
 			if (csr5 & RxDied) {		/* Missed a Rx frame. */
 				tp->stats.rx_errors++;
 				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 			}
 			if (csr5 & TimerInt) {
-				printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n",
-					   dev->name, csr5);
-				/* Hmmmmm, it's not clear what to do here. */
-			}
-			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)
-				&& tp->chip_id == DC21142) {
-				if (tulip_debug > 1)
-					printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n",
+				if (tulip_debug > 2)
+					printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
 						   dev->name, csr5);
-				t21142_lnk_change(dev);
+				outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+			}
+			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
+				if (tp->link_change)
+					(tp->link_change)(dev, csr5);
 			}
 			/* Clear all error sources, included undocumented ones! */
 			outl(0x0800f7ba, ioaddr + CSR5);
 		}
 		if (--work_budget < 0) {
 			if (tulip_debug > 1)
-				printk(KERN_WARNING "%s: Too much work at interrupt, "
+				printk(KERN_WARNING "%s: Too much work during an interrupt, "
 					   "csr5=0x%8.8x.\n", dev->name, csr5);
 			/* Acknowledge all interrupt sources. */
 			outl(0x8001ffff, ioaddr + CSR5);
-#ifdef notdef
-			/* Clear all but standard interrupt sources. */
-			outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
-#endif
+			/* Clear all interrupting sources, set timer to re-enable. */
+			outl(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt,
+				 ioaddr + CSR7);
+			outl(12, ioaddr + CSR11);
 			break;
 		}
 	} while (1);
 
-	if (tulip_debug > 3)
+	if (tulip_debug > 4)
 		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
 			   dev->name, inl(ioaddr + CSR5));
 
+#if defined(__i386__)
+	clear_bit(0, (void*)&dev->interrupt);
+#else
 	dev->interrupt = 0;
-	clear_bit(0, (void*)&tp->interrupt);
+#endif
 	return;
 }
 
-static int
-tulip_rx(struct device *dev)
+static int tulip_rx(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int entry = tp->cur_rx % RX_RING_SIZE;
@@ -2360,74 +2752,76 @@
 	if (tulip_debug > 4)
 		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
 			   tp->rx_ring[entry].status);
-	/* If we own the next entry, it's a new packet. Send it up. */
-	while (tp->rx_ring[entry].status >= 0) {
-		s32 status = tp->rx_ring[entry].status;
-
+	/* If we own the next entry, it is a new packet. Send it up. */
+	while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) {
+		s32 status = le32_to_cpu(tp->rx_ring[entry].status);
+
+		if (tulip_debug > 5)
+			printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n",
+				   dev->name, entry, status);
 		if (--rx_work_limit < 0)
 			break;
-		if ((status & 0x0300) != 0x0300) {
-			if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
-				if (tulip_debug > 1)
-					printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
-						   "multiple buffers, status %8.8x!\n",
+		if ((status & 0x38008300) != 0x0300) {
+			if ((status & 0x38000300) != 0x0300) {
+				/* Ingore earlier buffers. */
+				if ((status & 0xffff) != 0x7fff) {
+					if (tulip_debug > 1)
+						printk(KERN_WARNING "%s: Oversized Ethernet frame "
+							   "spanned multiple buffers, status %8.8x!\n",
+							   dev->name, status);
+					tp->stats.rx_length_errors++;
+				}
+			} else if (status & RxDescFatalErr) {
+				/* There was a fatal error. */
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
 						   dev->name, status);
-			  tp->stats.rx_length_errors++;
+				tp->stats.rx_errors++; /* end of a packet.*/
+				if (status & 0x0890) tp->stats.rx_length_errors++;
+				if (status & 0x0004) tp->stats.rx_frame_errors++;
+				if (status & 0x0002) tp->stats.rx_crc_errors++;
+				if (status & 0x0001) tp->stats.rx_fifo_errors++;
 			}
-		} else if (status & 0x8000) {
-			/* There was a fatal error. */
-			if (tulip_debug > 2)
-				printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
-					   dev->name, status);
-			tp->stats.rx_errors++; /* end of a packet.*/
-			if (status & 0x0890) tp->stats.rx_length_errors++;
-			if (status & 0x0004) tp->stats.rx_frame_errors++;
-			if (status & 0x0002) tp->stats.rx_crc_errors++;
-			if (status & 0x0001) tp->stats.rx_fifo_errors++;
 		} else {
 			/* Omit the four octet CRC from the length. */
-			short pkt_len = (status >> 16) - 4;
+			short pkt_len = ((status >> 16) & 0x7ff) - 4;
 			struct sk_buff *skb;
 
-			/* Check if the packet is long enough to just accept without
-			   copying to a properly sized skbuff. */
+#ifndef final_version
+			if (pkt_len > 1518) {
+				printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
+					   dev->name, pkt_len, pkt_len);
+				pkt_len = 1518;
+				tp->stats.rx_length_errors++;
+			}
+#endif
+			/* Check if the packet is long enough to accept without copying
+			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
-				&& (skb = DEV_ALLOC_SKB(pkt_len+2)) != NULL) {
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
-#if LINUX_VERSION_CODE < 0x10300
-				memcpy(skb->data, tp->rx_ring[entry].buffer1, pkt_len);
-#elif LINUX_VERSION_CODE < 0x20200  || defined(__alpha__)
-				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
-#else
-				eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
-								 pkt_len, 0);
+#if ! defined(__alpha__)
+				eth_copy_and_sum(skb, tp->rx_skbuff[entry]->tail, pkt_len, 0);
 				skb_put(skb, pkt_len);
+#else
+				memcpy(skb_put(skb, pkt_len), tp->rx_skbuff[entry]->tail,
+					   pkt_len);
 #endif
 				work_done++;
 			} else { 	/* Pass up the skb already on the Rx ring. */
-				skb = tp->rx_skbuff[entry];
+				char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
 				tp->rx_skbuff[entry] = NULL;
 #ifndef final_version
-				{
-					void *temp = skb_put(skb, pkt_len);
-					if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
-						printk(KERN_ERR "%s: Internal consistency error! The "
-						   "skbuff addresses do not match in tulip_rx:"
-							   " %p vs. %p / %p.\n", dev->name,
-							   bus_to_virt(tp->rx_ring[entry].buffer1),
-							   skb->head, temp);
-				}
-#else
-				skb_put(skb, pkt_len);
+				if (le32desc_to_virt(tp->rx_ring[entry].buffer1) != temp)
+					printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
+						   "do not match in tulip_rx: %p vs. %p / %p.\n",
+						   dev->name,
+						   le32desc_to_virt(tp->rx_ring[entry].buffer1),
+						   skb->head, temp);
 #endif
 			}
-#if LINUX_VERSION_CODE > 0x10300
 			skb->protocol = eth_type_trans(skb, dev);
-#else
-			skb->len = pkt_len;
-#endif
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 			tp->stats.rx_packets++;
@@ -2443,25 +2837,20 @@
 		entry = tp->dirty_rx % RX_RING_SIZE;
 		if (tp->rx_skbuff[entry] == NULL) {
 			struct sk_buff *skb;
-			skb = tp->rx_skbuff[entry] = DEV_ALLOC_SKB(PKT_BUF_SZ);
+			skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
 			if (skb == NULL)
 				break;
 			skb->dev = dev;			/* Mark as being used by this device. */
-#if LINUX_VERSION_CODE > 0x10300
-			tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
-#else
-			tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data);
-#endif
+			tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail);
 			work_done++;
 		}
-		tp->rx_ring[entry].status = 0x80000000;
+		tp->rx_ring[entry].status = cpu_to_le32(DescOwned);
 	}
 
 	return work_done;
 }
 
-static int
-tulip_close(struct device *dev)
+static int tulip_close(struct device *dev)
 {
 	long ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
@@ -2476,22 +2865,20 @@
 
 	/* Disable interrupts by clearing the interrupt mask. */
 	outl(0x00000000, ioaddr + CSR7);
-	/* Stop the chip's Tx and Rx processes. */
+	/* Stop the Tx and Rx processes. */
 	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
 	/* 21040 -- Leave the card in 10baseT state. */
 	if (tp->chip_id == DC21040)
 		outl(0x00000004, ioaddr + CSR13);
 
-	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+	if (inl(ioaddr + CSR6) != 0xffffffff)
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
 	del_timer(&tp->timer);
 
-#ifdef SA_SHIRQ
 	free_irq(dev->irq, dev);
-#else
-	free_irq(dev->irq);
-	irq2dev_map[dev->irq] = 0;
-#endif
+
+	dev->if_port = tp->saved_if_port;
 
 	/* Free all the skbuffs in the Rx queue. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
@@ -2504,31 +2891,26 @@
 #if LINUX_VERSION_CODE < 0x20100
 			skb->free = 1;
 #endif
-#if (LINUX_VERSION_CODE > 0x20155)
-			dev_kfree_skb(skb);
-#else
-			dev_kfree_skb(skb, FREE_WRITE);
-#endif
+			dev_free_skb(skb);
 		}
 	}
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		if (tp->tx_skbuff[i])
-#if (LINUX_VERSION_CODE > 0x20155)
-			dev_kfree_skb(tp->tx_skbuff[i]);
-#else
-			dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
-#endif
+			dev_free_skb(tp->tx_skbuff[i]);
 		tp->tx_skbuff[i] = 0;
 	}
 
+	/* Leave the driver in snooze, not sleep, mode. */
+	if (tp->flags & HAS_PWRDWN)
+		pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40,
+								   0x40000000);
 
 	MOD_DEC_USE_COUNT;
 
 	return 0;
 }
 
-static struct enet_statistics *
-tulip_get_stats(struct device *dev)
+static struct net_device_stats *tulip_get_stats(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
@@ -2551,31 +2933,32 @@
 
 	switch(cmd) {
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
-		if (tp->mtable  &&  tp->mtable->has_mii)
+		if (tp->mii_cnt)
 			data[0] = phy;
-		else if (tp->chip_id == DC21142)
+		else if (tp->flags & HAS_NWAY143)
 			data[0] = 32;
+		else if (tp->chip_id == COMET)
+			data[0] = 1;
 		else
 			return -ENODEV;
-		return 0;
 	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
-		if (data[0] == 32) {  /* 21142 pseudo-MII */
+		if (data[0] == 32  &&  (tp->flags & HAS_NWAY143)) {
 			int csr12 = inl(ioaddr + CSR12);
 			int csr14 = inl(ioaddr + CSR14);
 			switch (data[1]) {
 			case 0: {
-				data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000);
+				data[3] = (csr14<<5) & 0x1000;
 				break; }
 			case 1:
 				data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
 					+ (csr12&0x06 ? 0x04 : 0);
 				break;
 			case 4: {
-				int csr14 = inl(ioaddr + CSR14);
-				data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1;
+				data[3] = ((csr14>>9)&0x07C0) +
+					((inl(ioaddr + CSR6)>>3)&0x0040) + ((csr14>>1)&0x20) + 1;
 				break;
 			}
-			case 5: data[3] = inl(ioaddr + CSR12) >> 16; break;
+			case 5: data[3] = csr12 >> 16; break;
 			default: data[3] = 0; break;
 			}
 		} else {
@@ -2586,9 +2969,11 @@
 		}
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
-		if (data[0] == 32) {  /* 21142 pseudo-MII */
+		if (data[0] == 32  &&  (tp->flags & HAS_NWAY143)) {
+			if (data[1] == 5)
+				tp->to_advertise = data[2];
 		} else {
 			save_flags(flags);
 			cli();
@@ -2613,9 +2998,9 @@
    N.B. Do not use for bulk data, use a table-based routine instead.
    This is common code and should be moved to net/core/crc.c */
 static unsigned const ethernet_polynomial_le = 0xedb88320U;
-static inline unsigned ether_crc_le(int length, unsigned char *data)
+static inline u32 ether_crc_le(int length, unsigned char *data)
 {
-	unsigned int crc = 0xffffffff;	/* Initial value. */
+	u32 crc = 0xffffffff;	/* Initial value. */
 	while(--length >= 0) {
 		unsigned char current_octet = *data++;
 		int bit;
@@ -2629,80 +3014,107 @@
 	}
 	return crc;
 }
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+    int crc = -1;
+
+    while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 0; bit < 8; bit++, current_octet >>= 1)
+			crc = (crc << 1) ^
+				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+    }
+    return crc;
+}
 
-#ifdef NEW_MULTICAST
 static void set_rx_mode(struct device *dev)
-#else
-static void set_rx_mode(struct device *dev, int num_addrs, void *addrs)
-#endif
 {
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
 	tp->csr6 &= ~0x00D5;
 	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
-		outl(csr6 | 0x00C0, ioaddr + CSR6);
+		tp->csr6 |= 0x00C0;
+		csr6 |= 0x00C0;
 		/* Unconditionally log net taps. */
 		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
-		tp->csr6 |= 0xC0;
 	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
-		/* Too many to filter perfectly -- accept all multicasts. */
-		outl(csr6 | 0x0080, ioaddr + CSR6);
-		tp->csr6 |= 0x80;
+		/* Too many to filter well -- accept all multicasts. */
+		tp->csr6 |= 0x0080;
+		csr6 |= 0x0080;
+	} else	if (tp->flags & MC_HASH_ONLY) {
+		/* Some work-alikes have only a 64-entry hash filter table. */
+		/* Should verify correctness on big-endian/__powerpc__ */
+		struct dev_mc_list *mclist;
+		int i;
+		u32 mc_filter[2];		 /* Multicast hash filter */
+		if (dev->mc_count > 64) {		/* Arbitrary non-effective limit. */
+			tp->csr6 |= 0x0080;
+			csr6 |= 0x0080;
+		} else {
+			mc_filter[1] = mc_filter[0] = 0;
+			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+				 i++, mclist = mclist->next)
+				set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
+			if (tp->chip_id == AX88140) {
+				outl(2, ioaddr + CSR13);
+				outl(mc_filter[0], ioaddr + CSR14);
+				outl(3, ioaddr + CSR13);
+				outl(mc_filter[1], ioaddr + CSR14);
+			} else if (tp->chip_id == COMET) { /* Has a simple hash filter. */
+				outl(mc_filter[0], ioaddr + 0xAC);
+				outl(mc_filter[1], ioaddr + 0xB0);
+			}
+		}
 	} else {
-		u32 *setup_frm = tp->setup_frame;
+		u16 *eaddrs, *setup_frm = tp->setup_frame;
 		struct dev_mc_list *mclist;
-		u16 *eaddrs;
-		u32 tx_flags;
+		u32 tx_flags = 0x08000000 | 192;
 		int i;
 
+		/* Note that only the low-address shortword of setup_frame is valid!
+		   The values are doubled for big-endian architectures. */
 		if (dev->mc_count > 14) { /* Must use a multicast hash table. */
-		  u16 hash_table[32];
-		  memset(hash_table, 0, sizeof(hash_table));
-		  /* This should work on big-endian machines as well. */
-		  for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-			   i++, mclist = mclist->next)
-			  set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
-					  hash_table);
-		  /* Copy the hash table to the setup frame.
-			 NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */
-		  for (i = 0; i < 32; i++)
-			*setup_frm++ = hash_table[i];
-		  setup_frm += 7;
-		  tx_flags = 0x08400000 | 192;
-		  /* Too clever: i > 15 for fall-though. */
+			u16 hash_table[32];
+			tx_flags = 0x08400000 | 192;		/* Use hash filter. */
+			memset(hash_table, 0, sizeof(hash_table));
+			set_bit(255, hash_table); 			/* Broadcast entry */
+			/* This should work on big-endian machines as well. */
+			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+				 i++, mclist = mclist->next)
+				set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+						hash_table);
+			for (i = 0; i < 32; i++)
+				*setup_frm++ = *setup_frm++ = hash_table[i];
+			setup_frm = &tp->setup_frame[13*6];
 		} else {
-		  /* We have <= 15 addresses so we can use the wonderful
-			 16 address perfect filtering of the Tulip. */
-		  for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-			   i++, mclist = mclist->next) {
-			/* Note that only the low shortword of setup_frame[] is valid!
-			   This code may require tweaking for non-x86 architectures! */
-			eaddrs = (u16 *)mclist->dmi_addr;
-			*setup_frm++ = *eaddrs++;
-			*setup_frm++ = *eaddrs++;
-			*setup_frm++ = *eaddrs++;
-		  }
-		  /* Fill the rest of the table with our physical address.
-			 Once again, only the low shortword or setup_frame[] is valid! */
-		  *setup_frm++ = 0xffff;
-		  *setup_frm++ = 0xffff;
-		  *setup_frm++ = 0xffff;
-		  tx_flags = 0x08000000 | 192;
+			/* We have <= 14 addresses so we can use the wonderful
+			   16 address perfect filtering of the Tulip. */
+			for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+				 i++, mclist = mclist->next) {
+				eaddrs = (u16 *)mclist->dmi_addr;
+				*setup_frm++ = *setup_frm++ = *eaddrs++;
+				*setup_frm++ = *setup_frm++ = *eaddrs++;
+				*setup_frm++ = *setup_frm++ = *eaddrs++;
+			}
+			/* Fill the unused entries with the broadcast address. */
+			memset(setup_frm, 0xff, (15-i)*12);
+			setup_frm = &tp->setup_frame[15*6];
 		}
+		/* Fill the final entry with our physical address. */
 		eaddrs = (u16 *)dev->dev_addr;
-		do {
-			*setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2];
-		} while (++i < 15);
+		*setup_frm++ = *setup_frm++ = eaddrs[0];
+		*setup_frm++ = *setup_frm++ = eaddrs[1];
+		*setup_frm++ = *setup_frm++ = eaddrs[2];
 		/* Now add this frame to the Tx list. */
 		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
 			/* Same setup recently queued, we need not add it. */
 		} else {
 			unsigned long flags;
-			unsigned int entry, dummy = 0;
+			unsigned int entry;
 
 			save_flags(flags); cli();
 			entry = tp->cur_tx++ % TX_RING_SIZE;
@@ -2711,32 +3123,29 @@
 				/* Avoid a chip errata by prefixing a dummy entry. */
 				tp->tx_skbuff[entry] = 0;
 				tp->tx_ring[entry].length =
-					(entry == TX_RING_SIZE-1) ? 0x02000000 : 0;
+					(entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0;
 				tp->tx_ring[entry].buffer1 = 0;
-				/* race with chip, set DescOwned later */
-				dummy = entry;
+				tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
 				entry = tp->cur_tx++ % TX_RING_SIZE;
 			}
 
 			tp->tx_skbuff[entry] = 0;
 			/* Put the setup frame on the Tx list. */
 			if (entry == TX_RING_SIZE-1)
-				tx_flags |= 0x02000000;		/* Wrap ring. */
-			tp->tx_ring[entry].length = tx_flags;
-			tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
-			tp->tx_ring[entry].status = 0x80000000;
+				tx_flags |= DESC_RING_WRAP;		/* Wrap ring. */
+			tp->tx_ring[entry].length = cpu_to_le32(tx_flags);
+			tp->tx_ring[entry].buffer1 = virt_to_le32desc(tp->setup_frame);
+			tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
 			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
-				dev->tbusy = 1;
+				set_bit(0, (void*)&dev->tbusy);
 				tp->tx_full = 1;
 			}
-			if (dummy >= 0)
-				tp->tx_ring[dummy].status = DescOwned;
 			restore_flags(flags);
 			/* Trigger an immediate transmit demand. */
 			outl(0, ioaddr + CSR1);
 		}
-		outl(csr6 | 0x0000, ioaddr + CSR6);
 	}
+	outl(csr6 | 0x0000, ioaddr + CSR6);
 }
 
 #ifdef CARDBUS
@@ -2745,18 +3154,18 @@
 
 static dev_node_t *tulip_attach(dev_locator_t *loc)
 {
+	struct device *dev;
 	u16 dev_id;
 	u32 io;
-	u8 bus, devfn;
-	struct device *dev;
+	u8 bus, devfn, irq;
 
 	if (loc->bus != LOC_PCI) return NULL;
 	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
 	printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn);
 	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io);
 	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
-	io &= ~3;
-	dev = tulip_probe1(bus, devfn, NULL, DC21142, -1);
+	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
+	dev = tulip_probe1(bus, devfn, NULL, io & ~3, irq, DC21142, 0);
 	if (dev) {
 		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
 		strcpy(node->dev_name, dev->name);
@@ -2768,6 +3177,45 @@
 	return NULL;
 }
 
+static void tulip_suspend(dev_node_t *node)
+{
+	struct device **devp, **next;
+	printk(KERN_INFO "tulip_suspend(%s)\n", node->dev_name);
+	for (devp = &root_tulip_dev; *devp; devp = next) {
+		next = &((struct tulip_private *)(*devp)->priv)->next_module;
+		if (strcmp((*devp)->name, node->dev_name) == 0) break;
+	}
+	if (*devp) {
+		long ioaddr = (*devp)->base_addr;
+		struct tulip_private *tp = (struct tulip_private *)(*devp)->priv;
+		int csr6 = inl(ioaddr + CSR6);
+		/* Disable interrupts, stop the chip, gather stats. */
+		if (csr6 != 0xffffffff) {
+			outl(0x00000000, ioaddr + CSR7);
+			outl(csr6 & ~0x2002, ioaddr + CSR6);
+			tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+		}
+		tulip_close(*devp);
+		/* Put the 21143 into sleep mode. */
+		pcibios_write_config_dword(tp->pci_bus,tp->pci_devfn, 0x40,0x80000000);
+	}
+}
+
+static void tulip_resume(dev_node_t *node)
+{
+	struct device **devp, **next;
+	printk(KERN_INFO "tulip_resume(%s)\n", node->dev_name);
+	for (devp = &root_tulip_dev; *devp; devp = next) {
+		next = &((struct tulip_private *)(*devp)->priv)->next_module;
+		if (strcmp((*devp)->name, node->dev_name) == 0) break;
+	}
+	if (*devp) {
+		struct tulip_private *tp = (struct tulip_private *)(*devp)->priv;
+		pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0x0000);
+		tulip_open(*devp);
+	}
+}
+
 static void tulip_detach(dev_node_t *node)
 {
 	struct device **devp, **next;
@@ -2786,34 +3234,17 @@
 }
 
 struct driver_operations tulip_ops = {
-	"tulip_cb", tulip_attach, NULL, NULL, tulip_detach
+	"tulip_cb", tulip_attach, tulip_suspend, tulip_resume, tulip_detach
 };
 
 #endif  /* Cardbus support */
 
 
 #ifdef MODULE
-#if LINUX_VERSION_CODE > 0x20118
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(reverse_probe, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-#endif
-
-/* An additional parameter that may be passed in... */
-static int debug = -1;
-
-int
-init_module(void)
+int init_module(void)
 {
-	if (debug >= 0)
-		tulip_debug = debug;
-
 #ifdef CARDBUS
+	reverse_probe = 0;			/* Not used. */
 	register_driver(&tulip_ops);
 	return 0;
 #else
@@ -2821,8 +3252,7 @@
 #endif
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	struct device *next_dev;
 
@@ -2832,9 +3262,11 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_tulip_dev) {
-		next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
+		struct tulip_private *tp = (struct tulip_private *)root_tulip_dev->priv;
+		next_dev = tp->next_module;
 		unregister_netdev(root_tulip_dev);
-		release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE);
+		release_region(root_tulip_dev->base_addr,
+					   tulip_tbl[tp->chip_id].io_size);
 		kfree(root_tulip_dev);
 		root_tulip_dev = next_dev;
 	}
@@ -2844,8 +3276,9 @@
 
 /*
  * Local variables:
- *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/wavelan.c linux.14p6/drivers/net/wavelan.c
--- linux.vanilla/drivers/net/wavelan.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/net/wavelan.c	Wed Nov  3 00:22:30 1999
@@ -1910,18 +1910,18 @@
 
     case SIOCSIWNWID:
       /* Set NWID in WaveLAN. */
-      if(wrq->u.nwid.on)
+      if(!wrq->u.nwid.disabled)
 	{
-	  /* Set NWID in psa. */
-	  psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
-	  psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
+	  /* Set NWID in psa */
+	  psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8;
+	  psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
 	  psa.psa_nwid_select = 0x01;
 	  psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
 		    (unsigned char *)psa.psa_nwid, 3);
 
 	  /* Set NWID in mmc. */
-	  m.w.mmw_netw_id_l = wrq->u.nwid.nwid & 0xFF;
-	  m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+	  m.w.mmw_netw_id_l = psa.psa_nwid[1];
+	  m.w.mmw_netw_id_h = psa.psa_nwid[0];
 	  mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m,
 		    (unsigned char *)&m.w.mmw_netw_id_l, 2);
 	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
@@ -1945,8 +1945,9 @@
       /* Read the NWID. */
       psa_read(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
 	       (unsigned char *)psa.psa_nwid, 3);
-      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
-      wrq->u.nwid.on = psa.psa_nwid_select;
+      wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.disabled = !(psa.psa_nwid_select);
+      wrq->u.nwid.fixed = 1;	/* Superfluous */
       break;
 
     case SIOCSIWFREQ:
@@ -2006,83 +2007,96 @@
       wrq->u.sens.fixed = 1;
       break;
 
-     case SIOCSIWENCODE:
-       /* Set encryption key. */
-       if(!mmc_encr(ioaddr))
-	 {
-	   ret = -EOPNOTSUPP;
-	   break;
-	 }
-
-       if(wrq->u.encoding.method)
-	 {	/* Enable encryption. */
-	   int		i;
-	   long long	key = wrq->u.encoding.code;
-
-	   for(i = 7; i >= 0; i--)
-	     {
-	       psa.psa_encryption_key[i] = key & 0xFF;
-	       key >>= 8;
-	     }
-           psa.psa_encryption_select = 1;
-	   psa_write(ioaddr, lp->hacr,
-		     (char *) &psa.psa_encryption_select - (char *) &psa,
-		     (unsigned char *) &psa.psa_encryption_select, 8+1);
-
-           mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
-		   MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
-           mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
-		     (unsigned char *) &psa.psa_encryption_key, 8);
-	 }
-       else
-	 {	/* Disable encryption. */
-	   psa.psa_encryption_select = 0;
-	   psa_write(ioaddr, lp->hacr,
-		     (char *) &psa.psa_encryption_select - (char *) &psa,
-		     (unsigned char *) &psa.psa_encryption_select, 1);
-
-	   mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
-	 }
-       /* update the Wavelan checksum */
-       update_psa_checksum(dev, ioaddr, lp->hacr);
-       break;
-
-     case SIOCGIWENCODE:
-       /* Read the encryption key. */
-       if(!mmc_encr(ioaddr))
-	 {
-	   ret = -EOPNOTSUPP;
-	   break;
-	 }
-
-       /* Only super-user can see encryption key. */
-       if(!suser())
-	 {
-	   ret = -EPERM;
-	   break;
-	 }
-       else
-	 {
-	   int		i;
-	   long long	key = 0;
+    case SIOCSIWENCODE:
+      /* Set encryption key */
+      if(!mmc_encr(ioaddr))
+	{
+	  ret = -EOPNOTSUPP;
+	  break;
+	}
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+	{
+	  /* Check the size of the key */
+	  if(wrq->u.encoding.length != 8)
+	    {
+	      ret = -EINVAL;
+	      break;
+	    }
 
-	   psa_read(ioaddr, lp->hacr,
+	  /* Copy the key in the driver */
+	  if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer,
+			    wrq->u.encoding.length))
+	    {
+	      ret = -EFAULT;
+	      break;
+	    }
+
+	  psa.psa_encryption_select = 1;
+	  psa_write(ioaddr, lp->hacr,
 		    (char *) &psa.psa_encryption_select - (char *) &psa,
-		    (unsigned char *) &psa.psa_encryption_select, 1+8);
-	   for(i = 0; i < 8; i++)
-	     {
-	       key <<= 8;
-	       key += psa.psa_encryption_key[i];
-	     }
-	   wrq->u.encoding.code = key;
-
-	   /* encryption is enabled */
-	   if(psa.psa_encryption_select)
-	     wrq->u.encoding.method = mmc_encr(ioaddr);
-	   else
-	     wrq->u.encoding.method = 0;
-	 }
-       break;
+		    (unsigned char *) &psa.psa_encryption_select, 8+1);
+
+	  mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
+		  MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+	  mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
+		    (unsigned char *) &psa.psa_encryption_key, 8);
+	}
+
+      if(wrq->u.encoding.flags & IW_ENCODE_DISABLED)
+	{	/* disable encryption */
+	  psa.psa_encryption_select = 0;
+	  psa_write(ioaddr, lp->hacr,
+		    (char *) &psa.psa_encryption_select - (char *) &psa,
+		    (unsigned char *) &psa.psa_encryption_select, 1);
+
+	  mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
+	}
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+      break;
+
+    case SIOCGIWENCODE:
+      /* Read the encryption key */
+      if(!mmc_encr(ioaddr))
+	{
+	  ret = -EOPNOTSUPP;
+	  break;
+	}
+
+      /* only super-user can see encryption key */
+      if(!suser())
+	{
+	  ret = -EPERM;
+	  break;
+	}
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+	{
+	  /* Verify the user buffer */
+	  ret = verify_area(VERIFY_WRITE, wrq->u.encoding.pointer, 8);
+	  if(ret)
+	    break;
+
+	  psa_read(ioaddr, lp->hacr,
+		   (char *) &psa.psa_encryption_select - (char *) &psa,
+		   (unsigned char *) &psa.psa_encryption_select, 1+8);
+
+	  /* encryption is enabled ? */
+	  if(psa.psa_encryption_select)
+	    wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+	  else
+	    wrq->u.encoding.flags = IW_ENCODE_DISABLED;
+	  wrq->u.encoding.flags |= mmc_encr(ioaddr);
+
+	  /* Copy the key to the user buffer */
+	  wrq->u.encoding.length = 8;
+	  if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8))
+	    ret = -EFAULT;
+	}
+      break;
 
     case SIOCGIWRANGE:
       /* basic checking */
@@ -2116,6 +2130,19 @@
 
 	  range.num_bitrates = 1;
 	  range.bitrate[0] = 2000000;	/* 2 Mb/s */
+
+	  /* Encryption supported ? */
+	  if(mmc_encr(ioaddr))
+	    {
+	      range.encoding_size[0] = 8;	/* DES = 64 bits key */
+	      range.num_encoding_sizes = 1;
+	      range.max_encoding_tokens = 1;	/* Only one key possible */
+	    }
+	  else
+	    {
+	      range.num_encoding_sizes = 0;
+	      range.max_encoding_tokens = 0;
+	    }
 
 	  /* Copy structure to the user buffer. */
 	  if (copy_to_user(wrq->u.data.pointer, &range, sizeof(struct iw_range)))
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/wavelan.p.h linux.14p6/drivers/net/wavelan.p.h
--- linux.vanilla/drivers/net/wavelan.p.h	Sat Aug 14 02:27:30 1999
+++ linux.14p6/drivers/net/wavelan.p.h	Tue Nov 16 17:20:12 1999
@@ -300,6 +300,11 @@
  *	- Add the (short) list of bit-rates in range
  *	- Developp a new sensitivity... (sens.value & sens.fixed)
  *
+ * Changes made for release in 2.2.14 & 2.3.23 :
+ * -------------------------------------------
+ *	- Fix check for root permission (break instead of exit)
+ *	- New nwid & encoding setting (Wireless Extension 9)
+ *
  * Wishes & dreams:
  * ----------------
  *	- roaming (see Pcmcia driver)
@@ -390,7 +395,7 @@
 /************************ CONSTANTS & MACROS ************************/
 
 #ifdef DEBUG_VERSION_SHOW
-static const char	*version	= "wavelan.c : v20 (wireless extensions) 29/7/99\n";
+static const char	*version	= "wavelan.c : v21 (wireless extensions) 16/10/99\n";
 #endif
 
 /* Watchdog temporisation */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/z85230.c linux.14p6/drivers/net/z85230.c
--- linux.vanilla/drivers/net/z85230.c	Sat Aug 14 02:27:30 1999
+++ linux.14p6/drivers/net/z85230.c	Wed Nov  3 00:18:55 1999
@@ -14,10 +14,6 @@
  *	Asynchronous mode dropped for 2.2. For 2.3 we will attempt the
  *	unification of all the Z85x30 asynchronous drivers for real.
  *
- *	To Do:
- *	
- *	Finish DMA mode support.
- *
  *	Performance
  *
  *	Z85230:
@@ -169,7 +165,7 @@
 /*
  *	As above but for enhanced chips.
  */
-
+ 
 u8 z8530_hdlc_kilostream_85230[]=
 {
 	4,	SYNC_ENAB|SDLC|X1CLK,
@@ -354,13 +350,15 @@
 		z8530_tx_done(chan);
 	}
 		
-	if(altered&DCD)
+	if(altered&chan->dcdcheck)
 	{
-		if(status&DCD)
+		if(status&chan->dcdcheck)
 		{
 			printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);
 			write_zsreg(chan, R3, chan->regs[3]|RxENABLE);
-			if(chan->netdevice)
+			if(chan->netdevice &&
+			    ((chan->netdevice->type == ARPHRD_HDLC) ||
+			    (chan->netdevice->type == ARPHRD_PPP)))
 				sppp_reopen(chan->netdevice);
 		}
 		else
@@ -440,7 +438,6 @@
 		if(status&TxEOM)
 		{
 			flags=claim_dma_lock();
-			/* Transmit underrun */
 			disable_dma(chan->txdma);
 			clear_dma_ff(chan->txdma);	
 			chan->txdma_on=0;
@@ -448,13 +445,15 @@
 			z8530_tx_done(chan);
 		}
 	}
-	if(altered&DCD)
+	if(altered&chan->dcdcheck)
 	{
-		if(status&DCD)
+		if(status&chan->dcdcheck)
 		{
 			printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);
 			write_zsreg(chan, R3, chan->regs[3]|RxENABLE);
-			if(chan->netdevice)
+			if(chan->netdevice &&
+			    ((chan->netdevice->type == ARPHRD_HDLC) ||
+			    (chan->netdevice->type == ARPHRD_PPP)))
 				sppp_reopen(chan->netdevice);
 		}
 		else
@@ -1012,6 +1011,8 @@
 	   floating IRQ transition when we reset the chip */
 	dev->chanA.irqs=&z8530_nop;
 	dev->chanB.irqs=&z8530_nop;
+	dev->chanA.dcdcheck=DCD;
+	dev->chanB.dcdcheck=DCD;
 	/* Reset the chip */
 	write_zsreg(&dev->chanA, R9, 0xC0);
 	udelay(200);
@@ -1104,7 +1105,7 @@
 	c->mtu=1500;
 	c->max=0;
 	c->count=0;
-	c->status=0;	/* Fixme - check DCD now */
+	c->status=read_zsreg(c, R0);
 	c->sync=1;
 	write_zsreg(c, R3, c->regs[R3]|RxENABLE);
 	return 0;
@@ -1251,7 +1252,7 @@
 		 *	Save the ready state and the buffer currently
 		 *	being used as the DMA target
 		 */
-
+		 
 		int ready=c->dma_ready;
 		unsigned char *rxb=c->rx_buf[c->dma_num];
 		unsigned long flags;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/z85230.h linux.14p6/drivers/net/z85230.h
--- linux.vanilla/drivers/net/z85230.h	Sat Oct 17 23:33:45 1998
+++ linux.14p6/drivers/net/z85230.h	Wed Nov  3 00:18:55 1999
@@ -270,6 +270,7 @@
 	struct sk_buff	*skb;		/* Buffer dptr points into */
 	struct sk_buff	*skb2;		/* Pending buffer */
 	u8		status;		/* Current DCD */
+	u8		dcdcheck;	/* which bit to check for line */
 	u8		sync;		/* Set if in sync mode */
 
 	u8		regs[32];	/* Register map for the chip */
@@ -413,7 +414,7 @@
  *	Standard interrupt vector sets
  */
  
-struct z8530_irqhandler z8530_sync, z8530_async, z8530_nop;
+extern struct z8530_irqhandler z8530_sync, z8530_async, z8530_nop;
 
 /*
  *	Asynchronous Interfacing
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/pci/oldproc.c linux.14p6/drivers/pci/oldproc.c
--- linux.vanilla/drivers/pci/oldproc.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/pci/oldproc.c	Fri Oct 22 21:24:50 1999
@@ -526,6 +526,7 @@
 	DEVICE( INTEL,		INTEL_82441,	"82441FX Natoma"),
 	DEVICE( INTEL,		INTEL_82380FB,	"82380FB Mobile"),
 	DEVICE( INTEL,		INTEL_82439,	"82439HX Triton II"),
+	DEVICE(	INTEL,		INTEL_MEGARAID,	"OEM MegaRAID Controller"),
 	DEVICE(	INTEL,		INTEL_82371SB_0,"82371SB PIIX3 ISA"),
 	DEVICE(	INTEL,		INTEL_82371SB_1,"82371SB PIIX3 IDE"),
 	DEVICE( INTEL,		INTEL_82371SB_2,"82371SB PIIX3 USB"),
@@ -536,13 +537,16 @@
 	DEVICE( INTEL,		INTEL_82371AB_2,"82371AB PIIX4 USB"),
 	DEVICE( INTEL,		INTEL_82371AB_3,"82371AB PIIX4 ACPI"),
 	DEVICE( INTEL,		INTEL_82443LX_0,"440LX - 82443LX PAC Host"),
-	DEVICE( COMPUTONE,	COMPUTONE_IP2EX, "Computone IntelliPort Plus"),
 	DEVICE( INTEL,		INTEL_82443LX_1,"440LX - 82443LX PAC AGP"),
 	DEVICE( INTEL,		INTEL_82443BX_0,"440BX - 82443BX Host"),
 	DEVICE( INTEL,		INTEL_82443BX_1,"440BX - 82443BX AGP"),
 	DEVICE( INTEL,		INTEL_82443BX_2,"440BX - 82443BX Host (no AGP)"),
 	DEVICE( INTEL,		INTEL_P6,	"Orion P6"),
- 	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion P6"),
+ 	DEVICE( INTEL,		INTEL_82450GX,	"450KX/GX [Orion] - 82454KX/GX PCI Bridge"),
+ 	DEVICE( INTEL,		INTEL_82453GX,	"450KX/GX [Orion] - 82453KX/GX Memory Controller"),
+ 	DEVICE( INTEL,		INTEL_82451NX,	"450NX - 82451NX Memory & I/O Controller"),
+ 	DEVICE( INTEL,		INTEL_82454NX,	"450NX - 82454NX PCI Expander Bridge"),
+	DEVICE( COMPUTONE,	COMPUTONE_IP2EX, "Computone IntelliPort Plus"),
 	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7810,	"AIC-7810 RAID"),
 	DEVICE( ADAPTEC,	ADAPTEC_7821,	"AIC-7860"),
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/audio/amd7930.c linux.14p6/drivers/sbus/audio/amd7930.c
--- linux.vanilla/drivers/sbus/audio/amd7930.c	Wed Oct 20 01:12:39 1999
+++ linux.14p6/drivers/sbus/audio/amd7930.c	Tue Nov 16 15:13:28 1999
@@ -135,7 +135,7 @@
 	unsigned char xmit_idle_char;
 
 	/* Callback routine (and argument) when output is done on */
-	void (*output_callback)();
+	void (*output_callback)(void *, unsigned char);
 	void * output_callback_arg;
 
 	/* Current buffer that the driver is recording on channel */
@@ -144,7 +144,7 @@
 	volatile unsigned long input_limit;
 
 	/* Callback routine (and argument) when input is done on */
-	void (*input_callback)();
+	void (*input_callback)(void *, unsigned char, unsigned long);
 	void * input_callback_arg;
 };
 
@@ -520,7 +520,7 @@
 			channel->input_count = 0;
 			if (channel->input_callback)
 				(*channel->input_callback)
-					(channel->input_callback_arg, 1);
+					(channel->input_callback_arg, 1, 0);
 		}
 	}
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/audio/cs4215.h linux.14p6/drivers/sbus/audio/cs4215.h
--- linux.vanilla/drivers/sbus/audio/cs4215.h	Tue Mar 16 00:11:30 1999
+++ linux.14p6/drivers/sbus/audio/cs4215.h	Tue Nov 16 15:13:28 1999
@@ -11,11 +11,12 @@
 struct cs4215 {
 	__u8	data[4];	/* Data mode: Time slots 5-8 */
 	__u8	ctrl[4];	/* Ctrl mode: Time slots 1-4 */
-	__volatile__ struct dbri_mem td;
-	__volatile__ struct dbri_mem rd;
 	__u8	onboard;
-	__u32	status;
-        __u32	version;
+	__volatile__ __u32	status;
+        __volatile__ __u32	version;
+        int offset;
+        int ctrlmode;
+        int datamode;
 };
 
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/audio/cs4231.c linux.14p6/drivers/sbus/audio/cs4231.c
--- linux.vanilla/drivers/sbus/audio/cs4231.c	Wed Oct 20 01:12:40 1999
+++ linux.14p6/drivers/sbus/audio/cs4231.c	Tue Nov 16 15:13:28 1999
@@ -904,8 +904,9 @@
     if (l < 0) l = 0;
   }
 
-  l_adj = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
-  r_adj = r * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
+  /* See cs4231_play_gain for why we do things this way. -DaveM */
+  l_adj = ((l * (CS4231_MAX_GAIN + 1)) + AUDIO_MAX_GAIN) / (AUDIO_MAX_GAIN + 1);
+  r_adj = ((r * (CS4231_MAX_GAIN + 1)) + AUDIO_MAX_GAIN) / (AUDIO_MAX_GAIN + 1);
   
   CS4231_WRITE8(cs4231_chip, &(cs4231_chip->regs->iar), 0x0);
   old_gain = CS4231_READ8(cs4231_chip, &(cs4231_chip->regs->idr));
@@ -932,7 +933,7 @@
   int tmp = 0, r, l, r_adj, l_adj;
   unsigned char old_gain;
 
-  tprintk(("in play_gain: %d %c\n", value, balance));
+  tprintk(("in play_gain: %d %x, ", value, balance));
   r = l = value;
   if (balance < AUDIO_MID_BALANCE) {
     r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT));
@@ -942,11 +943,15 @@
     if (l < 0) l = 0;
   }
 
+  /* We have to round up else several OSS programs
+   * get confused because for small increments we
+   * would otherwise not increase the volume ever. -DaveM
+   */
   (l == 0) ? (l_adj = CS4231_MAX_DEV_ATEN) : (l_adj = CS4231_MAX_ATEN - 
-                                              (l * (CS4231_MAX_ATEN + 1) / 
+                              (((l * (CS4231_MAX_ATEN + 1)) + AUDIO_MAX_GAIN) / 
                                                (AUDIO_MAX_GAIN + 1)));
   (r == 0) ? (r_adj = CS4231_MAX_DEV_ATEN) : (r_adj = CS4231_MAX_ATEN -
-                                              (r * (CS4231_MAX_ATEN + 1) /
+                              (((r * (CS4231_MAX_ATEN + 1)) + AUDIO_MAX_GAIN) /
                                                (AUDIO_MAX_GAIN + 1)));
   
   CS4231_WRITE8(cs4231_chip, &(cs4231_chip->regs->iar), 0x6);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/audio/dbri.c linux.14p6/drivers/sbus/audio/dbri.c
--- linux.vanilla/drivers/sbus/audio/dbri.c	Wed Oct 20 01:12:40 1999
+++ linux.14p6/drivers/sbus/audio/dbri.c	Tue Nov 16 15:13:28 1999
@@ -64,6 +64,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/malloc.h>
+#include <linux/ioport.h>
 #include <linux/version.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
@@ -229,7 +230,7 @@
                         dbri->regs->reg8, dbri->regs->reg9)); 
 
 	dbri->regs->reg0 = D_R; /* Soft Reset */
-	for(i = 0; (dbri->regs->reg0 & D_R) && i < 10; i++)
+	for(i = 0; (dbri->regs->reg0 & D_R) && i < 64; i++)
 		udelay(10);
 }
 
@@ -370,9 +371,9 @@
         int rd;
         int status;
         void *buffer;
-        int count;
+        int count = 0;
         void (*callback)(void *, int, unsigned int) = NULL;
-        void *callback_arg;
+        void *callback_arg = NULL;
 
         if ((pipe < 0) || (pipe > 15)) {
                 printk("DBRI: invalid pipe in reception_complete_intr\n");
@@ -804,6 +805,7 @@
         dbri_cmdsend(dbri, cmd);
 }
 
+#if 0
 /* unlink_time_slot()
  *
  * I don't use this function, so it's basically untested.
@@ -865,6 +867,7 @@
 
         dbri_cmdsend(dbri, cmd);
 }
+#endif
 
 /* xmit_fixed() / recv_fixed()
  *
@@ -1475,7 +1478,7 @@
          * codec at this location on the CHI, so return false.
          */
 
-        i = 10;
+        i = 64;
         while (((dbri->mm.status & 0xe4) != 0x20) && --i) udelay(125);
         if (i == 0) {
 		return 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/openprom.c linux.14p6/drivers/sbus/char/openprom.c
--- linux.vanilla/drivers/sbus/char/openprom.c	Wed Oct 20 01:12:40 1999
+++ linux.14p6/drivers/sbus/char/openprom.c	Tue Nov 16 15:13:28 1999
@@ -209,7 +209,7 @@
 				     buf, len);
 		restore_flags(flags);
 
-		if (error <= 0)
+		if (error < 0)
 			error = -EINVAL;
 		break;
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/pcikbd.c linux.14p6/drivers/sbus/char/pcikbd.c
--- linux.vanilla/drivers/sbus/char/pcikbd.c	Sat Aug 14 02:26:16 1999
+++ linux.14p6/drivers/sbus/char/pcikbd.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: pcikbd.c,v 1.27 1999/05/09 06:40:47 ecd Exp $
+/* $Id: pcikbd.c,v 1.27.2.1 1999/10/01 05:09:36 davem Exp $
  * pcikbd.c: Ultra/AX PC keyboard support.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -934,10 +934,34 @@
 	aux_fasync,
 };
 
+static int aux_no_open(struct inode *inode, struct file *file)
+{
+	return -ENODEV;
+}
+
+struct file_operations psaux_no_fops = {
+	NULL,		/* seek */
+	NULL,
+	NULL,
+	NULL, 		/* readdir */
+	NULL,
+	NULL, 		/* ioctl */
+	NULL,		/* mmap */
+	aux_no_open,
+	NULL,		/* flush */
+	NULL,
+	NULL,
+	NULL,
+};
+
 static struct miscdevice psaux_mouse = {
 	PSMOUSE_MINOR, "ps2aux", &psaux_fops
 };
 
+static struct miscdevice psaux_no_mouse = {
+	PSMOUSE_MINOR, "ps2aux", &psaux_no_fops
+};
+
 __initfunc(int pcimouse_init(void))
 {
 	struct linux_ebus *ebus;
@@ -947,7 +971,7 @@
 	if (pcikbd_mrcoffee) {
 		if ((pcimouse_iobase = pcikbd_iobase) == 0) {
 			printk("pcimouse_init: no 8042 given\n");
-			return -ENODEV;
+			goto do_enodev;
 		}
 		pcimouse_irq = pcikbd_irq;
 	} else {
@@ -962,7 +986,7 @@
 			}
 		}
 		printk("pcimouse_init: no 8042 found\n");
-		return -ENODEV;
+		goto do_enodev;
 
 found:
 		pcimouse_iobase = child->base_address[0];
@@ -987,7 +1011,7 @@
 		        SA_SHIRQ, "mouse", (void *)pcimouse_iobase)) {
 		printk("8042: Cannot register IRQ %s\n",
 		       __irq_itoa(pcimouse_irq));
-		return -ENODEV;
+		goto do_enodev;
 	}
 
 	printk("8042(mouse) at %lx (irq %s)\n", pcimouse_iobase,
@@ -1016,8 +1040,17 @@
 	aux_end_atomic();
 
 	return 0;
+
+do_enodev:
+	misc_register(&psaux_no_mouse);
+	return -ENODEV;
 }
 
+__initfunc(int pcimouse_no_init(void))
+{
+	misc_register(&psaux_no_mouse);
+	return -ENODEV;
+}
 
 __initfunc(int ps2kbd_probe(unsigned long *memory_start))
 {
@@ -1034,7 +1067,7 @@
 	len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop));
 	if (len < 0) {
 		printk("ps2kbd_probe: no name of root node\n");
-		return -ENODEV;
+		goto do_enodev;
 	}
 	if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) {
 		pcikbd_mrcoffee = 1;	/* Brain damage detected */
@@ -1047,7 +1080,7 @@
         node = prom_getchild(prom_root_node);
 	node = prom_searchsiblings(node, "aliases");
 	if (!node)
-		return -ENODEV;
+		goto do_enodev;
 
 	len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
 	if (len > 0) {
@@ -1055,7 +1088,7 @@
 		kbnode = prom_finddevice(prop);
 	}
 	if (!kbnode)
-		return -ENODEV;
+		goto do_enodev;
 
 	len = prom_getproperty(node, "mouse", prop, sizeof(prop));
 	if (len > 0) {
@@ -1063,7 +1096,7 @@
 		msnode = prom_finddevice(prop);
 	}
 	if (!msnode)
-		return -ENODEV;
+		goto do_enodev;
 
 	/*
 	 * Find matching EBus nodes...
@@ -1131,6 +1164,8 @@
 		pnode = prom_getsibling(pnode);
 		pnode = prom_searchsiblings(pnode, "pci");
 	}
+do_enodev:
+	sunkbd_setinitfunc(memory_start, pcimouse_no_init);
 	return -ENODEV;
 
 found:
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/sab82532.c linux.14p6/drivers/sbus/char/sab82532.c
--- linux.vanilla/drivers/sbus/char/sab82532.c	Thu Mar 25 17:23:34 1999
+++ linux.14p6/drivers/sbus/char/sab82532.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.30 1999/03/24 11:34:52 davem Exp $
+/* $Id: sab82532.c,v 1.30.2.2 1999/10/12 14:11:20 davem Exp $
  * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -36,6 +36,12 @@
 
 static DECLARE_TASK_QUEUE(tq_serial);
 
+/* This is (one of many) a special gross hack to allow SU and
+ * SAB serials to co-exist on the same machine. -DaveM
+ */
+#undef SERIAL_BH
+#define SERIAL_BH	AURORA_BH
+
 static struct tty_driver serial_driver, callout_driver;
 static int sab82532_refcount;
 
@@ -250,6 +256,8 @@
 static void batten_down_hatches(struct sab82532 *info)
 {
 	unsigned char saved_rfc;
+	
+	if (!stop_a_enabled) return;
 
 	/* If we are doing kadb, we call the debugger
 	 * else we just drop into the boot monitor.
@@ -2136,7 +2144,7 @@
 
 __initfunc(static inline void show_serial_version(void))
 {
-	char *revision = "$Revision: 1.30 $";
+	char *revision = "$Revision: 1.30.2.2 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2146,6 +2154,8 @@
 	printk("SAB82532 serial driver version %s\n", serial_version);
 }
 
+extern int su_num_ports;
+
 /*
  * The serial driver boot-time initialization code!
  */
@@ -2169,7 +2179,7 @@
 	serial_driver.driver_name = "serial";
 	serial_driver.name = "ttyS";
 	serial_driver.major = TTY_MAJOR;
-	serial_driver.minor_start = 64;
+	serial_driver.minor_start = 64 + su_num_ports;
 	serial_driver.num = NR_PORTS;
 	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
 	serial_driver.subtype = SERIAL_TYPE_NORMAL;
@@ -2267,7 +2277,7 @@
 	
 		printk(KERN_INFO
 		       "ttyS%02d at 0x%lx (irq = %s) is a SAB82532 %s\n",
-		       info->line, (unsigned long)info->regs,
+		       info->line + su_num_ports, (unsigned long)info->regs,
 		       __irq_itoa(info->irq), sab82532_version[info->type]);
 	}
 
@@ -2565,8 +2575,9 @@
 __initfunc(int sab82532_console_init(void))
 {
 	extern int con_is_present(void);
+	extern int su_console_registered;
 
-	if (con_is_present())
+	if (con_is_present() || su_console_registered)
 		return 0;
 
 	if (!sab82532_chain) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/su.c linux.14p6/drivers/sbus/char/su.c
--- linux.vanilla/drivers/sbus/char/su.c	Tue Mar 16 00:11:30 1999
+++ linux.14p6/drivers/sbus/char/su.c	Tue Nov 16 15:13:28 1999
@@ -1,8 +1,8 @@
-/* $Id: su.c,v 1.18 1999/01/02 16:47:37 davem Exp $
+/* $Id: su.c,v 1.18.2.5 1999/10/14 08:44:35 davem Exp $
  * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- * Coypright (C) 1998  Pete Zaitcev   (zaitcev@metabyte.com)
+ * Copyright (C) 1998-1999  Pete Zaitcev   (zaitcev@metabyte.com)
  *
  * This is mainly a variation of drivers/char/serial.c,
  * credits go to authors mentioned therein.
@@ -92,6 +92,11 @@
 int su_serial_console_init(void);
 #endif
 
+enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT };
+static char *su_typev[] = { "???", "mouse", "kbd", "serial" };
+
+#define SU_PROPSIZE	128
+
 /*
  * serial.c saves memory when it allocates async_info upon first open.
  * We have parts of state structure together because we do call startup
@@ -107,8 +112,8 @@
 	int		 line;
 	int		 cflag;
 
-	int		 kbd_node;
-	int		 ms_node;
+	enum su_type	 port_type;	/* Hookup type: e.g. mouse */
+	int		 is_console;
 	int		 port_node;
 
 	char		 name[16];
@@ -145,6 +150,18 @@
 	unsigned long		last_active;	/* For async_struct, to be */
 };
 
+/*
+ * Scan status structure.
+ * "prop" is a local variable but it eats stack to keep it in each
+ * stack frame of a recursive procedure.
+ */
+struct su_probe_scan {
+	int msnode, kbnode;	/* PROM nodes for mouse and keyboard */
+	int msx, kbx;		/* minors for mouse and keyboard */
+	int devices;		/* scan index */
+	char prop[SU_PROPSIZE];
+};
+
 static char *serial_name = "PCIO serial driver";
 static char serial_version[16];
 
@@ -223,8 +240,6 @@
 	return 0;
 }
 
-#ifdef __sparc_v9__
-
 static inline
 unsigned int su_inb(struct su_struct *info, unsigned long offset)
 {
@@ -234,20 +249,7 @@
 static inline void
 su_outb(struct su_struct *info, unsigned long offset, int value)
 {
-	outb(value, info->port + offset);
-}
-
-#else
-
-static inline
-unsigned int su_inb(struct su_struct *info, unsigned long offset)
-{
-	return (unsigned int)(*(volatile unsigned char *)(info->port + offset));
-}
-
-static inline void
-su_outb(struct su_struct *info, unsigned long offset, int value)
-{
+#ifndef __sparc_v9__
 	/*
 	 * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are
 	 * connected with a gate then go to SlavIO. When IRQ4 goes tristated
@@ -257,10 +259,9 @@
 	 * This problem is similar to what Alpha people suffer, see serial.c.
 	 */
 	if (offset == UART_MCR) value |= UART_MCR_OUT2;
-	*(volatile unsigned char *)(info->port + offset) = value;
-}
-
 #endif
+	outb(value, info->port + offset);
+}
 
 #define serial_in(info, off)		su_inb(info, off)
 #define serial_inp(info, off)		su_inb(info, off)
@@ -341,14 +342,14 @@
 }
 
 static __inline__ void
-receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs)
+receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs, int is_brk)
 {
 	unsigned char status = 0;
 	unsigned char ch;
 
 	do {
 		ch = serial_inp(info, UART_RX);
-		if (info->kbd_node) {
+		if (info->port_type == SU_PORT_KBD) {
 			if(ch == SUNKBD_RESET) {
                         	l1a_state.kbd_id = 1;
                         	l1a_state.l1_down = 0;
@@ -368,7 +369,7 @@
                 	}
                 	sunkbd_inchar(ch, regs);
 		} else {
-			sun_mouse_inbyte(ch);
+			sun_mouse_inbyte(ch, is_brk);
 		}
 
 		status = su_inb(info, UART_LSR);
@@ -380,12 +381,15 @@
 {
 	struct tty_struct *tty = info->tty;
 	unsigned char ch;
-	int ignored = 0;
+	int ignored = 0, saw_console_brk = 0;
 	struct	async_icount *icount;
 
 	icount = &info->icount;
 	do {
 		ch = serial_inp(info, UART_RX);
+		if (info->is_console &&
+		    (ch == 0 || (*status &UART_LSR_BI)))
+			saw_console_brk = 1;
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 			break;
 		*tty->flip.char_buf_ptr = ch;
@@ -461,6 +465,8 @@
 	printk("E%02x.R%d", *status, tty->flip.count);
 #endif
 	tty_flip_buffer_push(tty);
+	if (saw_console_brk != 0)
+		batten_down_hatches();
 }
 
 static __inline__ void
@@ -529,7 +535,7 @@
 			    (status & UART_MSR_DCD))
 				hardpps();
 #endif
-	}
+		}
 		if (status & UART_MSR_DCTS)
 			icount->cts++;
 		wake_up_interruptible(&info->delta_msr_wait);
@@ -598,8 +604,9 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("status = %x...", status);
 #endif
-	if (status & UART_LSR_DR)
-		receive_kbd_ms_chars(info, regs);
+	if ((status & UART_LSR_DR) || (status & UART_LSR_BI))
+		receive_kbd_ms_chars(info, regs,
+				     (status & UART_LSR_BI) != 0);
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("end.\n");
@@ -775,7 +782,7 @@
 	/*
 	 * Allocate the IRQ if necessary
 	 */
-	if (info->kbd_node || info->ms_node) {
+	if (info->port_type != SU_PORT_PORT) {
 		retval = request_irq(info->irq, su_kbd_ms_interrupt,
 				     SA_SHIRQ, info->name, info);
 	} else {
@@ -956,7 +963,7 @@
 	int		bits;
 	unsigned long	flags;
 
-	if (!info->kbd_node && !info->ms_node) {
+	if (info->port_type == SU_PORT_PORT) {
 		if (!info->tty || !info->tty->termios)
 			return;
 		if (!info->port)
@@ -1133,9 +1140,9 @@
 	struct su_struct *info = su_table;
 	int lsr;
 
-	if (!info->kbd_node)
+	if (info->port_type != SU_PORT_KBD)
 		++info;
-	if (!info)
+	if (info->port_type != SU_PORT_KBD)
 		return;
 
 	do {
@@ -1151,9 +1158,9 @@
 {
 	struct su_struct *info = su_table;
 
-	if (!info->ms_node)
+	if (info->port_type != SU_PORT_MS)
 		++info;
-	if (!info)
+	if (info->port_type != SU_PORT_MS)
 		return;
 
 	info->cflag &= ~(CBAUDEX | CBAUD);
@@ -2026,6 +2033,8 @@
 	if ((line < 0) || (line >= NR_PORTS))
 		return -ENODEV;
 	info = su_table + line;
+	if (info->type == PORT_UNKNOWN)
+		return -ENODEV;
 	info->count++;
 	tty->driver_data = info;
 	info->tty = tty;
@@ -2202,9 +2211,9 @@
 
 /*
  * ---------------------------------------------------------------------
- * su_init() and friends
+ * su_XXX_init() and friends
  *
- * su_init() is called at boot-time to initialize the serial driver.
+ * su_XXX_init() is called at boot-time to initialize the serial driver.
  * ---------------------------------------------------------------------
  */
 
@@ -2215,7 +2224,7 @@
  */
 __initfunc(static __inline__ void show_su_version(void))
 {
-	char *revision = "$Revision: 1.18 $";
+	char *revision = "$Revision: 1.18.2.5 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2226,8 +2235,8 @@
 }
 
 /*
- * This routine is called by su_init() to initialize a specific serial
- * port.  It determines what type of UART chip this serial port is
+ * This routine is called by su_{serial|kbd_ms}_init() to initialize a specific
+ * serial port.  It determines what type of UART chip this serial port is
  * using: 8250, 16450, 16550, 16550A.  The important question is
  * whether or not this UART is a 16550A, since this will determine
  * whether or not we can use its FIFO features.
@@ -2236,38 +2245,38 @@
 autoconfig(struct su_struct *info)
 {
 	unsigned char status1, status2, scratch, scratch2;
-#ifdef __sparc_v9__
 	struct linux_ebus_device *dev = 0;
 	struct linux_ebus *ebus;
-#else
+#ifndef __sparc_v9__
 	struct linux_prom_registers reg0;
 #endif
 	unsigned long flags;
 
-#ifdef __sparc_v9__
+	if (!info->port_node || !info->port_type)
+		return;
+
+	/*
+	 * First we look for Ebus-bases su's
+	 */
 	for_each_ebus(ebus) {
 		for_each_ebusdev(dev, ebus) {
-			if (!strncmp(dev->prom_name, "su", 2)) {
-				if (dev->prom_node == info->kbd_node)
-					goto ebus_done;
-				if (dev->prom_node == info->ms_node)
-					goto ebus_done;
+			if (dev->prom_node == info->port_node) {
+				info->port = dev->base_address[0];
+				info->irq = dev->irqs[0];
+				goto ebus_done;
 			}
 		}
 	}
-ebus_done:
-	if (!dev)
-		return;
-
-	info->port = dev->base_address[0];
-	if (check_region(info->port, 8))
-		return;
 
-	info->irq = dev->irqs[0];
+#ifdef __sparc_v9__
+	/*
+	 * Not on Ebus, bailing.
+	 */
+	return;
 #else
-	if (!info->port_node)
-		return;
-
+	/*
+	 * Not on Ebus, must be OBIO.
+	 */
 	if (prom_getproperty(info->port_node, "reg",
 	    (char *)&reg0, sizeof(reg0)) == -1) {
 		prom_printf("su: no \"reg\" property\n");
@@ -2279,21 +2288,24 @@
 		prom_printf("su: cannot map\n");
 		return;
 	}
+
 	/*
-	 * There is no intr property on MrCoffee, so hardwire it. Krups?
+	 * There is no intr property on MrCoffee, so hardwire it.
 	 */
 	info->irq = IRQ_4M(13);
 #endif
 
-#ifdef DEBUG_SERIAL_OPEN
-	printk("Found 'su' at %016lx IRQ %s\n", dev->base_address[0],
-	       __irq_itoa(dev->irqs[0]));
+ebus_done:
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Found 'su' at %016lx IRQ %s\n", info->port,
+		__irq_itoa(info->irq));
 #endif
 
 	info->magic = SERIAL_MAGIC;
 
 	save_flags(flags); cli();
-	
+
 	/*
 	 * Do a simple existence test first; if we fail this, there's
 	 * no point trying anything else.
@@ -2312,17 +2324,20 @@
 		return;		/* We failed; there's nothing here */
 	}
 
-#if 0 /* P3: This does not work on MrCoffee. OUT2 is 0x80 - should work... */
 	scratch = serial_inp(info, UART_MCR);
 	serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
 	serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
 	status1 = serial_inp(info, UART_MSR) & 0xF0;
 	serial_outp(info, UART_MCR, scratch);
 	if (status1 != 0x90) {
+		/*
+		 * This code fragment used to fail, now it fixed itself.
+		 * We keep the printout for a case.
+		 */
+		printk("su: loopback returned status 0x%02x\n", status1);
 		restore_flags(flags);
 		return;
 	} 
-#endif
 
 	scratch2 = serial_in(info, UART_LCR);
 	serial_outp(info, UART_LCR, 0xBF);	/* set up for StarTech test */
@@ -2389,11 +2404,7 @@
 		return;
 	}
 
-	if (info->kbd_node || info->ms_node)
-		sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd");
-	else
-		strcpy(info->name, "su(serial)");
-
+	sprintf(info->name, "su(%s)", su_typev[info->port_type]);
 #ifdef __sparc_v9__
 	request_region(info->port, 8, info->name);
 #endif
@@ -2409,6 +2420,11 @@
 	restore_flags(flags);
 }
 
+/* This is used by the SAB driver to adjust where its minor
+ * numbers start, we always are probed for first.
+ */
+int su_num_ports = 0;
+
 /*
  * The serial driver boot-time initialization code!
  */
@@ -2499,11 +2515,18 @@
 		if (info->type == PORT_UNKNOWN)
 			continue;
 
-		printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n",
-		       info->name, info->port, __irq_itoa(info->irq),
+		printk(KERN_INFO "%s at 0x%lx (tty %d irq %s) is a %s\n",
+		       info->name, (long)info->port, i, __irq_itoa(info->irq),
 		       uart_config[info->type].name);
 	}
 
+	for (i = 0, info = su_table; i < NR_PORTS; i++, info++)
+		if (info->type == PORT_UNKNOWN)
+			break;
+
+	su_num_ports = i;
+	serial_driver.num = callout_driver.num = i;
+
 	return 0;
 }
 
@@ -2519,7 +2542,7 @@
 		info->type = PORT_UNKNOWN;
 		info->baud_base = BAUD_BASE;
 
-		if (info->kbd_node)
+		if (info->port_type == SU_PORT_KBD)
 			info->cflag = B1200 | CS8 | CLOCAL | CREAD;
 		else
 			info->cflag = B4800 | CS8 | CLOCAL | CREAD;
@@ -2528,12 +2551,12 @@
 		if (info->type == PORT_UNKNOWN)
 			continue;
 
-		printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n",
+		printk(KERN_INFO "%s at 0x%lx (irq = %s) is a %s\n",
 		       info->name, info->port, __irq_itoa(info->irq),
 		       uart_config[info->type].name);
 
 		startup(info);
-		if (info->kbd_node)
+		if (info->port_type == SU_PORT_KBD)
 			keyboard_zsinit(su_put_char_kbd);
 		else
 			sun_mouse_zsinit();
@@ -2541,154 +2564,131 @@
 	return 0;
 }
 
-__initfunc(int su_probe (unsigned long *memory_start))
+/*
+ * We got several platforms which present 'su' in different parts
+ * of device tree. 'su' may be found under obio, ebus, isa and pci.
+ * We walk over the tree and find them wherever PROM hides them.
+ */
+__initfunc(void su_probe_any(struct su_probe_scan *t, int sunode))
 {
-	struct su_struct *info = su_table;
-        int node, enode, tnode, sunode;
-	int kbnode = 0, msnode = 0;
-	int devices = 0;
-	char prop[128];
+	struct su_struct *info;
 	int len;
 
-	/*
-	 * Find su on MrCoffee. We return OK code if find any.
-	 * Then su_init finds every one and initializes them.
-	 * We do this early because MrCoffee got no aliases.
-	 */
-	node = prom_getchild(prom_root_node);
-	if ((node = prom_searchsiblings(node, "obio")) != 0) {
-		if ((sunode = prom_getchild(node)) != 0) {
-			if ((sunode = prom_searchsiblings(sunode, "su")) != 0) {
-				info->port_node = sunode;
-#ifdef CONFIG_SERIAL_CONSOLE
-				/*
-				 * Console must be initiated after the generic
-				 * initialization.
-				 * sunserial_setinitfunc inverts order, so
-				 * call this before next one.
-				 */
-				sunserial_setinitfunc(memory_start,
-						      su_serial_console_init);
-#endif
-        			sunserial_setinitfunc(memory_start,
-						      su_serial_init);
-				return 0;
+	if (t->devices >= NR_PORTS)
+		return;
+
+	for (; sunode != 0; sunode = prom_getsibling(sunode)) {
+		len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE);
+		if (len <= 1)
+			continue;		/* Broken PROM node */
+		if (strncmp(t->prop, "su", len) == 0 ||
+		    strncmp(t->prop, "serial", len) == 0 ||
+		    strncmp(t->prop, "su_pnp", len) == 0) {
+			info = &su_table[t->devices];
+			if (t->kbnode != 0 && sunode == t->kbnode) {
+				t->kbx = t->devices;
+				info->port_type = SU_PORT_KBD;
+			} else if (t->msnode != 0 && sunode == t->msnode) {
+				t->msx = t->devices;
+				info->port_type = SU_PORT_MS;
+			} else {
+				info->port_type = SU_PORT_PORT;
 			}
+			info->is_console = 0;
+			info->port_node = sunode;
+			++t->devices;
+		} else {
+			su_probe_any(t, prom_getchild(sunode));
 		}
 	}
+}
+
+__initfunc(int su_probe (unsigned long *memory_start))
+{
+	int node;
+	int len;
+	struct su_probe_scan scan;
+
+	/*
+	 * First, we scan the tree.
+	 */
+	scan.devices = 0;
+	scan.msx = -1;
+	scan.kbx = -1;
+	scan.kbnode = 0;
+	scan.msnode = 0;
 
 	/*
 	 * Get the nodes for keyboard and mouse from 'aliases'...
 	 */
         node = prom_getchild(prom_root_node);
 	node = prom_searchsiblings(node, "aliases");
-	if (!node)
-		return -ENODEV;
+	if (node != 0) {
 
-	len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
-	if (len > 0) {
-		prop[len] = 0;
-		kbnode = prom_finddevice(prop);
-	}
-	if (!kbnode)
-		return -ENODEV;
+		len = prom_getproperty(node, "keyboard", scan.prop,SU_PROPSIZE);
+		if (len > 0) {
+			scan.prop[len] = 0;
+			scan.kbnode = prom_finddevice(scan.prop);
+		}
 
-	len = prom_getproperty(node, "mouse", prop, sizeof(prop));
-	if (len > 0) {
-		prop[len] = 0;
-		msnode = prom_finddevice(prop);
+		len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE);
+		if (len > 0) {
+			scan.prop[len] = 0;
+			scan.msnode = prom_finddevice(scan.prop);
+		}
 	}
-	if (!msnode)
-		return -ENODEV;
 
-	/*
-	 * Find matching EBus nodes...
-	 */
-        node = prom_getchild(prom_root_node);
-	if ((node = prom_searchsiblings(node, "pci")) == 0) {
-		return -ENODEV;		/* Plain sparc */
-	}
+	su_probe_any(&scan, prom_getchild(prom_root_node));
 
 	/*
-	 * Check for SUNW,sabre on Ultra 5/10/AXi.
-	 */
-	len = prom_getproperty(node, "model", prop, sizeof(prop));
-	if ((len > 0) && !strncmp(prop, "SUNW,sabre", len)) {
-        	node = prom_getchild(node);
-		node = prom_searchsiblings(node, "pci");
+	 * Second, we process the special case of keyboard and mouse.
+	 *
+	 * Currently if we got keyboard and mouse hooked to "su" ports
+	 * we do not use any possible remaining "su" as a serial port.
+	 * Thus, we ignore values of .msx and .kbx, then compact ports.
+	 * Those who want to address this issue need to merge
+	 * su_serial_init() and su_ms_kbd_init().
+	 */
+	if (scan.msx != -1 && scan.kbx != -1) {
+		su_table[0].port_type = SU_PORT_MS;
+		su_table[0].is_console = 0;
+		su_table[0].port_node = scan.msnode;
+		su_table[1].port_type = SU_PORT_KBD;
+		su_table[1].is_console = 0;
+		su_table[1].port_node = scan.kbnode;
+
+        	sunserial_setinitfunc(memory_start, su_kbd_ms_init);
+        	rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+		sunkbd_setinitfunc(memory_start, sun_kbd_init);
+		kbd_ops.compute_shiftstate = sun_compute_shiftstate;
+		kbd_ops.setledstate = sun_setledstate;
+		kbd_ops.getledstate = sun_getledstate;
+		kbd_ops.setkeycode = sun_setkeycode;
+		kbd_ops.getkeycode = sun_getkeycode;
+#ifdef CONFIG_PCI
+		sunkbd_install_keymaps(memory_start, sun_key_maps,
+		    sun_keymap_count, sun_func_buf, sun_func_table,
+		    sun_funcbufsize, sun_funcbufleft,
+		    sun_accent_table, sun_accent_table_size);
+#endif
+		return 0;
+	}
+	if (scan.msx != -1 || scan.kbx != -1) {
+		printk("su_probe: cannot match keyboard and mouse, confused\n");
+		return -ENODEV;
 	}
 
+	if (scan.devices == 0)
+		return -ENODEV;
+
+#ifdef CONFIG_SERIAL_CONSOLE
 	/*
-	 * For each PCI bus...
+	 * Console must be initiated after the generic initialization.
+	 * sunserial_setinitfunc inverts order, so call this before next one.
 	 */
-	while (node) {
-		enode = prom_getchild(node);
-		enode = prom_searchsiblings(enode, "ebus");
-
-		/*
-		 * For each EBus on this PCI...
-		 */
-		while (enode) {
-			sunode = prom_getchild(enode);
-			tnode = prom_searchsiblings(sunode, "su");
-			if (!tnode)
-				tnode = prom_searchsiblings(sunode, "su_pnp");
-			sunode = tnode;
-
-			/*
-			 * For each 'su' on this EBus...
-			 */
-			while (sunode) {
-				/*
-				 * Does it match?
-				 */
-				if (sunode == kbnode) {
-					info->kbd_node = sunode;
-					++info;
-					++devices;
-				}
-				if (sunode == msnode) {
-					info->ms_node = sunode;
-					++info;
-					++devices;
-				}
-
-				/*
-				 * Found everything we need?
-				 */
-				if (devices == 2)
-					goto found;
-
-				sunode = prom_getsibling(sunode);
-				tnode = prom_searchsiblings(sunode, "su");
-				if (!tnode)
-					tnode = prom_searchsiblings(sunode,
-								    "su_pnp");
-				sunode = tnode;
-			}
-			enode = prom_getsibling(enode);
-			enode = prom_searchsiblings(enode, "ebus");
-		}
-		node = prom_getsibling(node);
-		node = prom_searchsiblings(node, "pci");
-	}
-	return -ENODEV;
-
-found:
-        sunserial_setinitfunc(memory_start, su_kbd_ms_init);
-        rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
-	sunkbd_setinitfunc(memory_start, sun_kbd_init);
-	kbd_ops.compute_shiftstate = sun_compute_shiftstate;
-	kbd_ops.setledstate = sun_setledstate;
-	kbd_ops.getledstate = sun_getledstate;
-	kbd_ops.setkeycode = sun_setkeycode;
-	kbd_ops.getkeycode = sun_getkeycode;
-#ifdef CONFIG_PCI
-	sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count,
-			       sun_func_buf, sun_func_table,
-			       sun_funcbufsize, sun_funcbufleft,
-			       sun_accent_table, sun_accent_table_size);
+	sunserial_setinitfunc(memory_start, su_serial_console_init);
 #endif
+	sunserial_setinitfunc(memory_start, su_serial_init);
 	return 0;
 }
 
@@ -2910,6 +2910,8 @@
 	if (su_inb(info, UART_LSR) == 0xff)
 		return -1;
 
+	info->is_console = 1;
+
 	return 0;
 }
 
@@ -2927,6 +2929,8 @@
 	NULL
 };
 
+int su_console_registered = 0;
+
 /*
  *	Register console.
  */
@@ -2942,6 +2946,7 @@
 		return 0;
 	sercons.index = 0;
 	register_console(&sercons);
+	su_console_registered = 1;
 	return 0;
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/sunkeymap.c linux.14p6/drivers/sbus/char/sunkeymap.c
--- linux.vanilla/drivers/sbus/char/sunkeymap.c	Mon Jan 27 08:42:54 1997
+++ linux.14p6/drivers/sbus/char/sunkeymap.c	Tue Nov 16 15:13:28 1999
@@ -1,3 +1,4 @@
+
 /* Do not edit this file! It was automatically generated by   */
 /*    loadkeys --mktable defkeymap.map > defkeymap.c          */
 
@@ -30,7 +31,7 @@
 	0xf110,	0xf111,	0xf112,	0xf703,	0xf603,	0xf11d,	0xf200,	0xf203,
 	0xf601,	0xf200,	0xf200,	0xf600,	0xf602,	0xf01b,	0xf021,	0xf040,
 	0xf023,	0xf024,	0xf025,	0xf05e,	0xf026,	0xf02a,	0xf028,	0xf029,
-	0xf05f,	0xf02b,	0xf07e,	0xf07f,	0xf115,	0xf200,	0xf30d,	0xf30c,
+	0xf05f,	0xf02b,	0xf07e,	0xf07f,	0xf115,	0xf03d,	0xf30d,	0xf30c,
 	0xf200,	0xf200,	0xf310,	0xf200,	0xf114,	0xf009,	0xfb51,	0xfb57,
 	0xfb45,	0xfb52,	0xfb54,	0xfb59,	0xfb55,	0xfb49,	0xfb4f,	0xfb50,
 	0xf07b,	0xf07d,	0xf07f,	0xf20e,	0xf307,	0xf308,	0xf309,	0xf30b,
@@ -38,7 +39,7 @@
 	0xfb46,	0xfb47,	0xfb48,	0xfb4a,	0xfb4b,	0xfb4c,	0xf03a,	0xf022,
 	0xf07c,	0xf201,	0xf30e,	0xf304,	0xf305,	0xf306,	0xf300,	0xf200,
 	0xf20b,	0xf200,	0xf208,	0xf700,	0xfb5a,	0xfb58,	0xfb43,	0xfb56,
-	0xfb42,	0xfb4e,	0xfb4d,	0xf03c,	0xf03e,	0xf03f,	0xf700,	0xf200,
+	0xfb42,	0xfb4e,	0xfb4d,	0xf03c,	0xf03e,	0xf03f,	0xf700,	0xf00a,
 	0xf301,	0xf302,	0xf303,	0xf200,	0xf200,	0xf200,	0xf11b,	0xf207,
 	0xf200,	0xf020,	0xf200,	0xf20a,	0xf200,	0xf30a,	0xf200,	0xf200,
 };
@@ -49,7 +50,7 @@
 	0xf512,	0xf513,	0xf514,	0xf703,	0xf603,	0xf11d,	0xf200,	0xf202,
 	0xf601,	0xf200,	0xf200,	0xf600,	0xf602,	0xf200,	0xf200,	0xf040,
 	0xf200,	0xf024,	0xf200,	0xf200,	0xf07b,	0xf05b,	0xf05d,	0xf07d,
-	0xf05c,	0xf200,	0xf200,	0xf200,	0xf115,	0xf200,	0xf30d,	0xf30c,
+	0xf05c,	0xf200,	0xf200,	0xf200,	0xf115,	0xf03d,	0xf30d,	0xf30c,
 	0xf200,	0xf200,	0xf310,	0xf200,	0xf114,	0xf200,	0xfb71,	0xfb77,
 	0xf918,	0xfb72,	0xfb74,	0xfb79,	0xfb75,	0xfb69,	0xfb6f,	0xfb70,
 	0xf200,	0xf07e,	0xf200,	0xf20e,	0xf911,	0xf912,	0xf913,	0xf30b,
@@ -57,7 +58,7 @@
 	0xf919,	0xfb67,	0xfb68,	0xfb6a,	0xfb6b,	0xfb6c,	0xf200,	0xf200,
 	0xf200,	0xf201,	0xf30e,	0xf90e,	0xf90f,	0xf910,	0xf90a,	0xf200,
 	0xf118,	0xf200,	0xf208,	0xf700,	0xfb7a,	0xfb78,	0xf916,	0xfb76,
-	0xf915,	0xfb6e,	0xfb6d,	0xf200,	0xf200,	0xf200,	0xf700,	0xf200,
+	0xf915,	0xfb6e,	0xfb6d,	0xf200,	0xf200,	0xf200,	0xf700,	0xf00a,
 	0xf90b,	0xf90c,	0xf90d,	0xf200,	0xf200,	0xf200,	0xf11b,	0xf207,
 	0xf200,	0xf200,	0xf200,	0xf119,	0xf200,	0xf30a,	0xf200,	0xf200,
 };
@@ -68,7 +69,7 @@
 	0xf106,	0xf107,	0xf108,	0xf703,	0xf603,	0xf11d,	0xf200,	0xf204,
 	0xf601,	0xf200,	0xf200,	0xf600,	0xf602,	0xf200,	0xf200,	0xf000,
 	0xf01b,	0xf01c,	0xf01d,	0xf01e,	0xf01f,	0xf07f,	0xf200,	0xf200,
-	0xf01f,	0xf200,	0xf000,	0xf008,	0xf115,	0xf200,	0xf30d,	0xf30c,
+	0xf01f,	0xf200,	0xf000,	0xf008,	0xf115,	0xf03d,	0xf30d,	0xf30c,
 	0xf200,	0xf200,	0xf310,	0xf200,	0xf114,	0xf200,	0xf011,	0xf017,
 	0xf005,	0xf012,	0xf014,	0xf019,	0xf015,	0xf009,	0xf00f,	0xf010,
 	0xf01b,	0xf01d,	0xf008,	0xf20e,	0xf307,	0xf308,	0xf309,	0xf30b,
@@ -76,7 +77,7 @@
 	0xf006,	0xf007,	0xf008,	0xf00a,	0xf00b,	0xf00c,	0xf200,	0xf007,
 	0xf01c,	0xf201,	0xf30e,	0xf304,	0xf305,	0xf306,	0xf300,	0xf200,
 	0xf118,	0xf200,	0xf208,	0xf700,	0xf01a,	0xf018,	0xf003,	0xf016,
-	0xf002,	0xf00e,	0xf00d,	0xf200,	0xf20e,	0xf07f,	0xf700,	0xf200,
+	0xf002,	0xf00e,	0xf00d,	0xf200,	0xf20e,	0xf07f,	0xf700,	0xf00a,
 	0xf301,	0xf302,	0xf303,	0xf200,	0xf200,	0xf200,	0xf11b,	0xf207,
 	0xf200,	0xf000,	0xf200,	0xf119,	0xf200,	0xf30a,	0xf200,	0xf200,
 };
@@ -87,7 +88,7 @@
 	0xf200,	0xf200,	0xf200,	0xf703,	0xf603,	0xf11d,	0xf200,	0xf200,
 	0xf601,	0xf200,	0xf200,	0xf600,	0xf602,	0xf200,	0xf200,	0xf000,
 	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
-	0xf01f,	0xf200,	0xf200,	0xf200,	0xf115,	0xf200,	0xf30d,	0xf30c,
+	0xf01f,	0xf200,	0xf200,	0xf200,	0xf115,	0xf03d,	0xf30d,	0xf30c,
 	0xf200,	0xf200,	0xf310,	0xf200,	0xf114,	0xf200,	0xf011,	0xf017,
 	0xf005,	0xf012,	0xf014,	0xf019,	0xf015,	0xf009,	0xf00f,	0xf010,
 	0xf200,	0xf200,	0xf200,	0xf20e,	0xf307,	0xf308,	0xf309,	0xf30b,
@@ -95,7 +96,7 @@
 	0xf006,	0xf007,	0xf008,	0xf00a,	0xf00b,	0xf00c,	0xf200,	0xf200,
 	0xf200,	0xf201,	0xf30e,	0xf304,	0xf305,	0xf306,	0xf300,	0xf200,
 	0xf118,	0xf200,	0xf208,	0xf700,	0xf01a,	0xf018,	0xf003,	0xf016,
-	0xf002,	0xf00e,	0xf00d,	0xf200,	0xf200,	0xf200,	0xf700,	0xf200,
+	0xf002,	0xf00e,	0xf00d,	0xf200,	0xf200,	0xf200,	0xf700,	0xf00a,
 	0xf301,	0xf302,	0xf303,	0xf200,	0xf200,	0xf200,	0xf11b,	0xf207,
 	0xf200,	0xf200,	0xf200,	0xf119,	0xf200,	0xf30a,	0xf200,	0xf200,
 };
@@ -106,15 +107,15 @@
 	0xf506,	0xf507,	0xf508,	0xf703,	0xf603,	0xf11d,	0xf200,	0xf209,
 	0xf210,	0xf200,	0xf200,	0xf600,	0xf211,	0xf81b,	0xf831,	0xf832,
 	0xf833,	0xf834,	0xf835,	0xf836,	0xf837,	0xf838,	0xf839,	0xf830,
-	0xf82d,	0xf83d,	0xf860,	0xf87f,	0xf115,	0xf200,	0xf30d,	0xf30c,
+	0xf82d,	0xf83d,	0xf860,	0xf87f,	0xf115,	0xf03d,	0xf30d,	0xf30c,
 	0xf200,	0xf200,	0xf310,	0xf200,	0xf114,	0xf809,	0xf871,	0xf877,
 	0xf865,	0xf872,	0xf874,	0xf879,	0xf875,	0xf869,	0xf86f,	0xf870,
-	0xf85b,	0xf85d,	0xf200,	0xf87f,	0xf907,	0xf908,	0xf909,	0xf30b,
+	0xf85b,	0xf85d,	0xf87f,	0xf20e,	0xf907,	0xf908,	0xf909,	0xf30b,
 	0xf200,	0xf200,	0xf117,	0xf200,	0xf702,	0xf861,	0xf873,	0xf864,
 	0xf866,	0xf867,	0xf868,	0xf86a,	0xf86b,	0xf86c,	0xf83b,	0xf827,
 	0xf85c,	0xf80d,	0xf30e,	0xf904,	0xf905,	0xf906,	0xf900,	0xf200,
 	0xf118,	0xf200,	0xf208,	0xf700,	0xf87a,	0xf878,	0xf863,	0xf876,
-	0xf862,	0xf86e,	0xf86d,	0xf82c,	0xf82e,	0xf82f,	0xf700,	0xf200,
+	0xf862,	0xf86e,	0xf86d,	0xf82c,	0xf82e,	0xf82f,	0xf700,	0xf00a,
 	0xf901,	0xf902,	0xf903,	0xf200,	0xf200,	0xf200,	0xf11b,	0xf207,
 	0xf200,	0xf820,	0xf200,	0xf119,	0xf200,	0xf30a,	0xf200,	0xf200,
 };
@@ -125,7 +126,7 @@
 	0xf506,	0xf507,	0xf508,	0xf703,	0xf603,	0xf11d,	0xf200,	0xf200,
 	0xf601,	0xf200,	0xf200,	0xf600,	0xf602,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
-	0xf200,	0xf200,	0xf200,	0xf200,	0xf115,	0xf200,	0xf30d,	0xf30c,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf115,	0xf03d,	0xf30d,	0xf30c,
 	0xf200,	0xf200,	0xf20c,	0xf200,	0xf114,	0xf200,	0xf811,	0xf817,
 	0xf805,	0xf812,	0xf814,	0xf819,	0xf815,	0xf809,	0xf80f,	0xf810,
 	0xf200,	0xf200,	0xf20c,	0xf20e,	0xf307,	0xf308,	0xf309,	0xf30b,
@@ -133,7 +134,7 @@
 	0xf806,	0xf807,	0xf808,	0xf80a,	0xf80b,	0xf80c,	0xf200,	0xf200,
 	0xf200,	0xf201,	0xf30e,	0xf304,	0xf305,	0xf306,	0xf300,	0xf200,
 	0xf118,	0xf200,	0xf208,	0xf700,	0xf81a,	0xf818,	0xf803,	0xf816,
-	0xf802,	0xf80e,	0xf80d,	0xf200,	0xf200,	0xf200,	0xf700,	0xf200,
+	0xf802,	0xf80e,	0xf80d,	0xf200,	0xf200,	0xf200,	0xf700,	0xf00a,
 	0xf301,	0xf302,	0xf303,	0xf200,	0xf200,	0xf200,	0xf11b,	0xf207,
 	0xf200,	0xf200,	0xf200,	0xf119,	0xf200,	0xf30a,	0xf200,	0xf200,
 };
@@ -147,6 +148,7 @@
 
 unsigned int keymap_count = 7;
 
+
 /*
  * Philosophy: most people do not define more strings, but they who do
  * often want quite a lot of string space. So, we statically allocate
@@ -183,6 +185,7 @@
 	'\033', '[', 'M', 0, 
 	'\033', '[', 'P', 0, 
 };
+
 
 char *funcbufptr = func_buf;
 int funcbufsize = sizeof(func_buf);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/sunkeymap.map linux.14p6/drivers/sbus/char/sunkeymap.map
--- linux.vanilla/drivers/sbus/char/sunkeymap.map	Mon Jan 27 08:42:54 1997
+++ linux.14p6/drivers/sbus/char/sunkeymap.map	Tue Nov 16 15:13:28 1999
@@ -160,7 +160,7 @@
 	alt	keycode				0x41 = Meta_bracketright
 keycode 0x42 = Delete		Delete
 	control	keycode				0x42 = BackSpace
-	alt	keycode				0x43 = Meta_Delete
+	alt	keycode				0x42 = Meta_Delete
 	control	alt	keycode			0x42 = Boot
 keycode 0x43 = Compose
 keycode 0x44 = KP_7
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/sunmouse.c linux.14p6/drivers/sbus/char/sunmouse.c
--- linux.vanilla/drivers/sbus/char/sunmouse.c	Sat Aug 14 02:25:56 1999
+++ linux.14p6/drivers/sbus/char/sunmouse.c	Tue Nov 16 15:13:28 1999
@@ -81,7 +81,7 @@
 	unsigned int tail;
 	union {
 		char stream [STREAM_SIZE];
-		Firm_event ev [0];
+		Firm_event ev [EV_SIZE];
 	} queue;
 };
 
@@ -142,8 +142,6 @@
 }
 
 /* Auto baud rate "detection".  ;-) */
-static int mouse_bogon_bytes = 0;
-static int mouse_baud_changing = 0;	/* For reporting things to the user. */
 static int mouse_baud = 4800;		/* Initial rate set by zilog driver. */
 
 /* Change the baud rate after receiving too many "bogon bytes". */
@@ -161,71 +159,112 @@
 		mouse_baud = 1200;
 
 	rs_change_mouse_baud(mouse_baud);
-	mouse_baud_changing = 1;
 }
 
-void mouse_baud_detection(unsigned char c)
+/* This tries to monitor the mouse state so that it
+ * can automatically adjust to the correct baud rate.
+ * The mouse spits out BRKs when the baud rate is
+ * incorrect.
+ *
+ * It returns non-zero if we should ignore this byte.
+ */
+int mouse_baud_detection(unsigned char c, int is_break)
 {
-	static int wait_for_synchron = 1;
+	static int mouse_got_break = 0;
 	static int ctr = 0;
 
-	if(wait_for_synchron) {
-		if((c < 0x80) || (c > 0x87))
-			mouse_bogon_bytes++;
-		else {
-			ctr = 0;
-			wait_for_synchron = 0;
-		}
-	} else {
+	if (is_break) {
+		/* Let a few normal bytes go by before
+		 * we jump the gun and say we need to
+		 * try another baud rate.
+		 */
+		if (mouse_got_break && ctr < 8)
+			return 1;
+
+		/* OK, we need to try another baud rate. */
+		sun_mouse_change_baud();
+		ctr = 0;
+		mouse_got_break = 1;
+		return 1;
+	}
+	if (mouse_got_break) {
 		ctr++;
-		if(ctr >= 4) {
-			ctr = 0;
-			wait_for_synchron = 1;
-			if(mouse_baud_changing == 1) {
-				printk(KERN_DEBUG "sunmouse: Successfully adjusted to %d baud.\n",
-				       mouse_baud);
-				mouse_baud_changing = 0;
-			}
+		if (c == 0x87) {
+			printk(KERN_INFO "sunmouse: Successfully "
+			       "adjusted to %d baud.\n", mouse_baud);
+			mouse_got_break = 0;
 		}
+		return 1;
 	}
-	if(mouse_bogon_bytes > 12) {
-		sun_mouse_change_baud();
-		mouse_bogon_bytes = 0;
-		wait_for_synchron = 1;
-	}
+
+	return 0;
 }
 
-/* The following is called from the zs driver when bytes are received on
- * the Mouse zs8530 channel.
+/* You ask me, why does this cap the lower bound at -127 and not
+ * -128?  Because the xf86 mouse code is crap and treats -128
+ * as an illegal value and resets it's protocol state machine
+ * when it sees this value.
+ */
+#define CLIP(__X)	(((__X) > 127) ? 127 : (((__X) < -127) ? -127 : (__X)))
+
+/* The following is called from the serial driver when bytes/breaks
+ * are received on the Mouse line.
  */
 void
-sun_mouse_inbyte(unsigned char byte)
+sun_mouse_inbyte(unsigned char byte, int is_break)
 {
 	signed char mvalue;
 	int d, pushed = 0;
 	Firm_event ev;
 
 	add_mouse_randomness (byte);
+#if 0
+	{
+		static int xxx = 0;
+		printk("mouse(%02x:%d) ",
+		       byte, is_break);
+		if (byte == 0x87) {
+			xxx = 0;
+			printk("\n");
+		}
+	}
+#endif
+	if (mouse_baud_detection(byte, is_break))
+		return;
+
 	if(!sunmouse.active)
 		return;
 
-	mouse_baud_detection(byte);
+	/* Ignore this if it is garbage. */
+	if (sunmouse.byte == 69) {
+		if (byte != 0x87)
+			return;
 
-	if (!gen_events){
-		push_char (byte);
-		return;
+		/* Ok, we've begun the state machine. */
+		sunmouse.byte = 0;
 	}
-
+#if 0
 	/* If the mouse sends us a byte from 0x80 to 0x87
 	 * we are starting at byte zero in the transaction
 	 * protocol.
 	 */
 	if(byte >= 0x80 && byte <= 0x87)
 		sunmouse.byte = 0;
+#endif
 
 	mvalue = (signed char) byte;
 	switch(sunmouse.byte) {
 	case 0:
+		/* If we get a bogus button byte, just skip it.
+		 * When we get here the baud detection code has
+		 * passed, so the only other things which can
+		 * cause this are dropped serial characters and
+		 * confused mouse.  We check this because otherwise
+		 * begin posting erroneous mouse events.
+		 */
+		if ((byte & 0xf0) != 0x80)
+			return;
+
 		/* Button state */
 		sunmouse.button_state = (~byte) & 0x7;
 #ifdef SMOUSE_DEBUG
@@ -258,6 +297,7 @@
 		printk("DX2<%d>", mvalue);
 #endif
 		sunmouse.delta_x += mvalue;
+		sunmouse.delta_x = CLIP(sunmouse.delta_x);
 		sunmouse.byte++;
 		return;
 	case 4:
@@ -266,7 +306,8 @@
 		printk("DY2<%d>", mvalue);
 #endif
 		sunmouse.delta_y += mvalue;
-		sunmouse.byte = 69;  /* Some ridiculous value */
+		sunmouse.delta_y = CLIP(sunmouse.delta_y);
+		sunmouse.byte = 0;  /* Back to button state */
 		break;
 	case 69:
 		/* Until we get the (0x80 -> 0x87) value we aren't
@@ -279,6 +320,12 @@
 		sunmouse.byte = 69;  /* What could cause this? */
 		return;
 	};
+	if (!gen_events){
+		push_char (~sunmouse.button_state & 0x87);
+		push_char (sunmouse.delta_x);
+		push_char (sunmouse.delta_y);
+		return;
+	}
 	d = bstate ^ pstate;
 	pstate = bstate;
 	if (d){
@@ -389,6 +436,10 @@
 			if (current->tss.flags & SPARC_FLAG_32BIT) {
 				Firm_event *q = get_from_queue();
 				
+				if ((end - p) <
+				    ((sizeof(Firm_event) - sizeof(struct timeval) +
+				      (sizeof(u32) * 2))))
+					break;
 				copy_to_user_ret((Firm_event *)p, q, 
 						 sizeof(Firm_event)-sizeof(struct timeval),
 						 -EFAULT);
@@ -400,6 +451,8 @@
 			} else
 #endif	
 			{	
+				if ((end - p) < sizeof(Firm_event))
+					break;
 				copy_to_user_ret((Firm_event *)p, get_from_queue(),
 				     		 sizeof(Firm_event), -EFAULT);
 				p += sizeof (Firm_event);
@@ -409,16 +462,25 @@
 		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 		return p-buffer;
 	} else {
-		int c;
-		
-		for (c = count; !queue_empty() && c; c--){
-			put_user_ret(sunmouse.queue.stream[sunmouse.tail], buffer, -EFAULT);
+		int c, limit = 3;
+
+		if (count < limit)
+			limit = count;
+		for (c = 0; c < limit; c++) {
+			put_user(sunmouse.queue.stream[sunmouse.tail], buffer);
 			buffer++;
 			sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE;
 		}
+		while (c < count) {
+			if (c >= 5)
+				break;
+			put_user(0, buffer);
+			buffer++;
+			c++;
+		}
 		sunmouse.ready = !queue_empty();
 		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
-		return count-c;
+		return c;
 	}
 	/* Only called if nothing was sent */
 	if (signal_pending(current))
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/sunmouse.h linux.14p6/drivers/sbus/char/sunmouse.h
--- linux.vanilla/drivers/sbus/char/sunmouse.h	Thu Sep  4 20:54:49 1997
+++ linux.14p6/drivers/sbus/char/sunmouse.h	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: sunmouse.h,v 1.1 1997/08/28 02:23:38 ecd Exp $
+/* $Id: sunmouse.h,v 1.1.6.1 1999/10/14 08:44:33 davem Exp $
  * sunmouse.h: Interface to the SUN mouse driver.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -8,6 +8,6 @@
 #define _SPARC_SUNMOUSE_H 1
 
 extern void sun_mouse_zsinit(void);
-extern void sun_mouse_inbyte(unsigned char);
+extern void sun_mouse_inbyte(unsigned char, int);
 
 #endif /* !(_SPARC_SUNMOUSE_H) */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/sunserial.c linux.14p6/drivers/sbus/char/sunserial.c
--- linux.vanilla/drivers/sbus/char/sunserial.c	Tue Mar 16 00:11:30 1999
+++ linux.14p6/drivers/sbus/char/sunserial.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: sunserial.c,v 1.68 1998/12/09 18:53:51 davem Exp $
+/* $Id: sunserial.c,v 1.68.2.2 1999/10/04 14:57:02 jj Exp $
  * serial.c: Serial port driver infrastructure for the Sparc.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -19,6 +19,7 @@
 #include "sunserial.h"
 
 int serial_console;
+int stop_a_enabled = 1;
 
 __initfunc(int con_is_present(void))
 {
@@ -389,12 +390,10 @@
 	 * So be very careful not to probe for keyboards if we are on a
 	 * serial console.
 	 */
-	if (!serial_console) {
-		if (ps2kbd_probe(&memory_start) == 0)
-			return memory_start;
-		if (su_probe(&memory_start) == 0)
-			return memory_start;
-	}
+	if (!serial_console)
+		ps2kbd_probe(&memory_start);
+	if (su_probe(&memory_start) == 0)
+		return memory_start;
 #endif
 
 	if (!ret)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/sunserial.h linux.14p6/drivers/sbus/char/sunserial.h
--- linux.vanilla/drivers/sbus/char/sunserial.h	Mon Jan 12 23:15:45 1998
+++ linux.14p6/drivers/sbus/char/sunserial.h	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: sunserial.h,v 1.17 1997/12/19 07:33:12 ecd Exp $
+/* $Id: sunserial.h,v 1.17.4.1 1999/10/04 14:57:08 jj Exp $
  * sunserial.h: SUN serial driver infrastructure (including keyboards).
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -41,6 +41,7 @@
 extern void sunkbd_setinitfunc(unsigned long *, int (*) (void));
 
 extern int serial_console;
+extern int stop_a_enabled;
 extern void sunserial_console_termios(struct console *);
 
 #ifdef CONFIG_PCI
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sbus/char/zs.c linux.14p6/drivers/sbus/char/zs.c
--- linux.vanilla/drivers/sbus/char/zs.c	Wed Oct 20 01:12:40 1999
+++ linux.14p6/drivers/sbus/char/zs.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.41.2.2 1999/09/21 15:50:45 davem Exp $
+/* $Id: zs.c,v 1.41.2.4 1999/10/14 08:44:40 davem Exp $
  * zs.c: Zilog serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -374,6 +374,8 @@
  */
 void batten_down_hatches(void)
 {
+	if (!stop_a_enabled)
+		return;
 	/* If we are doing kadb, we call the debugger
 	 * else we just drop into the boot monitor.
 	 * Note that we must flush the user windows
@@ -471,7 +473,7 @@
 			goto next_char;
 		}
 		if(info->cons_mouse) {
-			sun_mouse_inbyte(ch);
+			sun_mouse_inbyte(ch, 0);
 			do_queue_task = 0;
 			goto next_char;
 		}
@@ -584,8 +586,12 @@
 	 * 'break asserted' status change interrupt, call
 	 * the boot prom.
 	 */
-	if((status & BRK_ABRT) && info->break_abort)
-		batten_down_hatches();
+	if(status & BRK_ABRT) {
+		if (info->break_abort)
+			batten_down_hatches();
+		if (info->cons_mouse)
+			sun_mouse_inbyte(0, 1);
+	}
 
 	/* XXX Whee, put in a buffer somewhere, the status information
 	 * XXX whee whee whee... Where does the information go...
@@ -1849,7 +1855,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.41.2.2 $";
+	char *revision = "$Revision: 1.41.2.4 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/eata.c linux.14p6/drivers/scsi/eata.c
--- linux.vanilla/drivers/scsi/eata.c	Wed Oct 20 01:12:41 1999
+++ linux.14p6/drivers/scsi/eata.c	Tue Nov  2 08:58:40 1999
@@ -1,6 +1,13 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *      16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
+ *        + Updated to the new __setup interface for boot command line options.
+ *        + When loaded as a module, accepts the new parameter boot_options
+ *          which value is a string with the same format of the kernel boot
+ *          command line options. A valid example is:
+ *          modprobe eata 'boot_options=\"0x7410,0x230,lc:y,tc:n,mq:4\"'
+ *
  *       9 Sep 1999 Rev. 5.10 for linux 2.2.12 and 2.3.17
  *        + 64bit cleanup for Linux/Alpha platform support
  *          (contribution from H.J. Lu).
@@ -368,6 +375,7 @@
 #if defined(MODULE)
 #include <linux/module.h>
 
+MODULE_PARM(boot_options, "s");
 MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
 MODULE_PARM(linked_comm, "i");
 MODULE_PARM(tagged_comm, "i");
@@ -401,7 +409,13 @@
 #include <linux/config.h>
 #include <linux/pci.h>
 #include <linux/init.h>
+#include <linux/ctype.h>
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
 #include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
 
 #define SPIN_FLAGS unsigned long spin_flags;
 #define SPIN_LOCK spin_lock_irq(&io_request_lock);
@@ -410,8 +424,6 @@
 #define SPIN_UNLOCK_RESTORE \
                   spin_unlock_irqrestore(&io_request_lock, spin_flags);
 
-static int use_new_eh_code = TRUE;
-
 struct proc_dir_entry proc_scsi_eata2x = {
     PROC_SCSI_EATA2X, 6, "eata2x",
     S_IFDIR | S_IRUGO | S_IXUGO, 2
@@ -494,7 +506,7 @@
 #define ASOK              0x00
 #define ASST              0x01
 
-#if !defined(ARRAY_SIZE)
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
 #define ARRAY_SIZE(x) (sizeof (x) / sizeof((x)[0]))
 #endif
 
@@ -646,7 +658,7 @@
 /* Initialize num_boards so that ihdlr can work while detect is in progress */
 static unsigned int num_boards = MAX_BOARDS;
 
-static unsigned long io_port[] __initdata = {
+static unsigned long io_port[] = {
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -686,6 +698,8 @@
 static int tag_mode = TAG_MIXED;
 static int ext_tran = FALSE;
 static int rev_scan = TRUE;
+static int use_new_eh_code = TRUE;
+static char *boot_options = NULL;
 
 #if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE)
 static int tagged_comm = TRUE;
@@ -820,12 +834,6 @@
 
       if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break;
 
-#if 0
-      /* Don't bother if PCI vendor and/or device don't match. */
-      if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT)
-		continue;
-#endif
-
       if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
 
 #if defined(DEBUG_PCI_DETECT)
@@ -857,12 +865,6 @@
 
    while((dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
 
-#if 0
-      /* Don't bother if PCI vendor and/or device don't match. */
-      if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT)
-		continue;
-#endif
-
       if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
 
 #if defined(DEBUG_PCI_DETECT)
@@ -913,7 +915,7 @@
    if (info.sign != EATA_SIGNATURE) return FALSE;
 
    if (DEV2H(info.data_len) < EATA_2_0A_SIZE) {
-      printk("%s: config structure size (%d bytes) too short, detaching.\n",
+      printk("%s: config structure size (%ld bytes) too short, detaching.\n",
              name, DEV2H(info.data_len));
       return FALSE;
       }
@@ -1166,7 +1168,7 @@
    return TRUE;
 }
 
-void eata2x_setup(char *str, int *ints) {
+static void internal_setup(char *str, int *ints) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -1202,6 +1204,22 @@
    return;
 }
 
+static int option_setup(char *str) {
+   int ints[MAX_INT_PARAM];
+   char *cur = str;
+   int i = 1;
+
+   while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+      ints[i++] = simple_strtoul(cur, NULL, 0);
+
+      if ((cur = strchr(cur, ',')) != NULL) cur++;
+   }
+
+   ints[0] = i - 1;
+   internal_setup(cur, ints);
+   return 0;
+}
+
 static void add_pci_ports(void) {
 
 #if defined(CONFIG_PCI)
@@ -1216,12 +1234,6 @@
 
       if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break;
 
-#if 0
-      /* Don't bother if PCI vendor and/or device don't match. */
-      if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT)
-		continue;
-#endif
-
       if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
 
 #if defined(DEBUG_PCI_DETECT)
@@ -1247,6 +1259,8 @@
 
    tpnt->proc_dir = &proc_scsi_eata2x;
 
+   if(boot_options) option_setup(boot_options);
+
 #if defined(MODULE)
    /* io_port could have been modified when loading as a module */
    if(io_port[0] != SKIP) {
@@ -2281,4 +2295,15 @@
 Scsi_Host_Template driver_template = EATA;
 
 #include "scsi_module.c"
+
+#else
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+void eata2x_setup(char *str, int *ints) {
+   internal_setup(str, ints);
+}
+#else
+__setup("eata=", option_setup);
 #endif
+
+#endif /* end MODULE */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/eata.h linux.14p6/drivers/scsi/eata.h
--- linux.vanilla/drivers/scsi/eata.h	Wed Oct 20 01:12:41 1999
+++ linux.14p6/drivers/scsi/eata.h	Tue Nov 16 16:15:03 1999
@@ -16,9 +16,7 @@
 int eata2x_old_reset(Scsi_Cmnd *, unsigned int);
 int eata2x_biosparam(Disk *, kdev_t, int *);
 
-#define EATA_VERSION "5.10.00"
-
-#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define EATA_VERSION "5.11.01"
 
 #define EATA {                                                               \
                 name:              "EATA/DMA 2.0x rev. " EATA_VERSION " ",   \
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/esp.c linux.14p6/drivers/scsi/esp.c
--- linux.vanilla/drivers/scsi/esp.c	Wed Oct 20 01:12:41 1999
+++ linux.14p6/drivers/scsi/esp.c	Tue Nov 16 15:13:28 1999
@@ -1519,8 +1519,11 @@
 		 */
 		if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
 		   (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
-			/* But we are nice and allow tapes to disconnect. */
-			if(SDptr->type == TYPE_TAPE)
+			/* But we are nice and allow tapes and removable
+			 * disks (but not CDROMs) to disconnect.
+			 */
+			if(SDptr->type == TYPE_TAPE ||
+			   (SDptr->type != TYPE_ROM && SDptr->removable))
 				SDptr->disconnect = 1;
 			else
 				SDptr->disconnect = 0;
@@ -1538,7 +1541,8 @@
 		 */
 		if(esp->erev == fashme && !SDptr->wide) {
 			if(!SDptr->borken &&
-			   SDptr->type != TYPE_ROM) {
+			   SDptr->type != TYPE_ROM &&
+			   SDptr->removable == 0) {
 				build_wide_nego_msg(esp, 16);
 				SDptr->wide = 1;
 				esp->wnip = 1;
@@ -1556,6 +1560,11 @@
 					 "CDROM.\n", esp->esp_id));
 				cdrom_hwbug_wkaround = 1;
 				build_sync_nego_msg(esp, 0, 0);
+			} else if (SDptr->removable != 0) {
+				ESPMISC(("esp%d: Not negotiating sync/wide but "
+					 "allowing disconnect for removable media.\n",
+					 esp->esp_id));
+				build_sync_nego_msg(esp, 0, 0);
 			} else {
 				build_sync_nego_msg(esp, esp->sync_defp, 15);
 			}
@@ -1589,7 +1598,9 @@
 		 *           Therefore _no_ disconnects for SCSI1 targets
 		 *           thank you very much. ;-)
 		 */
-		if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) ||
+		if(((SDptr->scsi_level < 3) &&
+		    (SDptr->type != TYPE_TAPE) &&
+		    SDptr->removable == 0) ||
 		   cdrom_hwbug_wkaround || SDptr->borken) {
 			ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
 				 "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun));
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/fdomain.c linux.14p6/drivers/scsi/fdomain.c
--- linux.vanilla/drivers/scsi/fdomain.c	Tue Dec 29 19:44:53 1998
+++ linux.14p6/drivers/scsi/fdomain.c	Fri Oct 22 22:32:09 1999
@@ -875,6 +875,8 @@
    int              retcode;
    struct Scsi_Host *shpnt;
 #if DO_DETECT
+   int i = 0;
+   int j = 0;
    const int        buflen = 255;
    Scsi_Cmnd        SCinit;
    unsigned char    do_inquiry[] =       { INQUIRY, 0, 0, 0, buflen, 0 };
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/gdth.h linux.14p6/drivers/scsi/gdth.h
--- linux.vanilla/drivers/scsi/gdth.h	Sat Aug 14 02:25:41 1999
+++ linux.14p6/drivers/scsi/gdth.h	Tue Nov 16 16:15:03 1999
@@ -249,8 +249,10 @@
 #define HIGH_PRI        0x08
 
 /* data directions */
+#ifndef HOSTS_C
 #define DATA_IN         0x01000000L             /* data from target */
 #define DATA_OUT        0x00000000L             /* data to target */
+#endif
 
 /* BMIC registers (EISA controllers) */
 #define ID0REG          0x0c80                  /* board ID */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/ide-scsi.c linux.14p6/drivers/scsi/ide-scsi.c
--- linux.vanilla/drivers/scsi/ide-scsi.c	Sat Aug 14 02:27:31 1999
+++ linux.14p6/drivers/scsi/ide-scsi.c	Tue Nov 16 15:29:49 1999
@@ -302,13 +302,13 @@
 
 static inline unsigned long get_timeout(idescsi_pc_t *pc)
 {
-	return IDE_MAX(WAIT_CMD, pc->timeout - jiffies);
+	return IDE_MAX((30 * HZ), pc->timeout - jiffies);	/* CD-RW drives need long timeouts */
 }
 
 /*
  *	Our interrupt handler.
  */
-static void idescsi_pc_intr (ide_drive_t *drive)
+static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive)
 {
 	idescsi_scsi_t *scsi = drive->driver_data;
 	byte status, ireason;
@@ -338,15 +338,14 @@
 		if (status & ERR_STAT)
 			rq->errors++;
 		idescsi_end_request (1, HWGROUP(drive));
-		return;
+		return ide_stopped;
 	}
 	bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG);
 	ireason = IN_BYTE (IDE_IREASON_REG);
 
 	if (ireason & IDESCSI_IREASON_COD) {
 		printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	if (ireason & IDESCSI_IREASON_IO) {
 		temp = pc->actually_transferred + bcount;
@@ -366,7 +365,7 @@
 				pc->current_position += temp;
 				idescsi_discard_data (drive,bcount - temp);
 				ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc));
-				return;
+				return ide_started;
 			}
 #if IDESCSI_DEBUG_LOG
 			printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n");
@@ -390,32 +389,34 @@
 	pc->current_position+=bcount;
 
 	ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc));	/* And set the interrupt handler again */
+	return ide_started;
 }
 
-static void idescsi_transfer_pc (ide_drive_t *drive)
+static ide_startstop_t idescsi_transfer_pc (ide_drive_t *drive)
 {
 	idescsi_scsi_t *scsi = drive->driver_data;
 	idescsi_pc_t *pc = scsi->pc;
 	byte ireason;
+	ide_startstop_t startstop;
 
-	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
 		printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n");
-		return;
+		return startstop;
 	}
 	ireason = IN_BYTE (IDE_IREASON_REG);
 	if ((ireason & (IDESCSI_IREASON_IO | IDESCSI_IREASON_COD)) != IDESCSI_IREASON_COD) {
 		printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n");
-		ide_do_reset (drive);
-		return;
+		return ide_do_reset (drive);
 	}
 	ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc));	/* Set the interrupt routine */
 	atapi_output_bytes (drive, scsi->pc->c, 12);			/* Send the actual packet */
+	return ide_started;
 }
 
 /*
  *	Issue a packet command
  */
-static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
+static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
 {
 	idescsi_scsi_t *scsi = drive->driver_data;
 	int bcount;
@@ -443,16 +444,17 @@
 	if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
 		ide_set_handler (drive, &idescsi_transfer_pc, get_timeout(pc));
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+		return ide_started;
 	} else {
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
-		idescsi_transfer_pc (drive);
+		return idescsi_transfer_pc (drive);
 	}
 }
 
 /*
  *	idescsi_do_request is our request handling function.
  */
-static void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
 #if IDESCSI_DEBUG_LOG
 	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
@@ -460,11 +462,11 @@
 #endif /* IDESCSI_DEBUG_LOG */
 
 	if (rq->cmd == IDESCSI_PC_RQ) {
-		idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer);
-		return;
+		return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer);
 	}
 	printk (KERN_ERR "ide-scsi: %s: unsupported command in request queue (%x)\n", drive->name, rq->cmd);
 	idescsi_end_request (0,HWGROUP (drive));
+	return ide_stopped;
 }
 
 static int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/ips.h linux.14p6/drivers/scsi/ips.h
--- linux.vanilla/drivers/scsi/ips.h	Wed Oct 20 01:12:41 1999
+++ linux.14p6/drivers/scsi/ips.h	Thu Nov 11 22:00:20 1999
@@ -233,6 +233,7 @@
 /*
  * DCDB Table Equates
  */
+#ifndef HOSTS_C 
    #define NO_DISCONNECT                0x00
    #define DISCONNECT_ALLOWED           0x80
    #define NO_AUTO_REQUEST_SENSE        0x40
@@ -243,7 +244,6 @@
    #define TIMEOUT10                    0x10
    #define TIMEOUT60                    0x20
    #define TIMEOUT20M                   0x30
-
 /*
  * Host adapter Flags (bit numbers)
  */
@@ -256,7 +256,7 @@
  */
    #define SCB_ACTIVE                   0x00001
    #define SCB_WAITING                  0x00002
-
+#endif /* HOSTS_C */
 /*
  * Passthru stuff
  */
@@ -590,6 +590,8 @@
 /*
  * Inquiry Data Format
  */
+#ifndef HOSTS_C
+
 typedef struct {
    u8        DeviceType:5;
    u8        DeviceTypeQualifier:3;
@@ -614,6 +616,7 @@
    u8        Reserved3[40];
 } INQUIRYDATA, *PINQUIRYDATA;
 
+#endif
 /*
  * Read Capacity Data Format
  */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/megaraid.h linux.14p6/drivers/scsi/megaraid.h
--- linux.vanilla/drivers/scsi/megaraid.h	Sat Aug 28 20:00:57 1999
+++ linux.14p6/drivers/scsi/megaraid.h	Tue Nov 16 16:15:03 1999
@@ -11,6 +11,7 @@
 #define BOARD_QUARTZ            0x08000000L
 #define BOARD_40LD              0x04000000L
 
+#ifndef HOSTS_C
 #define SCB_FREE     0x0
 #define SCB_ACTIVE   0x1
 #define SCB_WAITQ    0x2
@@ -18,6 +19,7 @@
 #define SCB_COMPLETE 0x4
 #define SCB_ABORTED  0x5
 #define SCB_RESET    0x6
+#endif
 
 #define MEGA_CMD_TIMEOUT        10
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/psi240i.h linux.14p6/drivers/scsi/psi240i.h
--- linux.vanilla/drivers/scsi/psi240i.h	Mon Dec 28 06:19:22 1998
+++ linux.14p6/drivers/scsi/psi240i.h	Fri Oct 22 23:26:35 1999
@@ -211,6 +211,8 @@
 	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
 
 // SCSI inquiry data
+#ifndef HOSTS_C
+
 typedef struct _INQUIRYDATA
 	{
 	UCHAR DeviceType			:5;
@@ -235,6 +237,7 @@
     UCHAR VendorSpecific[20];
     UCHAR Reserved3[40];
 	}	INQUIRYDATA, *PINQUIRYDATA;
+#endif
 
 // IDE IDENTIFY data
 typedef struct _IDENTIFY_DATA
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/qlogicfc.c linux.14p6/drivers/scsi/qlogicfc.c
--- linux.vanilla/drivers/scsi/qlogicfc.c	Wed Oct 20 01:12:41 1999
+++ linux.14p6/drivers/scsi/qlogicfc.c	Fri Oct 22 21:25:11 1999
@@ -1855,7 +1855,7 @@
 
 static int isp2x00_init(struct Scsi_Host *sh)
 {
-	u_int io_base;
+	u_long io_base;
 	struct isp2x00_hostdata *hostdata;
 	u_char revision;
 	u_int irq;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/qlogicisp.c linux.14p6/drivers/scsi/qlogicisp.c
--- linux.vanilla/drivers/scsi/qlogicisp.c	Wed Oct 20 01:12:42 1999
+++ linux.14p6/drivers/scsi/qlogicisp.c	Fri Oct 22 21:25:11 1999
@@ -1262,6 +1262,13 @@
 		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
 	}
 #endif
+#ifdef __alpha__
+	/* Force ALPHA to use bus I/O and not bus MEM. 
+	   This is to avoid having to use HAE_MEM registers,
+	   which is broken on some platforms and with SMP.
+	*/
+	command &= ~PCI_COMMAND_MEMORY;
+#endif
 
 	if ((command & PCI_COMMAND_MEMORY) &&
 	    ((mem_base & 1) == 0)) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/qlogicpti.c linux.14p6/drivers/scsi/qlogicpti.c
--- linux.vanilla/drivers/scsi/qlogicpti.c	Wed Oct 20 01:12:42 1999
+++ linux.14p6/drivers/scsi/qlogicpti.c	Tue Nov 16 15:13:28 1999
@@ -43,7 +43,7 @@
 #include <linux/module.h>
 
 #define MAX_TARGETS	16
-#define MAX_LUNS	8
+#define MAX_LUNS	8	/* 32 for 1.31 F/W */
 
 #define DEFAULT_LOOP_COUNT	10000
 
@@ -244,6 +244,46 @@
 	return 0;
 }
 
+static inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti)
+{
+	int i;
+
+	qpti->host_param.initiator_scsi_id = qpti->scsi_id;
+	qpti->host_param.bus_reset_delay = 3;
+	qpti->host_param.retry_count = 0;
+	qpti->host_param.retry_delay = 5;
+	qpti->host_param.async_data_setup_time = 3;
+	qpti->host_param.req_ack_active_negation = 1;
+	qpti->host_param.data_line_active_negation = 1;
+	qpti->host_param.data_dma_burst_enable = 1;
+	qpti->host_param.command_dma_burst_enable = 1;
+	qpti->host_param.tag_aging = 8;
+	qpti->host_param.selection_timeout = 250;
+	qpti->host_param.max_queue_depth = 256;
+
+	for(i = 0; i < MAX_TARGETS; i++) {
+		/*
+		 * disconnect, parity, arq, reneg on reset, and, oddly enough
+		 * tags...the midlayer's notion of tagged support has to match
+		 * our device settings, and since we base whether we enable a
+		 * tag on a  per-cmnd basis upon what the midlayer sez, we
+		 * actually enable the capability here.
+		 */
+		qpti->dev_param[i].device_flags = 0xcd;
+		qpti->dev_param[i].execution_throttle = 16;
+		if (qpti->ultra) {
+			qpti->dev_param[i].synchronous_period = 12;
+			qpti->dev_param[i].synchronous_offset = 8;
+		} else {
+			qpti->dev_param[i].synchronous_period = 25;
+			qpti->dev_param[i].synchronous_offset = 12;
+		}
+		qpti->dev_param[i].device_enable = 1;
+	}
+	/* this is very important to set! */
+	qpti->sbits = 1 << qpti->scsi_id;
+}
+
 static int qlogicpti_reset_hardware(struct Scsi_Host *host)
 {
 	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
@@ -288,6 +328,10 @@
 		qregs->risc_mtreg = (RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT);
 	}
 
+	/* reset adapter and per-device default values. */
+	/* do it after finding out whether we're ultra mode capable */
+	qlogicpti_set_hostdev_defaults(qpti);
+
 	/* Release the RISC processor. */
 	qregs->hcctrl = HCCTRL_REL;
 
@@ -365,9 +409,11 @@
 		param[0] = MBOX_SET_TARGET_PARAMS;
 		param[1] = (i << 8);
 		param[2] = (qpti->dev_param[i].device_flags << 8);
-		if (qpti->is_pti == 0)	/* really, is it 1.31 f/w or later? */
-			param[2] |= 0xc0;
-		param[3] = 0;	/* no sync mode at first */
+		/*
+		 * Since we're now loading 1.31 f/w, force narrow/async.
+		 */
+		param[2] |= 0xc0;
+		param[3] = 0;	/* no offset, we do not have sync mode yet */
 		qlogicpti_mbox_command(qpti, param, 0);
 	}
 
@@ -392,9 +438,6 @@
 	unsigned short param[6];
 	unsigned short *risc_code, risc_code_addr, risc_code_length;
 	unsigned long flags;
-#if !defined(MODULE) && !defined(__sparc_v9__)
-	unsigned long dvma_addr;
-#endif
 	int i, timeout;
 
 	risc_code = &sbus_risc_code01[0];
@@ -409,6 +452,7 @@
 	for(i = 0; i < risc_code_length; i++)
 		csum += risc_code[i];
 	if(csum) {
+		restore_flags(flags);
 		printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!",
 		       qpti->qpti_id);
 		return 1;
@@ -420,6 +464,7 @@
 	while(--timeout && (qregs->sbus_ctrl & SBUS_CTRL_RESET))
 		udelay(20);
 	if(!timeout) {
+		restore_flags(flags);
 		printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id);
 		return 1;
 	}
@@ -449,6 +494,9 @@
 		qpti->differential = 0;
 	qregs->hcctrl = HCCTRL_REL;
 
+	/* This shouldn't be necessary- we've reset things so we should be
+	   running from the ROM now.. */
+
 	param[0] = MBOX_STOP_FIRMWARE;
 	param[1] = param[2] = param[3] = param[4] = param[5] = 0;
 	if(qlogicpti_mbox_command(qpti, param, 1)) {
@@ -459,38 +507,18 @@
 	}		
 
 	/* Load the firmware. */
-#if !defined(MODULE) && !defined(__sparc_v9__)
-	if (sparc_cpu_model != sun4d) {
-		dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code[0],
-							 (sizeof(u_short) * risc_code_length));
-		param[0] = MBOX_LOAD_RAM;
-		param[1] = risc_code_addr;
-		param[2] = (dvma_addr >> 16);
-		param[3] = (dvma_addr & 0xffff);
-		param[4] = (sizeof(u_short) * risc_code_length);
+	for(i = 0; i < risc_code_length; i++) {
+		param[0] = MBOX_WRITE_RAM_WORD;
+		param[1] = risc_code_addr + i;
+		param[2] = risc_code[i];
 		if(qlogicpti_mbox_command(qpti, param, 1) ||
-		   (param[0] != MBOX_COMMAND_COMPLETE)) {
-			printk(KERN_EMERG "qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
+		   param[0] != MBOX_COMMAND_COMPLETE) {
+			printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
 			       qpti->qpti_id);
 			restore_flags(flags);
 			return 1;
 		}
-		mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length));
-	} else
-#endif
-	/* We need to do it this slow way always on Ultra, SS[12]000. */
-		for(i = 0; i < risc_code_length; i++) {
-			param[0] = MBOX_WRITE_RAM_WORD;
-			param[1] = risc_code_addr + i;
-			param[2] = risc_code[i];
-			if(qlogicpti_mbox_command(qpti, param, 1) ||
-			   param[0] != MBOX_COMMAND_COMPLETE) {
-				printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
-				       qpti->qpti_id);
-				restore_flags(flags);
-				return 1;
-			}
-		}
+	}
 
 	/* Reset the ISP again. */
 	qregs->hcctrl = HCCTRL_RESET;
@@ -530,6 +558,17 @@
 	qpti->fware_minrev = param[2];
 	qpti->fware_micrev = param[3];
 
+	/* Set the clock rate */
+	param[0] = MBOX_SET_CLOCK_RATE;
+	param[1] = qpti->clock;
+	if(qlogicpti_mbox_command(qpti, param, 1) ||
+	   (param[0] != MBOX_COMMAND_COMPLETE)) {
+		printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n",
+		       qpti->qpti_id);
+		restore_flags(flags);
+		return 1;
+	}
+
 	if(qpti->is_pti != 0) {
 		/* Load scsi initiator ID and interrupt level into sbus static ram. */
 		param[0] = MBOX_WRITE_RAM_WORD;
@@ -578,39 +617,6 @@
 	return 0;
 }
 
-static inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti)
-{
-	int i;
-
-	qpti->host_param.initiator_scsi_id = qpti->scsi_id;
-	qpti->host_param.bus_reset_delay = 3;
-	qpti->host_param.retry_count = 0;
-	qpti->host_param.retry_delay = 5;
-	qpti->host_param.async_data_setup_time = 3;
-	qpti->host_param.req_ack_active_negation = 1;
-	qpti->host_param.data_line_active_negation = 1;
-	qpti->host_param.data_dma_burst_enable = 1;
-	qpti->host_param.command_dma_burst_enable = 1;
-	qpti->host_param.tag_aging = 8;
-	qpti->host_param.selection_timeout = 250;
-	qpti->host_param.max_queue_depth = 256;
-
-	for(i = 0; i < MAX_TARGETS; i++) {
-		/* disconnect, parity, arq, reneg on reset */
-		qpti->dev_param[i].device_flags = 0xc5;
-		qpti->dev_param[i].execution_throttle = 16;
-		if (qpti->ultra) {
-			qpti->dev_param[i].synchronous_period = 12;
-			qpti->dev_param[i].synchronous_offset = 8;
-		} else {
-			qpti->dev_param[i].synchronous_period = 16;
-			qpti->dev_param[i].synchronous_offset = 12;
-		}
-		qpti->dev_param[i].device_enable = 1;
-	}
-	qpti->sbits = 1 << qpti->host_param.initiator_scsi_id;
-}
-
 static void do_qlogicpti_intr_handler(int irq, void *dev_id, struct pt_regs *regs);
 #ifndef __sparc_v9__
 static void do_qlogicpti_intr_handler_sun4m(int irq, void *dev_id, struct pt_regs *regs);
@@ -629,6 +635,7 @@
 	int nqptis = 0, nqptis_in_use = 0;
 	int qpti_node;
 	int is_pti;
+	unsigned int cfreq;
 
 	tpnt->proc_dir = &proc_scsi_qlogicpti;
 	qptichain = 0;
@@ -735,6 +742,7 @@
 				qpti->qdev->reg_addrs[0].reg_size;
 
 			qpti_host->irq = qpti->irq = qpti->qdev->irqs[0];
+			qpti->gotirq = 0;
 
 			/* On Ultra and S{S1,C2}000 we must always call request_irq for each
 			 * qpti, so that imap registers get setup etc.
@@ -757,6 +765,7 @@
 				/* XXX Unmap regs, unregister scsi host, free things. */
 				continue;
 			}
+			qpti->gotirq = 1;
 qpti_irq_acquired:
 			printk("qpti%d: IRQ %s ",
 			       qpti->qpti_id, __irq_itoa(qpti->qhost->irq));
@@ -790,6 +799,15 @@
 				bsizes = (DMA_BURST32 - 1);
 			qpti->bursts = bsizes;
 
+			/* Check for what the clock input to this card is.
+			 * Default to 40Mhz.
+			 */
+			cfreq = prom_getintdefault(qpti->prom_node,"clock-frequency",40000000);
+        		qpti->clock = (cfreq + 500000)/1000000;
+			if (qpti->clock == 0) /* bullshit */
+				qpti->clock = 40;
+
+
 			/* Clear out Scsi_Cmnd array. */
 			memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots));
 
@@ -809,9 +827,6 @@
 #undef QSIZE
 
 
-			/* Set adapter and per-device default values. */
-			qlogicpti_set_hostdev_defaults(qpti);
-			
 			/* Load the firmware. */
 			if(qlogicpti_load_firmware(qpti))
 				panic("SBUS Qlogic/ISP firmware load failed");
@@ -856,17 +871,15 @@
 {
 	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
 	struct qlogicpti_regs *qregs = qpti->qregs;
-
 	/* Shut up the card. */
 	qregs->sbus_ctrl = 0;
-
 	/* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */
-	free_irq(host->irq, qpti);
+	if (qpti->gotirq)
+		free_irq(host->irq, qpti);
 	unmapioaddr((unsigned long)qregs);
 	/* QLGC,isp doesn't have status reg */
 	if (strcmp (qpti->prom_name, "QLGC,isp"))
 		unmapioaddr((unsigned long)qpti->sreg);
-
 	return 0;
 }
 
@@ -1019,30 +1032,28 @@
 	Cmnd->host_scribble = NULL;
 
 	if ((qpti->sbits & (1 << tgt)) == 0) {
-		if (Cmnd->cmnd[0] == 0x12 && host_byte(Cmnd->result) == DID_OK) {
+		int ok = host_byte(Cmnd->result) == DID_OK;
+		if (Cmnd->cmnd[0] == 0x12 && ok) {
 			unsigned char *iqd;
 			if (Cmnd->use_sg == 0) {
 				iqd = ((unsigned char *)Cmnd->buffer);
 			} else {
 				iqd = ((struct scatterlist *) Cmnd->request_buffer)->address;
 			}
-			/* Tags? */
-			if (iqd[7] & 0x2) {
-				qpti->dev_param[tgt].device_flags |= 0x8;
-			}
-			/* Sync Mode? */
+			/* tags handled in midlayer */
+			/* enable sync mode? */
 			if (iqd[7] & 0x10) {
 				qpti->dev_param[tgt].device_flags |= 0x10;
 			} else {
 				qpti->dev_param[tgt].synchronous_offset = 0;
 				qpti->dev_param[tgt].synchronous_period = 0;
 			}
-			/* Wide Capable? */
+			/* are we wide capable? */
 			if (iqd[7] & 0x20) {
 				qpti->dev_param[tgt].device_flags |= 0x20;
 			}
 			qpti->sbits |= (1 << tgt);
-		} else if (host_byte(Cmnd->result) != DID_OK) {
+		} else if (!ok) {
 			qpti->sbits |= (1 << tgt);
 		}
 	}
@@ -1051,28 +1062,64 @@
 
 int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
 {
+	unsigned long flags;
 	struct qlogicpti *qpti = (struct qlogicpti *) Cmnd->host->hostdata;
-	/* check to see if we're done scanning */
+
+	/*
+	 * done checking this host adapter?
+	 * If not, then rewrite the command
+	 * to finish through ourdone so we
+	 * can peek at Inquiry data results.
+	 */
+	if (qpti->sbits && qpti->sbits != 0xffff) {
+		Cmnd->host_scribble = (char *) done;
+		return qlogicpti_queuecommand(Cmnd, ourdone);
+	}
+	save_flags(flags); cli();
+
+	/*
+	 * We've peeked at all targets for this bus- time
+	 * to set parameters for devices for real now.
+	 */
 	if (qpti->sbits == 0xffff) {
 		int i;
-		unsigned long flags;
-		save_flags(flags); cli();
 		for(i = 0; i < MAX_TARGETS; i++) {
 			u_short param[6];
 			param[0] = MBOX_SET_TARGET_PARAMS;
 			param[1] = (i << 8);
 			param[2] = (qpti->dev_param[i].device_flags << 8);
-			param[3] = (qpti->dev_param[i].synchronous_offset << 8)|
-				qpti->dev_param[i].synchronous_period;
-			qlogicpti_mbox_command(qpti, param, 0);
+			if (qpti->dev_param[i].device_flags & 0x10) {
+				param[3] = (qpti->dev_param[i].synchronous_offset << 8) |
+					qpti->dev_param[i].synchronous_period;
+			} else {
+				param[3] = 0;
+			}
+			(void) qlogicpti_mbox_command(qpti, param, 0);
+		}
+		/*
+		 * set to zero so any traverse through ourdone
+		 * doesn't start the whole process again,
+		 */
+		qpti->sbits = 0;
+	}
+
+	/* check to see if we're done with all adapters... */
+	for (qpti = qptichain; qpti != NULL; qpti = qpti->next) {
+		if (qpti->sbits) {
+			break;
 		}
-		restore_flags(flags);
-		Cmnd->host->hostt->queuecommand = qlogicpti_queuecommand;
-		return qlogicpti_queuecommand(Cmnd, done);
-	} else {
-		Cmnd->host_scribble = (char *) done;
-		return qlogicpti_queuecommand(Cmnd, ourdone);
 	}
+
+	/*
+	 * if we hit the end of the chain w/o finding adapters still
+	 * capability-configuring, then we're done with all adapters
+	 * and can rock on..
+	 */
+	if (qpti == NULL)
+		Cmnd->host->hostt->queuecommand = qlogicpti_queuecommand;
+
+	restore_flags(flags);
+	return qlogicpti_queuecommand(Cmnd, done);
 }
 
 /*
@@ -1140,7 +1187,7 @@
 	return 0;
 }
 
-static int qlogicpti_return_status(struct Status_Entry *sts)
+static int qlogicpti_return_status(struct Status_Entry *sts, int id)
 {
 	int host_status = DID_ERROR;
 
@@ -1195,8 +1242,8 @@
 		host_status = DID_OK;
 		break;
 	      default:
-		printk(KERN_EMERG "qlogicpti : unknown completion status 0x%04x\n",
-		       sts->completion_status);
+		printk(KERN_EMERG "qpti%d: unknown completion status 0x%04x\n",
+		       id, sts->completion_status);
 		host_status = DID_ERROR;
 		break;
 	}
@@ -1259,7 +1306,8 @@
 			       sizeof(Cmnd->sense_buffer));
 
 		if(sts->hdr.entry_type == ENTRY_STATUS)
-			Cmnd->result = qlogicpti_return_status(sts);
+			Cmnd->result =
+			    qlogicpti_return_status(sts, qpti->qpti_id);
 		else
 			Cmnd->result = DID_ERROR << 16;
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/qlogicpti.h linux.14p6/drivers/scsi/qlogicpti.h
--- linux.vanilla/drivers/scsi/qlogicpti.h	Wed Oct 20 01:12:42 1999
+++ linux.14p6/drivers/scsi/qlogicpti.h	Tue Nov 16 15:13:28 1999
@@ -483,7 +483,7 @@
 	int                       prom_node;
 	char                      prom_name[64];
 	int                       irq;
-	char                      differential, ultra;
+	char                      differential, ultra, clock;
 	unsigned char             bursts;
 	struct	host_param        host_param;
 	struct	dev_param         dev_param[MAX_TARGETS];
@@ -496,8 +496,10 @@
 #define SREG_IMASK                0x0c   /* Interrupt level            */
 #define SREG_SPMASK               0x03   /* Mask for switch pack       */
 	unsigned char             swsreg;
-	unsigned char             is_pti; /* Non-zero if this is a PTI board. */
-	unsigned short            sbits;
+	unsigned int	
+		gotirq	:	1,	/* this instance got an irq */
+		is_pti	: 	1,	/* Non-zero if this is a PTI board. */
+		sbits	:	16;	/* syncmode known bits */
 };
 
 /* How to twiddle them bits... */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/scsi.c linux.14p6/drivers/scsi/scsi.c
--- linux.vanilla/drivers/scsi/scsi.c	Wed Oct 20 01:12:45 1999
+++ linux.14p6/drivers/scsi/scsi.c	Tue Nov 16 15:28:21 1999
@@ -258,6 +258,7 @@
 {"TEAC","CD-ROM","1.06", BLIST_NOLUN},          /* causes failed REQUEST SENSE on lun 1
 						 * for seagate controller, which causes
 						 * SCSI code to reset bus.*/
+	{"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN},	/* Responds to all lun */
 {"TEXEL","CD-ROM","1.06", BLIST_NOLUN},         /* causes failed REQUEST SENSE on lun 1
 						 * for seagate controller, which causes
 						 * SCSI code to reset bus.*/
@@ -268,7 +269,8 @@
 {"HP", "C1750A", "3226", BLIST_NOLUN},          /* scanjet iic */
 {"HP", "C1790A", "", BLIST_NOLUN},              /* scanjet iip */
 {"HP", "C2500A", "", BLIST_NOLUN},              /* scanjet iicx */
-{"YAMAHA", "CDR102", "1.00", BLIST_NOLUN},	/* extra reset */
+{"YAMAHA","CDR100","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
+{"YAMAHA","CDR102","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 {"RELISYS", "Scorpio", "*", BLIST_NOLUN},	/* responds to all LUN */
 
 /*
@@ -292,11 +294,10 @@
 {"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
 {"NEC","PD-1 ODX654P","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"MATSHITA","PD-1","*", BLIST_FORCELUN | BLIST_SINGLELUN},
-{"YAMAHA","CDR100","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
-{"YAMAHA","CDR102","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},
 {"CREATIVE","DVD-RAM RAM","*", BLIST_GHOST},
 {"MATSHITA","PD-2 LF-D100","*", BLIST_GHOST},
+{"HITACHI","GF-1050","*", BLIST_GHOST},        /* Hitachi SCSI DVD-RAM */
 {"TOSHIBA","CDROM","*", BLIST_ISROM},
 /*
  * Must be at end of list...
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/sg.c linux.14p6/drivers/scsi/sg.c
--- linux.vanilla/drivers/scsi/sg.c	Sat Aug 14 02:26:50 1999
+++ linux.14p6/drivers/scsi/sg.c	Wed Nov  3 00:27:23 1999
@@ -16,41 +16,28 @@
  *
  *  Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book.
  */
- static char * sg_version_str = "Version: 2.1.34 (990603)";
- static int sg_version_num = 20134; /* 2 digits for each component */
+ static char * sg_version_str = "Version: 2.1.36 (991008)";
+ static int sg_version_num = 20136; /* 2 digits for each component */
 /*
  *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
  *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
  *        the kernel/module needs to be built with CONFIG_SCSI_LOGGING
- *        (otherwise the macros compile to empty statements), then do
- *        something like: 'echo "scsi log all" > /proc/scsi/scsi' to log
- *        everything or 'echo "scsi log {token} #N" > /proc/scsi/scsi'
- *        where {token} is one of [error,timeout,scan,mlqueue,mlcomplete,
- *        llqueue,llcomplete,hlqueue,hlcomplete,ioctl] and #N is 0...7
- *        (with 0 meaning off). For example: 'scsi log timeout 7 > 
- *        /proc/scsi/scsi' to get all logging messages from this driver.
- *        Should use hlcomplete but it is too "noisy" (sd uses it).
- *
- *      - This driver obtains memory (heap) for the low-level driver to
- *        transfer/dma to and from. It is obtained from up to 3 sources:
- *              - obtain heap via get_free_pages()
- *              - obtain heap from the shared scsi dma pool
- *              - obtain heap from kernel directly (kmalloc) [last choice]
- *        Each open() attempts to obtain a "reserve" buffer of
- *        SG_DEF_RESERVED_SIZE bytes (or 0 bytes if opened O_RDONLY). The
- *        amount actually obtained [which could be 0 bytes] can be found from
- *        the SG_GET_RESERVED_SIZE ioctl(). This reserved buffer size can
- *        be changed by calling the SG_SET_RESERVED_SIZE ioctl(). Since this
- *        is an ambit claim, it should be followed by a SG_GET_RESERVED_SIZE
- *        ioctl() to find out how much was actually obtained.
- *        A subsequent write() to this file descriptor will use the
- *        reserved buffer unless:
- *              - it is already in use (eg during command queuing)
- *              - or the write() needs a buffer size larger than the
- *                reserved size
- *        In these cases the write() will attempt to get the required memory
- *        for the duration of this request but, if memory is low, it may
- *        fail with ENOMEM.
+ *        (otherwise the macros compile to empty statements). 
+ *        Then before running the program to be debugged enter: 
+ *          # echo "scsi log timeout 7" > /proc/scsi/scsi 
+ *        This will send copious output to the console and the log which
+ *        is usually /var/log/messages. To turn off debugging enter:
+ *          # echo "scsi log timeout 0" > /proc/scsi/scsi 
+ *        The 'timeout' token was chosen because it is relatively unused.
+ *        The token 'hlcomplete' should be used but that triggers too
+ *        much output from the sd device driver. To dump the current
+ *        state of the SCSI mid level data structures enter:
+ *          # echo "scsi dump 1" > /proc/scsi/scsi 
+ *        To dump the state of sg's data structures get the 'sg_debug'
+ *        program from the utilities and enter:
+ *          # sg_debug /dev/sga 
+ *        or any valid sg device name. The state of _all_ sg devices
+ *        will be sent to the console and the log.
  *
  *      - The 'alt_address' field in the scatter_list structure and the
  *        related 'mem_src' indicate the source of the heap allocation.
@@ -158,7 +145,7 @@
     char closed;        /* 1 -> fd closed but request(s) outstanding */
     char my_mem_src;    /* heap whereabouts of this Sg_fd object */
     char cmd_q;         /* 1 -> allow command queuing, 0 -> don't */
-    char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */
+    char underrun_flag; /* 1 -> flag underruns, 0 -> don't flag underruns */
     char next_cmd_len;  /* 0 -> automatic (def), >0 -> use on next write() */
 } Sg_fd; /* 1208 bytes long on i386 */
 
@@ -212,7 +199,6 @@
 static const int size_sg_header = sizeof(struct sg_header);
 
 
-
 static int sg_open(struct inode * inode, struct file * filp)
 {
     int dev = MINOR(inode->i_rdev);
@@ -316,7 +302,6 @@
     Sg_fd * sfp;
     Sg_request * srp;
     int req_pack_id = -1;
-    struct sg_header * shp = (struct sg_header *)buf;
 
     if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
         return -ENXIO;
@@ -329,8 +314,11 @@
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
     if ((k = verify_area(VERIFY_WRITE, buf, count)))
         return k;
-    if (sfp->force_packid && (count >= size_sg_header))
-        req_pack_id = shp->pack_id;
+    if (sfp->force_packid && (count >= size_sg_header)) {
+        struct sg_header hdr;
+        copy_from_user(&hdr, buf, size_sg_header);
+        req_pack_id = hdr.pack_id;
+    }
     srp = sg_get_request(sfp, req_pack_id);
     if (! srp) { /* now wait on packet to arrive */
         if (filp->f_flags & O_NONBLOCK)
@@ -342,8 +330,7 @@
         if (res)
             return res; /* -ERESTARTSYS because signal hit process */
     }
-    if (2 != sfp->underrun_flag)
-        srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
+    srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
 
     /* Now copy the result back to the user buffer.  */
     if (count >= size_sg_header) {
@@ -391,9 +378,8 @@
     if (count < (size_sg_header + 6))
         return -EIO;   /* The minimum scsi command length is 6 bytes. */ 
 
-    srp = sg_add_request(sfp);
-    if (! srp) {
-        SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full, domain error\n"));
+    if (! (srp = sg_add_request(sfp))) {
+        SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));
         return -EDOM;
     }
     __copy_from_user(&srp->header, buf, size_sg_header); 
@@ -404,7 +390,8 @@
         if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {
             SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));
             sfp->next_cmd_len = 0;
-            return -EDOM;
+            sg_remove_request(sfp, srp);
+            return -EIO;
         }
         cmd_size = sfp->next_cmd_len;
         sfp->next_cmd_len = 0; /* reset so only this write() effected */
@@ -422,9 +409,9 @@
                                                     srp->header.reply_len;
     mxsize -= size_sg_header;
     input_size -= size_sg_header;
-    if (input_size < 0) {
+    if ((input_size < 0)  || (srp->header.reply_len < 0)) {
         sg_remove_request(sfp, srp);
-        return -EIO; /* User did not pass enough bytes for this command. */
+        return -EIO; /* Count too small or reply_len negative. */
     }
     if ((k = sg_start_req(srp, mxsize, buf + cmd_size, input_size))) {
         SCSI_LOG_TIMEOUT(1, printk("sg_write: build err=%d\n", k));
@@ -432,8 +419,10 @@
         return k;    /* probably out of space --> ENOMEM */
     }
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */
-    if (! (SCpnt = scsi_allocate_device(NULL, sdp->device, 
-                                        !(filp->f_flags & O_NONBLOCK)))) {
+    spin_lock_irqsave(&io_request_lock, flags);
+    SCpnt = scsi_allocate_device(NULL, sdp->device, ! (filp->f_flags & O_NONBLOCK));
+    spin_unlock_irqrestore(&io_request_lock, flags);
+    if (! SCpnt) {
         sg_finish_rem_req(srp, NULL, 0);
         return -EAGAIN;   /* No available command blocks at the moment */
     }
@@ -490,7 +479,12 @@
     switch(cmd_in)
     {
     case SG_SET_TIMEOUT:
-        return get_user(sfp->timeout, (int *)arg);
+        result =  get_user(val, (int *)arg);
+        if (result) return result;
+        if (val < 0)
+            return -EIO;
+        sfp->timeout = val;
+        return 0;
     case SG_GET_TIMEOUT:  /* N.B. User receives timeout as return value */
         return sfp->timeout; /* strange ..., for backward compatibility */
     case SG_SET_FORCE_LOW_DMA:
@@ -514,14 +508,19 @@
         if (result) return result;
         else {
             Sg_scsi_id * sg_idp = (Sg_scsi_id *)arg;
-            __put_user((int)sdp->device->host->host_no, &sg_idp->host_no);
+            struct Scsi_Host * hostp = sdp->device->host;
+
+            __put_user((int)hostp->host_no, &sg_idp->host_no);
             __put_user((int)sdp->device->channel, &sg_idp->channel);
             __put_user((int)sdp->device->id, &sg_idp->scsi_id);
             __put_user((int)sdp->device->lun, &sg_idp->lun);
             __put_user((int)sdp->device->type, &sg_idp->scsi_type);
+            __put_user(hostp->cmd_per_lun ? hostp->cmd_per_lun :
+                       hostp->hostt->cmd_per_lun, &sg_idp->h_cmd_per_lun);
+            __put_user((short)sdp->device->queue_depth, 
+                       &sg_idp->d_queue_depth);
             __put_user(0, &sg_idp->unused1);
             __put_user(0, &sg_idp->unused2);
-            __put_user(0, &sg_idp->unused3);
             return 0;
         }
     case SG_SET_FORCE_PACK_ID:
@@ -605,6 +604,13 @@
         return put_user(sg_version_num, (int *)arg);
     case SG_EMULATED_HOST:
         return put_user(sdp->device->host->hostt->emulated, (int *)arg);
+    case SG_SCSI_RESET:
+        if (! scsi_block_when_processing_errors(sdp->device))
+            return -EBUSY;
+        result = get_user(val, (int *)arg);
+        if (result) return result;
+        /* Don't do anything till scsi mod level visibility */
+        return 0;
     case SCSI_IOCTL_SEND_COMMAND:
         /* Allow SCSI_IOCTL_SEND_COMMAND without checking suser() since the
            user already has read/write access to the generic device and so
@@ -686,9 +692,13 @@
     Sg_fd * sfp;
     Sg_request * srp = NULL;
     int closed = 0;
+    static const int min_sb_len = 
+                SG_MAX_SENSE > sizeof(SCpnt->sense_buffer) ? 
+                        sizeof(SCpnt->sense_buffer) : SG_MAX_SENSE;
 
     if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max)) {
         SCSI_LOG_TIMEOUT(1, printk("sg__done: bad args dev=%d\n", dev));
+        wake_up(&SCpnt->device->device_wait);
         scsi_release_command(SCpnt);
         SCpnt = NULL;
         return;
@@ -711,6 +721,7 @@
     }
     if (! srp) {
         SCSI_LOG_TIMEOUT(1, printk("sg__done: req missing, dev=%d\n", dev));
+        wake_up(&SCpnt->device->device_wait);
         scsi_release_command(SCpnt);
         SCpnt = NULL;
         return;
@@ -720,15 +731,12 @@
     srp->data.sglist_len = SCpnt->sglist_len;
     srp->data.bufflen = SCpnt->bufflen;
     srp->data.buffer = SCpnt->buffer;
-    if (2 == sfp->underrun_flag)
-        srp->header.pack_len = SCpnt->underflow;
     sg_clr_scpnt(SCpnt);
     srp->my_cmdp = NULL;
 
     SCSI_LOG_TIMEOUT(4, printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", 
                 dev, (int)status_byte(SCpnt->result), (int)SCpnt->result));
-    memcpy(srp->header.sense_buffer, SCpnt->sense_buffer,
-           sizeof(SCpnt->sense_buffer));
+    memcpy(srp->header.sense_buffer, SCpnt->sense_buffer, min_sb_len);
     switch (host_byte(SCpnt->result)) 
     { /* This setup of 'result' is for backward compatibility and is best
          ignored by the user who should use target, host + driver status */
@@ -781,6 +789,7 @@
     srp->header.host_status = host_byte(SCpnt->result);
     srp->header.driver_status = driver_byte(SCpnt->result);
 
+    wake_up(&SCpnt->device->device_wait);
     scsi_release_command(SCpnt);
     SCpnt = NULL;
     if (sfp->closed) { /* whoops this fd already released, cleanup */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/sym53c416.h linux.14p6/drivers/scsi/sym53c416.h
--- linux.vanilla/drivers/scsi/sym53c416.h	Sun Mar 28 19:04:11 1999
+++ linux.14p6/drivers/scsi/sym53c416.h	Tue Nov 16 16:15:03 1999
@@ -22,7 +22,9 @@
 #include <linux/version.h>
 #endif
 
+#ifndef LinuxVersionCode
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#endif
 
 #include <linux/types.h>
 #include <linux/kdev_t.h>
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/sym53c8xx_defs.h linux.14p6/drivers/scsi/sym53c8xx_defs.h
--- linux.vanilla/drivers/scsi/sym53c8xx_defs.h	Wed Oct 20 01:12:47 1999
+++ linux.14p6/drivers/scsi/sym53c8xx_defs.h	Tue Nov 16 16:15:03 1999
@@ -66,8 +66,9 @@
 #endif
 #include <linux/config.h>
 
+#ifndef LinuxVersionCode
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
-
+#endif
 /*
  * NCR PQS/PDS special device support.
  */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/u14-34f.c linux.14p6/drivers/scsi/u14-34f.c
--- linux.vanilla/drivers/scsi/u14-34f.c	Fri Oct  9 19:56:59 1998
+++ linux.14p6/drivers/scsi/u14-34f.c	Tue Nov  2 08:59:03 1999
@@ -1,6 +1,16 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *      16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
+ *        + Updated to the new __setup interface for boot command line options.
+ *        + When loaded as a module, accepts the new parameter boot_options
+ *          which value is a string with the same format of the kernel boot
+ *          command line options. A valid example is:
+ *          modprobe u14-34f 'boot_options=\"0x230,0x340,lc:y,mq:4\"'
+ *
+ *      22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11
+ *        + Removed pre-2.2 source code compatibility.
+ *
  *      26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
  *          Added command line option (et:[y|n]) to use the existing
  *          translation (returned by scsicam_bios_param) as disk geometry.
@@ -159,7 +169,7 @@
  *
  *          Multiple U14F and/or U34F host adapters are supported.
  *
- *  Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
+ *  Copyright (C) 1994-1999 Dario Ballabio (dario@milano.europe.dg.com)
  *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that redistributions of source
@@ -251,7 +261,7 @@
  *  After the optional list of detection probes, other possible command line
  *  options are:
  *
- *  eh:y  use new scsi code (linux 2.2 only);
+ *  eh:y  use new scsi code;
  *  eh:n  use old scsi code;
  *  et:y  use disk geometry returned by scsicam_bios_param;
  *  et:n  use disk geometry jumpered on the board;
@@ -318,7 +328,7 @@
 #if defined(MODULE)
 #include <linux/module.h>
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(boot_options, "s");
 MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
 MODULE_PARM(linked_comm, "i");
 MODULE_PARM(have_old_firmware, "i");
@@ -327,7 +337,6 @@
 MODULE_PARM(use_new_eh_code, "i");
 MODULE_PARM(ext_tran, "i");
 MODULE_AUTHOR("Dario Ballabio");
-#endif
 
 #endif
 
@@ -349,42 +358,21 @@
 #include "u14-34f.h"
 #include <linux/stat.h>
 #include <linux/config.h>
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
 #include <linux/init.h>
+#include <linux/ctype.h>
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
 #else
-#define __initfunc(A) A
-#define __initdata
-#define __init
+#include <linux/spinlock.h>
 #endif
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
-#include <asm/spinlock.h>
-#define IRQ_FLAGS
-#define IRQ_LOCK
-#define IRQ_LOCK_SAVE
-#define IRQ_UNLOCK
-#define IRQ_UNLOCK_RESTORE
 #define SPIN_FLAGS unsigned long spin_flags;
 #define SPIN_LOCK spin_lock_irq(&io_request_lock);
 #define SPIN_LOCK_SAVE spin_lock_irqsave(&io_request_lock, spin_flags);
 #define SPIN_UNLOCK spin_unlock_irq(&io_request_lock);
 #define SPIN_UNLOCK_RESTORE \
                   spin_unlock_irqrestore(&io_request_lock, spin_flags);
-static int use_new_eh_code = TRUE;
-#else
-#define IRQ_FLAGS unsigned long irq_flags;
-#define IRQ_LOCK cli();
-#define IRQ_LOCK_SAVE do {save_flags(irq_flags); cli();} while (0);
-#define IRQ_UNLOCK sti();
-#define IRQ_UNLOCK_RESTORE do {restore_flags(irq_flags);} while (0);
-#define SPIN_FLAGS
-#define SPIN_LOCK
-#define SPIN_LOCK_SAVE
-#define SPIN_UNLOCK
-#define SPIN_UNLOCK_RESTORE
-static int use_new_eh_code = FALSE;
-#endif
 
 struct proc_dir_entry proc_scsi_u14_34f = {
     PROC_SCSI_U14_34F, 6, "u14_34f",
@@ -467,7 +455,10 @@
 #define ASOK              0x00
 #define ASST              0x91
 
-#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#define ARRAY_SIZE(x) (sizeof (x) / sizeof((x)[0]))
+#endif
+
 #define YESNO(a) ((a) ? 'y' : 'n')
 #define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
 
@@ -532,7 +523,7 @@
 /* Initialize num_boards so that ihdlr can work while detect is in progress */
 static unsigned int num_boards = MAX_BOARDS;
 
-static unsigned long io_port[] __initdata = {
+static unsigned long io_port[] = {
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -570,6 +561,8 @@
 static int setup_done = FALSE;
 static int link_statistics = 0;
 static int ext_tran = FALSE;
+static int use_new_eh_code = TRUE;
+static char *boot_options = NULL;
 
 #if defined(HAVE_OLD_UX4F_FIRMWARE)
 static int have_old_firmware = TRUE;
@@ -592,9 +585,7 @@
 static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
    Scsi_Device *dev;
    int j, ntag = 0, nuntag = 0, tqd, utqd;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    j = ((struct hostdata *) host->hostdata)->board_number;
 
    for(dev = devlist; dev; dev = dev->next) {
@@ -642,7 +633,6 @@
              dev->queue_depth, link_suffix, tag_suffix);
       }
 
-   IRQ_UNLOCK_RESTORE
    return;
 }
 
@@ -686,10 +676,8 @@
    outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
 
    SPIN_UNLOCK
-   IRQ_UNLOCK
    time = jiffies;
    while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L);
-   IRQ_LOCK
    SPIN_LOCK
 
    if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) {
@@ -701,8 +689,8 @@
    return FALSE;
 }
 
-__initfunc (static inline int port_detect \
-      (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+static inline int port_detect \
+      (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char in_byte;
    char *bus_type, dma_name[16];
@@ -747,7 +735,9 @@
    sprintf(name, "%s%d", driver_name, j);
 
    if(check_region(port_base, REGION_SIZE)) {
+#if defined(DEBUG_DETECT)
       printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
+#endif
       return FALSE;
       }
 
@@ -781,11 +771,7 @@
 
    if (have_old_firmware) tpnt->use_clustering = DISABLE_CLUSTERING;
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
    tpnt->use_new_eh_code = use_new_eh_code;
-#else
-   use_new_eh_code = FALSE;
-#endif
 
    sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
 
@@ -894,7 +880,7 @@
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
 
    if (j == 0) {
-      printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n");
+      printk("UltraStor 14F/34F: Copyright (C) 1994-1999 Dario Ballabio.\n");
       printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c, et:%c.\n",
              driver_name, YESNO(have_old_firmware), YESNO(linked_comm),
              max_queue_depth, YESNO(use_new_eh_code), YESNO(ext_tran));
@@ -915,7 +901,7 @@
    return TRUE;
 }
 
-__initfunc (void u14_34f_setup(char *str, int *ints)) {
+static void internal_setup(char *str, int *ints) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -949,13 +935,30 @@
    return;
 }
 
-__initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
+static int option_setup(char *str) {
+   int ints[MAX_INT_PARAM];
+   char *cur = str;
+   int i = 1;
+
+   while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+      ints[i++] = simple_strtoul(cur, NULL, 0);
+
+      if ((cur = strchr(cur, ',')) != NULL) cur++;
+   }
+
+   ints[0] = i - 1;
+   internal_setup(cur, ints);
+   return 0;
+}
+
+int u14_34f_detect(Scsi_Host_Template *tpnt)
+{
    unsigned int j = 0, k;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    tpnt->proc_dir = &proc_scsi_u14_34f;
 
+   if(boot_options) option_setup(boot_options);
+
 #if defined(MODULE)
    /* io_port could have been modified when loading as a module */
    if(io_port[0] != SKIP) {
@@ -974,7 +977,6 @@
       }
 
    num_boards = j;
-   IRQ_UNLOCK_RESTORE
    return j;
 }
 
@@ -1110,11 +1112,8 @@
 
 int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    int rtn;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    rtn = do_qcomm(SCpnt, done);
-   IRQ_UNLOCK_RESTORE
    return rtn;
 }
 
@@ -1186,16 +1185,11 @@
 
 int u14_34f_old_abort(Scsi_Cmnd *SCarg) {
    int rtn;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    rtn = do_old_abort(SCarg);
-   IRQ_UNLOCK_RESTORE
    return rtn;
 }
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
-
 static inline int do_abort(Scsi_Cmnd *SCarg) {
    unsigned int i, j;
 
@@ -1273,8 +1267,6 @@
    return do_abort(SCarg);
 }
 
-#endif /* new_eh_code */
-
 static inline int do_old_reset(Scsi_Cmnd *SCarg) {
    unsigned int i, j, time, k, c, limit = 0;
    int arg_done = FALSE;
@@ -1362,10 +1354,8 @@
 
    HD(j)->in_reset = TRUE;
    SPIN_UNLOCK
-   IRQ_UNLOCK
    time = jiffies;
    while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
-   IRQ_LOCK
    SPIN_LOCK
    printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
 
@@ -1401,7 +1391,6 @@
          continue;
 
       SCpnt->scsi_done(SCpnt);
-      IRQ_LOCK
       }
 
    HD(j)->in_reset = FALSE;
@@ -1419,16 +1408,11 @@
 
 int u14_34f_old_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
    int rtn;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    rtn = do_old_reset(SCarg);
-   IRQ_UNLOCK_RESTORE
    return rtn;
 }
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
-
 static inline int do_reset(Scsi_Cmnd *SCarg) {
    unsigned int i, j, time, k, c, limit = 0;
    int arg_done = FALSE;
@@ -1510,10 +1494,8 @@
 
    HD(j)->in_reset = TRUE;
    SPIN_UNLOCK
-   IRQ_UNLOCK
    time = jiffies;
    while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
-   IRQ_LOCK
    SPIN_LOCK
    printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
 
@@ -1549,7 +1531,6 @@
          continue;
 
       SCpnt->scsi_done(SCpnt);
-      IRQ_LOCK
       }
 
    HD(j)->in_reset = FALSE;
@@ -1566,8 +1547,6 @@
    return do_reset(SCarg);
 }
 
-#endif /* new_eh_code */
-
 int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) {
    unsigned int j = 0;
    int size = disk->capacity;
@@ -1956,24 +1935,18 @@
 
 static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) {
    unsigned int j;
-   IRQ_FLAGS
    SPIN_FLAGS
 
    /* Check if the interrupt must be processed by this handler */
    if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
 
    SPIN_LOCK_SAVE
-   IRQ_LOCK_SAVE
    ihdlr(irq, j);
-   IRQ_UNLOCK_RESTORE
    SPIN_UNLOCK_RESTORE
 }
 
 int u14_34f_release(struct Scsi_Host *shpnt) {
    unsigned int i, j;
-   IRQ_FLAGS
-
-   IRQ_LOCK_SAVE
 
    for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++);
 
@@ -1989,7 +1962,6 @@
 
    release_region(sh[j]->io_port, sh[j]->n_io_port);
    scsi_unregister(sh[j]);
-   IRQ_UNLOCK_RESTORE
    return FALSE;
 }
 
@@ -1997,4 +1969,15 @@
 Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
 
 #include "scsi_module.c"
+
+#else
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+void u14_34f_setup(char *str, int *ints) {
+   internal_setup(str, ints);
+}
+#else
+__setup("u14-34f=", option_setup);
 #endif
+
+#endif /* end MODULE */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/u14-34f.h linux.14p6/drivers/scsi/u14-34f.h
--- linux.vanilla/drivers/scsi/u14-34f.h	Sun Mar 28 19:04:08 1999
+++ linux.14p6/drivers/scsi/u14-34f.h	Tue Nov 16 16:15:03 1999
@@ -16,11 +16,7 @@
 int u14_34f_old_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "4.33.00"
-
-#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+#define U14_34F_VERSION "5.11.01"
 
 #define ULTRASTOR_14_34F {                                                   \
                 name:         "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
@@ -39,22 +35,5 @@
                 use_clustering:          ENABLE_CLUSTERING,                  \
                 use_new_eh_code:         1    /* Enable new error code */    \
                 }
-
-#else /* Use old scsi code */
-
-#define ULTRASTOR_14_34F {                                                   \
-                name:         "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
-                detect:                  u14_34f_detect,                     \
-                release:                 u14_34f_release,                    \
-                queuecommand:            u14_34f_queuecommand,               \
-                abort:                   u14_34f_old_abort,                  \
-                reset:                   u14_34f_old_reset,                  \
-                bios_param:              u14_34f_biosparam,                  \
-                this_id:                 7,                                  \
-                unchecked_isa_dma:       1,                                  \
-                use_clustering:          ENABLE_CLUSTERING                   \
-                }
-
-#endif
 
 #endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/Config.in linux.14p6/drivers/sound/Config.in
--- linux.vanilla/drivers/sound/Config.in	Wed Oct 20 01:12:47 1999
+++ linux.14p6/drivers/sound/Config.in	Fri Oct 29 23:23:20 1999
@@ -28,6 +28,9 @@
       hex 'Gameport I/O 200,208,210,218' CONFIG_SOUND_ES1371_GAMEPORT 200
    fi
 fi
+
+dep_tristate 'ESS Maestro' CONFIG_SOUND_MAESTRO $CONFIG_SOUND
+
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    dep_tristate 'ESS Solo1 (Experimental)' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND
 fi
@@ -114,8 +117,6 @@
       comment 'Enter -1 to the following question if you have something else such as SB16/32.'
       int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1
   fi
-  
-  dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS
   
   dep_tristate 'Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS
   if [ "$CONFIG_SOUND_GUS" = "y" -o "$CONFIG_SOUND_GUS" = "m" ]; then
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/Makefile linux.14p6/drivers/sound/Makefile
--- linux.vanilla/drivers/sound/Makefile	Wed Oct 20 01:12:47 1999
+++ linux.14p6/drivers/sound/Makefile	Fri Oct 29 23:22:40 1999
@@ -43,7 +43,6 @@
 obj-$(CONFIG_SOUND)		+= soundcore.o
 obj-$(CONFIG_DMASOUND)		+= dmasound.o
 obj-$(CONFIG_SOUND_OSS)		+= sound.o
-obj-$(CONFIG_SOUND_ADLIB)	+= adlib_card.o opl3.o
 obj-$(CONFIG_SOUND_CS4232)	+= cs4232.o ad1848.o 
 
 # In theory, there's probably no reason to include the uart401 code
@@ -84,6 +83,7 @@
 obj-$(CONFIG_SOUND_ES1370)	+= es1370.o
 obj-$(CONFIG_SOUND_ES1371)	+= es1371.o
 obj-$(CONFIG_SOUND_ESSSOLO1)	+= esssolo1.o
+obj-$(CONFIG_SOUND_MAESTRO)	+= maestro.o
 obj-$(CONFIG_SOUND_SONICVIBES)	+= sonicvibes.o
 
 # Declare multi-part drivers.
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/ac97.h linux.14p6/drivers/sound/ac97.h
--- linux.vanilla/drivers/sound/ac97.h	Wed Oct 20 01:12:47 1999
+++ linux.14p6/drivers/sound/ac97.h	Tue Nov 16 17:22:20 1999
@@ -33,6 +33,19 @@
 
 /* registers 0x0028 - 0x0058 are reserved */
 
+/* AC'97 2.0 */
+#define AC97_EXTENDED_ID	0x0028	/* Extended Audio ID */
+#define AC97_EXTENDED_STATUS	0x002A	/* Extended Audio Status */
+#define AC97_PCM_FRONT_DAC_RATE 0x002C  /* PCM Front DAC Rate */
+#define AC97_PCM_SURR_DAC_RATE  0x002E  /* PCM Surround DAC Rate */
+#define AC97_PCM_LFE_DAC_RATE   0x0030  /* PCM LFE DAC Rate */
+#define AC97_PCM_LR_DAC_RATE	0x0032	/* PCM LR DAC Rate */
+#define AC97_PCM_MIC_ADC_RATE   0x0034  /* PCM MIC ADC Rate */
+#define AC97_CENTER_LFE_MASTER  0x0036  /* Center + LFE Master Volume */
+#define AC97_SURROUND_MASTER    0x0038  /* Surround (Rear) Master Volume */
+#define AC97_RESERVED_3A	0x003A	/* Reserved */
+/* range 0x3c-0x58 - MODEM */
+
 /* registers 0x005a - 0x007a are vendor reserved */
 
 #define  AC97_VENDOR_ID1         0x007c
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/adlib_card.c linux.14p6/drivers/sound/adlib_card.c
--- linux.vanilla/drivers/sound/adlib_card.c	Thu May 14 18:33:17 1998
+++ linux.14p6/drivers/sound/adlib_card.c	Fri Oct 22 22:52:53 1999
@@ -16,8 +16,6 @@
 #include "sound_config.h"
 #include "soundmodule.h"
 
-#ifdef CONFIG_YM3812
-
 void attach_adlib_card(struct address_info *hw_config)
 {
 	hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp);
@@ -69,5 +67,4 @@
 	SOUND_LOCK_END;
 }
 
-#endif
 #endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/es1370.c linux.14p6/drivers/sound/es1370.c
--- linux.vanilla/drivers/sound/es1370.c	Sat Aug 14 02:27:34 1999
+++ linux.14p6/drivers/sound/es1370.c	Fri Oct 22 22:34:17 1999
@@ -101,6 +101,17 @@
  *    15.06.99   0.23  Fix bad allocation bug.
  *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
  *    28.06.99   0.24  Add pci_set_master
+ *    02.08.99   0.25  Added workaround for the "phantom write" bug first
+ *                     documented by Dave Sharpless from Anchor Games
+ *    03.08.99   0.26  adapt to Linus' new __setup/__initcall
+ *                     added kernel command line option "es1370=joystick[,lineout[,micbias]]"
+ *                     removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge
+ *    12.08.99   0.27  module_init/__setup fixes
+ *    19.08.99   0.28  SOUND_MIXER_IMIX fixes, reported by Gianluca <gialluca@mail.tiscalinet.it>
+ *    31.08.99   0.29  add spin_lock_init
+ *                     __initlocaldata to fix gcc 2.7.x problems
+ *    03.09.99   0.30  change read semantics for MIDI to match
+ *                     OSS more closely; remove possible wakeup race
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -123,7 +134,6 @@
 
 /*****************************************************************************/
       
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/string.h>
@@ -175,12 +185,14 @@
 #define ES1370_REG_DAC2_SCOUNT    0x28
 #define ES1370_REG_ADC_SCOUNT     0x2c
 
-#define ES1370_REG_DAC1_FRAMEADR  0xc30
-#define ES1370_REG_DAC1_FRAMECNT  0xc34
-#define ES1370_REG_DAC2_FRAMEADR  0xc38
-#define ES1370_REG_DAC2_FRAMECNT  0xc3c
-#define ES1370_REG_ADC_FRAMEADR   0xd30
-#define ES1370_REG_ADC_FRAMECNT   0xd34
+#define ES1370_REG_DAC1_FRAMEADR    0xc30
+#define ES1370_REG_DAC1_FRAMECNT    0xc34
+#define ES1370_REG_DAC2_FRAMEADR    0xc38
+#define ES1370_REG_DAC2_FRAMECNT    0xc3c
+#define ES1370_REG_ADC_FRAMEADR     0xd30
+#define ES1370_REG_ADC_FRAMECNT     0xd34
+#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
+#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
 
 #define ES1370_FMT_U8_MONO     0
 #define ES1370_FMT_U8_STEREO   1
@@ -360,6 +372,13 @@
 
 static struct es1370_state *devs = NULL;
 
+/*
+ * The following buffer is used to point the phantom write channel to,
+ * so that it cannot wreak havoc. The attribute makes sure it doesn't
+ * cross a page boundary and ensures dword alignment for the DMA engine
+ */
+static unsigned char bugbuf[16] __attribute__ ((aligned (16)));
+
 /* --------------------------------------------------------------------- */
 
 extern inline unsigned ld2(unsigned int x)
@@ -779,10 +798,36 @@
 	[SOUND_MIXER_OGAIN]  = { 9, 0xf, 0x0, 0, 0x0000, 1 }    /* mono out */
 };
 
+static void set_recsrc(struct es1370_state *s, unsigned int val)
+{
+	unsigned int i, j;
+
+	for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+		if (!(val & (1 << i)))
+			continue;
+		if (!mixtable[i].recmask) {
+			val &= ~(1 << i);
+			continue;
+		}
+		j |= mixtable[i].recmask;
+	}
+	s->mix.recsrc = val;
+	wrcodec(s, 0x12, j & 0xd5);
+	wrcodec(s, 0x13, j & 0xaa);
+	wrcodec(s, 0x14, (j >> 8) & 0x17);
+	wrcodec(s, 0x15, (j >> 8) & 0x0f);
+	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);
+}
+
 static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg)
 {
 	unsigned long flags;
-	int i, val, j;
+	int i, val;
 	unsigned char l, r, rl, rr;
 
 	VALIDATE_STATE(s);
@@ -887,34 +932,13 @@
 	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 */
+		get_user_ret(s->mix.imix, (int *)arg, -EFAULT);
+		set_recsrc(s, s->mix.recsrc);
+		return 0;
 
 	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++) {
-			if (!(val & (1 << i)))
-				continue;
-			if (!mixtable[i].recmask) {
-				val &= ~(1 << i);
-				continue;
-			}
-			j |= mixtable[i].recmask;
-		}
-		s->mix.recsrc = val;
-		wrcodec(s, 0x12, j & 0xd5);
-		wrcodec(s, 0x13, j & 0xaa);
-		wrcodec(s, 0x14, (j >> 8) & 0x17);
-		wrcodec(s, 0x15, (j >> 8) & 0x0f);
-		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);
+		set_recsrc(s, val);
 		return 0;
 
 	default:
@@ -1033,7 +1057,7 @@
 
 static int drain_dac1(struct es1370_state *s, int nonblock)
 {
-        struct wait_queue wait = { current, NULL };
+	struct wait_queue wait = { current, NULL };
 	unsigned long flags;
 	int count, tmo;
 	
@@ -1054,9 +1078,10 @@
                         current->state = TASK_RUNNING;
                         return -EBUSY;
                 }
-		tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+		tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2
+			/ dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
-		if (!schedule_timeout(tmo ? : 1) && tmo)
+		if (!schedule_timeout(tmo + 1))
 			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac1.wait, &wait);
@@ -1089,9 +1114,10 @@
                         current->state = TASK_RUNNING;
                         return -EBUSY;
                 }
-		tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV);
+		tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2
+			/ DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV);
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
-		if (!schedule_timeout(tmo ? : 1) && tmo)
+		if (!schedule_timeout(tmo + 1))
 			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac2.wait, &wait);
@@ -2049,6 +2075,7 @@
 static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	struct wait_queue wait = { current, NULL };
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2059,7 +2086,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+        add_wait_queue(&s->midi.iwait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.ird;
@@ -2070,15 +2100,25 @@
 		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;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIINBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.ird = ptr;
@@ -2087,13 +2127,17 @@
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
+		break;
 	}
+	current->state = TASK_RUNNING;
+        remove_wait_queue(&s->midi.iwait, &wait);
 	return ret;
 }
 
 static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	struct wait_queue wait = { current, NULL };
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2104,7 +2148,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.owr;
@@ -2117,15 +2164,25 @@
 		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;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIOUTBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.owr = ptr;
@@ -2138,6 +2195,8 @@
 		es1370_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
+	current->state = TASK_RUNNING;
+        remove_wait_queue(&s->midi.owait, &wait);
 	return ret;
 }
 
@@ -2217,7 +2276,7 @@
 static int es1370_midi_release(struct inode *inode, struct file *file)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
-        struct wait_queue wait = { current, NULL };
+	struct wait_queue wait = { current, NULL };
 	unsigned long flags;
 	unsigned count, tmo;
 
@@ -2321,7 +2380,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.24 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.30 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 || 
@@ -2341,6 +2400,7 @@
 		init_waitqueue(&s->midi.iwait);
 		init_waitqueue(&s->midi.owait);
 		s->open_sem = MUTEX;
+		spin_lock_init(&s->lock);
 		s->magic = ES1370_MAGIC;
 		s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
 		s->irq = pcidev->irq;
@@ -2385,6 +2445,10 @@
 		/* initialize the chips */
 		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
 		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		/* point phantom write channel to "bugbuf" */
+		outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
+		outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff));
+		outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff));
 		pci_set_master(pcidev);  /* enable bus mastering */
 		wrcodec(s, 0x16, 3); /* no RST, PD */
 		wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!!  */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/es1371.c linux.14p6/drivers/sound/es1371.c
--- linux.vanilla/drivers/sound/es1371.c	Sat Aug 14 02:27:34 1999
+++ linux.14p6/drivers/sound/es1371.c	Fri Oct 22 22:34:18 1999
@@ -3,7 +3,7 @@
 /*
  *      es1371.c  --  Creative Ensoniq ES1371.
  *
- *      Copyright (C) 1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *      Copyright (C) 1998-1999  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
@@ -68,14 +68,31 @@
  *    15.06.99   0.12  Fix bad allocation bug.
  *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
  *    28.06.99   0.13  Add pci_set_master
- *    21.07.99   0.14  S/PDIF module option for cards revision >= 4. Initial version
- *                     by Dave Platt <dplatt@snulbug.mtview.ca.us>.
+ *    03.08.99   0.14  adapt to Linus' new __setup/__initcall
+ *                     added kernel command line option "es1371=joystickaddr"
+ *                     removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge
+ *    10.08.99   0.15  (Re)added S/PDIF module option for cards revision >= 4.
+ *                     Initial version by Dave Platt <dplatt@snulbug.mtview.ca.us>.
+ *                     module_init/__setup fixes
+ *    08.16.99   0.16  Joe Cotellese <joec@ensoniq.com>
+ *                     Added detection for ES1371 revision ID so that we can
+ *                     detect the ES1373 and later parts.
+ *                     added AC97 #defines for readability
+ *                     added a /proc file system for dumping hardware state
+ *                     updated SRC and CODEC w/r functions to accomodate bugs
+ *                     in some versions of the ES137x chips.
+ *    31.08.99   0.17  add spin_lock_init
+ *                     __initlocaldata to fix gcc 2.7.x problems
+ *                     replaced current->state = x with set_current_state(x)
+ *    03.09.99   0.18  change read semantics for MIDI to match
+ *                     OSS more closely; remove possible wakeup race
+ *    21.10.99   0.19  Round sampling rates, requested by
+ *                     Kasamatsu Kenichi <t29w0267@ip.media.kyoto-u.ac.jp>
  *
  */
 
 /*****************************************************************************/
       
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/string.h>
@@ -86,17 +103,21 @@
 #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 <linux/bitops.h>
+#include <linux/proc_fs.h>
 #include <asm/spinlock.h>
+#include <asm/io.h>
+#include <asm/dma.h>
 #include <asm/uaccess.h>
 #include <asm/hardirq.h>
+#include "ac97.h"
 
 /* --------------------------------------------------------------------- */
 
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#undef ES1371_DEBUG
 
 /* --------------------------------------------------------------------- */
 
@@ -107,6 +128,18 @@
 #define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
 #endif
 
+/* ES1371 chip ID */
+/* This is a little confusing because all ES1371 compatible chips have the
+   same DEVICE_ID, the only thing differentiating them is the REV_ID field.
+   This is only significant if you want to enable features on the later parts.
+   Yes, I know it's stupid and why didn't we use the sub IDs?
+*/
+#define ES1371REV_ES1373_A  0x04
+#define ES1371REV_ES1373_B  0x06
+#define ES1371REV_CT5880_A  0x07
+#define ES1371REV_ES1371_B  0x09
+
+
 #define ES1371_MAGIC  ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371)
 
 #define ES1371_EXTENT             0x40
@@ -206,14 +239,22 @@
 #define UCTRL_CNTRL_SWR 0x03        /* software reset command */
 
 /* sample rate converter */
+#define SRC_OKSTATE        1
+
 #define SRC_RAMADDR_MASK   0xfe000000
 #define SRC_RAMADDR_SHIFT  25
+#define SRC_DAC1FREEZE     (1UL << 21)
+#define SRC_DAC2FREEZE      (1UL << 20)
+#define SRC_ADCFREEZE      (1UL << 19)
+
+
 #define SRC_WE             0x01000000  /* read/write control for SRC RAM */
 #define SRC_BUSY           0x00800000  /* SRC busy */
 #define SRC_DIS            0x00400000  /* 1 = disable SRC */
 #define SRC_DDAC1          0x00200000  /* 1 = disable accum update for DAC1 */
 #define SRC_DDAC2          0x00100000  /* 1 = disable accum update for DAC2 */
 #define SRC_DADC           0x00080000  /* 1 = disable accum update for ADC2 */
+#define SRC_CTLMASK        0x00780000
 #define SRC_RAMDATA_MASK   0x0000ffff
 #define SRC_RAMDATA_SHIFT  0
 
@@ -293,7 +334,7 @@
 
 
 /* misc stuff */
-
+#define POLL_COUNT   0x1000
 #define FMODE_DAC         4           /* slight misuse of mode_t */
 
 /* MIDI buffer sizes */
@@ -352,8 +393,13 @@
 	/* hardware resources */
 	unsigned long io; /* long for SPARC */
 	unsigned int irq;
-
-	/* mixer registers; there is no HW readback */
+        u8 rev; /* the chip revision */
+        
+#ifdef ES1371_DEBUG
+        /* debug /proc entry */
+	struct proc_dir_entry *ps;
+#endif /* ES1371_DEBUG */
+        /* mixer registers; there is no HW readback */
 	struct {
 		unsigned short codec_id;
 		unsigned int modcnt;
@@ -438,31 +484,12 @@
 }
 
 /* --------------------------------------------------------------------- */
-/*
- * 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 unsigned wait_src_ready(struct es1371_state *s)
 {
 	unsigned int t, r;
 
-	for (t = 0; t < 1000; t++) {
+	for (t = 0; t < POLL_COUNT; t++) {
 		if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY))
 			return r;
 		udelay(1);
@@ -473,29 +500,53 @@
 
 static unsigned src_read(struct es1371_state *s, unsigned reg)
 {
-	unsigned int r;
+        unsigned int temp,i,orig;
 
-	r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC);
-	r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK;
-	outl(r, s->io + ES1371_REG_SRCONV);
-	return (wait_src_ready(s) & SRC_RAMDATA_MASK) >> SRC_RAMDATA_SHIFT;
-}
+        /* wait for ready */
+        temp = wait_src_ready (s);
+
+        /* we can only access the SRC at certain times, make sure
+           we're allowed to before we read */
+           
+        orig = temp;
+        /* expose the SRC state bits */
+        outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL,
+               s->io + ES1371_REG_SRCONV);
+
+        /* now, wait for busy and the correct time to read */
+        temp = wait_src_ready (s);
+
+        if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){
+                /* wait for the right state */
+                for (i=0; i<POLL_COUNT; i++){
+                        temp = inl (s->io + ES1371_REG_SRCONV);
+                        if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 ))
+                                break;
+                }
+        }
 
+        /* hide the state bits */
+        outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV);
+        return temp;
+                        
+                
+}
 
 static void src_write(struct es1371_state *s, unsigned reg, unsigned data)
 {
+      
 	unsigned int r;
 
 	r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC);
 	r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK;
 	r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK;
 	outl(r | SRC_WE, s->io + ES1371_REG_SRCONV);
+
 }
 
 /* --------------------------------------------------------------------- */
 
 /* most of the following here is black magic */
-
 static void set_adc_rate(struct es1371_state *s, unsigned rate)
 {
 	unsigned long flags;
@@ -532,6 +583,7 @@
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
+
 static void set_dac1_rate(struct es1371_state *s, unsigned rate)
 {
 	unsigned long flags;
@@ -541,8 +593,8 @@
 		rate = 48000;
 	if (rate < 4000)
 		rate = 4000;
-        freq = (rate << 15) / 3000;
-	s->dac1rate = (freq * 3000) >> 15;
+        freq = ((rate << 15) + 1500) / 3000;
+	s->dac1rate = (freq * 3000 + 16384) >> 15;
 	spin_lock_irqsave(&s->lock, flags);
 	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1;
 	outl(r, s->io + ES1371_REG_SRCONV);
@@ -564,8 +616,8 @@
 		rate = 48000;
 	if (rate < 4000)
 		rate = 4000;
-        freq = (rate << 15) / 3000;
-	s->dac2rate = (freq * 3000) >> 15;
+        freq = ((rate << 15) + 1500) / 3000;
+	s->dac2rate = (freq * 3000 + 16384) >> 15;
 	spin_lock_irqsave(&s->lock, flags);
 	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2;
 	outl(r, s->io + ES1371_REG_SRCONV);
@@ -580,26 +632,80 @@
 
 /* --------------------------------------------------------------------- */
 
+static void __init src_init(struct es1371_state *s)
+{
+        unsigned int i;
+
+        /* before we enable or disable the SRC we need
+           to wait for it to become ready */
+        wait_src_ready(s);
+
+        outl(SRC_DIS, s->io + ES1371_REG_SRCONV);
+
+        for (i = 0; i < 0x80; i++)
+                src_write(s, i, 0);
+
+        src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4);
+        src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10);
+        src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4);
+        src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10);
+        src_write(s, SRCREG_VOL_ADC, 1 << 12);
+        src_write(s, SRCREG_VOL_ADC+1, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC1, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC1+1, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC2, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC2+1, 1 << 12);
+        set_adc_rate(s, 22050);
+        set_dac1_rate(s, 22050);
+        set_dac2_rate(s, 22050);
+
+        /* WARNING:
+         * enabling the sample rate converter without properly programming
+         * its parameters causes the chip to lock up (the SRC busy bit will
+         * be stuck high, and I've found no way to rectify this other than
+         * power cycle)
+         */
+        wait_src_ready(s);
+        outl(0, s->io+ES1371_REG_SRCONV);
+}
+
+/* --------------------------------------------------------------------- */
+
 static void wrcodec(struct es1371_state *s, unsigned addr, unsigned data)
 {
 	unsigned long flags;
 	unsigned t, x;
-
-	for (t = 0; t < 0x1000; t++)
+        
+	for (t = 0; t < POLL_COUNT; t++)
 		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
 			break;
 	spin_lock_irqsave(&s->lock, flags);
-	/* save the current state for later */
-	x = inl(s->io+ES1371_REG_SRCONV);
-	/* enable SRC state data in SRC mux */
-	outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000,
+
+        /* save the current state for later */
+        x = wait_src_ready(s);
+
+        /* enable SRC state data in SRC mux */
+	outl(( x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000,
 	     s->io+ES1371_REG_SRCONV);
-	/* wait for a SAFE time to write addr/data and then do it, dammit */
-	for (t = 0; t < 0x1000; t++)
-		if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000)
-			break;
+
+        /* wait for not busy (state 0) first to avoid
+           transition states */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0 )
+                    break;
+                udelay(1);
+        }
+        
+        /* wait for a SAFE time to write addr/data and then do it, dammit */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000)
+                    break;
+                udelay(1);
+        }
+
 	outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
 	     ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC);
+
 	/* restore SRC reg */
 	wait_src_ready(s);
 	outl(x, s->io+ES1371_REG_SRCONV);
@@ -611,28 +717,50 @@
 	unsigned long flags;
 	unsigned t, x;
 
+        /* wait for WIP to go away */
 	for (t = 0; t < 0x1000; t++)
 		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
 			break;
 	spin_lock_irqsave(&s->lock, flags);
+
 	/* save the current state for later */
-	x = inl(s->io+ES1371_REG_SRCONV);
+	x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC));
+
 	/* enable SRC state data in SRC mux */
-	outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000,
-	     s->io+ES1371_REG_SRCONV);
-	/* wait for a SAFE time to write addr/data and then do it, dammit */
-	for (t = 0; t < 0x1000; t++)
-		if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000)
-			break;
+	outl( x | 0x00010000,
+              s->io+ES1371_REG_SRCONV);
+
+        /* wait for not busy (state 0) first to avoid
+           transition states */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0 )
+                    break;
+                udelay(1);
+        }
+        
+        /* wait for a SAFE time to write addr/data and then do it, dammit */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000)
+                    break;
+                udelay(1);
+        }
+
 	outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC);
 	/* restore SRC reg */
 	wait_src_ready(s);
 	outl(x, s->io+ES1371_REG_SRCONV);
 	spin_unlock_irqrestore(&s->lock, flags);
-	/* now wait for the stinkin' data (RDY) */
+
+        /* wait for WIP again */
 	for (t = 0; t < 0x1000; t++)
+		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
+			break;
+        
+	/* now wait for the stinkin' data (RDY) */
+	for (t = 0; t < POLL_COUNT; t++)
 		if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY)
 			break;
+        
 	return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
 }
 
@@ -993,6 +1121,21 @@
 
 /* --------------------------------------------------------------------- */
 
+/*
+ * AC97 Mixer Register to Connections mapping of the Concert 97 board
+ *
+ * AC97_MASTER_VOL_STEREO   Line Out
+ * AC97_MASTER_VOL_MONO     TAD Output
+ * AC97_PCBEEP_VOL          none
+ * AC97_PHONE_VOL           TAD Input (mono)
+ * AC97_MIC_VOL             MIC Input (mono)
+ * AC97_LINEIN_VOL          Line Input (stereo)
+ * AC97_CD_VOL              CD Input (stereo)
+ * AC97_VIDEO_VOL           none
+ * AC97_AUX_VOL             Aux Input (stereo)
+ * AC97_PCMOUT_VOL          Wave Output (stereo)
+ */
+
 #define AC97_PESSIMISTIC
 
 /*
@@ -1018,25 +1161,25 @@
 static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = 
 {
 	/* 5 bit stereo */
-	[SOUND_MIXER_LINE] = 0x10,
-	[SOUND_MIXER_CD] = 0x12,
-	[SOUND_MIXER_VIDEO] = 0x14,
-	[SOUND_MIXER_LINE1] = 0x16,
-	[SOUND_MIXER_PCM] = 0x18,
+	[SOUND_MIXER_LINE] = AC97_LINEIN_VOL,
+	[SOUND_MIXER_CD] = AC97_CD_VOL,
+	[SOUND_MIXER_VIDEO] = AC97_VIDEO_VOL,
+	[SOUND_MIXER_LINE1] = AC97_AUX_VOL,
+	[SOUND_MIXER_PCM] = AC97_PCMOUT_VOL,
 	/* 6 bit stereo */
-	[SOUND_MIXER_VOLUME] = 0x02,
-	[SOUND_MIXER_PHONEOUT] = 0x04,
+	[SOUND_MIXER_VOLUME] = AC97_MASTER_VOL_STEREO,
+	[SOUND_MIXER_PHONEOUT] = AC97_HEADPHONE_VOL,
 	/* 6 bit mono */
-	[SOUND_MIXER_OGAIN] = 0x06,
-	[SOUND_MIXER_PHONEIN] = 0x0c,
+	[SOUND_MIXER_OGAIN] = AC97_MASTER_VOL_MONO,
+	[SOUND_MIXER_PHONEIN] = AC97_PHONE_VOL,
 	/* 4 bit mono but shifted by 1 */
-	[SOUND_MIXER_SPEAKER] = 0x08,
+	[SOUND_MIXER_SPEAKER] = AC97_MASTER_TONE,
 	/* 6 bit mono + preamp */
-	[SOUND_MIXER_MIC] = 0x0e,
+	[SOUND_MIXER_MIC] = AC97_MIC_VOL,
 	/* 4 bit stereo */
-	[SOUND_MIXER_RECLEV] = 0x1c,
+	[SOUND_MIXER_RECLEV] = AC97_RECORD_GAIN,
 	/* 4 bit mono */
-	[SOUND_MIXER_IGAIN] = 0x1e
+	[SOUND_MIXER_IGAIN] = AC97_RECORD_GAIN_MIC
 };
 
 #ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
@@ -1049,8 +1192,8 @@
 
 	switch (ch) {
 	case SOUND_MIXER_MIC:
-		j = rdcodec(s, 0x0e);
-		if (j & 0x8000)
+		j = rdcodec(s, AC97_MIC_VOL);
+		if (j & AC97_MUTE)
 			return put_user(0, (int *)arg);
 #ifdef AC97_PESSIMISTIC
 		return put_user(0x4949 - 0x202 * (j & 0x1f) + ((j & 0x40) ? 0x1b1b : 0), (int *)arg);
@@ -1061,7 +1204,7 @@
 	case SOUND_MIXER_OGAIN:
 	case SOUND_MIXER_PHONEIN:
 		j = rdcodec(s, volreg[ch]);
-		if (j & 0x8000)
+		if (j & AC97_MUTE)
 			return put_user(0, (int *)arg);
 #ifdef AC97_PESSIMISTIC
 		return put_user(0x6464 - 0x303 * (j & 0x1f), (int *)arg);
@@ -1075,7 +1218,7 @@
 		/* fall through */
 	case SOUND_MIXER_VOLUME:
 		j = rdcodec(s, volreg[ch]);
-		if (j & 0x8000)
+		if (j & AC97_MUTE)
 			return put_user(0, (int *)arg);
 #ifdef AC97_PESSIMISTIC
 		return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg);
@@ -1084,8 +1227,8 @@
 #endif /* AC97_PESSIMISTIC */
 		
 	case SOUND_MIXER_SPEAKER:
-		j = rdcodec(s, 0x0a);
-		if (j & 0x8000)
+		j = rdcodec(s, AC97_PCBEEP_VOL);
+		if (j & AC97_MUTE
 			return put_user(0, (int *)arg);
 		return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg);
 		
@@ -1095,7 +1238,7 @@
 	case SOUND_MIXER_LINE1:
 	case SOUND_MIXER_PCM:
 		j = rdcodec(s, volreg[ch]);
-		if (j & 0x8000)
+		if (j & AC97_MUTE)
 			return put_user(0, (int *)arg);
 		return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg);
 
@@ -1103,23 +1246,23 @@
 	case SOUND_MIXER_TREBLE:
 		if (!(s->mix.codec_id & CODEC_ID_BASSTREBLE))
 			return -EINVAL;
-		j = rdcodec(s, 0x08);
+		j = rdcodec(s, AC97_MASTER_TONE);
 		if (ch == SOUND_MIXER_BASS)
 			j >>= 8;
 		return put_user((((j & 15) * 100) / 15) * 0x101, (int *)arg);
 	
 		/* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */
 	case SOUND_MIXER_RECLEV:
-		j = rdcodec(s, 0x1c);
-		if (j & 0x8000)
+		j = rdcodec(s, AC97_RECORD_GAIN);
+		if (j & AC97_MUTE)
 			return put_user(0, (int *)arg);
 		return put_user((swab(j)  & 0xf0f) * 6 + 0xa0a, (int *)arg);
 		
 	case SOUND_MIXER_IGAIN:
 		if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC))
 			return -EINVAL;
-		j = rdcodec(s, 0x1e);
-		if (j & 0x8000)
+		j = rdcodec(s, AC97_RECORD_GAIN_MIC);
+		if (j & AC97_MUTE)
 			return put_user(0, (int *)arg);
 		return put_user((j & 0xf) * 0x606 + 0xa0a, (int *)arg);
 	
@@ -1174,7 +1317,7 @@
 	case SOUND_MIXER_LINE1:
 	case SOUND_MIXER_PCM:
 		if (l1 < 7 && r1 < 7) {
-			wrcodec(s, volreg[ch], 0x8000);
+			wrcodec(s, volreg[ch], AC97_MUTE);
 			return 0;
 		}
 		if (l1 < 7)
@@ -1191,7 +1334,7 @@
 	case SOUND_MIXER_VOLUME:
 #ifdef AC97_PESSIMISTIC
 		if (l1 < 7 && r1 < 7) {
-			wrcodec(s, volreg[ch], 0x8000);
+			wrcodec(s, volreg[ch], AC97_MUTE);
 			return 0;
 		}
 		if (l1 < 7)
@@ -1202,7 +1345,7 @@
 		return 0;
 #else /* AC97_PESSIMISTIC */
 		if (l1 < 4 && r1 < 4) {
-			wrcodec(s, volreg[ch], 0x8000);
+			wrcodec(s, volreg[ch], AC97_MUTE);
 			return 0;
 		}
 		if (l1 < 4)
@@ -1216,21 +1359,21 @@
 	case SOUND_MIXER_OGAIN:
 	case SOUND_MIXER_PHONEIN:
 #ifdef AC97_PESSIMISTIC
-		wrcodec(s, volreg[ch], (l1 < 7) ? 0x8000 : (100 - l1) / 3);
+		wrcodec(s, volreg[ch], (l1 < 7) ? AC97_MUTE : (100 - l1) / 3);
 		return 0;
 #else /* AC97_PESSIMISTIC */
-		wrcodec(s, volreg[ch], (l1 < 4) ? 0x8000 : (2 * (100 - l1) / 3));
+		wrcodec(s, volreg[ch], (l1 < 4) ? AC97_MUTE : (2 * (100 - l1) / 3));
 		return 0;
 #endif /* AC97_PESSIMISTIC */
 			
 	case SOUND_MIXER_SPEAKER:
-		wrcodec(s, 0x0a, (l1 < 10) ? 0x8000 : ((100 - l1) / 6) << 1);
+		wrcodec(s, AC97_PCBEEP_VOL, (l1 < 10) ? AC97_MUTE : ((100 - l1) / 6) << 1);
 		return 0;
 
 	case SOUND_MIXER_MIC:
 #ifdef AC97_PESSIMISTIC
 		if (l1 < 11) {
-			wrcodec(s, 0x0e, 0x8000);
+			wrcodec(s, AC97_MIC_VOL, AC97_MUTE);
 			return 0;
 		}
 		i = 0;
@@ -1240,11 +1383,11 @@
 		}
 		if (l1 < 11) 
 			l1 = 11;
-		wrcodec(s, 0x0e, ((73 - l1) / 2) | i);
+		wrcodec(s, AC97_MIC_VOL, ((73 - l1) / 2) | i);
 		return 0;
 #else /* AC97_PESSIMISTIC */
 		if (l1 < 9) {
-			wrcodec(s, 0x0e, 0x8000);
+			wrcodec(s, AC97_MIC_VOL, AC97_MUTE);
 			return 0;
 		}
 		i = 0;
@@ -1254,37 +1397,37 @@
 		}
 		if (l1 < 9) 
 			l1 = 9;
-		wrcodec(s, 0x0e, (((87 - l1) * 4) / 5) | i);
+		wrcodec(s, AC97_MIC_VOL, (((87 - l1) * 4) / 5) | i);
 		return 0;
 #endif /* AC97_PESSIMISTIC */
 		
 	case SOUND_MIXER_BASS:
 		val = ((l1 * 15) / 100) & 0xf;
-		wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0x00ff) | (val << 8));
+		wrcodec(s, AC97_MASTER_TONE, (rdcodec(s, AC97_MASTER_TONE) & 0x00ff) | (val << 8));
 		return 0;
 
 	case SOUND_MIXER_TREBLE:
 		val = ((l1 * 15) / 100) & 0xf;
-		wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0xff00) | val);
+		wrcodec(s, AC97_MASTER_TONE, (rdcodec(s, AC97_MASTER_TONE) & 0xff00) | val);
 		return 0;
 		
 		/* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */
 	case SOUND_MIXER_RECLEV:
 		if (l1 < 10 || r1 < 10) {
-			wrcodec(s, 0x1c, 0x8000);
+			wrcodec(s, AC97_RECORD_GAIN, AC97_MUTE);
 			return 0;
 		}
 		if (l1 < 10)
 			l1 = 10;
 		if (r1 < 10)
 			r1 = 10;
-		wrcodec(s, 0x1c, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6));
+		wrcodec(s, AC97_RECORD_GAIN, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6));
 		return 0;
 
 	case SOUND_MIXER_IGAIN:
 		if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC))
 			return -EINVAL;
-		wrcodec(s, 0x1e, (l1 < 10) ? 0x8000 : ((l1 - 10) / 6) & 0xf);
+		wrcodec(s, AC97_RECORD_GAIN_MIC, (l1 < 10) ? AC97_MUTE : ((l1 - 10) / 6) & 0xf);
 		return 0;
 		
 	default:
@@ -1302,8 +1445,8 @@
 			return -EINVAL;
 		get_user_ret(val, (int *)arg, -EFAULT);
 		if (val & 1)
-			wrcodec(s, 0x22, ((val << 3) & 0xf00) | ((val >> 1) & 0xf));
-		val = rdcodec(s, 0x22);
+			wrcodec(s, AC97_3D_CONTROL, ((val << 3) & 0xf00) | ((val >> 1) & 0xf));
+		val = rdcodec(s, AC97_3D_CONTROL);
 		return put_user(((val & 0xf) << 1) | ((val & 0xf00) >> 3), (int *)arg);
 	}
         if (cmd == SOUND_MIXER_INFO) {
@@ -1330,7 +1473,7 @@
         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(recsrc[rdcodec(s, 0x1a) & 7], (int *)arg);
+			return put_user(recsrc[rdcodec(s, AC97_RECORD_SELECT) & 7], (int *)arg);
 			
                 case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
 			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO |
@@ -1377,10 +1520,10 @@
                 if (i == 0)
                         return 0; /*val = mixer_recmask(s);*/
                 else if (i > 1) 
-                        val &= ~recsrc[rdcodec(s, 0x1a) & 7];
+                        val &= ~recsrc[rdcodec(s, AC97_RECORD_SELECT) & 7];
                 for (i = 0; i < 8; i++) {
 			if (val & recsrc[i]) {
-				wrcodec(s, 0x1a, 0x101 * i);
+				wrcodec(s, AC97_RECORD_SELECT, 0x101 * i);
 				return 0;
 			}
 		}
@@ -1485,10 +1628,10 @@
                         current->state = TASK_RUNNING;
                         return -EBUSY;
                 }
-		tmo = (count * HZ) / s->dac1rate;
+		tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate;
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
-		if (!schedule_timeout(tmo ? : 1) && tmo)
-			printk(KERN_DEBUG "es1371: dma timed out??\n");
+		if (!schedule_timeout(tmo + 1))
+			printk(KERN_DEBUG "es1371: dac1 dma timed out??\n");
         }
         remove_wait_queue(&s->dma_dac1.wait, &wait);
         current->state = TASK_RUNNING;
@@ -1520,10 +1663,10 @@
                         current->state = TASK_RUNNING;
                         return -EBUSY;
                 }
-		tmo = (count * HZ) / s->dac2rate;
+		tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate;
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
-		if (!schedule_timeout(tmo ? : 1) && tmo)
-			printk(KERN_DEBUG "es1371: dma timed out??\n");
+		if (!schedule_timeout(tmo + 1))
+			printk(KERN_DEBUG "es1371: dac2 dma timed out??\n");
         }
         remove_wait_queue(&s->dma_dac2.wait, &wait);
         current->state = TASK_RUNNING;
@@ -2467,6 +2610,7 @@
 static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	struct wait_queue wait = { current, NULL };
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2477,7 +2621,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+        add_wait_queue(&s->midi.iwait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.ird;
@@ -2488,15 +2635,25 @@
 		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;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIINBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.ird = ptr;
@@ -2505,13 +2662,17 @@
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
+		break;
 	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&s->midi.iwait, &wait);
 	return ret;
 }
 
 static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	struct wait_queue wait = { current, NULL };
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2522,7 +2683,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.owr;
@@ -2535,15 +2699,25 @@
 		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;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIOUTBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.owr = ptr;
@@ -2556,6 +2730,8 @@
 		es1371_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&s->midi.owait, &wait);
 	return ret;
 }
 
@@ -2697,6 +2873,44 @@
 
 /* --------------------------------------------------------------------- */
 
+/*
+ * for debugging purposes, we'll create a proc device that dumps the
+ *  CODEC chipstate
+ */
+
+#ifdef ES1371_DEBUG
+static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data)
+{
+        int len = 0;
+
+        struct es1371_state *s = devs;
+        int cnt;
+
+        /* print out header */
+        len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n");
+
+        /* print out CODEC state */
+        len += sprintf (buf + len, "AC97 CODEC state\n");
+        
+        for (cnt=0; cnt <= 0x7e; cnt = cnt +2)
+                len+= sprintf (buf + len, "reg:0x%02x  val:0x%04x\n", cnt, rdcodec(s , cnt));
+                
+        if (fpos >=len){
+                *start = buf;
+                *eof =1;
+                return 0;
+        }
+        *start = buf + fpos;
+        if ((len -= fpos) > length)
+                return length;
+        *eof =1;
+        return len;
+
+}
+#endif /* ES1371_DEBUG */
+
+/* --------------------------------------------------------------------- */
+
 /* maximum number of devices */
 #define NR_DEVICE 5
 
@@ -2740,12 +2954,11 @@
 	struct pci_dev *pcidev = NULL;
 	mm_segment_t fs;
 	int i, val, val2, index = 0;
-	u8 revision;
 	unsigned cssr;
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1371: version v0.13 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1371: version v0.19 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
@@ -2765,9 +2978,11 @@
 		init_waitqueue(&s->midi.iwait);
 		init_waitqueue(&s->midi.owait);
 		s->open_sem = MUTEX;
+		spin_lock_init(&s->lock);
 		s->magic = ES1371_MAGIC;
 		s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
 		s->irq = pcidev->irq;
+		pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev);
 		if (check_region(s->io, ES1371_EXTENT)) {
 			printk(KERN_ERR "es1371: io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1);
 			goto err_region;
@@ -2788,6 +3003,13 @@
 			goto err_dev3;
 		if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0)
 			goto err_dev4;
+#ifdef ES1371_DEBUG
+                /* intialize the debug proc device */
+                s->ps = create_proc_entry("es1371", S_IFREG | S_IRUGO, NULL);
+                if (s->ps)
+                        s->ps->read_proc = proc_es1371_dump;
+#endif /* ES1371_DEBUG */
+
 		/* initialize codec registers */
 		s->ctrl = 0;
 		if ((joystick[index] & ~0x18) == 0x200) {
@@ -2800,14 +3022,13 @@
 		s->sctrl = 0;
 		cssr = 0;
 		/* check to see if s/pdif mode is being requested */
-		pci_read_config_byte(pcidev, PCI_REVISION_ID, &revision);
 		if (spdif[index]) {
-			if (revision >= 4) {
+			if (s->rev >= 4) {
 				printk(KERN_INFO "es1371: enabling S/PDIF output\n");
 				cssr |= STAT_EN_SPDIF;
 				s->ctrl |= CTRL_SPDIFEN_B;
 			} else {
-				printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", revision);
+				printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", s->rev);
 			}
 		}
 		/* initialize the chips */
@@ -2820,35 +3041,12 @@
 		udelay(2);
 		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
 		/* init the sample rate converter */
-		outl(SRC_DIS, s->io + ES1371_REG_SRCONV);
-		for (val = 0; val < 0x80; val++)
-			src_write(s, val, 0);
-		src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4);
-		src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10);
-		src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4);
-		src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10);
-		src_write(s, SRCREG_VOL_ADC, 1 << 12);
-		src_write(s, SRCREG_VOL_ADC+1, 1 << 12);
-		src_write(s, SRCREG_VOL_DAC1, 1 << 12);
-		src_write(s, SRCREG_VOL_DAC1+1, 1 << 12);
-		src_write(s, SRCREG_VOL_DAC2, 1 << 12);
-		src_write(s, SRCREG_VOL_DAC2+1, 1 << 12);
-		set_adc_rate(s, 22050);
-		set_dac1_rate(s, 22050);
-		set_dac2_rate(s, 22050);
-		/* WARNING:
-		 * enabling the sample rate converter without properly programming
-		 * its parameters causes the chip to lock up (the SRC busy bit will
-		 * be stuck high, and I've found no way to rectify this other than
-		 * power cycle)
-		 */
-		wait_src_ready(s);
-		outl(0, s->io+ES1371_REG_SRCONV);
+                src_init(s);
 		/* codec init */
-		wrcodec(s, 0x00, 0); /* reset codec */
-		s->mix.codec_id = rdcodec(s, 0x00);  /* get codec ID */
-		val = rdcodec(s, 0x7c);
-		val2 = rdcodec(s, 0x7e);
+		wrcodec(s, AC97_RESET, 0); /* reset codec */
+		s->mix.codec_id = rdcodec(s, AC97_RESET);  /* get codec ID */
+		val = rdcodec(s, AC97_VENDOR_ID1);
+		val2 = rdcodec(s, AC97_VENDOR_ID2);
 		printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", 
 		       (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff);
 		printk(KERN_INFO "es1371: codec features");
@@ -2875,6 +3073,7 @@
 		printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none");
 		val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK;
 		printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown");
+
 		fs = get_fs();
 		set_fs(KERNEL_DS);
 		val = SOUND_MASK_LINE;
@@ -2929,6 +3128,10 @@
 
 	while ((s = devs)) {
 		devs = devs->next;
+#ifdef ES1371_DEBUG
+		if (s->ps)
+			remove_proc_entry("es1371", NULL);
+#endif /* ES1371_DEBUG */
 		outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */
 		outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */
 		synchronize_irq();
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/esssolo1.c linux.14p6/drivers/sound/esssolo1.c
--- linux.vanilla/drivers/sound/esssolo1.c	Wed Oct 20 01:12:48 1999
+++ linux.14p6/drivers/sound/esssolo1.c	Fri Oct 22 22:34:17 1999
@@ -54,12 +54,17 @@
  *                     The fun part is that the Windows Solo1 driver doesn't
  *                     seem to do these tricks.
  *                     Bugs remaining: plops and clicks when starting/stopping playback
+ *    31.08.99   0.7   add spin_lock_init
+ *                     replaced current->state = x with set_current_state(x)
+ *    03.09.99   0.8   change read semantics for MIDI to match
+ *                     OSS more closely; remove possible wakeup race
+ *    07.10.99   0.9   Fix initialization; complain if sequencer writes time out
+ *                     Revised resource grabbing for the FM synthesizer
  *
  */
 
 /*****************************************************************************/
       
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/string.h>
@@ -96,7 +101,7 @@
 
 #define SOLO1_MAGIC  ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1)
 
-#define DDMABASE_OFFSET           0x10    /* chip bug workaround kludge */
+#define DDMABASE_OFFSET           0    /* chip bug workaround kludge */
 #define DDMABASE_EXTENT           16
 
 #define IOBASE_EXTENT             16
@@ -105,6 +110,7 @@
 #define MPUBASE_EXTENT            4
 #define GPBASE_EXTENT             4
 
+#define FMSYNTH_EXTENT            4
 
 /* MIDI buffer sizes */
 
@@ -137,6 +143,8 @@
 #define wait_queue_head_t struct wait_queue *
 #define init_waitqueue_head(w) *(w) = 0
 #define init_MUTEX(m) *(m) = MUTEX
+#define __set_current_state(x) do { current->state = (x); } while (0)
+#define set_current_state(x) __set_current_state(x)
 #endif
 
 /* --------------------------------------------------------------------- */
@@ -226,7 +234,7 @@
 {
         int i;
 	unsigned long flags;
-        
+
 	/* the __cli stunt is to send the data within the command window */
         for (i = 0; i < 0xffff; i++) {
 		__save_flags(flags);
@@ -238,6 +246,8 @@
                 }
 		__restore_flags(flags);
 	}
+	printk(KERN_ERR "esssolo1: write_seq timeout\n");
+	outb(data, s->sbbase+0xc);
 }
 
 extern inline int read_seq(struct solo1_state *s, unsigned char *data)
@@ -251,6 +261,7 @@
                         *data = inb(s->sbbase+0xa);
                         return 1;
                 }
+	printk(KERN_ERR "esssolo1: read_seq timeout\n");
         return 0;
 }
 
@@ -952,7 +963,7 @@
 	
 	if (s->dma_dac.mapped)
 		return 0;
-        current->state = TASK_INTERRUPTIBLE;
+        __set_current_state(TASK_INTERRUPTIBLE);
         add_wait_queue(&s->dma_dac.wait, &wait);
         for (;;) {
                 spin_lock_irqsave(&s->lock, flags);
@@ -964,7 +975,7 @@
                         break;
                 if (nonblock) {
                         remove_wait_queue(&s->dma_dac.wait, &wait);
-                        current->state = TASK_RUNNING;
+                        set_current_state(TASK_RUNNING);
                         return -EBUSY;
                 }
 		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate;
@@ -976,7 +987,7 @@
                         printk(KERN_DEBUG "solo1: dma timed out??\n");
         }
         remove_wait_queue(&s->dma_dac.wait, &wait);
-        current->state = TASK_RUNNING;
+        set_current_state(TASK_RUNNING);
         if (signal_pending(current))
                 return -ERESTARTSYS;
         return 0;
@@ -1622,6 +1633,7 @@
 static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -1632,7 +1644,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+	add_wait_queue(&s->midi.iwait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.ird;
@@ -1643,15 +1658,25 @@
 		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;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			__set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIINBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.ird = ptr;
@@ -1660,13 +1685,17 @@
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
+		break;
 	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.iwait, &wait);
 	return ret;
 }
 
 static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -1677,7 +1706,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.owr;
@@ -1690,15 +1722,25 @@
 		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;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			__set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIOUTBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.owr = ptr;
@@ -1711,6 +1753,8 @@
 		solo1_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.owait, &wait);
 	return ret;
 }
 
@@ -1979,6 +2023,12 @@
 			return -ERESTARTSYS;
 		down(&s->open_sem);
 	}
+	if (check_region(s->sbbase, FMSYNTH_EXTENT)) {
+		up(&s->open_sem);
+		printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n");
+		return -EBUSY;
+	}
+	request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1");
 	/* init the stuff */
 	outb(1, s->sbbase);
 	outb(0x20, s->sbbase+1); /* enable waveforms */
@@ -2006,6 +2056,7 @@
 		outb(regb, s->sbbase+2);
 		outb(0, s->sbbase+3);
 	}
+	release_region(s->sbbase, FMSYNTH_EXTENT);
 	up(&s->open_sem);
 	wake_up(&s->open_wait);
 	MOD_DEC_USE_COUNT;
@@ -2062,7 +2113,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "solo1: version v0.6 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "solo1: version v0.9 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) {
 		if (pcidev->base_address[0] == 0 ||
@@ -2087,6 +2138,7 @@
 		init_waitqueue_head(&s->midi.iwait);
 		init_waitqueue_head(&s->midi.owait);
 		init_MUTEX(&s->open_sem);
+		spin_lock_init(&s->lock);
 		s->magic = SOLO1_MAGIC;
 		s->pcidev = pcidev;
 		s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
@@ -2097,14 +2149,14 @@
 		s->gpbase = pcidev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
 		s->irq = pcidev->irq;
 		if (check_region(s->iobase, IOBASE_EXTENT) ||
-		    check_region(s->sbbase+4, SBBASE_EXTENT-4) ||
+		    check_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT) ||
 		    check_region(s->ddmabase, DDMABASE_EXTENT) ||
 		    check_region(s->mpubase, MPUBASE_EXTENT)) {
 			printk(KERN_ERR "solo1: io ports in use\n");
 			goto err_region;
 		}
 		request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1");
-		request_region(s->sbbase+4, SBBASE_EXTENT-4, "ESS Solo1");  /* allow OPL3 synth module */
+		request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1");  /* allow OPL3 synth module */
 		request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1");
 		request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1");
 		if (request_irq(s->irq, solo1_interrupt, SA_SHIRQ, "ESS Solo1", s)) {
@@ -2131,6 +2183,10 @@
 		if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0)
 			goto err_dev4;
 		/* initialize the chips */
+		if (!reset_ctrl(s)) {
+			printk(KERN_ERR "esssolo1: cannot reset controller\n");
+			goto err;
+		}
 		outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */
 
 		/* initialize mixer regs */
@@ -2165,6 +2221,8 @@
 		index++;
 		continue;
 
+	err:
+		unregister_sound_dsp(s->dev_dmfm);
 	err_dev4:
 		unregister_sound_dsp(s->dev_midi);
 	err_dev3:
@@ -2176,7 +2234,7 @@
 		free_irq(s->irq, s);
 	err_irq:
 		release_region(s->iobase, IOBASE_EXTENT);
-		release_region(s->sbbase+4, SBBASE_EXTENT-4);
+		release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT);
 		release_region(s->ddmabase, DDMABASE_EXTENT);
 		release_region(s->mpubase, MPUBASE_EXTENT);
 	err_region:
@@ -2206,7 +2264,7 @@
 		pci_write_config_word(s->pcidev, 0x60, 0); /* turn off DDMA controller address space */
 		free_irq(s->irq, s);
 		release_region(s->iobase, IOBASE_EXTENT);
-		release_region(s->sbbase+4, SBBASE_EXTENT-4);
+		release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT);
 		release_region(s->ddmabase, DDMABASE_EXTENT);
 		release_region(s->mpubase, MPUBASE_EXTENT);
 		unregister_sound_dsp(s->dev_audio);
@@ -2222,5 +2280,4 @@
 
 module_init(init_solo1);
 module_exit(cleanup_solo1);
-
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/lowlevel/miroaci.h linux.14p6/drivers/sound/lowlevel/miroaci.h
--- linux.vanilla/drivers/sound/lowlevel/miroaci.h	Sun Aug 23 21:32:25 1998
+++ linux.14p6/drivers/sound/lowlevel/miroaci.h	Fri Oct 29 23:43:20 1999
@@ -1,6 +1,11 @@
+#if defined(CONFIG_ACI_MIXER) || defined(CONFIG_ACI_MIXER_MODULE)
 extern int aci_implied_cmd(unsigned char opcode);
 extern int aci_write_cmd(unsigned char opcode, unsigned char parameter);
 extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2);
 extern int aci_read_cmd(unsigned char opcode, int length, unsigned char *parameter);
 extern int aci_indexed_cmd(unsigned char opcode, unsigned char index, unsigned char *parameter);
+#else
 
+#error Compiling a driver that needs the ACI-mixer but without ACI-mixer support
+
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/maestro.c linux.14p6/drivers/sound/maestro.c
--- linux.vanilla/drivers/sound/maestro.c	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/sound/maestro.c	Fri Nov 12 16:40:50 1999
@@ -0,0 +1,3542 @@
+/*****************************************************************************
+ *
+ *      ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x
+ *
+ *      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.
+ *
+ *	(c) Copyright 1999	 Alan Cox <alan.cox@linux.org>
+ *
+ *	Based heavily on SonicVibes.c:
+ *      Copyright (C) 1998-1999  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	Heavily modified by Zach Brown <zab@redhat.com> based on lunch
+ *	with ESS engineers.  Many thanks to Howard Kim for providing 
+ *	contacts and hardware.  Honorable mention goes to Eric 
+ *	Brombaugh for all sorts of things.  Best regards to the 
+ *	proprietors of Hack Central for fine lodging.
+ *
+ *  Supported devices:
+ *  /dev/dsp0-3    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *
+ *  Hardware Description
+ *
+ *	A working Maestro setup contains the Maestro chip wired to a 
+ *	codec or 2.  In the Maestro we have the APUs, the ASSP, and the
+ *	Wavecache.  The APUs can be though of as virtual audio routing
+ *	channels.  They can take data from a number of sources and perform
+ *	basic encodings of the data.  The wavecache is a storehouse for
+ *	PCM data.  Typically it deals with PCI and interracts with the
+ *	APUs.  The ASSP is a wacky DSP like device that ESS is loth
+ *	to release docs on.  Thankfully it isn't required on the Maestro
+ *	until you start doing insane things like FM emulation and surround
+ *	encoding.  The codecs are almost always AC-97 compliant codecs, 
+ *	but it appears that early Maestros may have had PT101 (an ESS
+ *	part?) wired to them.  The only real difference in the Maestro
+ *	families is external goop like docking capability, memory for
+ *	the ASSP, and initialization differences.
+ *
+ *  Driver Operation
+ *
+ *	We only drive the APU/Wavecache as typical DACs and drive the
+ *	mixers in the codecs.  There are 64 APUs.  We assign 6 to each
+ *	/dev/dsp? device.  2 channels for output, and 4 channels for
+ *	input.
+ *
+ *	Each APU can do a number of things, but we only really use
+ *	3 basic functions.  For playback we use them to convert PCM
+ *	data fetched over PCI by the wavecahche into analog data that
+ *	is handed to the codec.  One APU for mono, and a pair for stereo.
+ *	When in stereo, the combination of smarts in the APU and Wavecache
+ *	decide which wavecache gets the left or right channel.
+ *
+ *	For record we still use the old overly mono system.  For each in
+ *	coming channel the data comes in from the codec, through a 'input'
+ *	APU, through another rate converter APU, and then into memory via
+ *	the wavecache and PCI.  If its stereo, we mash it back into LRLR in
+ *	software.  The pass between the 2 APUs is supposedly what requires us
+ *	to have a 512 byte buffer sitting around in wavecache/memory.
+ *
+ *	The wavecache makes our life even more fun.  First off, it can
+ *	only address the first 28 bits of PCI address space, making it
+ *	useless on quite a few architectures.  Secondly, its insane.
+ *	It claims to fetch from 4 regions of PCI space, each 4 meg in length.
+ *	But that doesn't really work.  You can only use 1 region.  So all our
+ *	allocations have to be in 4meg of each other.  Booo.  Hiss.
+ *	So we have a module parameter, dsps_order, that is the order of
+ *	the number of dsps to provide.  All their buffer space is allocated
+ *	on open time.  The sonicvibes OSS routines we inherited really want
+ *	power of 2 buffers, so we have all those next to each other, then
+ *	512 byte regions for the recording wavecaches.  This ends up
+ *	wasting quite a bit of memory.  The only fixes I can see would be 
+ *	getting a kernel allocator that could work in zones, or figuring out
+ *	just how to coerce the WP into doing what we want.
+ *
+ *	The indirection of the various registers means we have to spinlock
+ *	nearly all register accesses.  We have the main register indirection
+ *	like the wave cache, maestro registers, etc.  Then we have beasts
+ *	like the APU interface that is indirect registers gotten at through
+ *	the main maestro indirection.  Ouch.  We spinlock around the actual
+ *	ports on a per card basis.  This means spinlock activity at each IO
+ *	operation, but the only IO operation clusters are in non critical 
+ *	paths and it makes the code far easier to follow.  Interrupts are
+ *	blocked while holding the locks because the int handler has to
+ *	get at some of them :(.  The mixer interface doesn't, however.
+ *	We also have an OSS state lock that is thrown around in a few
+ *	places.
+ *
+ *	This driver has brute force APM suspend support.  We catch suspend
+ *	notifications and stop all work being done on the chip.  Any people
+ *	that try between this shutdown and the real suspend operation will
+ *	be put to sleep.  When we resume we restore our software state on
+ *	the chip and wake up the people that were using it.  The code thats
+ *	being used now is quite dirty and assumes we're on a uni-processor
+ *	machine.  Much of it will need to be cleaned up for SMP ACPI or 
+ *	similar.
+ *	
+ * History
+ *  v0.12 - Nov 12 1999 - Zach Brown <zab@redhat.com>
+ *	brown bag volume max fix..
+ *  v0.11 - Nov 11 1999 - Zach Brown <zab@redhat.com>
+ *	use proper stereo apu decoding, mmap/write should work.
+ *	make volume sliders more useful, tweak rate calculation.
+ *	fix lame 8bit format reporting bug.  duh. apm apu saving buglet also
+ *	fix maestro 1 clock freq "bug", remove pt101 support
+ *  v0.10 - Oct 28 1999 - Zach Brown <zab@redhat.com>
+ *	aha, so, sometimes the WP writes a status word to offset 0
+ *	  from one of the PCMBARs.  rearrange allocation accordingly..
+ *	  cheers again to Eric for being a good hacker in investigating this.
+ *	Jeroen Hoogervorst submits 7500 fix out of nowhere.  yay.  :)
+ *  v0.09 - Oct 23 1999 - Zach Brown <zab@redhat.com>
+ *	added APM support.
+ *	re-order something such that some 2Es now work.  Magic!
+ *	new codec reset routine.  made some codecs come to life.
+ *	fix clear_advance, sync some control with ESS.
+ *	now write to all base regs to be paranoid.
+ *  v0.08 - Oct 20 1999 - Zach Brown <zab@redhat.com>
+ *	Fix initial buflen bug.  I am so smart.  also smp compiling..
+ *	I owe Eric yet another beer: fixed recmask, igain, 
+ *	  muting, and adc sync consistency.  Go Team.
+ *  v0.07 - Oct 4 1999 - Zach Brown <zab@redhat.com>
+ *	tweak adc/dac, formating, and stuff to allow full duplex
+ *	allocate dsps memory at open() so we can fit in the wavecache window
+ *	fix wavecache braindamage.  again.  no more scribbling?
+ *	fix ess 1921 codec bug on some laptops.
+ *	fix dumb pci scanning bug
+ *	started 2.3 cleanup, redid spinlocks, little cleanups
+ *  v0.06 - Sep 20 1999 - Zach Brown <zab@redhat.com>
+ *	fix wavecache thinkos.  limit to 1 /dev/dsp.
+ *	eric is wearing his thinking toque this week.
+ *		spotted apu mode bugs and gain ramping problem
+ *	don't touch weird mixer regs, make recmask optional
+ *	fixed igain inversion, defaults for mixers, clean up rec_start
+ *	make mono recording work.
+ *	report subsystem stuff, please send reports.
+ *	littles: parallel out, amp now
+ *  v0.05 - Sep 17 1999 - Zach Brown <zab@redhat.com>
+ *	merged and fixed up Eric's initial recording code
+ *	munged format handling to catch misuse, needs rewrite.
+ *	revert ring bus init, fixup shared int, add pci busmaster setting
+ *	fix mixer oss interface, fix mic mute and recmask
+ *	mask off unsupported mixers, reset with all 1s, modularize defaults
+ *	make sure bob is running while we need it
+ *	got rid of device limit, initial minimal apm hooks
+ *	pull out dead code/includes, only allow multimedia/audio maestros
+ *  v0.04 - Sep 01 1999 - Zach Brown <zab@redhat.com>
+ *	copied memory leak fix from sonicvibes driver
+ *	different ac97 reset, play with 2.0 ac97, simplify ring bus setup
+ *	bob freq code, region sanity, jitter sync fix; all from Eric 
+ *
+ * TODO
+ *	some people get indir reg timeouts?
+ *	fix bob frequency
+ *	endianness
+ *	do smart things with ac97 2.0 bits.
+ *	docking and dual codecs and 978?
+ *	leave 54->61 open
+ *
+ *	it also would be fun to have a mode that would not use pci dma at all
+ *	but would copy into the wavecache on board memory and use that 
+ *	on architectures that don't like the maestro's pci dma ickiness.
+ */
+
+/*****************************************************************************/
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+
+ #ifdef MODULE
+  #include <linux/module.h>
+  #ifdef MODVERSIONS
+   #include <linux/modversions.h>
+  #endif
+ #endif
+ #define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL}
+ #define wait_queue_head_t struct wait_queue *
+ #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK)
+ #define SILLY_INIT_SEM(SEM) SEM=MUTEX;
+ #define init_waitqueue_head init_waitqueue
+ #define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC)
+
+#else
+
+ #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start)
+ #define SILLY_INIT_SEM(SEM) init_MUTEX(&SEM)
+ #define SILLY_MAKE_INIT(FUNC) __init FUNC
+
+#endif
+
+#include <linux/string.h>
+#include <linux/ctype.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/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/spinlock.h>
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int maestro_apm_callback(apm_event_t ae);
+static int in_suspend=0;
+wait_queue_head_t suspend_queue;
+static void check_suspend(void);
+#define CHECK_SUSPEND check_suspend();
+#else
+#define CHECK_SUSPEND
+#endif
+
+#include "maestro.h"
+
+/* --------------------------------------------------------------------- */
+
+#define M_DEBUG 1
+
+#ifdef M_DEBUG
+static int debug=0;
+static int dsps_order=0;
+#define M_printk(args...) {if (debug) printk(args);}
+#else
+#define M_printk(x)
+#endif
+
+/* --------------------------------------------------------------------- */
+#define DRIVER_VERSION "0.12"
+
+#ifndef PCI_VENDOR_ESS
+#define PCI_VENDOR_ESS			0x125D
+#define PCI_DEVICE_ID_ESS_ESS1968	0x1968		/* Maestro 2	*/
+#define PCI_DEVICE_ID_ESS_ESS1978      	0x1978		/* Maestro 2E	*/
+
+#define PCI_VENDOR_ESS_OLD		0x1285		/* Platform Tech, 
+						the people the maestro 
+						was bought from */
+#define PCI_DEVICE_ID_ESS_ESS0100	0x0100		/* maestro 1 */
+#endif /* PCI_VENDOR_ESS */
+
+#define ESS_CHAN_HARD		0x100
+
+
+/* changed so that I could actually find all the
+	references and fix them up.  its a little more readable now. */
+#define ESS_FMT_STEREO	0x01
+#define ESS_FMT_16BIT	0x02
+#define ESS_FMT_MASK	0x03
+#define ESS_DAC_SHIFT	0   
+#define ESS_ADC_SHIFT	4
+
+#define ESS_STATE_MAGIC		0x125D1968
+#define ESS_CARD_MAGIC		0x19283746
+
+#define DAC_RUNNING		1
+#define ADC_RUNNING		2
+
+#define MAX_DSP_ORDER	2
+#define MAX_DSPS	(1<<MAX_DSP_ORDER)
+#define NR_DSPS		(1<<dsps_order)
+#define NR_IDRS		32
+
+#define SND_DEV_DSP16   5 
+
+#define NR_APUS		64
+#define NR_APU_REGS	16
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+enum card_types_t {
+	TYPE_MAESTRO,
+	TYPE_MAESTRO2,
+	TYPE_MAESTRO2E
+};
+
+static const char *card_names[]={
+	[TYPE_MAESTRO] = "ESS Maestro",
+	[TYPE_MAESTRO2] = "ESS Maestro 2",
+	[TYPE_MAESTRO2E] = "ESS Maestro 2E"
+};
+
+static int clock_freq[]={
+	[TYPE_MAESTRO] = (49152000L / 1024L),
+	[TYPE_MAESTRO2] = (50000000L / 1024L),
+	[TYPE_MAESTRO2E] = (50000000L / 1024L)
+};
+
+/* --------------------------------------------------------------------- */
+
+struct ess_state {
+	unsigned int magic;
+	/* FIXME: we probably want submixers in here, but only one record pair */
+	u8 apu[6];		/* l/r output, l/r intput converters, l/r input apus */
+	u8 apu_mode[6];		/* Running mode for this APU */
+	u8 apu_pan[6];		/* Panning setup for this APU */
+	u32 apu_base[6];	/* base address for this apu */
+	struct ess_card *card;	/* Card info */
+	/* wave stuff */
+	unsigned int rateadc, ratedac;
+	unsigned char fmt, enable;
+
+	int index;
+
+	/* this locks around the oss state in the driver */
+	spinlock_t lock;
+	/* only let 1 be opening at a time */
+	struct semaphore open_sem;
+	wait_queue_head_t open_wait;
+	mode_t open_mode;
+
+	/* soundcore stuff */
+	int dev_audio;
+
+	struct dmabuf {
+		void *rawbuf;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		/* XXX zab - swptr only in here so that it can be referenced by
+			clear_advance, as far as I can tell :( */
+		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;	/* our oss buffers are ready to go */
+		unsigned endcleared:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+		u16 base;		/* Offset for ptr */
+	} dma_dac, dma_adc;
+
+	/* pointer to each dsp?s piece of the apu->src buffer page */
+	void *mixbuf;
+};
+	
+struct ess_card {
+	unsigned int magic;
+
+	/* We keep maestro cards in a linked list */
+	struct ess_card *next;
+
+	int dev_mixer;
+
+	int card_type;
+
+	/* as most of this is static,
+		perhaps it should be a pointer to a global struct */
+	struct mixer_goo {
+		int modcnt;
+		int supported_mixers;
+		int stereo_mixers;
+		int record_sources;
+		/* the caller must guarantee arg sanity before calling these */
+/*		int (*read_mixer)(struct ess_card *card, int index);*/
+		void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right);
+		int (*recmask_io)(struct ess_card *card,int rw,int mask);
+		unsigned int mixer_state[SOUND_MIXER_NRDEVICES];
+	} mix;
+	
+	struct ess_state channels[MAX_DSPS];
+	u16 maestro_map[NR_IDRS];	/* Register map */
+#ifdef CONFIG_APM
+	/* we have to store this junk so that we can come back from a
+		suspend */
+	u16 apu_map[NR_APUS][NR_APU_REGS];	/* contents of apu regs */
+#endif
+
+	/* this locks around the physical registers on the card */
+	spinlock_t lock;
+
+	/* memory for this card.. wavecache limited :(*/
+	void *dmapages;
+	int dmaorder;
+
+	/* hardware resources */
+	struct pci_dev pcidev; /* uck.. */
+	u32 iobase;
+	u32 irq;
+
+	int bob_freq;
+	char dsps_open;
+};
+
+static 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;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static struct ess_card *devs = NULL;
+
+/* --------------------------------------------------------------------- */
+
+
+/*
+ *	ESS Maestro AC97 codec programming interface.
+ */
+	 
+static void maestro_ac97_set(int io, u8 cmd, u16 val)
+{
+	int i;
+	/*
+	 *	Wait for the codec bus to be free 
+	 */
+
+	CHECK_SUSPEND;
+	 
+	for(i=0;i<10000;i++)
+	{
+		if(!(inb(io+ESS_AC97_INDEX)&1)) 
+			break;
+	}
+	/*
+	 *	Write the bus
+	 */ 
+	outw(val, io+ESS_AC97_DATA);
+	mdelay(1);
+	outb(cmd, io+ESS_AC97_INDEX);
+	mdelay(1);
+}
+
+static u16 maestro_ac97_get(int io, u8 cmd)
+{
+	int sanity=10000;
+	u16 data;
+	int i;
+	
+	CHECK_SUSPEND;
+	/*
+	 *	Wait for the codec bus to be free 
+	 */
+	 
+	for(i=0;i<10000;i++)
+	{
+		if(!(inb(io+ESS_AC97_INDEX)&1))
+			break;
+	}
+
+	outb(cmd|0x80, io+ESS_AC97_INDEX);
+	mdelay(1);
+	
+	while(inb(io+ESS_AC97_INDEX)&1)
+	{
+		sanity--;
+		if(!sanity)
+		{
+			printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd);
+			return 0;
+		}
+	}
+	data=inw(io+ESS_AC97_DATA);
+	mdelay(1);
+	return data;
+}
+
+/* OSS interface to the ac97s.. */
+
+#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\
+	SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\
+	SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN)
+
+#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \
+	SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\
+	SOUND_MASK_SPEAKER)
+
+#define AC97_RECORD_MASK (SOUND_MASK_MIC|\
+	SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\
+	SOUND_MASK_PHONEIN)
+
+#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<<FOO) )
+
+/* this table has default mixer values for all OSS mixers.
+	be sure to fill it in if you add oss mixers
+	to anyone's supported mixer defines */
+
+ unsigned int mixer_defaults[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_VOLUME] =          0x3232,
+	[SOUND_MIXER_BASS] =            0x3232,
+	[SOUND_MIXER_TREBLE] =          0x3232,
+	[SOUND_MIXER_SPEAKER] =         0x3232,
+	[SOUND_MIXER_MIC] =     0x8000, /* annoying */
+	[SOUND_MIXER_LINE] =    0x3232,
+	[SOUND_MIXER_CD] =      0x3232,
+	[SOUND_MIXER_VIDEO] =   0x3232,
+	[SOUND_MIXER_LINE1] =   0x3232,
+	[SOUND_MIXER_PCM] =             0x3232,
+	[SOUND_MIXER_IGAIN] =           0x3232
+};
+	
+static struct ac97_mixer_hw {
+	unsigned char offset;
+	int scale;
+} ac97_hw[SOUND_MIXER_NRDEVICES]= {
+	[SOUND_MIXER_VOLUME]	=	{0x02,63},
+	[SOUND_MIXER_BASS]	=	{0x08,15},
+	[SOUND_MIXER_TREBLE]	=	{0x08,15},
+	[SOUND_MIXER_SPEAKER]	=	{0x0a,15},
+	[SOUND_MIXER_MIC]	=	{0x0e,31},
+	[SOUND_MIXER_LINE]	=	{0x10,31},
+	[SOUND_MIXER_CD]	=	{0x12,31},
+	[SOUND_MIXER_VIDEO]	=	{0x14,31},
+	[SOUND_MIXER_LINE1]	=	{0x16,31},
+	[SOUND_MIXER_PCM]	=	{0x18,31},
+	[SOUND_MIXER_IGAIN]	=	{0x1c,15}
+};
+
+#if 0 /* *shrug* removed simply because we never used it.
+		feel free to implement again if needed */
+
+/* reads the given OSS mixer from the ac97
+	the caller must have insured that the ac97 knows
+	about that given mixer, and should be holding a
+	spinlock for the card */
+static int ac97_read_mixer(struct ess_card *card, int mixer) 
+{
+	u16 val;
+	int ret=0;
+	struct ac97_mixer_hw *mh = &ac97_hw[mixer];
+
+	val = maestro_ac97_get(card->iobase , mh->offset);
+
+	if(AC97_STEREO_MASK & (1<<mixer)) {
+		/* nice stereo mixers .. */
+		int left,right;
+
+		left = (val >> 8)  & 0x7f;
+		right = val  & 0x7f;
+
+		if (mixer == SOUND_MIXER_IGAIN) {
+			right = (right * 100) / mh->scale;
+			left = (left * 100) / mh->scale;
+		else {
+			right = 100 - ((right * 100) / mh->scale);
+			left = 100 - ((left * 100) / mh->scale);
+		}
+
+		ret = left | (right << 8);
+	} else if (mixer == SOUND_MIXER_SPEAKER) {
+		ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
+	} else if (mixer == SOUND_MIXER_MIC) {
+		ret = 100 - (((val & 0x1f) * 100) / mh->scale);
+	/*  the low bit is optional in the tone sliders and masking
+		it lets is avoid the 0xf 'bypass'.. */
+	} else if (mixer == SOUND_MIXER_BASS) {
+		ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
+	} else if (mixer == SOUND_MIXER_TREBLE) {
+		ret = 100 - (((val & 0xe) * 100) / mh->scale);
+	}
+
+	M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret);
+
+	return ret;
+}
+#endif
+
+/* write the OSS encoded volume to the given OSS encoded mixer,
+	again caller's job to make sure all is well in arg land,
+	call with spinlock held */
+	
+/* linear scale -> log */
+unsigned char lin2log[101] = 
+{
+0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 ,
+50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 ,
+63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 ,
+72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 ,
+78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 ,
+83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 ,
+87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 ,
+90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 ,
+93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 ,
+95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 ,
+97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 
+};
+
+static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right)
+{
+	u16 val=0;
+	struct ac97_mixer_hw *mh = &ac97_hw[mixer];
+
+	M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right);
+
+	if(AC97_STEREO_MASK & (1<<mixer)) {
+		/* stereo mixers, mute them if we can */
+
+		if (mixer == SOUND_MIXER_IGAIN) {
+			/* igain's slider is reversed.. */
+			right = (right * mh->scale) / 100;
+			left = (left * mh->scale) / 100;
+			if ((left == 0) && (right == 0))
+				val |= 0x8000;
+		} else {
+			/* log conversion for the stereo controls */
+			if((left == 0) && (right == 0))
+				val = 0x8000;
+			right = ((100 - lin2log[right]) * mh->scale) / 100;
+			left = ((100 - lin2log[left]) * mh->scale) / 100;
+		}
+
+		val |= (left << 8) | right;
+
+	} else if (mixer == SOUND_MIXER_SPEAKER) {
+		val = (((100 - left) * mh->scale) / 100) << 1;
+	} else if (mixer == SOUND_MIXER_MIC) {
+		val = maestro_ac97_get(card->iobase , mh->offset) & ~0x801f;
+		val |= (((100 - left) * mh->scale) / 100);
+	/*  the low bit is optional in the tone sliders and masking
+		it lets is avoid the 0xf 'bypass'.. */
+	} else if (mixer == SOUND_MIXER_BASS) {
+		val = maestro_ac97_get(card->iobase , mh->offset) & ~0x0f00;
+		val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00;
+	} else if (mixer == SOUND_MIXER_TREBLE)  {
+		val = maestro_ac97_get(card->iobase , mh->offset) & ~0x000f;
+		val |= (((100 - left) * mh->scale) / 100) & 0x000e;
+	}
+
+	maestro_ac97_set(card->iobase , mh->offset, val);
+	
+	M_printk(" -> %x\n",val);
+}
+
+/* the following tables allow us to go from 
+	OSS <-> ac97 quickly. */
+
+enum ac97_recsettings {
+	AC97_REC_MIC=0,
+	AC97_REC_CD,
+	AC97_REC_VIDEO,
+	AC97_REC_AUX,
+	AC97_REC_LINE,
+	AC97_REC_STEREO, /* combination of all enabled outputs..  */
+	AC97_REC_MONO,        /*.. or the mono equivalent */
+	AC97_REC_PHONE        
+};
+
+static unsigned int ac97_oss_mask[] = {
+	[AC97_REC_MIC] = SOUND_MASK_MIC, 
+	[AC97_REC_CD] = SOUND_MASK_CD, 
+	[AC97_REC_VIDEO] = SOUND_MASK_VIDEO, 
+	[AC97_REC_AUX] = SOUND_MASK_LINE1, 
+	[AC97_REC_LINE] = SOUND_MASK_LINE, 
+	[AC97_REC_PHONE] = SOUND_MASK_PHONEIN
+};
+
+/* indexed by bit position */
+static unsigned int ac97_oss_rm[] = {
+	[SOUND_MIXER_MIC] = AC97_REC_MIC,
+	[SOUND_MIXER_CD] = AC97_REC_CD,
+	[SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
+	[SOUND_MIXER_LINE1] = AC97_REC_AUX,
+	[SOUND_MIXER_LINE] = AC97_REC_LINE,
+	[SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
+};
+	
+/* read or write the recmask 
+	the ac97 can really have left and right recording
+	inputs independantly set, but OSS doesn't seem to 
+	want us to express that to the user. 
+	the caller guarantees that we have a supported bit set,
+	and they must be holding the card's spinlock */
+static int 
+ac97_recmask_io(struct ess_card *card, int read, int mask) 
+{
+	unsigned int val = ac97_oss_mask[ maestro_ac97_get(card->iobase, 0x1a) & 0x7 ];
+
+	if (read) return val;
+
+	/* oss can have many inputs, maestro cant.  try
+		to pick the 'new' one */
+
+	if (mask != val) mask &= ~val;
+
+	val = ffs(mask) - 1; 
+	val = ac97_oss_rm[val];
+	val |= val << 8;  /* set both channels */
+
+	M_printk("maestro: setting ac97 recmask to 0x%x\n",val);
+
+	maestro_ac97_set(card->iobase,0x1a,val);
+
+	return 0;
+};
+
+/*
+ *	The Maestro can be wired to a standard AC97 compliant codec
+ *	(see www.intel.com for the pdf's on this), or to a PT101 codec
+ *	which appears to be the ES1918 (data sheet on the esstech.com.tw site)
+ *
+ *	The PT101 setup is untested.
+ */
+ 
+static u16 maestro_ac97_init(struct ess_card *card, int iobase)
+{
+	u16 vend1, vend2, caps;
+
+	card->mix.supported_mixers = AC97_SUPPORTED_MASK;
+	card->mix.stereo_mixers = AC97_STEREO_MASK;
+	card->mix.record_sources = AC97_RECORD_MASK;
+/*	card->mix.read_mixer = ac97_read_mixer;*/
+	card->mix.write_mixer = ac97_write_mixer;
+	card->mix.recmask_io = ac97_recmask_io;
+
+	vend1 = maestro_ac97_get(iobase, 0x7c);
+	vend2 = maestro_ac97_get(iobase, 0x7e);
+
+	caps = maestro_ac97_get(iobase, 0x00);
+
+	printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n",
+		vend1,vend2,caps,maestro_ac97_get(iobase,0x26) & 0xf);
+
+	if (! (caps & 0x4) ) {
+		/* no bass/treble nobs */
+		card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
+	}
+
+	/* XXX endianness, dork head. */
+	/* vendor specifc bits.. */
+	switch ((long)(vend1 << 16) | vend2) {
+	case 0x545200ff:	/* TriTech */
+		/* no idea what this does */
+		maestro_ac97_set(iobase,0x2a,0x0001);
+		maestro_ac97_set(iobase,0x2c,0x0000);
+		maestro_ac97_set(iobase,0x2c,0xffff);
+		break;
+	case 0x83847609:	/* ESS 1921 */
+		/* writing to 0xe (mic) or 0x1a (recmask) seems
+			to hang this codec */
+		card->mix.supported_mixers &= ~(SOUND_MASK_MIC);
+		card->mix.record_sources = 0;
+		card->mix.recmask_io = NULL;
+#if 0	/* don't ask.  I have yet to see what these actually do. */
+		maestro_ac97_set(iobase,0x76,0xABBA); /* o/~ Take a chance on me o/~ */
+		udelay(20);
+		maestro_ac97_set(iobase,0x78,0x3002);
+		udelay(20);
+		maestro_ac97_set(iobase,0x78,0x3802);
+		udelay(20);
+#endif
+		break;
+	default: break;
+	}
+
+	maestro_ac97_set(iobase, 0x1E, 0x0404);
+	/* null misc stuff */
+	maestro_ac97_set(iobase, 0x20, 0x0000);
+
+	return 0;
+}
+
+#if 0  /* there has been 1 person on the planet with a pt101 that we
+	know of.  If they care, they can put this back in :) */
+static u16 maestro_pt101_init(struct ess_card *card,int iobase)
+{
+	printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
+	/* who knows.. */
+	maestro_ac97_set(iobase, 0x2A, 0x0001);
+	maestro_ac97_set(iobase, 0x2C, 0x0000);
+	maestro_ac97_set(iobase, 0x2C, 0xFFFF);
+	maestro_ac97_set(iobase, 0x10, 0x9F1F);
+	maestro_ac97_set(iobase, 0x12, 0x0808);
+	maestro_ac97_set(iobase, 0x14, 0x9F1F);
+	maestro_ac97_set(iobase, 0x16, 0x9F1F);
+	maestro_ac97_set(iobase, 0x18, 0x0404);
+	maestro_ac97_set(iobase, 0x1A, 0x0000);
+	maestro_ac97_set(iobase, 0x1C, 0x0000);
+	maestro_ac97_set(iobase, 0x02, 0x0404);
+	maestro_ac97_set(iobase, 0x04, 0x0808);
+	maestro_ac97_set(iobase, 0x0C, 0x801F);
+	maestro_ac97_set(iobase, 0x0E, 0x801F);
+	return 0;
+}
+#endif
+
+/* this is very magic, and very slow.. */
+static void 
+maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev)
+{
+	u16 save_68;
+	u16 w;
+
+	outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
+	outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+	outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+	/* reset the first codec */
+	outw(0x0000,  ioaddr+0x36);
+	save_68 = inw(ioaddr+0x68);
+	pci_read_config_word(pcidev, 0x58, &w);	/* something magical with gpio and bus arb. */
+	if( w & 0x1)
+		save_68 |= 0x10;
+	outw(0xfffe, ioaddr + 0x64);	/* tickly gpio 0.. */
+	outw(0x0001, ioaddr + 0x68);
+	outw(0x0000, ioaddr + 0x60);
+	udelay(20);
+	outw(0x0001, ioaddr + 0x60);
+	mdelay(20);
+
+	outw(save_68 | 0x1, ioaddr + 0x68);	/* now restore .. */
+	outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38);
+	outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a);
+	outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c);
+
+	/* now the second codec */
+	outw(0x0000,  ioaddr+0x36);
+	outw(0xfff7, ioaddr + 0x64);
+	save_68 = inw(ioaddr+0x68);
+	outw(0x0009, ioaddr + 0x68);
+	outw(0x0001, ioaddr + 0x60);
+	udelay(20);
+	outw(0x0009, ioaddr + 0x60);
+	mdelay(500);	/* .. ouch.. */
+	outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
+	outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+	outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+#if 0 /* the loop here needs to be much better if we want it.. */
+	M_printk("trying software reset\n");
+	/* try and do a software reset */
+	outb(0x80|0x7c, ioaddr + 0x30);
+	for (w=0; ; w++) {
+		if ((inw(ioaddr+ 0x30) & 1) == 0) {
+			if(inb(ioaddr + 0x32) !=0) break;
+
+			outb(0x80|0x7d, ioaddr + 0x30);
+			if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break;
+			outb(0x80|0x7f, ioaddr + 0x30);
+			if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break;
+		}
+
+		if( w > 10000) {
+			outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37);  /* do a software reset */
+			mdelay(500); /* oh my.. */
+			outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37);  
+			udelay(1);
+			outw( 0x80, ioaddr+0x30);
+			for(w = 0 ; w < 10000; w++) {
+				if((inw(ioaddr + 0x30) & 1) ==0) break;
+			}
+		}
+	}
+#endif
+}
+/*
+ *	Indirect register access. Not all registers are readable so we
+ *	need to keep register state ourselves
+ */
+ 
+#define WRITEABLE_MAP	0xEFFFFF
+#define READABLE_MAP	0x64003F
+
+/*
+ *	The Maestro engineers were a little indirection happy. These indirected
+ *	registers themselves include indirect registers at another layer
+ */
+
+static void __maestro_write(struct ess_card *card, u16 reg, u16 data)
+{
+	long ioaddr = card->iobase;
+
+	outw(reg, ioaddr+0x02);
+	outw(data, ioaddr+0x00);
+	if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg);
+	else card->maestro_map[reg]=data;
+
+}
+ 
+static void maestro_write(struct ess_state *s, u16 reg, u16 data)
+{
+	unsigned long flags;
+
+	CHECK_SUSPEND;
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	__maestro_write(s->card,reg,data);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+}
+
+static u16 __maestro_read(struct ess_card *card, u16 reg)
+{
+	long ioaddr = card->iobase;
+
+	outw(reg, ioaddr+0x02);
+	return card->maestro_map[reg]=inw(ioaddr+0x00);
+}
+
+static u16 maestro_read(struct ess_state *s, u16 reg)
+{
+	if(READABLE_MAP & (1<<reg))
+	{
+		unsigned long flags;
+		CHECK_SUSPEND;
+		spin_lock_irqsave(&s->card->lock,flags);
+
+		__maestro_read(s->card,reg);
+
+		spin_unlock_irqrestore(&s->card->lock,flags);
+	}
+	return s->card->maestro_map[reg];
+}
+
+/*
+ *	These routines handle accessing the second level indirections to the
+ *	wave ram.
+ */
+
+/*
+ *	The register names are the ones ESS uses (see 104T31.ZIP)
+ */
+ 
+#define IDR0_DATA_PORT		0x00
+#define IDR1_CRAM_POINTER	0x01
+#define IDR2_CRAM_DATA		0x02
+#define IDR3_WAVE_DATA		0x03
+#define IDR4_WAVE_PTR_LOW	0x04
+#define IDR5_WAVE_PTR_HI	0x05
+#define IDR6_TIMER_CTRL		0x06
+#define IDR7_WAVE_ROMRAM	0x07
+
+static void apu_index_set(struct ess_card *card, u16 index)
+{
+	int i;
+	__maestro_write(card, IDR1_CRAM_POINTER, index);
+	for(i=0;i<1000;i++)
+		if(__maestro_read(card, IDR1_CRAM_POINTER)==index)
+			return;
+	printk(KERN_WARNING "maestro: APU register select failed.\n");
+}
+
+static void apu_data_set(struct ess_card *card, u16 data)
+{
+	int i;
+	for(i=0;i<1000;i++)
+	{
+		if(__maestro_read(card, IDR0_DATA_PORT)==data)
+			return;
+		__maestro_write(card, IDR0_DATA_PORT, data);
+	}
+}
+
+/*
+ *	This is the public interface for APU manipulation. It handles the
+ *	interlock to avoid two APU writes in parallel etc. Don't diddle
+ *	directly with the stuff above.
+ */
+
+static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data)
+{
+	unsigned long flags;
+	
+	CHECK_SUSPEND;
+
+	if(channel&ESS_CHAN_HARD)
+		channel&=~ESS_CHAN_HARD;
+	else
+	{
+		if(channel>5)
+			printk("BAD CHANNEL %d.\n",channel);
+		else
+			channel = s->apu[channel];
+#ifdef	CONFIG_APM
+		/* store based on real hardware apu/reg */
+		s->card->apu_map[channel][reg]=data;
+#endif
+	}
+	reg|=(channel<<4);
+	
+	/* hooray for double indirection!! */
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	apu_index_set(s->card, reg);
+	apu_data_set(s->card, data);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+}
+
+static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg)
+{
+	unsigned long flags;
+	u16 v;
+	
+	CHECK_SUSPEND;
+
+	if(channel&ESS_CHAN_HARD)
+		channel&=~ESS_CHAN_HARD;
+	else
+		channel = s->apu[channel];
+
+	reg|=(channel<<4);
+	
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	apu_index_set(s->card, reg);
+	v=__maestro_read(s->card, IDR0_DATA_PORT);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+	return v;
+}
+
+
+/*
+ *	The wavecache buffers between the APUs and
+ *	pci bus mastering
+ */
+ 
+static void wave_set_register(struct ess_state *s, u16 reg, u16 value)
+{
+	long ioaddr = s->card->iobase;
+	unsigned long flags;
+	CHECK_SUSPEND;
+	
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	outw(reg, ioaddr+0x10);
+	outw(value, ioaddr+0x12);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+}
+
+static u16 wave_get_register(struct ess_state *s, u16 reg)
+{
+	long ioaddr = s->card->iobase;
+	unsigned long flags;
+	u16 value;
+	CHECK_SUSPEND;
+	
+	spin_lock_irqsave(&s->card->lock,flags);
+	outw(reg, ioaddr+0x10);
+	value=inw(ioaddr+0x12);
+	spin_unlock_irqrestore(&s->card->lock,flags);
+	
+	return value;
+}
+
+static void sound_reset(int ioaddr)
+{
+	outw(0x2000, 0x18+ioaddr);
+	udelay(1);
+	outw(0x0000, 0x18+ioaddr);
+	udelay(1);
+}
+
+/* sets the play formats of these apus, should be passed the already shifted format */
+static void set_apu_fmt(struct ess_state *s, int apu, int mode)
+{
+	int apu_fmt = 0x10;
+
+	if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; 
+	if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; 
+	s->apu_mode[apu]   = apu_fmt;
+	s->apu_mode[apu+1] = apu_fmt;
+}
+
+/* this only fixes the output apu mode to be later set by start_dac and
+	company.  output apu modes are set in ess_rec_setup */
+static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data)
+{
+	s->fmt = (s->fmt & mask) | data;
+	set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK);
+}
+
+/* this is off by a little bit.. */
+static u32 compute_rate(struct ess_state *s, u32 freq)
+{
+	u32 clock = clock_freq[s->card->card_type];     
+
+	if (freq == 48000) return 0x10000;
+
+	return ((freq / clock) <<16 )+  
+		(((freq % clock) << 16) / clock);
+}
+
+static void set_dac_rate(struct ess_state *s, unsigned int rate)
+{
+	u32 freq;
+	int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+
+	s->ratedac = rate;
+
+	if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO))
+		rate >>= 1;
+
+/*	M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/
+
+	freq = compute_rate(s, rate);
+	
+	/* Load the frequency, turn on 6dB */
+	apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 0, 3, freq>>8);
+	apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 1, 3, freq>>8);
+}
+
+static void set_adc_rate(struct ess_state *s, unsigned rate)
+{
+	u32 freq;
+
+	/* Sample Rate conversion APUs don't like 0x10000 for their rate */
+	if (rate > 47999)
+		rate = 47999;
+	if (rate < 4000)
+		rate = 4000;
+
+	s->rateadc = rate;
+
+	freq = compute_rate(s, rate);
+	
+	/* Load the frequency, turn on 6dB */
+	apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 2, 3, freq>>8);
+	apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 3, 3, freq>>8);
+
+	/* fix mixer rate at 48khz.  and its _must_ be 0x10000. */
+	freq = 0x10000;
+
+	apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 4, 3, freq>>8);
+	apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 5, 3, freq>>8);
+}
+
+/* Stop our host of recording apus */
+extern inline void stop_adc(struct ess_state *s)
+{
+	/* XXX lets hope we don't have to lock around this */
+	if (! (s->enable & ADC_RUNNING)) return;
+
+	s->enable &= ~ADC_RUNNING;
+	apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F);
+	apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F);
+	apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F);
+	apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F);
+}	
+
+/* stop output apus */
+extern inline void stop_dac(struct ess_state *s)
+{
+	/* XXX have to lock around this? */
+	if (! (s->enable & DAC_RUNNING)) return;
+
+	s->enable &= ~DAC_RUNNING;
+	apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F);
+	apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F);
+}	
+
+static void start_dac(struct ess_state *s)
+{
+	/* XXX locks? */
+	if (	(s->dma_dac.mapped || s->dma_dac.count > 0) && 
+		s->dma_dac.ready &&
+		(! (s->enable & DAC_RUNNING)) ) {
+
+		s->enable |= DAC_RUNNING;
+
+		apu_set_register(s, 0, 0, 
+			(apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]);
+
+		if((s->fmt >> ESS_DAC_SHIFT)  & ESS_FMT_STEREO) 
+			apu_set_register(s, 1, 0, 
+				(apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]);
+	}
+}	
+
+static void start_adc(struct ess_state *s)
+{
+	/* XXX locks? */
+	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 & ADC_RUNNING)) ) {
+
+		s->enable |= ADC_RUNNING;
+		apu_set_register(s, 2, 0, 
+			(apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]);
+		apu_set_register(s, 4, 0, 
+			(apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]);
+
+		if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
+			apu_set_register(s, 3, 0, 
+				(apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]);
+			apu_set_register(s, 5, 0, 
+				(apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]);
+		}
+			
+	}
+}	
+
+
+/*
+ *	Native play back driver 
+ */
+
+/* the mode passed should be already shifted and masked */
+static void 
+ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size)
+{
+	u32 pa;
+	u32 tmpval;
+	int high_apu = 0;
+	int channel;
+
+	M_printk("mode=%d rate=%d buf=%p len=%d.\n",
+		mode, rate, buffer, size);
+		
+	/* all maestro sizes are in 16bit words */
+	size >>=1;
+
+	if(mode&ESS_FMT_STEREO) {
+		high_apu++;
+		/* only 16/stereo gets size divided */
+		if(mode&ESS_FMT_16BIT)
+			size>>=1;
+	}
+	
+	for(channel=0; channel <= high_apu; channel++)
+	{
+		pa = virt_to_bus(buffer);
+
+		/* set the wavecache control reg */
+		tmpval = (pa - 0x10) & 0xFFF8;
+		if(!(mode & ESS_FMT_16BIT)) tmpval |= 4;
+		if(mode & ESS_FMT_STEREO) tmpval |= 2;
+		ess->apu_base[channel]=tmpval;
+		wave_set_register(ess, ess->apu[channel]<<3, tmpval);
+		
+		pa -= virt_to_bus(ess->card->dmapages);
+		pa>>=1; /* words */
+		
+		/* base offset of dma calcs when reading the pointer
+			on the left one */
+		if(!channel) ess->dma_dac.base = pa&0xFFFF;
+		
+		pa|=0x00400000;			/* System RAM */
+
+		/* XXX the 16bit here might not be needed.. */
+		if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) {
+			if(channel) 
+				pa|=0x00800000;			/* Stereo */
+			pa>>=1;
+		}
+			
+/* XXX think about endianess when writing these registers */
+		M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa);
+		/* Load the buffer into the wave engine */
+		apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8);
+		apu_set_register(ess, channel, 5, pa&0xFFFF);
+		apu_set_register(ess, channel, 6, (pa+size)&0xFFFF);
+		/* setting loop == sample len */
+		apu_set_register(ess, channel, 7, size);
+		
+		/* clear effects/env.. */
+		apu_set_register(ess, channel, 8, 0x0000);
+		/* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */
+		apu_set_register(ess, channel, 9, 0xD000);
+
+		/* clear routing stuff */
+		apu_set_register(ess, channel, 11, 0x0000);
+		/* dma on, no envelopes, filter to all 1s) */
+		apu_set_register(ess, channel, 0, 0x400F);
+		
+		if(mode&ESS_FMT_16BIT)
+			ess->apu_mode[channel]=0x10;
+		else
+			ess->apu_mode[channel]=0x30;
+
+		if(mode&ESS_FMT_STEREO) {
+			/* set panning: left or right */
+			apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0));
+			ess->apu_mode[channel] += 0x10;
+		} else
+			apu_set_register(ess, channel, 10, 0x8F08);
+	}
+	
+	/* clear WP interupts */
+	outw(1, ess->card->iobase+0x04);
+	/* enable WP ints */
+	outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18);
+
+	/* go team! */
+	set_dac_rate(ess,rate);
+	start_dac(ess);
+}
+
+/*
+ *	Native record driver 
+ */
+
+/* again, passed mode is alrady shifted/masked */
+static void 
+ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size)
+{
+	int apu_step = 2;
+	int channel;
+
+	M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n",
+		mode, rate, buffer, size);
+		
+	/* all maestro sizes are in 16bit words */
+	size >>=1;
+
+	/* we're given the full size of the buffer, but
+	in stereo each channel will only use its half */
+	if(mode&ESS_FMT_STEREO) {
+		size >>=1; 
+		apu_step = 1;
+	}
+	
+	/* APU assignments: 2 = mono/left SRC
+	                    3 = right SRC
+	                    4 = mono/left Input Mixer
+	                    5 = right Input Mixer */
+	for(channel=2;channel<6;channel+=apu_step)
+	{
+		int i;
+		int bsize, route;
+		u32 pa;
+		u32 tmpval;
+
+		/* data seems to flow from the codec, through an apu into
+			the 'mixbuf' bit of page, then through the SRC apu
+			and out to the real 'buffer'.  ok.  sure.  */
+		
+		if(channel & 0x04) {
+			/* ok, we're an input mixer going from adc
+				through the mixbuf to the other apus */
+
+			if(!(channel & 0x01)) { 
+				pa = virt_to_bus(ess->mixbuf);
+			} else {
+				pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4));
+			}
+
+			/* we source from a 'magic' apu */
+			bsize = PAGE_SIZE >> 5;	/* half of this channels alloc, in words */
+			route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */
+			ess->apu_mode[channel] = 0x90;  /* Input Mixer */
+
+		} else {  
+			/* we're a rate converter taking
+				input from the input apus and outputing it to
+				system memory */
+			if(!(channel & 0x01))  {
+				pa = virt_to_bus(buffer);
+			} else {
+				/* right channel records its split half.
+				*2 accomodates for rampant shifting earlier */
+				pa = virt_to_bus(buffer + size*2);
+			}
+
+			ess->apu_mode[channel] = 0xB0;  /* Sample Rate Converter */
+
+			bsize = size; 
+			/* get input from inputing apu */
+			route = channel + 2;
+		}
+
+		M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel);
+		
+		/* set the wavecache control reg */
+		tmpval = (pa - 0x10) & 0xFFF8;
+		ess->apu_base[channel]=tmpval;
+		wave_set_register(ess, ess->apu[channel]<<3, tmpval);
+		
+		pa -= virt_to_bus(ess->card->dmapages);
+		pa>>=1; /* words */
+		
+		/* base offset of dma calcs when reading the pointer
+			on this left one */
+		if(channel==2) ess->dma_adc.base = pa&0xFFFF;
+
+		pa|=0x00400000;			/* bit 22 -> System RAM */
+
+		M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", 
+			ess->apu[channel], pa, bsize, route);
+		
+		/* Begin loading the APU */		
+		for(i=0;i<15;i++)		/* clear all PBRs */
+			apu_set_register(ess, channel, i, 0x0000);
+			
+		apu_set_register(ess, channel, 0, 0x400F);
+
+		/* need to enable subgroups.. and we should probably
+			have different groups for different /dev/dsps..  */
+ 		apu_set_register(ess, channel, 2, 0x8);
+				
+		/* Load the buffer into the wave engine */
+		apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8);
+		/* XXX reg is little endian.. */
+		apu_set_register(ess, channel, 5, pa&0xFFFF);
+		apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF);
+		apu_set_register(ess, channel, 7, bsize);
+				
+		/* clear effects/env.. */
+		apu_set_register(ess, channel, 8, 0x00F0);
+		
+		/* amplitude now?  sure.  why not.  */
+		apu_set_register(ess, channel, 9, 0x0000);
+
+		/* set filter tune, radius, polar pan */
+		apu_set_register(ess, channel, 10, 0x8F08);
+
+		/* route input */
+		apu_set_register(ess, channel, 11, route);
+	}
+	
+	/* clear WP interupts */
+	outw(1, ess->card->iobase+0x04);
+	/* enable WP ints */
+	outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18);
+
+	/* let 'er rip */
+	set_adc_rate(ess,rate);
+	start_adc(ess);
+}
+/* --------------------------------------------------------------------- */
+
+static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count)
+{
+	M_printk("set_dmaa??\n");
+}
+
+static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count)
+{
+	M_printk("set_dmac??\n");
+}
+
+/* Playback pointer */
+extern __inline__ unsigned get_dmaa(struct ess_state *s)
+{
+	int offset;
+
+	offset = apu_get_register(s,0,5);
+
+/*	M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */
+	
+	offset-=s->dma_dac.base;
+
+	return (offset&0xFFFE)<<1; /* hardware is in words */
+}
+
+/* Record pointer */
+extern __inline__ unsigned get_dmac(struct ess_state *s)
+{
+	int offset;
+
+	offset = apu_get_register(s,2,5);
+
+/*	M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */
+	
+	/* The offset is an address not a position relative to base */
+	offset-=s->dma_adc.base;
+	
+	return (offset&0xFFFE)<<1; /* hardware is in words */
+}
+
+/*
+ *	Meet Bob, the timer...
+ */
+
+static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void stop_bob(struct ess_state *s)
+{
+	/* Mask IDR 11,17 */
+	maestro_write(s,  0x11, maestro_read(s, 0x11)&~1);
+	maestro_write(s,  0x17, maestro_read(s, 0x17)&~1);
+}
+
+/* eventually we could be clever and limit bob ints
+	to the frequency at which our smallest duration
+	chunks may expire */
+#define ESS_SYSCLK	50000000
+static void start_bob(struct ess_state *s)
+{
+	int prescale;
+	int divide;
+	
+	/* XXX make freq selector much smarter, see calc_bob_rate */
+	int freq = 150;		/* requested frequency - calculate what we want here. */
+	
+	/* compute ideal interrupt frequency for buffer size & play rate */
+	/* first, find best prescaler value to match freq */
+	for(prescale=5;prescale<12;prescale++)
+		if(freq > (ESS_SYSCLK>>(prescale+9)))
+			break;
+			
+	/* next, back off prescaler whilst getting divider into optimum range */
+	divide=1;
+	while((prescale > 5) && (divide<32))
+	{
+		prescale--;
+		divide <<=1;
+	}
+	divide>>=1;
+	
+	/* now fine-tune the divider for best match */
+	for(;divide<31;divide++)
+		if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1)))
+			break;
+	
+	/* divide = 0 is illegal, but don't let prescale = 4! */
+	if(divide == 0)
+	{
+		divide++;
+		if(prescale>5)
+			prescale--;
+	}
+
+	maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */
+	
+	/* Now set IDR 11/17 */
+	maestro_write(s, 0x11, maestro_read(s, 0x11)|1);
+	maestro_write(s, 0x17, maestro_read(s, 0x17)|1);
+}
+/* --------------------------------------------------------------------- */
+
+/* this quickly calculates the frequency needed for bob
+	and sets it if its different than what bob is
+	currently running at.  its called often so 
+	needs to be fairly quick. */
+#define BOB_MIN 50
+#define BOB_MAX 400
+static void calc_bob_rate(struct ess_state *s) {
+#if 0 /* this thing tries to set the frequency of bob such that
+	there are 2 interrupts / buffer walked by the dac/adc.  That
+	is probably very wrong for people who actually care about 
+	mid buffer positioning.  it should be calculated as bytes/interrupt
+	and that needs to be decided :)  so for now just use the static 150
+	in start_bob.*/
+
+	unsigned int dac_rate=2,adc_rate=1,newrate;
+	static int israte=-1;
+
+	if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN;
+	else  {
+		dac_rate =	(2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) /
+				(s->dma_dac.fragsize) ;
+	}
+		
+	if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN;
+	else {
+		adc_rate =	(2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) /
+				(s->dma_adc.fragsize) ;
+	}
+
+	if(dac_rate > adc_rate) newrate = adc_rate;
+	else newrate=dac_rate;
+
+	if(newrate > BOB_MAX) newrate = BOB_MAX;
+	else {
+		if(newrate < BOB_MIN) 
+			newrate = BOB_MIN;
+	}
+
+	if( israte != newrate) {
+		printk("dac: %d  adc: %d rate: %d\n",dac_rate,adc_rate,israte);
+		israte=newrate;
+	}
+#endif
+
+}
+
+static int 
+prog_dmabuf(struct ess_state *s, unsigned rec)
+{
+	struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+	unsigned rate = rec ? s->rateadc : s->ratedac;
+	unsigned bytepersec;
+	unsigned bufs;
+	unsigned char fmt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	fmt = s->fmt;
+	if (rec) {
+		stop_adc(s);
+		fmt >>= ESS_ADC_SHIFT;
+	} else {
+		stop_dac(s);
+		fmt >>= ESS_DAC_SHIFT;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	fmt &= ESS_FMT_MASK;
+
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+
+	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;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+
+	M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize);
+
+	memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize);
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (rec) {
+		ess_rec_setup(s, fmt, s->rateadc, 
+			db->rawbuf, db->numfrag << db->fragshift);
+	} else {
+		ess_play_setup(s, fmt, s->ratedac, 
+			db->rawbuf, db->numfrag << db->fragshift);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	db->ready = 1;
+
+	return 0;
+}
+
+static __inline__ void 
+clear_advance(struct ess_state *s)
+{
+	unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 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);
+		/* account for wrapping? */
+		bptr = 0;
+		len -= x;
+	}
+	memset(buf + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void 
+ess_update_ptr(struct ess_state *s)
+{
+	unsigned hwptr;
+	int diff;
+
+	/* update ADC pointer */
+	if (s->dma_adc.ready) {
+		/* oh boy should this all be re-written.  everything in the current code paths think
+		that the various counters/pointers are expressed in bytes to the user but we have
+		two apus doing stereo stuff so we fix it up here.. it propogates to all the various
+		counters from here.  Notice that this means that mono recording is very very
+		broken right now.  */
+		if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
+			hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize;
+		} else {
+			hwptr = get_dmac(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))) {
+				/* FILL ME 
+				wrindir(s, SV_CIENABLE, s->enable); */
+				stop_adc(s); 
+				/* brute force everyone back in sync, sigh */
+				s->dma_adc.count = 0;
+				s->dma_adc.swptr = 0;
+				s->dma_adc.hwptr = 0;
+				s->dma_adc.error++;
+			}
+		}
+	}
+	/* update DAC pointer */
+	if (s->dma_dac.ready) {
+		hwptr = get_dmaa(s) % s->dma_dac.dmasize; 
+		/* the apu only reports the length it has seen, not the
+			length of the memory that has been used (the WP
+			knows that */
+		if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT))
+			hwptr<<=1;
+
+		diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+/*		M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/
+		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;
+/*			M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */
+			if (s->dma_dac.count <= 0) {
+				/* FILL ME 
+				wrindir(s, SV_CIENABLE, s->enable); */
+				/* XXX how on earth can calling this with the lock held work.. */
+				stop_dac(s);
+				/* brute force everyone back in sync, sigh */
+				s->dma_dac.count = 0; 
+				s->dma_dac.swptr = 0; 
+				s->dma_dac.hwptr = 0; 
+				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);
+			}
+		}
+	}
+}
+
+static void 
+ess_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct ess_state *s;
+        struct ess_card *c = (struct ess_card *)dev_id;
+	int i;
+	u32 event;
+
+	if ( ! (event = inb(c->iobase+0x1A)) ) return;
+
+	outw(inw(c->iobase+4)&1, c->iobase+4);
+
+/*	M_printk("maestro int: %x\n",event);*/
+
+	if(event&(1<<6))
+	{
+		/* XXX if we have a hw volume control int enable
+			all the ints?  doesn't make sense.. */
+		event = inw(c->iobase+0x18);
+		outb(0xFF, c->iobase+0x1A);
+	}
+	else
+	{
+		/* else ack 'em all, i imagine */
+		outb(0xFF, c->iobase+0x1A);
+	}
+		
+	/*
+	 *	Update the pointers for all APU's we are running.
+	 */
+	for(i=0;i<NR_DSPS;i++)
+	{
+		s=&c->channels[i];
+		if(s->dev_audio == -1)
+			break;
+		spin_lock(&s->lock);
+		ess_update_ptr(s);
+		spin_unlock(&s->lock);
+	}
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n";
+
+#define VALIDATE_MAGIC(FOO,MAG)                         \
+({                                                \
+	if (!(FOO) || (FOO)->magic != MAG) { \
+		printk(invalid_magic,__FUNCTION__);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC)
+#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC)
+
+static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) 
+{
+	unsigned int left,right;
+	/* cleanse input a little */
+	right = ((val >> 8)  & 0xff) ;
+	left = (val  & 0xff) ;
+
+	if(right > 100) right = 100;
+	if(left > 100) left = 100;
+
+	card->mix.mixer_state[mixer]=(right << 8) | left;
+	card->mix.write_mixer(card,mixer,left,right);
+}
+
+static void
+mixer_push_state(struct ess_card *card)
+{
+	int i;
+	for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) {
+		if( ! supported_mixer(card,i)) continue;
+
+		set_mixer(card,i,card->mix.mixer_state[i]);
+	}
+}
+
+static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg)
+{
+	int i, val=0;
+
+	VALIDATE_CARD(card);
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, card_names[card->card_type], sizeof(info.id));
+		strncpy(info.name,card_names[card->card_type],sizeof(info.name));
+		info.modify_counter = card->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, card_names[card->card_type], sizeof(info.id));
+		strncpy(info.name,card_names[card->card_type],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: /* give them the current record source */
+
+			if(!card->mix.recmask_io) {
+				val = 0;
+			} else {
+				spin_lock(&card->lock);
+				val = card->mix.recmask_io(card,1,0);
+				spin_unlock(&card->lock);
+			}
+			break;
+			
+                case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
+			val = card->mix.supported_mixers;
+			break;
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			val = card->mix.record_sources;
+			break;
+			
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			val = card->mix.stereo_mixers;
+			break;
+			
+                case SOUND_MIXER_CAPS:
+			val = SOUND_CAP_EXCL_INPUT;
+			break;
+
+		default: /* read a specific mixer */
+			i = _IOC_NR(cmd);
+
+			if ( ! supported_mixer(card,i)) 
+				return -EINVAL;
+
+			/* do we ever want to touch the hardware? */
+/*			spin_lock(&card->lock);
+			val = card->mix.read_mixer(card,i);
+			spin_unlock(&card->lock);*/
+
+			val = card->mix.mixer_state[i];
+/*			M_printk("returned 0x%x for mixer %d\n",val,i);*/
+
+			break;
+		}
+		return put_user(val,(int *)arg);
+	}
+	
+        if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ))
+		return -EINVAL;
+	
+	card->mix.modcnt++;
+
+	get_user_ret(val, (int *)arg, -EFAULT);
+
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+
+		if (!card->mix.recmask_io) return -EINVAL;
+		if(!val) return 0;
+		if(! (val &= card->mix.record_sources)) return -EINVAL;
+
+		spin_lock(&card->lock);
+		card->mix.recmask_io(card,0,val);
+		spin_unlock(&card->lock);
+		return 0;
+
+	default:
+		i = _IOC_NR(cmd);
+
+		if ( ! supported_mixer(card,i)) 
+			return -EINVAL;
+
+		spin_lock(&card->lock);
+		set_mixer(card,i,val);
+		spin_unlock(&card->lock);
+
+		return 0;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t ess_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ess_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct ess_card *card = devs;
+
+	while (card && card->dev_mixer != minor)
+		card = card->next;
+	if (!card)
+		return -ENODEV;
+
+	file->private_data = card;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int ess_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct ess_card *card = (struct ess_card *)file->private_data;
+
+	VALIDATE_CARD(card);
+	
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct ess_card *card = (struct ess_card *)file->private_data;
+
+	VALIDATE_CARD(card);
+
+	return mixer_ioctl(card, cmd, arg);
+}
+
+static /*const*/ struct file_operations ess_mixer_fops = {
+	&ess_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&ess_ioctl_mixdev,
+	NULL,  /* mmap */
+	&ess_open_mixdev,
+	NULL,	/* flush */
+	&ess_release_mixdev,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct ess_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait,current);
+	unsigned long flags;
+	int count;
+	signed long 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 (;;) {
+		/* XXX uhm.. questionable locking*/
+                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 >> ESS_DAC_SHIFT) & ESS_FMT_MASK];
+		/* XXX this is just broken.  someone is waking us up alot, or schedule_timeout is broken.
+			or something.  who cares. - zach */
+		if (!schedule_timeout(tmo ? tmo : 1) && tmo)
+			M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies);
+        }
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+	current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* Zach sez: "god this is gross.." */
+static int 
+comb_stereo(unsigned char *real_buffer,unsigned char  *tmp_buffer, int offset, 
+	int count, int bufsize)
+{  
+	/* No such thing as stereo recording, so we
+	use dual input mixers.  which means we have to 
+	combine mono to stereo buffer.  yuck. 
+
+	but we don't have to be able to work a byte at a time..*/
+
+	unsigned char *so,*left,*right;
+	int i;
+
+	so = tmp_buffer;
+	left = real_buffer + offset;
+	right = real_buffer + bufsize/2 + offset;
+
+/*	M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/
+
+	for(i=count/4; i ; i--) {
+		(*(so+2)) = *(right++);
+		(*(so+3)) = *(right++);
+		(*so) = *(left++);
+		(*(so+1)) = *(left++);
+		so+=4;
+	}
+
+	return 0;
+}
+
+/* in this loop, dma_adc.count signifies the amount of data thats waiting
+	to be copied to the user's buffer.  it is filled by the interrupt
+	handler and drained by this loop. */
+static ssize_t 
+ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+	unsigned char *combbuf = NULL;
+	
+	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;
+	if(!(combbuf = kmalloc(count,GFP_KERNEL)))
+		return -ENOMEM;
+	ret = 0;
+
+	calc_bob_rate(s);
+
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		/* remember, all these things are expressed in bytes to be
+			sent to the user.. hence the evil / 2 down below */
+		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 ) cnt &= ~3;
+
+		if (cnt <= 0) {
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK) 
+			{
+				ret = ret ? ret : -EAGAIN;
+				goto rec_return_free;
+			}
+			if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
+#ifdef CONFIG_APM
+				if(! in_suspend)
+#endif
+				printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, 
+				       s->dma_adc.hwptr, s->dma_adc.swptr);
+				stop_adc(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift);
+				/* program enhanced mode registers */
+				/* FILL ME */
+/*				wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8);
+				wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */
+				s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current)) 
+			{
+				ret = ret ? ret : -ERESTARTSYS;
+				goto rec_return_free;
+			}
+			continue;
+		}
+	
+		if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
+			/* swptr/2 so that we know the real offset in each apu's buffer */
+			comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize);
+			if (copy_to_user(buffer, combbuf, cnt)) {
+				ret = ret ? ret : -EFAULT;
+				goto rec_return_free;
+			}
+		} else  {
+			if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+				ret = ret ? ret : -EFAULT;
+				goto rec_return_free;
+			}
+		}
+
+		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);
+	}
+
+rec_return_free:
+	if(combbuf) kfree(combbuf);
+	return ret;
+}
+
+static ssize_t 
+ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct ess_state *s = (struct ess_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;
+
+	calc_bob_rate(s);
+
+	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) {
+				if(!ret) ret = -EAGAIN;
+				goto return_free;
+			}
+			if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
+#ifdef CONFIG_APM
+				if(! in_suspend)
+#endif
+				printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, 
+				       s->dma_dac.hwptr, s->dma_dac.swptr);
+				stop_dac(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift);
+				/* program enhanced mode registers */
+/*				wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8);
+				wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */
+				/* FILL ME */
+				s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current)) {
+				if (!ret) ret = -ERESTARTSYS;
+				goto return_free;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+			if (!ret) ret = -EFAULT;
+			goto return_free;
+		}
+
+		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_free:
+	return ret;
+}
+
+static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct ess_state *s = (struct ess_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);
+	ess_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 ess_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct ess_state *s = (struct ess_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 0
+	/* if we can have the wp/wc do the combining
+		we can turn this back on.  */
+	      if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(s, 0)) != 0)
+			return ret;
+		db = &s->dma_adc;
+	} else  
+#endif
+		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 ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret;
+	unsigned char fmtm, fmtd;
+
+/*	printk("maestro: ess_ioctl: cmd %d\n", cmd);*/
+	
+	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, file->f_flags & O_NONBLOCK);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		/* XXX fix */
+		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 |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+			else
+				fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			if (val)
+				fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+			else
+				fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+		}
+		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 |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+					   : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_U8|AFMT_S16_LE, (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;
+	/* fixed at 16bit for now */
+				fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT;
+#if 0
+				if (val == AFMT_S16_LE)
+					fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT);
+#endif
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+ 		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? 
+			(ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+			: (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 
+				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 & ADC_RUNNING))
+			val |= PCM_ENABLE_INPUT;
+		if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) 
+			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 & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		ess_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 & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		ess_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);
+		ess_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);
+		ess_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);
+		ess_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) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+					   : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+					   : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return -EINVAL;
+}
+
+static void
+set_base_registers(struct ess_state *s,void *vaddr)
+{
+	unsigned long packed_phys = virt_to_bus(vaddr)>>12;
+	wave_set_register(s, 0x01FC , packed_phys);
+	wave_set_register(s, 0x01FD , packed_phys);
+	wave_set_register(s, 0x01FE , packed_phys);
+	wave_set_register(s, 0x01FF , packed_phys);
+}
+
+/* we allocate a large power of two for all our memory.
+	this is cut up into (not to scale :):
+	|silly fifo word	| 512byte mixbuf per adc	| dac/adc * channels |
+*/
+static int
+allocate_buffers(struct ess_state *s)
+{
+	void *rawbuf=NULL;
+	int order,i;
+	unsigned long mapend,map;
+
+	/* alloc as big a chunk as we can */
+	for (order = (dsps_order + (15-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--)
+		if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
+			break;
+
+	if (!rawbuf)
+		return 1;
+
+	M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<<order,order, rawbuf);
+
+	if ((virt_to_bus(rawbuf) + (PAGE_SIZE << order) - 1) & ~((1<<28)-1))  {
+		printk(KERN_ERR "maestro: DMA buffer beyond 256MB! busaddr 0x%lx  size %ld\n",
+			virt_to_bus(rawbuf), PAGE_SIZE << order);
+		kfree(rawbuf);
+		return 1;
+	}
+
+	s->card->dmapages = rawbuf;
+	s->card->dmaorder = order;
+
+	/* play bufs are in the same first region as record bufs */
+	set_base_registers(s,rawbuf);
+
+	M_printk("maestro: writing %lx (%lx) to the wp\n",virt_to_bus(rawbuf),
+		((virt_to_bus(rawbuf))&0xFFE00000)>>12);
+
+	for(i=0;i<NR_DSPS;i++) {
+		struct ess_state *ess = &s->card->channels[i];
+
+		if(ess->dev_audio == -1)
+			continue;
+
+		ess->dma_dac.ready = s->dma_dac.mapped = 0;
+		ess->dma_adc.ready = s->dma_adc.mapped = 0;
+		ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1;
+
+		/* offset dac and adc buffers starting half way through and then at each [da][ad]c's
+			order's intervals.. */
+		ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 )));
+		ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder);
+		/* offset mixbuf by a mixbuf so that the lame status fifo can
+			happily scribble away.. */ 
+		ess->mixbuf = rawbuf + (512 * (i+1));
+
+		M_printk("maestro: setup apu %d: %p %p %p\n",i,ess->dma_dac.rawbuf,
+			ess->dma_adc.rawbuf, ess->mixbuf);
+
+	}
+
+	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+	mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1);
+	for (map = MAP_NR(rawbuf); map <= mapend; map++) {
+		set_bit(PG_reserved, &mem_map[map].flags);
+	}
+
+	return 0;
+} 
+static void
+free_buffers(struct ess_state *s)
+{
+	unsigned long map, mapend;
+
+	s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL;
+	s->dma_dac.mapped = s->dma_adc.mapped = 0;
+	s->dma_dac.ready = s->dma_adc.ready = 0;
+
+	M_printk("maestro: freeing %p\n",s->card->dmapages);
+	/* undo marking the pages as reserved */
+
+	mapend = MAP_NR(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1);
+	for (map = MAP_NR(s->card->dmapages); map <= mapend; map++)
+		clear_bit(PG_reserved, &mem_map[map].flags);    
+
+	free_pages((unsigned long)s->card->dmapages,s->card->dmaorder);
+	s->card->dmapages = NULL;
+}
+
+static int 
+ess_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct ess_card *c = devs;
+	struct ess_state *s = NULL, *sp;
+	int i;
+	unsigned char fmtm = ~0, fmts = 0;
+
+	/*
+	 *	Scan the cards and find the channel. We only
+	 *	do this at open time so it is ok
+	 */
+	 
+	while (c!=NULL)
+	{
+		for(i=0;i<NR_DSPS;i++)
+		{
+			sp=&c->channels[i];
+			if(sp->dev_audio < 0)
+				continue;
+			if((sp->dev_audio ^ minor) & ~0xf)
+				continue;
+			s=sp;
+		}
+		c=c->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 -EWOULDBLOCK;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+
+	/* under semaphore.. */
+	if ((s->card->dmapages==NULL) && allocate_buffers(s)) {
+		up(&s->open_sem);
+		return -ENOMEM;
+	}
+
+	if (file->f_mode & FMODE_READ) {
+/*
+		fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */
+
+		fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT);
+		fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT;
+
+		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 &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+
+		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);
+
+	/* we're covered by the open_sem */
+	if( ! s->card->dsps_open )  {
+		start_bob(s);
+	}
+	s->card->dsps_open++;
+	M_printk("maestro: open, %d bobs now\n",s->card->dsps_open);
+
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int 
+ess_release(struct inode *inode, struct file *file)
+{
+	struct ess_state *s = (struct ess_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);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+	}
+		
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	/* we're covered by the open_sem */
+	M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1);
+	if( --s->card->dsps_open <= 0) {
+		stop_bob(s);
+		free_buffers(s);
+	}
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct file_operations ess_audio_fops = {
+	&ess_llseek,
+	&ess_read,
+	&ess_write,
+	NULL,  /* readdir */
+	&ess_poll,
+	&ess_ioctl,
+	&ess_mmap,
+	&ess_open,
+	NULL,	/* flush */
+	&ess_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+static int
+maestro_config(struct ess_card *card) 
+{
+	struct pci_dev *pcidev = &card->pcidev;
+	struct ess_state *ess = &card->channels[0];
+	int apu,iobase  = card->iobase;
+	u16 w;
+	u32 n;
+
+	/*
+	 *	Disable ACPI
+	 */
+	 
+	pci_write_config_dword(pcidev, 0x54, 0x00000000);
+	pci_write_config_dword(pcidev, 0x56, 0x00000000);
+	
+	/*
+	 *	Use TDMA for now. TDMA works on all boards, so while its
+	 *	not the most efficient its the simplest.
+	 */ 
+	 
+	pci_read_config_word(pcidev, 0x50, &w);
+
+	/* Clear DMA bits */
+	w&=~(1<<10|1<<9|1<<8);
+
+	/* TDMA on */
+	w|= (1<<8);
+
+	/*
+	 *	Some of these are undocumented bits
+	 */
+	 
+	w&=~(1<<13)|(1<<14);		/* PIC Snoop mode bits */
+	w&=~(1<<11);			/* Safeguard off */
+	w|= (1<<7);			/* Posted write */
+	w|= (1<<6);			/* ISA timing on */
+	/* XXX huh?  claims to be reserved.. */
+	w&=~(1<<5);			/* Don't swap left/right */
+	w&=~(1<<1);			/* Subtractive decode off */
+	
+	pci_write_config_word(pcidev, 0x50, w);
+	
+	pci_read_config_word(pcidev, 0x52, &w);
+	w&=~(1<<15);		/* Turn off internal clock multiplier */
+	/* XXX how do we know which to use? */
+	w&=~(1<<14);		/* External clock */
+	
+	w&=~(1<<7);		/* HWV off */
+	w&=~(1<<6);		/* Debounce off */
+	w&=~(1<<5);		/* GPIO 4:5 */
+	w|= (1<<4);             /* Disconnect from the CHI.  Enabling this made a dell 7500 work. */
+	w&=~(1<<3);		/* IDMA off (undocumented) */
+	w&=~(1<<2);		/* MIDI fix off (undoc) */
+	w&=~(1<<1);		/* reserved, always write 0 */
+	w&=~(1<<0);		/* IRQ to ISA off (undoc) */
+	pci_write_config_word(pcidev, 0x52, w);
+
+	/*
+	 *	DDMA off
+	 */
+
+	pci_read_config_word(pcidev, 0x60, &w);
+	w&=~(1<<0);
+	pci_write_config_word(pcidev, 0x60, w);
+	
+	/*
+	 *	Legacy mode
+	 */
+
+	pci_read_config_word(pcidev, 0x40, &w);
+	w|=(1<<15);	/* legacy decode off */
+	w&=~(1<<14);	/* Disable SIRQ */
+	w&=~(0x1f);	/* disable mpu irq/io, game port, fm, SB */
+	 
+	pci_write_config_word(pcidev, 0x40, w);
+
+	/* stake our claim on the iospace */
+	request_region(iobase, 256, card_names[card->card_type]);
+	
+	sound_reset(iobase);
+
+	/*
+	 *	Ring Bus Setup
+	 */
+
+	/* setup usual 0x34 stuff.. 0x36 may be chip specific */
+        outw(0xC090, iobase+0x34); /* direct sound, stereo */
+        udelay(20);
+        outw(0x3000, iobase+0x36); /* direct sound, stereo */
+        udelay(20);
+
+
+	/*
+	 *	Reset the CODEC
+	 */
+	 
+	maestro_ac97_reset(iobase,pcidev);
+	
+	/*
+	 *	Ring Bus Setup
+	 */
+	 	 
+	n=inl(iobase+0x34);
+	n&=~0xF000;
+	n|=12<<12;		/* Direct Sound, Stereo */
+	outl(n, iobase+0x34);
+
+	n=inl(iobase+0x34);
+	n&=~0x0F00;		/* Modem off */
+	outl(n, iobase+0x34);
+
+	n=inl(iobase+0x34);
+	n&=~0x00F0;
+	n|=9<<4;		/* DAC, Stereo */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n&=~0x000F;		/* ASSP off */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n|=(1<<29);		/* Enable ring bus */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n|=(1<<28);		/* Enable serial bus */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n&=~0x00F00000;		/* MIC off */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n&=~0x000F0000;		/* I2S off */
+	outl(n, iobase+0x34);
+	
+
+	w=inw(iobase+0x18);
+	w&=~(1<<7);		/* ClkRun off */
+	outw(w, iobase+0x18);
+
+	w=inw(iobase+0x18);
+	w&=~(1<<6);		/* Harpo off */
+	outw(w, iobase+0x18);
+	
+	w=inw(iobase+0x18);
+	w&=~(1<<4);		/* ASSP irq off */
+	outw(w, iobase+0x18);
+	
+	w=inw(iobase+0x18);
+	w&=~(1<<3);		/* ISDN irq off */
+	outw(w, iobase+0x18);
+	
+	w=inw(iobase+0x18);
+	w|=(1<<2);		/* Direct Sound IRQ on */
+	outw(w, iobase+0x18);
+
+	w=inw(iobase+0x18);
+	w&=~(1<<1);		/* MPU401 IRQ off */
+	outw(w, iobase+0x18);
+
+	w=inw(iobase+0x18);
+	w|=(1<<0);		/* SB IRQ on */
+	outw(w, iobase+0x18);
+
+	/* it appears some maestros (dell 7500) only work if these are set,
+		regardless of wether we use the assp or not. */
+
+	outb(0, iobase+0xA4); 
+	outb(3, iobase+0xA2); 
+	outb(0, iobase+0xA6);
+	
+	for(apu=0;apu<16;apu++)
+	{
+		/* Write 0 into the buffer area 0x1E0->1EF */
+		outw(0x01E0+apu, 0x10+iobase);
+		outw(0x0000, 0x12+iobase);
+	
+		/*
+		 * The 1.10 test program seem to write 0 into the buffer area
+		 * 0x1D0-0x1DF too.
+		 */
+		outw(0x01D0+apu, 0x10+iobase);
+		outw(0x0000, 0x12+iobase);
+	}
+
+#if 1
+	wave_set_register(ess, IDR7_WAVE_ROMRAM, 
+		(wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00));
+	wave_set_register(ess, IDR7_WAVE_ROMRAM,
+		wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100);
+	wave_set_register(ess, IDR7_WAVE_ROMRAM,
+		wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200);
+	wave_set_register(ess, IDR7_WAVE_ROMRAM,
+		wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400);
+#else		
+	maestro_write(ess, IDR7_WAVE_ROMRAM, 
+		(maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00));
+	maestro_write(ess, IDR7_WAVE_ROMRAM,
+		maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100);
+	maestro_write(ess, IDR7_WAVE_ROMRAM,
+		maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200);
+	maestro_write(ess, IDR7_WAVE_ROMRAM,
+		maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400);
+#endif
+	
+	maestro_write(ess, IDR2_CRAM_DATA, 0x0000);
+	maestro_write(ess, 0x08, 0xB004);
+	/* Now back to the DirectSound stuff */
+	maestro_write(ess, 0x09, 0x001B);
+	maestro_write(ess, 0x0A, 0x8000);
+	maestro_write(ess, 0x0B, 0x3F37);
+	maestro_write(ess, 0x0C, 0x0098);
+	
+	/* parallel out ?? */
+	maestro_write(ess, 0x0C, 
+		(maestro_read(ess, 0x0C)&~0xF000)|0x8000); 
+	/* parallel in, has something to do with recording :) */
+	maestro_write(ess, 0x0C, 
+		(maestro_read(ess, 0x0C)&~0x0F00)|0x0500);
+
+	maestro_write(ess, 0x0D, 0x7632);
+			
+	/* Wave cache control on - test off, sg off, 
+		enable, enable extra chans 1Mb */
+
+	outw(inw(0x14+iobase)|(1<<8),0x14+iobase);
+	outw(inw(0x14+iobase)&0xFE03,0x14+iobase);
+	outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase);
+	outw(inw(0x14+iobase)|(1<<7),0x14+iobase);
+
+	outw(0xA1A0, 0x14+iobase);      /* 0300 ? */
+
+	/* Now clear the APU control ram */	
+	for(apu=0;apu<NR_APUS;apu++)
+	{
+		for(w=0;w<NR_APU_REGS;w++)
+			apu_set_register(ess, apu|ESS_CHAN_HARD, w, 0);
+		
+	}
+
+	return 0;
+	
+}
+
+
+static int 
+maestro_install(struct pci_dev *pcidev, int card_type)
+{
+	u32 n;
+	int iobase;
+	int i;
+	struct ess_card *card;
+	struct ess_state *ess;
+	int num = 0;
+
+	/* don't pick up weird modem maestros */
+	if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+		return 0;
+			
+	iobase = SILLY_PCI_BASE_ADDRESS(pcidev); 
+
+	if(check_region(iobase, 256))
+	{
+		printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase);
+		return 0;
+	}
+
+	/* this was tripping up some machines */
+	if(pcidev->irq == 0) 
+	{
+		printk(KERN_WARNING "maestro: pci subsystem reports irq 0, this might not be correct.\n");
+	}
+
+	/* just to be sure */
+	pci_set_master(pcidev);
+
+	card = kmalloc(sizeof(struct ess_card), GFP_KERNEL);
+	if(card == NULL)
+	{
+		printk(KERN_WARNING "maestro: out of memory\n");
+		return 0;
+	}
+	
+	memset(card, 0, sizeof(*card));
+	memcpy(&card->pcidev,pcidev,sizeof(card->pcidev));
+
+#ifdef CONFIG_APM
+	if (apm_register_callback(maestro_apm_callback)) {
+		printk(KERN_WARNING "maestro: apm suspend might not work.\n");
+	}
+#endif
+
+	card->iobase = iobase;
+	card->card_type = card_type;
+	card->irq = pcidev->irq;
+	card->next = devs;
+	card->magic = ESS_CARD_MAGIC;
+	spin_lock_init(&card->lock);
+	devs = card;
+	
+	/* init our groups of 6 apus */
+	for(i=0;i<NR_DSPS;i++)
+	{
+		struct ess_state *s=&card->channels[i];
+
+		s->index = i;
+
+		s->card = card;
+		init_waitqueue_head(&s->dma_adc.wait);
+		init_waitqueue_head(&s->dma_dac.wait);
+		init_waitqueue_head(&s->open_wait);
+		spin_lock_init(&s->lock);
+		SILLY_INIT_SEM(s->open_sem);
+		s->magic = ESS_STATE_MAGIC;
+		
+		s->apu[0] = 6*i;
+		s->apu[1] = (6*i)+1;
+		s->apu[2] = (6*i)+2;
+		s->apu[3] = (6*i)+3;
+		s->apu[4] = (6*i)+4;
+		s->apu[5] = (6*i)+5;
+		
+		if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
+			printk("maestro: BOTCH!\n");
+		/* register devices */
+		if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0)
+			break;
+	}
+	
+	num = i;
+	
+	/* clear the rest if we ran out of slots to register */
+	for(;i<NR_DSPS;i++)
+	{
+		struct ess_state *s=&card->channels[i];
+		s->dev_audio = -1;
+	}
+	
+	ess = &card->channels[0];
+
+	/*
+	 *	Ok card ready. Begin setup proper
+	 */
+
+	printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", 
+		card_names[card_type],iobase,card->irq);
+	pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n);
+	printk(KERN_INFO "maestro:  subvendor id: 0x%08x\n",n); 
+
+	maestro_config(card);
+
+	if(maestro_ac97_get(iobase, 0x00)==0x0080) {
+		printk(KERN_ERR "maestro: my goodness!  you seem to have a pt101 codec, which is quite rare.\n"
+				"\tyou should tell someone about this.\n");
+	} else {
+		maestro_ac97_init(card,iobase);
+	}
+
+	if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) {
+		printk("maestro: couldn't register mixer!\n");
+	} else {
+		memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state));
+		mixer_push_state(card);
+	}
+	
+	if(request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card))
+	{
+		printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq);
+		unregister_sound_mixer(card->dev_mixer);
+		for(i=0;i<NR_DSPS;i++)
+		{
+			struct ess_state *s = &card->channels[i];
+			if(s->dev_audio != -1)
+				unregister_sound_dsp(s->dev_audio);
+		}
+		release_region(card->iobase, 256);		
+		kfree(card);
+		return 0;
+	}
+
+	printk(KERN_INFO "maestro: %d channels configured.\n", num);
+	return 1; 
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int SILLY_MAKE_INIT(init_maestro(void))
+#endif
+{
+	struct pci_dev *pcidev = NULL;
+	int foundone = 0;
+
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO "maestro: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n");
+
+	pcidev = NULL;
+
+	if (dsps_order < 0)   {
+		dsps_order = 1;
+		printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order);
+	}
+	else if (dsps_order > MAX_DSP_ORDER)  {
+		dsps_order = MAX_DSP_ORDER;
+		printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order);
+	}
+
+#ifdef CONFIG_APM
+	init_waitqueue_head(&suspend_queue);
+#endif
+
+	/*
+	 *	Find the ESS Maestro 2.
+	 */
+
+	while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, pcidev))!=NULL ) {
+		if (maestro_install(pcidev, TYPE_MAESTRO2))
+			foundone=1;
+	}
+
+	/*
+	 *	Find the ESS Maestro 2E
+	 */
+
+	while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, pcidev))!=NULL) {
+		if (maestro_install(pcidev, TYPE_MAESTRO2E))
+			foundone=1;
+	}
+
+	/*
+	 *	ESS Maestro 1
+	 */
+
+	while((pcidev = pci_find_device(PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, pcidev))!=NULL) {
+		if (maestro_install(pcidev, TYPE_MAESTRO))
+			foundone=1;
+	}
+	if( ! foundone ) {
+		printk("maestro: no devices found.\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+MODULE_AUTHOR("Zach Brown <zab@redhat.com>, Alan Cox <alan@redhat.com>");
+MODULE_DESCRIPTION("ESS Maestro Driver");
+#ifdef M_DEBUG
+MODULE_PARM(debug,"i");
+#endif
+MODULE_PARM(dsps_order,"i");
+
+void cleanup_module(void)
+{
+	struct ess_card *s;
+
+#ifdef CONFIG_APM
+	apm_unregister_callback(maestro_apm_callback);
+#endif
+	while ((s = devs)) {
+		int i;
+		devs = devs->next;
+	
+		/* XXX maybe should force stop bob, but should be all 
+			stopped by _release by now */
+		free_irq(s->irq, s);
+		unregister_sound_mixer(s->dev_mixer);
+		for(i=0;i<NR_DSPS;i++)
+		{
+			struct ess_state *ess = &s->channels[i];
+			if(ess->dev_audio != -1)
+				unregister_sound_dsp(ess->dev_audio);
+		}
+		release_region(s->iobase, 256);
+		kfree(s);
+	}
+	M_printk("maestro: unloading\n");
+}
+
+#endif /* MODULE */
+#ifdef CONFIG_APM
+
+void
+check_suspend(void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	if(!in_suspend) return;
+
+	in_suspend++;
+	add_wait_queue(&suspend_queue, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule();
+	remove_wait_queue(&suspend_queue, &wait);
+	current->state = TASK_RUNNING;
+}
+
+static int 
+maestro_suspend(void)
+{
+	struct ess_card *card;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	for (card = devs; card ; card = card->next) {
+		int i,j;
+
+		M_printk("maestro: apm in dev %p\n",card);
+
+		for(i=0;i<NR_DSPS;i++) {
+			struct ess_state *s = &card->channels[i];
+
+			if(s->dev_audio == -1)
+				continue;
+
+			M_printk("maestro: stopping apus for device %d\n",i);
+			stop_dac(s);
+			stop_adc(s);
+			for(j=0;j<6;j++) 
+				card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5);
+
+		}
+
+		/* get rid of interrupts? */
+		if( card->dsps_open > 0)
+			stop_bob(&card->channels[0]);
+	}
+	in_suspend=1;
+
+	restore_flags(flags);
+
+	/* we'll let the bios do the rest of the power down.. */
+
+	return 0;
+}
+static int 
+maestro_resume(void)
+{
+	struct ess_card *card;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	in_suspend=0;
+	M_printk("maestro: resuming\n");
+
+	/* first lets just bring everything back. .*/
+	for (card = devs; card ; card = card->next) {
+		int i;
+
+		M_printk("maestro: apm in dev %p\n",card);
+
+		maestro_config(card);
+		/* need to restore the base pointers.. */ 
+		if(card->dmapages) 
+			set_base_registers(&card->channels[0],card->dmapages);
+
+		mixer_push_state(card);
+
+		for(i=0;i<NR_DSPS;i++) {
+			struct ess_state *s = &card->channels[i];
+			int chan,reg;
+
+			if(s->dev_audio == -1)
+				continue;
+
+			for(chan = 0 ; chan < 6 ; chan++) {
+				wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]);
+				for(reg = 1 ; reg < NR_APU_REGS ; reg++)  
+					apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]);
+			}
+			for(chan = 0 ; chan < 6 ; chan++)  
+				apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F);
+		}
+	}
+
+	/* now we flip on the music */
+	for (card = devs; card ; card = card->next) {
+		int i;
+
+		M_printk("maestro: apm in dev %p\n",card);
+
+		for(i=0;i<NR_DSPS;i++) {
+			struct ess_state *s = &card->channels[i];
+
+			/* these use the apu_mode, and can handle
+				spurious calls */
+			start_dac(s);	
+			start_adc(s);	
+		}
+		if( card->dsps_open > 0)
+			start_bob(&card->channels[0]);
+	}
+
+	restore_flags(flags);
+
+	wake_up(&suspend_queue);
+
+	return 0;
+}
+
+int 
+maestro_apm_callback(apm_event_t ae) {
+
+	M_printk("maestro: apm event received: 0x%x\n",ae);
+
+	switch(ae) {
+	case APM_SYS_SUSPEND: 
+	case APM_CRITICAL_SUSPEND: 
+	case APM_USER_SUSPEND: 
+		maestro_suspend();break;
+	case APM_NORMAL_RESUME: 
+	case APM_CRITICAL_RESUME: 
+	case APM_STANDBY_RESUME: 
+		maestro_resume();break;
+	default: break;
+	}
+
+	return 0;
+}
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/maestro.h linux.14p6/drivers/sound/maestro.h
--- linux.vanilla/drivers/sound/maestro.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/drivers/sound/maestro.h	Fri Nov 12 16:40:50 1999
@@ -0,0 +1,60 @@
+/*
+ *	Registers for the ESS PCI cards
+ */
+ 
+/*
+ *	Memory access
+ */
+ 
+#define ESS_MEM_DATA		0x00
+#define	ESS_MEM_INDEX		0x02
+
+/*
+ *	AC-97 Codec port. Delay 1uS after each write. This is used to
+ *	talk AC-97 (see intel.com). Write data then register.
+ */
+ 
+#define ESS_AC97_INDEX		0x30		/* byte wide */
+#define ESS_AC97_DATA		0x32
+
+/* 
+ *	Reading is a bit different. You write register|0x80 to ubdex
+ *	delay 1uS poll the low bit of index, when it clears read the
+ *	data value.
+ */
+
+/*
+ *	Control port. Not yet fully understood
+ *	The value 0xC090 gets loaded to it then 0x0000 and 0x2800
+ *	to the data port. Then after 4uS the value 0x300 is written
+ */
+ 
+#define RING_BUS_CTRL_L		0x34
+#define RING_BUS_CTRL_H		0x36
+
+/*
+ *	This is also used during setup. The value 0x17 is written to it
+ */
+ 
+#define ESS_SETUP_18		0x18
+
+/*
+ *	And this one gets 0x000b
+ */
+ 
+#define ESS_SETUP_A2		0xA2
+
+/*
+ *	And this 0x0000
+ */
+ 
+#define ESS_SETUP_A4		0xA4
+#define ESS_SETUP_A6		0xA6
+
+/*
+ *	Stuff to do with Harpo - the wave stuff
+ */
+ 
+#define ESS_WAVETABLE_SIZE	0x14
+#define 	ESS_WAVETABLE_2M	0xA180
+
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/msnd_pinnacle.c linux.14p6/drivers/sound/msnd_pinnacle.c
--- linux.vanilla/drivers/sound/msnd_pinnacle.c	Wed Oct 20 01:12:48 1999
+++ linux.14p6/drivers/sound/msnd_pinnacle.c	Fri Oct 22 21:25:36 1999
@@ -46,6 +46,7 @@
 #  include <linux/init.h>
 #endif
 #include <asm/irq.h>
+#include <asm/io.h>
 #include "sound_config.h"
 #include "sound_firmware.h"
 #ifdef MSND_CLASSIC
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/nm256_audio.c linux.14p6/drivers/sound/nm256_audio.c
--- linux.vanilla/drivers/sound/nm256_audio.c	Wed Oct 20 01:12:48 1999
+++ linux.14p6/drivers/sound/nm256_audio.c	Fri Oct 22 21:25:36 1999
@@ -14,6 +14,8 @@
 #include <linux/config.h>
 #include <linux/pci.h>
 #include <linux/module.h>
+#include <linux/delay.h>
+
 #include "sound_config.h"
 #include "soundmodule.h"
 #include "nm256.h"
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/opl3.c linux.14p6/drivers/sound/opl3.c
--- linux.vanilla/drivers/sound/opl3.c	Wed Aug 19 22:48:30 1998
+++ linux.14p6/drivers/sound/opl3.c	Fri Oct 22 22:52:53 1999
@@ -32,8 +32,6 @@
 #include "sound_config.h"
 #include "soundmodule.h"
 
-#ifdef CONFIG_YM3812
-
 #include "opl3.h"
 
 #define MAX_VOICE	18
@@ -1223,5 +1221,3 @@
 EXPORT_SYMBOL(opl3_init);
 EXPORT_SYMBOL(opl3_detect);
 MODULE_PARM(io, "i");
-
-#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/sb_ess.c linux.14p6/drivers/sound/sb_ess.c
--- linux.vanilla/drivers/sound/sb_ess.c	Sat Aug 28 20:00:58 1999
+++ linux.14p6/drivers/sound/sb_ess.c	Fri Oct 29 23:29:02 1999
@@ -10,29 +10,29 @@
  *
  * History:
  *
- * Rolf Fokkens	(Dec 20 1998):	ES188x recording level support on a per
+ * Rolf Fokkens	 (Dec 20 1998):	ES188x recording level support on a per
  * fokkensr@vertis.nl			input basis.
- *				(Dec 24 1998):	Recognition of ES1788, ES1887, ES1888,
+ *				 (Dec 24 1998):	Recognition of ES1788, ES1887, ES1888,
  *								ES1868, ES1869 and ES1878. Could be used for
  *								specific handling in the future. All except
  *								ES1887 and ES1888 and ES688 are handled like
  *								ES1688.
- *				(Dec 27 1998):	RECLEV for all (?) ES1688+ chips. ES188x now
+ *				 (Dec 27 1998):	RECLEV for all (?) ES1688+ chips. ES188x now
  *								have the "Dec 20" support + RECLEV
- *				(Jan  2 1999):	Preparation for Full Duplex. This means
+ *				 (Jan  2 1999):	Preparation for Full Duplex. This means
  *								Audio 2 is now used for playback when dma16
  *								is specified. The next step would be to use
  *								Audio 1 and Audio 2 at the same time.
- *				(Jan  9 1999):	Put all ESS stuff into sb_ess.[ch], this
+ *				 (Jan  9 1999):	Put all ESS stuff into sb_ess.[ch], this
  *								includes both the ESS stuff that has been in
  *								sb_*[ch] before I touched it and the ESS support
  *								I added later
- *				(Jan 23 1999):	Full Duplex seems to work. I wrote a small
+ *				 (Jan 23 1999):	Full Duplex seems to work. I wrote a small
  *								test proggy which works OK. Haven't found
  *								any applications to test it though. So why did
  *								I bother to create it anyway?? :) Just for
  *								fun.
- *				(May  2 1999):	I tried to be too smart by "introducing"
+ *				 (May  2 1999):	I tried to be too smart by "introducing"
  *								ess_calc_best_speed (). The idea was that two
  *								dividers could be used to setup a samplerate,
  *								ess_calc_best_speed () would choose the best.
@@ -40,10 +40,12 @@
  *								recording problems for high samplerates. I
  *								fixed this by removing ess_calc_best_speed ()
  *								and just doing what the documentation says. 
- * Andy Sloane  (June 4 1999):  Stole some code from ALSA to fix the playback
+ * Andy Sloane   (Jun  4 1999): Stole some code from ALSA to fix the playback
  * andy@guildsoftware.com		speed on ES1869, ES1879, ES1887, and ES1888.
  * 								1879's were previously ignored by this driver;
  * 								added (untested) support for those.
+ * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for
+ * zezo@inet.bg					_ALL_ ESS models, not only ES1887
  *
  * 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
@@ -52,7 +54,7 @@
  * - RECLEV support for ES1688 and later
  * - 6 bits playback level support chips later than ES1688
  * - Recording level support on a per-device basis for ES1887
- * - Full-Duplex for ES1887 (under development)
+ * - Full-Duplex for ES1887
  *
  * Full duplex is enabled by specifying dma16. While the normal dma must
  * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit
@@ -1305,6 +1307,13 @@
 int ess_dsp_init (sb_devc *devc, struct address_info *hw_config)
 {
 	/*
+	 * Caller also checks this, but anyway
+	 */
+	if (devc->model != MDL_ESS) {
+		printk (KERN_INFO "ess_dsp_init for non ESS chip\n");
+		return 1;
+	}
+	/*
 	 * 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.
@@ -1324,15 +1333,12 @@
 		if (devc->dma8 != devc->dma16 && devc->dma16 != -1) {
 			devc->duplex = 1;
 		}
-
-		if (!ess_set_dma_hw (devc)) {
-			free_irq(devc->irq, devc);
-			return 0;
-		}
-		return 1;
-	} else {
-		return -1;
 	}
+	if (!ess_set_dma_hw (devc)) {
+		free_irq(devc->irq, devc);
+		return 0;
+	}
+	return 1;
 }
 
 /****************************************************************************
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/sonicvibes.c linux.14p6/drivers/sound/sonicvibes.c
--- linux.vanilla/drivers/sound/sonicvibes.c	Wed Oct 20 01:12:48 1999
+++ linux.14p6/drivers/sound/sonicvibes.c	Fri Oct 22 22:34:17 1999
@@ -71,6 +71,14 @@
  *    15.06.99   0.15  Fix bad allocation bug.
  *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
  *    28.06.99   0.16  Add pci_set_master
+ *    03.08.99   0.17  adapt to Linus' new __setup/__initcall
+ *                     added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr"
+ *    12.08.99   0.18  module_init/__setup fixes
+ *    24.08.99   0.19  get rid of the dmaio kludge, replace with allocate_resource
+ *    31.08.99   0.20  add spin_lock_init
+ *                     __initlocaldata to fix gcc 2.7.x problems
+ *    03.09.99   0.21  change read semantics for MIDI to match
+ *                     OSS more closely; remove possible wakeup race
  *
  */
 
@@ -1265,9 +1273,9 @@
                         current->state = TASK_RUNNING;
                         return -EBUSY;
                 }
-		tmo = (count * HZ) / s->ratedac;
+		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac;
 		tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK];
-		if (!schedule_timeout(tmo ? : 1) && tmo)
+		if (!schedule_timeout(tmo + 1))
 			printk(KERN_DEBUG "sv: dma timed out??\n");
         }
         remove_wait_queue(&s->dma_dac.wait, &wait);
@@ -1870,6 +1878,7 @@
 static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct sv_state *s = (struct sv_state *)file->private_data;
+	struct wait_queue wait = { current, NULL };
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -1880,7 +1889,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+	add_wait_queue(&s->midi.iwait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.ird;
@@ -1891,15 +1903,25 @@
 		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;
+                      if (file->f_flags & O_NONBLOCK) {
+                              if (!ret)
+                                      ret = -EAGAIN;
+                              break;
+                      }
+                      current->state = TASK_INTERRUPTIBLE;
+                      schedule();
+                      if (signal_pending(current)) {
+                              if (!ret)
+                                      ret = -ERESTARTSYS;
+                              break;
+                      }
 			continue;
 		}
-		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIINBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.ird = ptr;
@@ -1908,13 +1930,17 @@
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
+		break;
 	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&s->midi.iwait, &wait);
 	return ret;
 }
 
 static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct sv_state *s = (struct sv_state *)file->private_data;
+	struct wait_queue wait = { current, NULL };
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -1925,7 +1951,10 @@
 		return -ESPIPE;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+	if (count == 0)
+		return 0;
 	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		ptr = s->midi.owr;
@@ -1938,15 +1967,25 @@
 		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;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		ptr = (ptr + cnt) % MIDIOUTBUF;
 		spin_lock_irqsave(&s->lock, flags);
 		s->midi.owr = ptr;
@@ -1959,6 +1998,8 @@
 		sv_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&s->midi.owait, &wait);
 	return ret;
 }
 
@@ -2324,7 +2365,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.16 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.21 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
@@ -2353,6 +2394,7 @@
 		init_waitqueue(&s->midi.iwait);
 		init_waitqueue(&s->midi.owait);
 		s->open_sem = MUTEX;
+		spin_lock_init(&s->lock);
 		s->magic = SV_MAGIC;
 		s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
 		s->ioenh = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
@@ -2423,8 +2465,8 @@
 		wrindir(s, SV_CIDRIVECONTROL, 0);  /* drive current 16mA */
 		wrindir(s, SV_CIENABLE, s->enable = 0);  /* disable DMAA and DMAC */
 		outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK);
-		//outb(0xff, s->iodmaa + SV_DMA_RESET);
-		//outb(0xff, s->iodmac + SV_DMA_RESET);
+		/* outb(0xff, s->iodmaa + SV_DMA_RESET); */
+		/* outb(0xff, s->iodmac + SV_DMA_RESET); */
 		inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
 		wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */
 		wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */
@@ -2524,8 +2566,8 @@
 		synchronize_irq();
 		inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
 		wrindir(s, SV_CIENABLE, 0);     /* disable DMAA and DMAC */
-		//outb(0, s->iodmaa + SV_DMA_RESET);
-		//outb(0, s->iodmac + SV_DMA_RESET);
+		/*outb(0, s->iodmaa + SV_DMA_RESET);*/
+		/*outb(0, s->iodmac + SV_DMA_RESET);*/
 		free_irq(s->irq, s);
 		release_region(s->iodmac, SV_EXTENT_DMA);
 		release_region(s->iodmaa, SV_EXTENT_DMA);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/sound_core.c linux.14p6/drivers/sound/sound_core.c
--- linux.vanilla/drivers/sound/sound_core.c	Sat Aug 28 20:00:58 1999
+++ linux.14p6/drivers/sound/sound_core.c	Fri Oct 29 23:25:13 1999
@@ -55,6 +55,9 @@
 #ifdef CONFIG_SOUND_SONICVIBES
 extern int init_sonicvibes(void);
 #endif
+#ifdef CONFIG_SOUND_MAESTRO
+extern int init_maestro(void);
+#endif
 #ifdef CONFIG_SOUND_ES1370
 extern int init_es1370(void);
 #endif
@@ -409,6 +412,9 @@
 #endif
 #ifdef CONFIG_SOUND_ES1371
 	init_es1371();
+#endif
+#ifdef CONFIG_SOUND_MAESTRO
+	init_maestro();
 #endif
 #ifdef CONFIG_SOUND_MSNDCLAS
 	msnd_classic_init();
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/atyfb.c linux.14p6/drivers/video/atyfb.c
--- linux.vanilla/drivers/video/atyfb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/atyfb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/*  $Id: atyfb.c,v 1.106.2.4 1999/09/02 06:34:46 paulus Exp $
+/*  $Id: atyfb.c,v 1.106.2.6 1999/10/14 08:44:47 davem Exp $
  *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
  *
  *	Copyright (C) 1997-1998  Geert Uytterhoeven
@@ -227,8 +227,8 @@
     } fbcon_cmap;
     u8 blitter_may_be_busy;
 #ifdef __sparc__
-    u8 open;
     u8 mmaped;
+    int open;
     int vtconsole;
     int consolecnt;
 #endif
@@ -1896,10 +1896,8 @@
     struct fb_info_aty *fb = (struct fb_info_aty *)info;
 
     if (user) {
-	if (fb->open)
-	    return -EBUSY;
+	fb->open++;
 	fb->mmaped = 0;
-	fb->open = 1;
 	fb->vtconsole = -1;
     } else {
 	fb->consolecnt++;
@@ -1909,17 +1907,54 @@
     return(0);
 }
 
+struct fb_var_screeninfo default_var = {
+    /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
+    640, 480, 640, 480, 0, 0, 8, 0,
+    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+    0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
+    0, FB_VMODE_NONINTERLACED
+};
+
 static int atyfb_release(struct fb_info *info, int user)
 {
 #ifdef __sparc__
     struct fb_info_aty *fb = (struct fb_info_aty *)info;
 
     if (user) {
-	if (fb->vtconsole != -1)
-	    vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
-	fb->open = 0;
-	fb->mmaped = 0;
-	fb->vtconsole = -1;
+	fb->open--;
+	udelay(1000);
+	wait_for_idle(fb);
+	if (!fb->open) {
+		int was_mmaped = fb->mmaped;
+
+		fb->mmaped = 0;
+		if (fb->vtconsole != -1)
+			vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
+		fb->vtconsole = -1;
+
+		if (was_mmaped) {
+			struct fb_var_screeninfo var;
+
+			/* Now reset the default display config, we have no
+			 * idea what the program(s) which mmap'd the chip did
+			 * to the configuration, nor whether it restored it
+			 * correctly.
+			 */
+			var = default_var;
+			if (noaccel)
+				var.accel_flags &= ~FB_ACCELF_TEXT;
+			else
+				var.accel_flags |= FB_ACCELF_TEXT;
+			if (var.yres == var.yres_virtual) {
+				u32 vram = (fb->total_vram - (PAGE_SIZE << 2));
+				var.yres_virtual = ((vram * 8) / var.bits_per_pixel) /
+					var.xres_virtual;
+				if (var.yres_virtual < var.yres)
+					var.yres_virtual = var.yres;
+			}
+			atyfb_set_var(&var, -1, &fb->fb_info);
+		}
+	}
     } else {
 	fb->consolecnt--;
     }
@@ -1980,15 +2015,6 @@
 
     return 0;
 }
-
-
-struct fb_var_screeninfo default_var = {
-    /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
-    640, 480, 640, 480, 0, 0, 8, 0,
-    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-    0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
-    0, FB_VMODE_NONINTERLACED
-};
 
 
     /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/bwtwofb.c linux.14p6/drivers/video/bwtwofb.c
--- linux.vanilla/drivers/video/bwtwofb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/bwtwofb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: bwtwofb.c,v 1.7.2.1 1999/08/26 05:21:13 shadow Exp $
+/* $Id: bwtwofb.c,v 1.7.2.3 1999/10/06 10:52:36 anton Exp $
  * bwtwofb.c: BWtwo frame buffer driver
  *
  * Copyright (C) 1998 Jakub Jelinek   (jj@ultra.linux.cz)
@@ -224,7 +224,9 @@
 #endif
 	fb->margins = bw2_margins;
 	
+#ifndef CONFIG_SUN4
 	fb->physbase = __get_phys(fb->sbdp->sbus_vaddrs[0]);
+#endif
 	fb->mmap_map = bw2_mmap_map;
 
 #ifdef __sparc_v9__
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/cgfourteenfb.c linux.14p6/drivers/video/cgfourteenfb.c
--- linux.vanilla/drivers/video/cgfourteenfb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/cgfourteenfb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: cgfourteenfb.c,v 1.4 1999/01/26 10:55:03 jj Exp $
+/* $Id: cgfourteenfb.c,v 1.4.2.1 1999/09/28 15:59:58 davem Exp $
  * cgfourteenfb.c: CGfourteen frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/cgsixfb.c linux.14p6/drivers/video/cgsixfb.c
--- linux.vanilla/drivers/video/cgsixfb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/cgsixfb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: cgsixfb.c,v 1.16.2.1 1999/05/25 00:59:35 davem Exp $
+/* $Id: cgsixfb.c,v 1.16.2.2 1999/09/28 15:59:55 davem Exp $
  * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/cgthreefb.c linux.14p6/drivers/video/cgthreefb.c
--- linux.vanilla/drivers/video/cgthreefb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/cgthreefb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: cgthreefb.c,v 1.4 1999/01/26 10:55:01 jj Exp $
+/* $Id: cgthreefb.c,v 1.4.2.1 1999/09/28 16:00:08 davem Exp $
  * cgthreefb.c: CGthree frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/creatorfb.c linux.14p6/drivers/video/creatorfb.c
--- linux.vanilla/drivers/video/creatorfb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/creatorfb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: creatorfb.c,v 1.27 1999/03/28 12:37:12 jj Exp $
+/* $Id: creatorfb.c,v 1.27.2.2 1999/11/05 09:07:56 davem Exp $
  * creatorfb.c: Creator/Creator3D frame buffer driver
  *
  * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
@@ -539,6 +539,36 @@
 	/* Not used if hw cursor */
 }
 
+#if 0
+static void ffb_blank(struct fb_info_sbusfb *fb)
+{
+	struct ffb_dac *dac = fb->s.ffb.dac;
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&fb->lock, flags);
+	dac->type = 0x6000;
+	tmp = (dac->value & ~0x1);
+	dac->type = 0x6000;
+	dac->value = tmp;
+	spin_unlock_irqrestore(&fb->lock, flags);
+}
+#endif
+
+static void ffb_unblank(struct fb_info_sbusfb *fb)
+{
+	struct ffb_dac *dac = fb->s.ffb.dac;
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&fb->lock, flags);
+	dac->type = 0x6000;
+	tmp = (dac->value | 0x1);
+	dac->type = 0x6000;
+	dac->value = tmp;
+	spin_unlock_irqrestore(&fb->lock, flags);
+}
+
 static void ffb_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
 {
 	struct ffb_dac *dac = fb->s.ffb.dac;
@@ -726,6 +756,16 @@
 	fb->setcurshape = ffb_setcurshape;
 	fb->switch_from_graph = ffb_switch_from_graph;
 	fb->fill = ffb_fill;
+#if 0
+	/* XXX Can't enable this for now, I've seen cases
+	 * XXX where the VC was blanked, and Xsun24 was started
+	 * XXX via a remote login, the sunfb code did not
+	 * XXX unblank creator when it was mmap'd for some
+	 * XXX reason, investigate later... -DaveM
+	 */
+	fb->blank = ffb_blank;
+	fb->unblank = ffb_unblank;
+#endif
 	
 	/* If there are any read errors or fifo overflow conditions,
 	 * clear them now.
@@ -755,5 +795,12 @@
 	if (afb)
 		fb->s.ffb.dac_rev = 10;
 	
+	/* Unblank it just to be sure.  When there are multiple
+	 * FFB/AFB cards in the system, or it is not the OBP
+	 * chosen console, it will have video outputs off in
+	 * the DAC.
+	 */
+	ffb_unblank(fb);
+
 	return idstring;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/fbmem.c linux.14p6/drivers/video/fbmem.c
--- linux.vanilla/drivers/video/fbmem.c	Sat Aug 14 02:27:34 1999
+++ linux.14p6/drivers/video/fbmem.c	Tue Nov 16 15:13:28 1999
@@ -217,7 +217,7 @@
 static int PROC_CONSOLE(struct fb_info *info)
 {
 	int fgc;
-	
+
 	if (info->display_fg != NULL)
 		fgc = info->display_fg->vc_num;
 	else
@@ -353,6 +353,20 @@
        /* tell console var has changed */
        if (newfb->changevar)
 	   newfb->changevar(unit);
+       /* fixme: this really needs lock protection */
+       conp->vc_display_fg = &newfb->display_fg;
+       if (!newfb->display_fg)
+	   newfb->display_fg = conp;
+       if (oldfb != newfb && oldfb->display_fg == conp) {
+	   int i;
+	   for (i = 0; i < MAX_NR_CONSOLES; i++)
+	       if (fb_display[i].conp && con2fb_map[i] == oldidx) {
+		   oldfb->display_fg = fb_display[i].conp;
+		   break;
+	       }
+	   if (i == MAX_NR_CONSOLES)
+	       oldfb->display_fg = NULL;
+       }
     }
 }
 
@@ -447,6 +461,11 @@
 		    for (i = 0; i < MAX_NR_CONSOLES; i++)
 			set_con2fb_map(i, con2fb.framebuffer);
 		return 0;
+	case FBIOBLANK:
+		if (info->blank == 0)
+			return -EINVAL;
+		(*info->blank)(arg, info);
+		return 0;
 	default:
 		return fb->fb_ioctl(inode, file, cmd, arg, PROC_CONSOLE(info),
 				    info);
@@ -468,12 +487,16 @@
 		return -ENODEV;
 	if (fb->fb_mmap)
 		return fb->fb_mmap(info, file, vma);
+#if defined(__sparc__) && !defined(__sparc_v9__)
+	/* Should never get here, all fb drivers should have their own
+	   mmap routines */
+	return -EINVAL;
+#else
 	fb->fb_get_fix(&fix, PROC_CONSOLE(info), info);
 
 	/* frame buffer memory */
 	start = (unsigned long)fix.smem_start;
 	len = (start & ~PAGE_MASK)+fix.smem_len;
-	start &= PAGE_MASK;
 	len = (len+~PAGE_MASK) & PAGE_MASK;
 	if (vma->vm_offset >= len) {
 		/* memory mapped io */
@@ -483,14 +506,37 @@
 			return -EINVAL;
 		start = (unsigned long)fix.mmio_start;
 		len = (start & ~PAGE_MASK)+fix.mmio_len;
-		start &= PAGE_MASK;
 		len = (len+~PAGE_MASK) & PAGE_MASK;
 	}
+	start &= PAGE_MASK;
 	if ((vma->vm_end - vma->vm_start + vma->vm_offset) > len)
 		return -EINVAL;
 	vma->vm_offset += start;
 	if (vma->vm_offset & ~PAGE_MASK)
 		return -ENXIO;
+#if defined(__sparc_v9__)
+	vma->vm_flags |= (VM_SHM | VM_LOCKED);
+	{
+		unsigned long align, j;
+		for (align = 0x400000; align > PAGE_SIZE; align >>= 3)
+			if (len >= align && !((start & ~PAGE_MASK) & (align - 1)))
+				break;
+		if (align > PAGE_SIZE && vma->vm_start & (align - 1)) {
+			/* align as much as possible */
+			struct vm_area_struct *vmm;
+			j = (-vma->vm_start) & (align - 1);
+			vmm = find_vma(current->mm, vma->vm_start);
+			if (!vmm || vmm->vm_start >= vma->vm_end + j) {
+				vma->vm_start += j;
+				vma->vm_end += j;
+			}
+		}
+	}
+	if (io_remap_page_range(vma->vm_start, vma->vm_offset,
+				vma->vm_end - vma->vm_start, vma->vm_page_prot, 0))
+		return -EAGAIN;
+	vma->vm_flags |= VM_IO;
+#else
 #if defined(__mc68000__)
 	if (CPU_IS_020_OR_030)
 		pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030;
@@ -503,9 +549,6 @@
 	pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
 #elif defined(__alpha__)
 	/* Caching is off in the I/O space quadrant by design.  */
-#elif defined(__sparc__)
-	/* Should never get here, all fb drivers should have their own
-	   mmap routines */
 #elif defined(__i386__)
 	if (boot_cpu_data.x86 > 3)
 		pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
@@ -525,7 +568,9 @@
 	if (remap_page_range(vma->vm_start, vma->vm_offset,
 			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
 		return -EAGAIN;
+#endif /* !__sparc_v9__ */
 	return 0;
+#endif /* ! sparc32 */
 }
 
 static int
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/leofb.c linux.14p6/drivers/video/leofb.c
--- linux.vanilla/drivers/video/leofb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/leofb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: leofb.c,v 1.6 1999/04/01 13:03:25 jj Exp $
+/* $Id: leofb.c,v 1.6.2.1 1999/09/28 16:00:03 davem Exp $
  * leofb.c: Leo (ZX) 24/8bit frame buffer driver
  *
  * Copyright (C) 1996-1999 Jakub Jelinek (jj@ultra.linux.cz)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/matroxfb.c linux.14p6/drivers/video/matroxfb.c
--- linux.vanilla/drivers/video/matroxfb.c	Sat Aug 14 02:26:17 1999
+++ linux.14p6/drivers/video/matroxfb.c	Wed Oct 27 18:41:36 1999
@@ -1,10 +1,10 @@
 /*
  *
- * Hardware accelerated Matrox Millennium I, II, Mystique and G200
+ * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
  *
  * (c) 1998,1999 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
- * Version: 1.15 1999/04/19
+ * Version: 1.19a 1999/08/12 (2.2.x branch)
  *
  * MTRR stuff: 1998 Tom Rini <tmrini@ntplx.net>
  *
@@ -14,8 +14,8 @@
  *               "Kurt Garloff" <garloff@kg1.ping.de>
  *                     Betatesting, fixes, ideas, videomodes, videomodes timmings
  *
- *               "Tom Rini" <tmrini@ntplx.net>
- *                     MTRR stuff, betatesting, fixes, ideas
+ *               "Tom Rini" <trini@disparity.net>
+ *                     MTRR stuff, PPC cleanups, betatesting, fixes, ideas
  *
  *               "Bibek Sahu" <scorpio@dodds.net>
  *                     Access device through readb|w|l and write b|w|l
@@ -60,6 +60,9 @@
  *               "Cort Dougan" <cort@cs.nmt.edu>
  *                     CHRP fixes and PReP cleanup
  *
+ *               "Mark Vojkovich" <mvojkovi@ucsd.edu>
+ *                     G400 support
+ *
  * (following author is not in any relation with this code, but his code
  *  is included in this driver)
  *
@@ -87,6 +90,11 @@
 /* Debug register calls, too? */
 #undef MATROXFB_DEBUG_REG
 
+/* Log reentrancy attempts - you must have printstate() patch applied */
+#undef MATROXFB_DEBUG_REENTER
+/* you must define DEBUG_REENTER to get debugged CONSOLEBH... */
+#undef MATROXFB_DEBUG_CONSOLEBH
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -127,12 +135,15 @@
 #include <video/macmodes.h>
 #endif
 
+/* always compile support for 32MB... It cost almost nothing */
+#define CONFIG_FB_MATROX_32MB
+
 #define FBCON_HAS_VGATEXT
 
 #ifdef MATROXFB_DEBUG
 
 #define DEBUG
-#define DBG(x)		printk("matroxfb: %s\n", (x));
+#define DBG(x)		printk(KERN_DEBUG "matroxfb: %s\n", (x));
 
 #ifdef MATROXFB_DEBUG_HEAVY
 #define DBG_HEAVY(x)	DBG(x)
@@ -217,6 +228,9 @@
 #ifndef PCI_DEVICE_ID_MATROX_G100_AGP
 #define PCI_DEVICE_ID_MATROX_G100_AGP	0x1001
 #endif
+#ifndef PCI_DEVICE_ID_MATROX_G400_AGP
+#define PCI_DEVICE_ID_MATROX_G400_AGP	0x0525
+#endif
 
 #ifndef PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP
 #define PCI_SS_ID_MATROX_GENERIC		0xFF00
@@ -422,7 +436,7 @@
 	unsigned char	MiscOutReg;
 	unsigned char	DACpal[768];
 	unsigned char	CRTC[25];
-	unsigned char	CRTCEXT[6];
+	unsigned char	CRTCEXT[9];
 	unsigned char	SEQ[5];
 	/* unused for MGA mode, but who knows... */
 	unsigned char	GCTL[9];
@@ -453,7 +467,8 @@
 }
 
 #define PMXINFO(p) mxinfo(p),
-#define MINFO_FROM_DISP(x) struct matrox_fb_info* minfo = mxinfo(x)
+#define MINFO_FROM(x)	   struct matrox_fb_info* minfo = x
+#define MINFO_FROM_DISP(x) MINFO_FROM(mxinfo(x))
 
 #else
 
@@ -476,6 +491,7 @@
 #endif
 
 #define PMXINFO(p)
+#define MINFO_FROM(x)
 #define MINFO_FROM_DISP(x)
 
 #endif
@@ -563,6 +579,9 @@
 		int		hwcursor;
 		int		blink;
 		int		sgram;
+#ifdef CONFIG_FB_MATROX_32MB
+		int		support32MB;
+#endif
 
 		int		accelerator;
 		int		text_type_aux;
@@ -609,6 +628,8 @@
 #if defined(CONFIG_FB_OF)
 unsigned char nvram_read_byte(int);
 int matrox_of_init(struct device_node *dp);
+static int default_vmode = VMODE_NVRAM;
+static int default_cmode = CMODE_NVRAM;
 #endif
 
 #define curr_ydstorg(x)	ACCESS_FBINFO2(x, curr.ydstorg.pixels)
@@ -678,6 +699,8 @@
 
 #define M_RESET		0x1E40
 
+#define M_AGP2PLL	0x1E4C
+
 #define M_OPMODE	0x1E54
 #define     M_OPMODE_DMA_GEN_WRITE	0x00
 #define     M_OPMODE_DMA_BLIT		0x04
@@ -713,6 +736,9 @@
 #define M_EXTVGA_INDEX	0x1FDE
 #define M_EXTVGA_DATA	0x1FDF
 
+/* G200 only */
+#define M_SRCORG	0x2CB4
+
 #define M_RAMDAC_BASE	0x3C00
 
 /* fortunately, same on TVP3026 and MGA1064 */
@@ -723,6 +749,9 @@
 #define M_X_INDEX	0x00
 #define M_X_DATAREG	0x0A
 
+#define DAC_XGENIOCTRL		0x2A
+#define DAC_XGENIODATA		0x2B
+
 #ifdef CONFIG_FB_MATROX_MILLENIUM
 #define TVP3026_INDEX		0x00
 #define TVP3026_PALWRADD	0x00
@@ -1054,6 +1083,48 @@
 #define isMilleniumII(x) (0)
 #endif
 
+#ifdef MATROXFB_DEBUG_REENTER
+static atomic_t guard_counter = ATOMIC_INIT(1);
+static atomic_t guard_printing = ATOMIC_INIT(1);
+static void guard_start(void) {
+	if (atomic_dec_and_test(&guard_counter)) {	/* first level */
+		if (!(bh_mask & (1 << CONSOLE_BH)))	/* and CONSOLE_BH disabled */
+			return;				/* is OK */
+		/* otherwise it is first level with CONSOLE_BH enabled -
+		   - if we are __sti or SMP, reentering from console_bh possible */
+		atomic_dec(&guard_printing);	/* disable reentrancy warning */
+		printk(KERN_DEBUG "matroxfb entered without CONSOLE_BH disabled\n");
+#ifdef printstate
+		printstate();
+#endif
+		atomic_inc(&guard_printing);
+		return;
+	}
+	/* real reentering... You should be already warned by code above */
+	if (atomic_dec_and_test(&guard_printing)) {
+#ifdef printstate
+		printstate();
+#endif
+	}
+	atomic_inc(&guard_printing);
+}
+
+static inline void guard_end(void) {
+	atomic_inc(&guard_counter);
+}
+
+#define CRITBEGIN guard_start();
+#define CRITEND   guard_end();
+
+#else
+
+#define CRITBEGIN
+#define CRITEND
+
+#endif
+
+#define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l))
+
 static void matrox_cfbX_init(WPMINFO struct display* p) {
 	u_int32_t maccess;
 	u_int32_t mpitch;
@@ -1101,7 +1172,7 @@
 	mga_outl(M_OPMODE, mopmode);
 	mga_outl(M_CXBNDRY, 0xFFFF0000);
 	mga_outl(M_YTOP, 0);
-	mga_outl(M_YBOT, 0x007FFFFF);
+	mga_outl(M_YBOT, 0x01FFFFFF);
 	mga_outl(M_MACCESS, maccess);
 	ACCESS_FBINFO(accel.m_dwg_rect) = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO;
 	if (isMilleniumII(MINFO)) ACCESS_FBINFO(accel.m_dwg_rect) |= M_DWG_TRANSC;
@@ -1113,7 +1184,9 @@
 	MINFO_FROM_DISP(p);
 
 	DBG("matrox_cfbX_bmove")
-	
+
+	CRITBEGIN
+
 	sx *= fontwidth(p);
 	dx *= fontwidth(p);
 	width *= fontwidth(p);
@@ -1142,8 +1215,10 @@
 	mga_outl(M_AR0, end);
 	mga_outl(M_AR3, start);
 	mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
-	mga_outl(M_YDSTLEN | M_EXEC, ((dy)<<16) | height);
+	mga_ydstlen(dy, height);
 	WaitTillIdle();
+
+	CRITEND
 }
 
 #ifdef FBCON_HAS_CFB4
@@ -1154,7 +1229,9 @@
 	   also odd, that means that we cannot use acceleration */
 	
 	DBG("matrox_cfb4_bmove")
-	
+
+	CRITBEGIN
+
 	if ((sx | dx | width) & fontwidth(p) & 1) {
 		fbcon_cfb4_bmove(p, sy, sx, dy, dx, height, width);
 		return;
@@ -1194,6 +1271,8 @@
 	mga_outl(M_YDST, dy*pixx >> 5);
 	mga_outl(M_LEN | M_EXEC, height);
 	WaitTillIdle();
+
+	CRITEND
 }
 #endif
 
@@ -1201,13 +1280,17 @@
 		int width) {
 	
 	DBG("matroxfb_accel_clear")
-	
-	mga_fifo(4);
+
+	CRITBEGIN
+
+	mga_fifo(5);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_REPLACE);
 	mga_outl(M_FCOL, color);
 	mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
-	mga_outl(M_YDSTLEN | M_EXEC, (sy << 16) | height);
+	mga_ydstlen(sy, height);
 	WaitTillIdle();
+
+	CRITEND
 }
 
 static void matrox_cfbX_clear(u_int32_t color, struct display* p, int sy, int sx, int height, int width) {
@@ -1225,7 +1308,9 @@
 	MINFO_FROM_DISP(p);
 
 	DBG("matrox_cfb4_clear")
-	
+
+	CRITBEGIN
+
 	whattodo = 0; 
 	bgx = attr_bgcol_ec(p, conp);
 	bgx |= bgx << 4;
@@ -1277,6 +1362,8 @@
 			}
 		}
 	}
+
+	CRITEND
 }
 #endif
 
@@ -1323,7 +1410,10 @@
 	charcell = fontwidth(p) * fontheight(p);
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
-	mga_fifo(7);
+
+	CRITBEGIN
+
+	mga_fifo(8);
 	mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
 	
 	mga_outl(M_FCOL, fgx);
@@ -1332,8 +1422,10 @@
 	ar3 = ACCESS_FBINFO(fastfont.mgabase) + (c & p->charmask) * charcell;
 	mga_outl(M_AR3, ar3);
 	mga_outl(M_AR0, (ar3 + charcell - 1) & 0x0003FFFF);
-	mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+	mga_ydstlen(yy, fontheight(p));
 	WaitTillIdle();
+
+	CRITEND
 }
 	
 static void matrox_cfbX_putc(u_int32_t fgx, u_int32_t bgx, struct display* p, int c, int yy, int xx) {
@@ -1345,6 +1437,9 @@
 	
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
+
+	CRITBEGIN
+
 #ifdef __BIG_ENDIAN
 	WaitTillIdle();
 	mga_outl(M_OPMODE, M_OPMODE_8BPP);
@@ -1367,7 +1462,7 @@
 		mga_outl(M_BCOL, bgx);
 		mga_outl(M_AR3, 0);
 		mga_outl(M_AR0, fontheight(p)*fontwidth(p)-1);
-		mga_outl(M_YDSTLEN | M_EXEC, (yy<<16) | fontheight(p));
+		mga_ydstlen(yy, fontheight(p));
 		mga_memcpy_toio(ACCESS_FBINFO(mmio.vbase), 0, p->fontdata+(c&p->charmask)*charcell, charcell);
 	} else {
 		u8* chardata = p->fontdata+(c&p->charmask)*fontheight(p)*step;
@@ -1379,7 +1474,7 @@
 		mga_outl(M_AR5, 0);
 		mga_outl(M_AR3, 0);
 		mga_outl(M_AR0, ar0);
-		mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+		mga_ydstlen(yy, fontheight(p));
 
 		switch (step) {
 		case 1:	
@@ -1410,6 +1505,7 @@
 #ifdef __BIG_ENDIAN
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
+	CRITEND
 }
 
 #ifdef FBCON_HAS_CFB8
@@ -1464,6 +1560,9 @@
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
 	charcell = fontwidth(p) * fontheight(p);
+
+	CRITBEGIN
+
 	mga_fifo(3);
 	mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
 	mga_outl(M_FCOL, fgx);
@@ -1475,10 +1574,12 @@
 		mga_outl(M_FXBNDRY, ((xx + fontwidth(p) - 1) << 16) | xx);
 		mga_outl(M_AR3, ar3);
 		mga_outl(M_AR0, (ar3 + charcell - 1) & 0x0003FFFF);
-		mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+		mga_ydstlen(yy, fontheight(p));
 		xx += fontwidth(p);
 	}
 	WaitTillIdle();
+
+	CRITEND
 }
 
 static void matrox_cfbX_putcs(u_int32_t fgx, u_int32_t bgx, struct display* p, const unsigned short* s, int count, int yy, int xx) {
@@ -1504,7 +1605,7 @@
 		step = 4;
 	charcell = fontheight(p)*step;
 	xlen = (charcell + 3) & ~3;
-	ydstlen = (yy<<16) | fontheight(p);
+	ydstlen = (yy << 16) | fontheight(p);
 	if (fontwidth(p) == step << 3) {
 		ar0 = fontheight(p)*fontwidth(p) - 1;
 		easy = 1;
@@ -1512,6 +1613,9 @@
 		ar0 = fontwidth(p) - 1;
 		easy = 0;
 	}
+
+	CRITBEGIN
+
 #ifdef __BIG_ENDIAN
 	WaitTillIdle();
 	mga_outl(M_OPMODE, M_OPMODE_8BPP);
@@ -1529,7 +1633,7 @@
 	while (count--) {
 		u_int8_t* chardata = p->fontdata + (scr_readw(s++) & p->charmask)*charcell;
 
-		mga_fifo(5);
+		mga_fifo(6);
 		mga_writel(mmio, M_FXBNDRY, fxbndry);
 		mga_writel(mmio, M_AR0, ar0);
 		mga_writel(mmio, M_AR3, 0);
@@ -1573,6 +1677,7 @@
 #ifdef __BIG_ENDIAN
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
+	CRITEND
 }
 
 #ifdef FBCON_HAS_CFB8
@@ -1635,6 +1740,8 @@
 	xx |= (xx + fontwidth(p)) << 16;
 	xx >>= 1;
 
+	CRITBEGIN
+	
 	mga_fifo(5);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
 	mga_outl(M_FCOL, 0xFFFFFFFF);
@@ -1642,6 +1749,8 @@
 	mga_outl(M_YDST, yy * p->var.xres_virtual >> 6);
 	mga_outl(M_LEN | M_EXEC, fontheight(p));
 	WaitTillIdle();
+
+	CRITEND
 } 
 #endif
 
@@ -1654,12 +1763,16 @@
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
 
+	CRITBEGIN
+	
 	mga_fifo(4);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
 	mga_outl(M_FCOL, 0x0F0F0F0F);
 	mga_outl(M_FXBNDRY, ((xx + fontwidth(p)) << 16) | xx);
-	mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+	mga_ydstlen(yy, fontheight(p));
 	WaitTillIdle();
+
+	CRITEND
 }
 #endif
 
@@ -1671,12 +1784,16 @@
 	yy *= fontheight(p);
 	xx *= fontwidth(p);
 
+	CRITBEGIN
+	
 	mga_fifo(4);
 	mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
 	mga_outl(M_FCOL, 0xFFFFFFFF);
 	mga_outl(M_FXBNDRY, ((xx + fontwidth(p)) << 16) | xx);
-	mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+	mga_ydstlen(yy, fontheight(p));
 	WaitTillIdle();
+
+	CRITEND
 }
 
 static void matrox_cfbX_clear_margins(struct vc_data* conp, struct display* p, int bottom_only) {
@@ -1840,6 +1957,9 @@
 
 	DBG("matroxfb_ti3026_cursor")
 	
+	if (ACCESS_FBINFO(currcon_display) != p)
+		return;
+
 	if (mode == CM_ERASE) {
 		if (ACCESS_FBINFO(cursor.state) != CM_ERASE) {
 			spin_lock_irqsave(&ACCESS_FBINFO(lock.DAC), flags);
@@ -1914,6 +2034,9 @@
 	xline = (~0) << (32 - ACCESS_FBINFO(cursor.w));
 	cursorbase = ACCESS_FBINFO(video.vbase);
 	h = ACCESS_FBINFO(features.DAC1064.cursorimage);
+
+	CRITBEGIN
+
 #ifdef __BIG_ENDIAN
 	WaitTillIdle();
 	mga_outl(M_OPMODE, M_OPMODE_32BPP);
@@ -1944,12 +2067,17 @@
 #ifdef __BIG_ENDIAN
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
+
+	CRITEND
 }
 	
 static void matroxfb_DAC1064_cursor(struct display* p, int mode, int x, int y) {
 	unsigned long flags;
 	MINFO_FROM_DISP(p);
 
+	if (ACCESS_FBINFO(currcon_display) != p)
+		return;
+
 	if (mode == CM_ERASE) {
 		if (ACCESS_FBINFO(cursor.state) != CM_ERASE) {
 			spin_lock_irqsave(&ACCESS_FBINFO(lock.DAC), flags);
@@ -2010,6 +2138,9 @@
 	fsize = (p->userfont?FNTCHARCNT(p->fontdata):256) * fontheight(p);
 	if (((fsize * width + 31) / 32) * 4 > ACCESS_FBINFO(fastfont.size))
 		return 0;
+
+	CRITBEGIN
+
 	mga_outl(M_OPMODE, M_OPMODE_8BPP);
 	if (width <= 8) {
 		if (width == 8)
@@ -2100,6 +2231,9 @@
 		}
 	}
 	mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
+
+	CRITEND
+
 	return 1;
 }
 
@@ -2117,6 +2251,8 @@
 	unsigned int step;
 	MINFO_FROM_DISP(p);
 
+	CRITBEGIN
+
 	step = ACCESS_FBINFO(devflags.textstep);
 	srcoff = (sy * p->next_line) + (sx * step);
 	dstoff = (dy * p->next_line) + (dx * step);
@@ -2144,6 +2280,7 @@
 			height--;
 		}
 	}
+	CRITEND
 }
 
 static void matrox_text_clear(struct vc_data* conp, struct display* p, int sy, int sx,
@@ -2156,6 +2293,9 @@
 	step = ACCESS_FBINFO(devflags.textstep);
 	offs = sy * p->next_line + sx * step;
 	val = ntohs((attr_bgcol(p, conp->vc_video_erase_char) << 4) | attr_fgcol(p, conp->vc_video_erase_char) | (' ' << 8));
+
+	CRITBEGIN
+	
 	while (height > 0) {
 		int i;
 		for (i = width; i > 0; offs += step, i--)
@@ -2163,6 +2303,7 @@
 		offs += p->next_line - width * step;
 		height--;
 	}
+	CRITEND
 }
 
 static void matrox_text_putc(struct vc_data* conp, struct display* p, int c, int yy, int xx) {
@@ -2175,7 +2316,12 @@
 	offs = yy * p->next_line + xx * step;
 	chr = attr_fgcol(p,c) | (attr_bgcol(p,c) << 4) | ((c & p->charmask) << 8);
 	if (chr & 0x10000) chr |= 0x08;
+
+	CRITBEGIN
+	
 	mga_writew(ACCESS_FBINFO(video.vbase), offs, ntohs(chr));
+
+	CRITEND
 }
 
 static void matrox_text_putcs(struct vc_data* conp, struct display* p, const unsigned short* s,
@@ -2188,12 +2334,17 @@
 	step = ACCESS_FBINFO(devflags.textstep);
 	offs = yy * p->next_line + xx * step;
 	attr = attr_fgcol(p, scr_readw(s)) | (attr_bgcol(p, scr_readw(s)) << 4);
+
+	CRITBEGIN
+	
 	while (count-- > 0) {
 		unsigned int chr = ((scr_readw(s++)) & p->charmask) << 8;
 		if (chr & 0x10000) chr ^= 0x10008;
 		mga_writew(ACCESS_FBINFO(video.vbase), offs, ntohs(attr|chr));
 		offs += step;
 	}
+
+	CRITEND
 }
 
 static void matrox_text_revc(struct display* p, int xx, int yy) {
@@ -2203,7 +2354,12 @@
 
 	step = ACCESS_FBINFO(devflags.textstep);
 	offs = yy * p->next_line + xx * step + 1;
+	
+	CRITBEGIN
+	
 	mga_writeb(ACCESS_FBINFO(video.vbase), offs, mga_readb(ACCESS_FBINFO(video.vbase), offs) ^ 0x77);
+
+	CRITEND
 }
 
 static int matrox_text_loadfont(WPMINFO struct display* p) {
@@ -2221,6 +2377,9 @@
 	dst = ACCESS_FBINFO(video.vbase);
 	i = 2;
 	font = (u_int8_t*)p->fontdata;
+
+	CRITBEGIN
+
 	mga_setr(M_SEQ_INDEX, 0x02, 0x04);
 	while (fsize--) {
 		int l;
@@ -2233,6 +2392,9 @@
 		i += (32 - fontheight(p)) * ACCESS_FBINFO(devflags.vgastep);
 	}
 	mga_setr(M_SEQ_INDEX, 0x02, 0x03);
+
+	CRITEND
+
 	return 1;
 }
 
@@ -2242,17 +2404,31 @@
 		return;
 
 	matroxfb_createcursorshape(PMINFO p, 0);
+
+	CRITBEGIN
+	
 	mga_setr(M_CRTC_INDEX, 0x0A, ACCESS_FBINFO(cursor.u));
 	mga_setr(M_CRTC_INDEX, 0x0B, ACCESS_FBINFO(cursor.d) - 1);
+
+	CRITEND
 }
 
 static void matrox_text_cursor(struct display* p, int mode, int x, int y) {
 	unsigned int pos;
 	MINFO_FROM_DISP(p);
 
+	if (ACCESS_FBINFO(currcon_display) != p)
+		return;
+
 	if (mode == CM_ERASE) {
 		if (ACCESS_FBINFO(cursor.state) != CM_ERASE) {
+
+			CRITBEGIN
+			
 			mga_setr(M_CRTC_INDEX, 0x0A, 0x20);
+
+			CRITEND
+
 			ACCESS_FBINFO(cursor.state) = CM_ERASE;
 		}
 		return;
@@ -2264,10 +2440,16 @@
 	ACCESS_FBINFO(cursor.x) = x;
 	ACCESS_FBINFO(cursor.y) = y;
 	pos = p->next_line / ACCESS_FBINFO(devflags.textstep) * y + x;
+
+	CRITBEGIN
+	
 	mga_setr(M_CRTC_INDEX, 0x0F, pos);
 	mga_setr(M_CRTC_INDEX, 0x0E, pos >> 8);
 
 	mga_setr(M_CRTC_INDEX, 0x0A, ACCESS_FBINFO(cursor.u));
+
+	CRITEND
+
 	ACCESS_FBINFO(cursor.state) = CM_DRAW;
 }
 
@@ -2523,6 +2705,9 @@
 static void matrox_pan_var(WPMINFO struct fb_var_screeninfo *var) {
 	unsigned int pos;
 	unsigned short p0, p1, p2;
+#ifdef CONFIG_FB_MATROX_32MB
+	unsigned int p3;
+#endif
 	struct display *disp;
 
 	DBG("matrox_pan_var")
@@ -2536,10 +2721,22 @@
 	}
 	p0 = ACCESS_FBINFO(currenthw)->CRTC[0x0D] = pos & 0xFF;
 	p1 = ACCESS_FBINFO(currenthw)->CRTC[0x0C] = (pos & 0xFF00) >> 8;
-	p2 = ACCESS_FBINFO(currenthw)->CRTCEXT[0] = (ACCESS_FBINFO(currenthw)->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F);
+	p2 = ACCESS_FBINFO(currenthw)->CRTCEXT[0] = (ACCESS_FBINFO(currenthw)->CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40);
+#ifdef CONFIG_FB_MATROX_32MB
+	p3 = ACCESS_FBINFO(currenthw)->CRTCEXT[8] = pos >> 21;
+#endif	
+
+	CRITBEGIN
+
 	mga_setr(M_CRTC_INDEX, 0x0D, p0);
 	mga_setr(M_CRTC_INDEX, 0x0C, p1);
+#ifdef CONFIG_FB_MATROX_32MB
+	if (ACCESS_FBINFO(devflags.support32MB))
+		mga_setr(M_EXTVGA_INDEX, 0x08, p3);
+#endif
 	mga_setr(M_EXTVGA_INDEX, 0x00, p2);
+
+	CRITEND
 }
 
 	/*
@@ -2627,13 +2824,16 @@
 		case 0:		return xres;
 		case 4:		rounding = 128;
 				break;
-		case 8:		rounding = 64;
+		case 8:		rounding = 64;	/* doc says 64; 32 is OK for G400 */
 				break;
 		case 16:	rounding = 32;
 				break;
-		case 24:	rounding = 64;
+		case 24:	rounding = 64;	/* doc says 64; 32 is OK for G400 */
 				break;
 		default:	rounding = 16;
+				/* on G400, 16 really does not work */
+				if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400)
+					rounding = 32;
 				break;
 	}
 	if (isInterleave(MINFO)) {
@@ -3140,7 +3340,7 @@
 	hw->DACclk[2] = p;
 }
 
-__initfunc(static void DAC1064_setmclk(CPMINFO struct matrox_hw_state* hw, int oscinfo, unsigned long fmem)) {
+static void __init DAC1064_setmclk(CPMINFO struct matrox_hw_state* hw, int oscinfo, unsigned long fmem){
 	u_int32_t mx;
 
 	DBG("DAC1064_setmclk")
@@ -3288,6 +3488,8 @@
 static void DAC1064_restore_1(CPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw) {
 	
 	DBG("DAC1064_restore_1")
+
+	CRITBEGIN
 	
 	outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]);
 	outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]);
@@ -3298,6 +3500,8 @@
 		for (i = 0; i < sizeof(MGA1064_DAC_regs); i++)
 			outDAC1064(PMINFO MGA1064_DAC_regs[i], hw->DACreg[i]);
 	}
+
+	CRITEND
 }
 
 static void DAC1064_restore_2(WPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw, struct display* p) {
@@ -3305,6 +3509,8 @@
 	unsigned int tmout;
 	
 	DBG("DAC1064_restore_2")
+
+	CRITBEGIN
 	
 	for (i = 0; i < 3; i++)
 		outDAC1064(PMINFO M1064_XPIXPLLCM + i, hw->DACclk[i]);
@@ -3313,6 +3519,9 @@
 			break;
 		udelay(10);
 	};
+
+	CRITEND
+
 	if (!tmout)
 		printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
 
@@ -3585,7 +3794,9 @@
 			var->pixclock = 15000;	/* limit for "normal" gclk & mclk */
 #endif
 	}
-
+	/* YDSTLEN contains only signed 16bit value */
+	if (var->yres_virtual > 32767)
+		var->yres_virtual = 32767;
 	if (var->yres_virtual < var->yres)
 		var->yres = var->yres_virtual;
 	if (var->xres_virtual < var->xres)
@@ -3684,7 +3895,7 @@
 }
 
 #ifdef CONFIG_FB_MATROX_MILLENIUM
-__initfunc(static void ti3026_setMCLK(CPMINFO struct matrox_hw_state* hw, int fout)) {
+static void __init ti3026_setMCLK(CPMINFO struct matrox_hw_state* hw, int fout){
 	unsigned int f_pll;
 	unsigned int pclk_m, pclk_n, pclk_p;
 	unsigned int mclk_m, mclk_n, mclk_p;
@@ -3783,7 +3994,7 @@
 		printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
 }
 
-__initfunc(static void ti3026_ramdac_init(WPMINFO struct matrox_hw_state* hw)) {
+static void __init ti3026_ramdac_init(WPMINFO struct matrox_hw_state* hw){
 
 	DBG("ti3026_ramdac_init")
 
@@ -3800,7 +4011,7 @@
 }
 #endif
 
-__initfunc(static void matroxfb_fastfont_init(struct matrox_fb_info* minfo)) {
+static void matroxfb_fastfont_init(struct matrox_fb_info* minfo){
 	unsigned int size;
 
 	size = ACCESS_FBINFO(fastfont.size);
@@ -3824,7 +4035,7 @@
 }
 
 #ifdef CONFIG_FB_MATROX_MYSTIQUE
-__initfunc(static void MGA1064_ramdac_init(WPMINFO struct matrox_hw_state* hw)) {
+static void __init MGA1064_ramdac_init(WPMINFO struct matrox_hw_state* hw){
 
 	DBG("MGA1064_ramdac_init");
 
@@ -3841,7 +4052,7 @@
 	DAC1064_setmclk(PMINFO hw, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333);
 }
 
-__initfunc(static int MGA1064_preinit(WPMINFO struct matrox_hw_state* hw)) {
+static int __init MGA1064_preinit(WPMINFO struct matrox_hw_state* hw){
 	static const int vxres_mystique[] = { 512,        640, 768,  800,  832,  960, 
 					     1024, 1152, 1280,      1600, 1664, 1920, 
 					     2048,    0};
@@ -3873,7 +4084,7 @@
 	return 0;
 }
 
-__initfunc(static void MGA1064_reset(WPMINFO struct matrox_hw_state* hw)) {
+static void __init MGA1064_reset(WPMINFO struct matrox_hw_state* hw){
 
 	DBG("MGA1064_reset");
 
@@ -3893,7 +4104,7 @@
 static int def50 = 0;	/* reg50, & 0x0F, & 0x3000 (only 0x0000, 0x1000, 0x2000 (0x3000 disallowed and treated as 0) */
 #endif
 
-__initfunc(static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p)) {
+static void __init MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p){
 	int reg;
 	int selClk;
 	int clk;
@@ -3937,7 +4148,7 @@
 	outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS);
 }
 
-__initfunc(static void MGAG100_setPixClock(CPMINFO int flags, int freq)) {
+static void __init MGAG100_setPixClock(CPMINFO int flags, int freq){
 	unsigned int m, n, p;
 
 	DBG("MGAG100_setPixClock")
@@ -3946,7 +4157,7 @@
 	MGAG100_progPixClock(PMINFO flags, m, n, p);
 }
 
-__initfunc(static int MGAG100_preinit(WPMINFO struct matrox_hw_state* hw)) {
+static int __init MGAG100_preinit(WPMINFO struct matrox_hw_state* hw){
 	static const int vxres_g100[] = {  512,        640, 768,  800,  832,  960, 
                                           1024, 1152, 1280,      1600, 1664, 1920, 
                                           2048, 0};
@@ -4036,7 +4247,7 @@
 	return 0;
 }
 	
-__initfunc(static void MGAG100_reset(WPMINFO struct matrox_hw_state* hw)) {
+static void __init MGAG100_reset(WPMINFO struct matrox_hw_state* hw){
 	u_int8_t b;
 
 	DBG("MGAG100_reset")
@@ -4104,6 +4315,8 @@
 		dprintk("%02X:", hw->ATTR[i]);
 	dprintk("\n");
 
+	CRITBEGIN
+
 	mga_inb(M_ATTR_RESET);
 	mga_outb(M_ATTR_INDEX, 0);
 	mga_outb(M_MISC_REG, hw->MiscOutReg);
@@ -4125,6 +4338,8 @@
 		mga_outb(M_DAC_VAL, hw->DACpal[i]);
 	mga_inb(M_ATTR_RESET);
 	mga_outb(M_ATTR_INDEX, 0x20);
+
+	CRITEND
 }
 
 static int matrox_setcolreg(unsigned regno, unsigned red, unsigned green,
@@ -4227,10 +4442,15 @@
 	int i;
 
 	DBG("MGA1064_restore")
+
+	CRITBEGIN
 	
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
 	mga_outb(M_IEN, 0x00);
 	mga_outb(M_CACHEFLUSH, 0x00);
+
+	CRITEND
+
 	DAC1064_restore_1(PMINFO hw, oldhw);
 	vgaHWrestore(PMINFO hw, oldhw);
 	for (i = 0; i < 6; i++)
@@ -4244,10 +4464,18 @@
 	int i;
 
 	DBG("MGAG100_restore")
-	
+
+	CRITBEGIN
+
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
+	CRITEND
+
 	DAC1064_restore_1(PMINFO hw, oldhw);
 	vgaHWrestore(PMINFO hw, oldhw);
+#ifdef CONFIG_FB_MATROX_32MB
+	if (ACCESS_FBINFO(devflags.support32MB))
+		mga_setr(M_EXTVGA_INDEX, 8, hw->CRTCEXT[8]);
+#endif
 	for (i = 0; i < 6; i++)
 		mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
 	DAC1064_restore_2(PMINFO hw, oldhw, p);
@@ -4265,9 +4493,16 @@
 		dprintk("%02X:", hw->CRTCEXT[i]);
 	dprintk("\n");
 
+	CRITBEGIN
+
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
 
+	CRITEND
+
 	vgaHWrestore(PMINFO hw, oldhw);
+
+	CRITBEGIN
+
 	for (i = 0; i < 6; i++)
 		mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
 
@@ -4285,11 +4520,13 @@
 		oldhw->DACclk[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
 		oldhw->DACclk[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA);
 	}
+	CRITEND
 	if (!oldhw || memcmp(hw->DACclk, oldhw->DACclk, 6)) {
 		/* agrhh... setting up PLL is very slow on Millenium... */
 		/* Mystique PLL is locked in few ms, but Millenium PLL lock takes about 0.15 s... */
 		/* Maybe even we should call schedule() ? */
 
+		CRITBEGIN
 		outTi3026(PMINFO TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]);
 		outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A);
 		outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0);
@@ -4307,24 +4544,31 @@
 					break;
 				udelay(10);
 			}
+
+			CRITEND
+
 			if (!tmout)
 				printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
 			else
 				dprintk(KERN_INFO "PixelPLL: %d\n", 500000-tmout);
+			CRITBEGIN
 		}
 		outTi3026(PMINFO TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]);
 		outTi3026(PMINFO TVP3026_XPLLADDR, 0x00);
 		for (i = 3; i < 6; i++)
 			outTi3026(PMINFO TVP3026_XLOOPPLLDATA, hw->DACclk[i]);
+		CRITEND
 		if ((hw->MiscOutReg & 0x08) && ((hw->DACclk[5] & 0x80) == 0x80)) {
 			int tmout;
 
+			CRITBEGIN
 			outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F);
 			for (tmout = 500000; tmout; --tmout) {
 				if (inTi3026(PMINFO TVP3026_XLOOPPLLDATA) & 0x40) 
 					break;
 				udelay(10);
 			}
+			CRITEND
 			if (!tmout)
 				printk(KERN_ERR "matroxfb: Loop PLL not locked after 5 secs\n");
 			else
@@ -4379,7 +4623,7 @@
 	strcpy(fix->id,"MATROX");
 
 	fix->smem_start = (void*)ACCESS_FBINFO(video.base) + ACCESS_FBINFO(curr.ydstorg.bytes);
-	fix->smem_len = ACCESS_FBINFO(video.len) - ACCESS_FBINFO(curr.ydstorg.bytes);
+	fix->smem_len = ACCESS_FBINFO(video.len_usable) - ACCESS_FBINFO(curr.ydstorg.bytes);
 	fix->type = p->type;
 	fix->type_aux = p->type_aux;
 	fix->visual = p->visual;
@@ -4531,7 +4775,8 @@
 
 			hw->CRTC[0x0D] = pos & 0xFF;
 			hw->CRTC[0x0C] = (pos & 0xFF00) >> 8;
-			hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F);
+			hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40);
+			hw->CRTCEXT[8] = pos >> 21;
 			ACCESS_FBINFO(hw_switch->restore(PMINFO hw, ohw, display));
 			ACCESS_FBINFO(cursor.redraw) = 1;
 			ACCESS_FBINFO(currenthw) = hw;
@@ -4717,10 +4962,16 @@
 		case 4:  seq = 0x20; crtc = 0x30; break;
 		default: seq = 0x00; crtc = 0x00; break;
 	}
+
+	CRITBEGIN
+	
 	mga_outb(M_SEQ_INDEX, 1);
 	mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
 	mga_outb(M_EXTVGA_INDEX, 1);
 	mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
+
+	CRITEND
+
 #undef minfo
 }
 
@@ -4739,7 +4990,8 @@
 #define RS1056x344	12	/* 132 x 43 text */
 #define RS1056x400	13	/* 132 x 50 text */
 #define RS1056x480	14	/* 132 x 60 text */
-/* 0F-FF */
+#define RSNoxNo		2	/* FIXME! change it to 15 after modedb patch (2.3 only) */
+/* 10-FF */
 static struct { int xres, yres, left, right, upper, lower, hslen, vslen, vfreq; } timmings[] __initdata = {
 	{  640,  400,  48, 16, 39,  8,  96, 2, 70 },
 	{  640,  480,  48, 16, 33, 10,  96, 2, 60 },
@@ -4754,7 +5006,8 @@
 	{  640,  350,  48, 16, 39,  8,  96, 2, 70 },
 	{ 1056,  344,  96, 24, 59, 44, 160, 2, 70 },
 	{ 1056,  400,  96, 24, 39,  8, 160, 2, 70 },
-	{ 1056,  480,  96, 24, 36, 12, 160, 3, 60 }
+	{ 1056,  480,  96, 24, 36, 12, 160, 3, 60 },
+	{    0,    0,  ~0, ~0, ~0, ~0,   0, 0,  0 }
 };
 
 #define RSDepth(X)	(((X) >> 8) & 0x0F)
@@ -4775,13 +5028,14 @@
 	{ {  0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, {  0, 0, 0},  4 },
 	{ { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, {  0, 0, 0}, 24 },
 	{ {  0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, {  0, 0, 0},  0 },	/* textmode with (default) VGA8x16 */
-	{ {  0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, {  0, 0, 0},  0 }	/* textmode hardwired to VGA8x8 */
+	{ {  0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, {  0, 0, 0},  0 },	/* textmode hardwired to VGA8x8 */
 };
 
 #define RSCreate(X,Y)	((X) | ((Y) << 8))
 static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __initdata = {
 /* default must be first */
 #ifdef FBCON_HAS_CFB8
+	{    ~0, RSCreate(RSNoxNo,     RS8bpp ) },
 	{ 0x101, RSCreate(RS640x480,   RS8bpp ) },
 	{ 0x100, RSCreate(RS640x400,   RS8bpp ) },
 	{ 0x180, RSCreate(RS768x576,   RS8bpp ) },
@@ -4793,14 +5047,8 @@
 	{ 0x198, RSCreate(RS1408x1056, RS8bpp ) },
 	{ 0x11C, RSCreate(RS1600x1200, RS8bpp ) },
 #endif
-#ifdef FBCON_HAS_CFB4
-	{ 0x010, RSCreate(RS640x350,   RS4bpp ) },
-	{ 0x012, RSCreate(RS640x480,   RS4bpp ) },
-	{ 0x102, RSCreate(RS800x600,   RS4bpp ) },
-	{ 0x104, RSCreate(RS1024x768,  RS4bpp ) },
-	{ 0x106, RSCreate(RS1280x1024, RS4bpp ) },
-#endif
 #ifdef FBCON_HAS_CFB16
+	{    ~0, RSCreate(RSNoxNo,     RS15bpp) },
 	{ 0x110, RSCreate(RS640x480,   RS15bpp) },
 	{ 0x181, RSCreate(RS768x576,   RS15bpp) },
 	{ 0x113, RSCreate(RS800x600,   RS15bpp) },
@@ -4821,6 +5069,7 @@
 	{ 0x11E, RSCreate(RS1600x1200, RS16bpp) },
 #endif
 #ifdef FBCON_HAS_CFB24
+	{    ~0, RSCreate(RSNoxNo,     RS24bpp) },
 	{ 0x1B2, RSCreate(RS640x480,   RS24bpp) },
 	{ 0x184, RSCreate(RS768x576,   RS24bpp) },
 	{ 0x1B5, RSCreate(RS800x600,   RS24bpp) },
@@ -4832,6 +5081,7 @@
 	{ 0x1BF, RSCreate(RS1600x1200, RS24bpp) },
 #endif
 #ifdef FBCON_HAS_CFB32
+	{    ~0, RSCreate(RSNoxNo,     RS32bpp) },
 	{ 0x112, RSCreate(RS640x480,   RS32bpp) },
 	{ 0x183, RSCreate(RS768x576,   RS32bpp) },
 	{ 0x115, RSCreate(RS800x600,   RS32bpp) },
@@ -4843,6 +5093,7 @@
 	{ 0x11F, RSCreate(RS1600x1200, RS32bpp) },
 #endif
 #ifdef FBCON_HAS_VGATEXT
+	{    ~0, RSCreate(RSNoxNo,     RSText)  },
 	{ 0x002, RSCreate(RS640x400,   RSText)  },	/* 80x25 */
 	{ 0x003, RSCreate(RS640x400,   RSText)  },	/* 80x25 */
 	{ 0x007, RSCreate(RS640x400,   RSText)  },	/* 80x25 */
@@ -4853,6 +5104,14 @@
 	{ 0x10B, RSCreate(RS1056x400,  RSText8) },	/* 132x50 */
 	{ 0x10C, RSCreate(RS1056x480,  RSText8) },	/* 132x60 */
 #endif
+#ifdef FBCON_HAS_CFB4
+	{    ~0, RSCreate(RSNoxNo,     RS4bpp ) },
+	{ 0x010, RSCreate(RS640x350,   RS4bpp ) },
+	{ 0x012, RSCreate(RS640x480,   RS4bpp ) },
+	{ 0x102, RSCreate(RS800x600,   RS4bpp ) },
+	{ 0x104, RSCreate(RS1024x768,  RS4bpp ) },
+	{ 0x106, RSCreate(RS1280x1024, RS4bpp ) },
+#endif
 	{     0, 0                                 }};
 
 /* initialized by setup, see explanation at end of file (search for MODULE_PARM_DESC) */
@@ -4871,19 +5130,21 @@
 static int hwcursor = 1;		/* "matrox:nohwcursor" */
 static int blink = 1;			/* "matrox:noblink" */
 static int sgram = 0;			/* "matrox:sgram" */
+#ifdef CONFIG_MTRR
 static int mtrr = 1;			/* "matrox:nomtrr" */
+#endif
 static int grayscale = 0;		/* "matrox:grayscale" */
 static unsigned int fastfont = 0;	/* "matrox:fastfont:xxxxx" */
 static int dev = -1;			/* "matrox:dev:xxxxx" */
-static unsigned int vesa = 0x101;	/* "matrox:vesa:xxxxx" */
+static unsigned int vesa = ~0;		/* "matrox:vesa:xxxxx" */
 static int depth = -1;			/* "matrox:depth:xxxxx" */
 static unsigned int xres = 0;		/* "matrox:xres:xxxxx" */
 static unsigned int yres = 0;		/* "matrox:yres:xxxxx" */
-static unsigned int upper = 0;		/* "matrox:upper:xxxxx" */
-static unsigned int lower = 0;		/* "matrox:lower:xxxxx" */
+static unsigned int upper = ~0;		/* "matrox:upper:xxxxx" */
+static unsigned int lower = ~0;		/* "matrox:lower:xxxxx" */
 static unsigned int vslen = 0;		/* "matrox:vslen:xxxxx" */
-static unsigned int left = 0;		/* "matrox:left:xxxxx" */
-static unsigned int right = 0;		/* "matrox:right:xxxxx" */
+static unsigned int left = ~0;		/* "matrox:left:xxxxx" */
+static unsigned int right = ~0;		/* "matrox:right:xxxxx" */
 static unsigned int hslen = 0;		/* "matrox:hslen:xxxxx" */
 static unsigned int pixclock = 0;	/* "matrox:pixclock:xxxxx" */
 static int sync = -1;			/* "matrox:sync:xxxxx" */
@@ -4891,6 +5152,7 @@
 static unsigned int fh = 0;		/* "matrox:fh:xxxxxk" */
 static unsigned int maxclk = 0;		/* "matrox:maxclk:xxxxM" */
 static char fontname[64];		/* "matrox:font:xxxxx" */
+static char videomode[64];		/* "matrox:mode:xxxxx" or "matrox:xxxxx" */
 
 #ifndef MODULE
 __initfunc(void matroxfb_setup(char *options, int *ints)) {
@@ -4954,6 +5216,31 @@
 			fv = simple_strtoul(this_opt+3, NULL, 0);
 		else if (!strncmp(this_opt, "mem:", 4)) 
 			mem = simple_strtoul(this_opt+4, NULL, 0);
+		else if (!strncmp(this_opt, "mode:", 5))
+			strcpy(videomode, this_opt+5);
+#ifdef CONFIG_FB_OF
+		else if (!strncmp(this_opt, "vmode:", 6)) {
+			unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
+            		if (vmode > 0 && vmode <= VMODE_MAX)
+				default_vmode = vmode;
+		} else if (!strncmp(this_opt, "cmode:", 6)) {
+			unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0);
+			switch (cmode) {
+				case 0:
+				case 8:
+					default_cmode = CMODE_8;
+					break;
+				case 15:
+				case 16:
+					default_cmode = CMODE_16;
+					break;
+				case 24:
+				case 32:
+					default_cmode = CMODE_32;
+					break;
+			}
+		}
+#endif
 		else if (!strncmp(this_opt, "fastfont:", 9))
 			fastfont = simple_strtoul(this_opt+9, NULL, 0);
 		else if (!strcmp(this_opt, "nofastfont"))	/* fastfont:N and nofastfont (nofastfont = fastfont:0) */
@@ -4987,8 +5274,10 @@
 				nobios = !value;
 			else if (!strcmp(this_opt, "init"))
 				noinit = !value;
+#ifdef CONFIG_MTRR
 			else if (!strcmp(this_opt, "mtrr"))
 				mtrr = value;
+#endif
 			else if (!strcmp(this_opt, "inv24"))
 				inv24 = value;
 			else if (!strcmp(this_opt, "cross4MB"))
@@ -5004,6 +5293,7 @@
 			}
 		}
 	}
+	return;
 }
 #endif
 
@@ -5012,7 +5302,7 @@
 	unsigned int offs;
 	unsigned int offs2;
 	unsigned char store;
-	unsigned char bytes[16];
+	unsigned char bytes[32];
 	unsigned char* tmp;
 	unsigned long cbase;
 	unsigned long mbase;
@@ -5025,7 +5315,7 @@
 	maxSize &= ~0x1FFFFF;	/* must be X*2MB (really it must be 2 or X*4MB) */
 	/* at least 2MB */
 	if (maxSize < 0x0200000) return 0;
-	if (maxSize > 0x1000000) maxSize = 0x1000000;
+	if (maxSize > 0x2000000) maxSize = 0x2000000;
 
 	mga_outb(M_EXTVGA_INDEX, 0x03);
 	mga_outb(M_EXTVGA_DATA, mga_inb(M_EXTVGA_DATA) | 0x80);
@@ -5163,20 +5453,28 @@
 
 struct video_board {
 	int maxvram;
+	int maxdisplayable;
 	int accelID;
 	struct matrox_switch* lowlevel;
 		 };
 #ifdef CONFIG_FB_MATROX_MILLENIUM
-static struct video_board vbMillenium __initdata	= {0x0800000,	FB_ACCEL_MATROX_MGA2064W,	&matrox_millenium};
-static struct video_board vbMillenium2 __initdata	= {0x1000000,	FB_ACCEL_MATROX_MGA2164W,	&matrox_millenium};
-static struct video_board vbMillenium2A __initdata	= {0x1000000,	FB_ACCEL_MATROX_MGA2164W_AGP,	&matrox_millenium};
+static struct video_board vbMillenium __initdata   = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA2064W,	  &matrox_millenium};
+static struct video_board vbMillenium2 __initdata  = {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W,	  &matrox_millenium};
+static struct video_board vbMillenium2A __initdata = {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W_AGP, &matrox_millenium};
 #endif	/* CONFIG_FB_MATROX_MILLENIUM */
 #ifdef CONFIG_FB_MATROX_MYSTIQUE
-static struct video_board vbMystique __initdata		= {0x0800000,	FB_ACCEL_MATROX_MGA1064SG,	&matrox_mystique};
+static struct video_board vbMystique __initdata    = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA1064SG,	  &matrox_mystique};
 #endif	/* CONFIG_FB_MATROX_MYSTIQUE */
 #ifdef CONFIG_FB_MATROX_G100
-static struct video_board vbG100 __initdata		= {0x0800000,	FB_ACCEL_MATROX_MGAG100,	&matrox_G100};
-static struct video_board vbG200 __initdata		= {0x1000000,	FB_ACCEL_MATROX_MGAG200,	&matrox_G100};
+static struct video_board vbG100 __initdata	   = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGAG100,	  &matrox_G100};
+static struct video_board vbG200 __initdata	   = {0x1000000, 0x1000000, FB_ACCEL_MATROX_MGAG200,	  &matrox_G100};
+#ifdef CONFIG_FB_MATROX_32MB
+/* from doc it looks like that accelerator can draw only to low 16MB :-( Direct accesses & displaying are OK for
+   whole 32MB */
+static struct video_board vbG400 __initdata	   = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400,	  &matrox_G100};
+#else
+static struct video_board vbG400 __initdata	   = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400,	  &matrox_G100};
+#endif
 #endif
 
 #define DEVF_VIDEO64BIT	0x01
@@ -5185,6 +5483,15 @@
 #define	DEVF_MILLENIUM2	0x08
 #define DEVF_CROSS4MB	0x10
 #define DEVF_TEXT4B	0x20
+#define DEVF_DDC_8_2	0x40
+#define DEVF_DMA	0x80
+#define DEVF_SUPPORT32MB	0x100
+#define DEVF_ANY_VXRES		0x200
+
+#define DEVF_G100	(DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2) /* no doc, no vxres... */
+#define DEVF_G200	(DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2 | DEVF_ANY_VXRES)
+#define DEVF_G400	(DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2 | DEVF_ANY_VXRES | DEVF_SUPPORT32MB)
+
 static struct board {
 	unsigned short vendor, device, rev, svid, sid;
 	unsigned int flags;
@@ -5229,88 +5536,94 @@
 #ifdef CONFIG_FB_MATROX_G100
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G100,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_MGA_G100_PCI,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G100,
 		230000,
 		&vbG100,
 		"MGA-G100 (PCI)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G100,	0xFF,
 		0,			0,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G100,
 		230000,
 		&vbG100,
 		"unknown G100 (PCI)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G100_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_GENERIC,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G100,
 		230000,
 		&vbG100,
 		"MGA-G100 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G100_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_MGA_G100_AGP,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G100,
 		230000,
 		&vbG100,
 		"MGA-G100 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G100_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_SIEMENS_NIXDORF,	PCI_SS_ID_SIEMENS_MGA_G100_AGP,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G100,
 		230000,
 		&vbG100,
 		"MGA-G100 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G100_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G100,
 		230000,
 		&vbG100,
 		"Productiva G100 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G100_AGP,	0xFF,
 		0,			0,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G100,
 		230000,
 		&vbG100,
 		"unknown G100 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G200_PCI,	0xFF,
 		0,			0,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G200,
 		250000,
 		&vbG200,
 		"unknown G200 (PCI)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G200_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_GENERIC,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G200,
 		220000,
 		&vbG200,
 		"MGA-G200 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G200_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G200,
 		230000,
 		&vbG200,
 		"Mystique G200 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G200_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_MILLENIUM_G200_AGP,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G200,
 		250000,
 		&vbG200,
 		"Millennium G200 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G200_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_MATROX,	PCI_SS_ID_MATROX_MARVEL_G200_AGP,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G200,
 		230000,
 		&vbG200,
 		"Marvel G200 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G200_AGP,	0xFF,
 		PCI_SS_VENDOR_ID_SIEMENS_NIXDORF,	PCI_SS_ID_SIEMENS_MGA_G200_AGP,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G200,
 		230000,
 		&vbG200,
 		"MGA-G200 (AGP)"},
 	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G200_AGP,	0xFF,
 		0,			0,
-		DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+		DEVF_G200,
 		230000,
 		&vbG200,
 		"unknown G200 (AGP)"},
+	{PCI_VENDOR_ID_MATROX,	PCI_DEVICE_ID_MATROX_G400_AGP,	0xFF,
+		0,			0,
+		DEVF_G400,
+		360000,
+		&vbG400,
+		"unknown G400 (AGP)"},
 #endif
 	{0,			0,				0xFF,
 		0,			0,
@@ -5348,6 +5661,10 @@
 		ACCESS_FBINFO(devflags.vgastepdisp) = 64;
 		ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP8;
 	}
+#ifdef CONFIG_FB_MATROX_32MB
+	ACCESS_FBINFO(devflags.support32MB) = b->flags & DEVF_SUPPORT32MB;
+#endif
+	ACCESS_FBINFO(devflags.precise_width) = !(b->flags & DEVF_ANY_VXRES);
 	ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode);
 	ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode);
 
@@ -5472,8 +5789,8 @@
 		return -ENOMEM;
 	}
 	ACCESS_FBINFO(video.len_usable) = ACCESS_FBINFO(video.len);
-	if (ACCESS_FBINFO(video.len_usable) > 0x08000000)
-		ACCESS_FBINFO(video.len_usable) = 0x08000000;
+	if (ACCESS_FBINFO(video.len_usable) > b->base->maxdisplayable)
+		ACCESS_FBINFO(video.len_usable) = b->base->maxdisplayable;
 #ifdef CONFIG_MTRR
 	if (mtrr) {
 		ACCESS_FBINFO(mtrr.vram) = mtrr_add(video_base_phys, ACCESS_FBINFO(video.len), MTRR_TYPE_WRCOMB, 1);
@@ -5482,11 +5799,16 @@
 	}
 #endif	/* CONFIG_MTRR */
 
+	if (!ACCESS_FBINFO(devflags.novga))
+		request_region(0x3C0, 32, "matrox");
+	ACCESS_FBINFO(hw_switch->reset(PMINFO hw));
+
 /* validate params, autodetect k, M */
 	if (fh < 1000) fh *= 1000;	/* 1kHz minimum */
 	if (maxclk < 1000) maxclk *= 1000;	/* kHz -> Hz, MHz -> kHz */
 	if (maxclk < 1000000) maxclk *= 1000;	/* kHz -> Hz, 1MHz minimum */
-	vesa &= 0x1DFF;		/* mask out clearscreen, acceleration and so on */
+	if (vesa != ~0)
+		vesa &= 0x1DFF;		/* mask out clearscreen, acceleration and so on */
 
 	ACCESS_FBINFO(fbcon.monspecs.hfmin) = 0;
 	ACCESS_FBINFO(fbcon.monspecs.hfmax) = fh;
@@ -5504,19 +5826,19 @@
 	}
 	{
 		int res = RSResolution(RSptr->info)-1;
-		if (!left)
+		if (left == ~0)
 			left = timmings[res].left;
 		if (!xres)
 			xres = timmings[res].xres;
-		if (!right)
+		if (right == ~0)
 			right = timmings[res].right;
 		if (!hslen)
 			hslen = timmings[res].hslen;
-		if (!upper)
+		if (upper == ~0)
 			upper = timmings[res].upper;
 		if (!yres)
 			yres = timmings[res].yres;
-		if (!lower)
+		if (lower == ~0)
 			lower = timmings[res].lower;
 		if (!vslen)
 			vslen = timmings[res].vslen;
@@ -5525,38 +5847,6 @@
 		if (depth == -1)
 			depth = RSDepth(RSptr->info);
 	}
-	if (sync == -1) {
-		sync = 0;
-		if (yres < 400)
-			sync |= FB_SYNC_HOR_HIGH_ACT;
-		else if (yres < 480)
-			sync |= FB_SYNC_VERT_HIGH_ACT;
-	}
-	if (xres < 320)
-		xres = 320;
-	if (xres > 2048)
-		xres = 2048;
-	if (yres < 200)
-		yres = 200;
-	if (yres > 2048)
-		yres = 2048;
-	{
-		unsigned int tmp;
-
-		if (fv) {
-			tmp = fv * (upper + yres + lower + vslen);
-			if ((tmp < fh) || (fh == 0)) fh = tmp;
-		}
-		if (fh) {
-			tmp = fh * (left + xres + right + hslen);
-			if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp;
-		}
-		maxclk = (maxclk + 499) / 500;
-		if (maxclk) {
-			tmp = (2000000000 + maxclk) / maxclk;
-			if (tmp > pixclock) pixclock = tmp;
-		}
-	}
 	if ((depth == RSText8) && (!*ACCESS_FBINFO(fbcon.fontname))) {
 		strcpy(ACCESS_FBINFO(fbcon.fontname), "VGA8x8");
 	}
@@ -5564,27 +5854,9 @@
 	vesafb_defined.green = colors[depth-1].green;
 	vesafb_defined.blue = colors[depth-1].blue;
 	vesafb_defined.bits_per_pixel = colors[depth-1].bits_per_pixel;
-	if (pixclock < 2000)		/* > 500MHz */
-		pixclock = 4000;	/* 250MHz */
-	if (pixclock > 1000000)
-		pixclock = 1000000;	/* 1MHz */
-	vesafb_defined.xres = xres;
-	vesafb_defined.yres = yres;
-	vesafb_defined.xoffset = 0;
-	vesafb_defined.yoffset = 0;
 	vesafb_defined.grayscale = grayscale;
-	vesafb_defined.pixclock = pixclock;
-	vesafb_defined.left_margin = left;
-	vesafb_defined.right_margin = right;
-	vesafb_defined.hsync_len = hslen;
-	vesafb_defined.upper_margin = upper;
-	vesafb_defined.lower_margin = lower;
-	vesafb_defined.vsync_len = vslen;
-	vesafb_defined.sync = sync;
 	vesafb_defined.vmode = 0;
-
-	if (!ACCESS_FBINFO(devflags.novga))
-		request_region(0x3C0, 32, "matrox");
+	vesafb_defined.sync = -1;
 	if (noaccel)
 		vesafb_defined.accel_flags &= ~FB_ACCELF_TEXT;
 
@@ -5597,8 +5869,67 @@
 	ACCESS_FBINFO(fbcon.updatevar) = &matroxfb_updatevar;
 	ACCESS_FBINFO(fbcon.blank) = &matroxfb_blank;
 	ACCESS_FBINFO(fbcon.flags) = FBINFO_FLAG_DEFAULT;
-	ACCESS_FBINFO(hw_switch->reset(PMINFO hw));
 	ACCESS_FBINFO(video.len_usable) &= PAGE_MASK;
+
+#if 0
+	fb_find_mode(&vesafb_defined, &ACCESS_FBINFO(fbcon), videomode[0]?videomode:NULL,
+		NULL, 0, NULL, vesafb_defined.bits_per_pixel);
+#endif
+	/* mode modifiers */
+	if (hslen)
+		vesafb_defined.hsync_len = hslen;
+	if (vslen)
+		vesafb_defined.vsync_len = vslen;
+	if (left != ~0)
+		vesafb_defined.left_margin = left;
+	if (right != ~0)
+		vesafb_defined.right_margin = right;
+	if (upper != ~0)
+		vesafb_defined.upper_margin = upper;
+	if (lower != ~0)
+		vesafb_defined.lower_margin = lower;
+	if (xres)
+		vesafb_defined.xres = xres;
+	if (yres)
+		vesafb_defined.yres = yres;
+	if (sync != -1)
+		vesafb_defined.sync = sync;
+	else if (vesafb_defined.sync == -1) {
+		vesafb_defined.sync = 0;
+		if (vesafb_defined.yres < 400)
+			vesafb_defined.sync |= FB_SYNC_HOR_HIGH_ACT;
+		else if (vesafb_defined.yres < 480)
+			vesafb_defined.sync |= FB_SYNC_VERT_HIGH_ACT;
+	}
+	/* fv, fh, maxclk limits was specified */
+	{
+		unsigned int tmp;
+
+		if (fv) {
+			tmp = fv * (vesafb_defined.upper_margin + vesafb_defined.yres 
+			          + vesafb_defined.lower_margin + vesafb_defined.vsync_len);
+			if ((tmp < fh) || (fh == 0)) fh = tmp;
+		}
+		if (fh) {
+			tmp = fh * (vesafb_defined.left_margin + vesafb_defined.xres 
+			          + vesafb_defined.right_margin + vesafb_defined.hsync_len);
+			if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp;
+		}
+		maxclk = (maxclk + 499) / 500;
+		if (maxclk) {
+			tmp = (2000000000 + maxclk) / maxclk;
+			if (tmp > pixclock) pixclock = tmp;
+		}
+	}
+	if (pixclock) {
+		if (pixclock < 2000)		/* > 500MHz */
+			pixclock = 4000;	/* 250MHz */
+		if (pixclock > 1000000)
+			pixclock = 1000000;	/* 1MHz */
+		vesafb_defined.pixclock = pixclock;
+	}
+		
+	/* FIXME: Where to move this?! */
 #if defined(CONFIG_FB_OF)
 #if defined(CONFIG_FB_COMPAT_XPMAC)
 	strcpy(ACCESS_FBINFO(matrox_name), "MTRX,");	/* OpenFirmware naming convension */
@@ -5608,30 +5939,30 @@
 #endif
 	if ((xres <= 640) && (yres <= 480)) {
 		struct fb_var_screeninfo var;
-		int default_vmode = nvram_read_byte(NV_VMODE);
-		int default_cmode = nvram_read_byte(NV_CMODE);
-
-		if ((default_vmode <= 0) || (default_vmode > VMODE_MAX))
+		if (default_vmode == VMODE_NVRAM) {
+			default_vmode = nvram_read_byte(NV_VMODE);
+			if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+				default_vmode = VMODE_CHOOSE;
+		}
+		if (default_vmode <= 0 || default_vmode > VMODE_MAX)
 			default_vmode = VMODE_640_480_60;
-		if ((default_cmode < CMODE_8) || (default_cmode > CMODE_32))
+		if (default_cmode == CMODE_NVRAM)
+			default_cmode = nvram_read_byte(NV_CMODE);
+		if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
 			default_cmode = CMODE_8;
 		if (!mac_vmode_to_var(default_vmode, default_cmode, &var)) {
 			var.accel_flags = vesafb_defined.accel_flags;
 			var.xoffset = var.yoffset = 0;
 			vesafb_defined = var; /* Note: mac_vmode_to_var() doesnot set all parameters */
-		}
+              }
 	}
 #endif
-	{
-		int pixel_size = vesafb_defined.bits_per_pixel;
-
-		vesafb_defined.xres_virtual = matroxfb_pitch_adjust(PMINFO vesafb_defined.xres, pixel_size);
-		if (nopan) {
-			vesafb_defined.yres_virtual = vesafb_defined.yres;
-		} else {
-			vesafb_defined.yres_virtual = 65536; /* large enough to be INF, but small enough
-			                                        to yres_virtual * xres_virtual < 2^32 */
-		}
+	vesafb_defined.xres_virtual = vesafb_defined.xres;
+	if (nopan) {
+		vesafb_defined.yres_virtual = vesafb_defined.yres;
+	} else {
+		vesafb_defined.yres_virtual = 65536; /* large enough to be INF, but small enough
+		                                        to yres_virtual * xres_virtual < 2^32 */
 	}
 	if (matroxfb_set_var(&vesafb_defined, -2, &ACCESS_FBINFO(fbcon))) {
 		printk(KERN_ERR "matroxfb: cannot set required parameters\n");
@@ -5647,8 +5978,9 @@
 /* We do not have to set currcon to 0... register_framebuffer do it for us on first console
  * and we do not want currcon == 0 for subsequent framebuffers */
 
-	if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0)
+	if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0) {
 		return -EINVAL;
+	}
 	printk("fb%d: %s frame buffer device\n",
 	       GET_FB_IDX(ACCESS_FBINFO(fbcon.node)), ACCESS_FBINFO(fbcon.modename));
 	if (ACCESS_FBINFO(currcon) < 0) {
@@ -5781,8 +6113,8 @@
 
 #else
 
-MODULE_AUTHOR("(c) 1998 Petr Vandrovec <vandrove@vc.cvut.cz>");
-MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200");
+MODULE_AUTHOR("(c) 1998,1999 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200/G400");
 MODULE_PARM(mem, "i");
 MODULE_PARM_DESC(mem, "Size of available memory in MB, KB or B (2,4,8,12,16MB, default=autodetect)");
 MODULE_PARM(disabled, "i");
@@ -5802,7 +6134,7 @@
 MODULE_PARM(mtrr, "i");
 MODULE_PARM_DESC(mtrr, "This speeds up video memory accesses (0=disabled or 1) (default=1)");
 MODULE_PARM(sgram, "i");
-MODULE_PARM_DESC(sgram, "Indicates that G200 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
+MODULE_PARM_DESC(sgram, "Indicates that G200/G400 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
 MODULE_PARM(inv24, "i");
 MODULE_PARM_DESC(inv24, "Inverts clock polarity for 24bpp and loop frequency > 100MHz (default=do not invert polarity)");
 MODULE_PARM(inverse, "i");
@@ -5855,6 +6187,12 @@
 MODULE_PARM_DESC(grayscale, "Sets display into grayscale. Works perfectly with paletized videomode (4, 8bpp), some limitations apply to 16, 24 and 32bpp videomodes (default=nograyscale)");
 MODULE_PARM(cross4MB, "i");
 MODULE_PARM_DESC(cross4MB, "Specifies that 4MB boundary can be in middle of line. (default=autodetected)");
+#ifdef CONFIG_FB_OF
+MODULE_PARM(vmode, "i");
+MODULE_PARM_DESC(vmode, "Specify the vmode mode number that should be used (640x480 default)");
+MODULE_PARM(cmode, "i");
+MODULE_PARM_DESC(cmode, "Specify the video depth that should be used (8bit default)");
+#endif
 
 __initfunc(int init_module(void)) {
 
@@ -5862,7 +6200,7 @@
 
 #ifdef DEBUG
 	if( disabled )
-		return 0;
+		return -ENXIO;
 #endif /* DEBUG */
 
 	if (depth == 0)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/pm2fb.c linux.14p6/drivers/video/pm2fb.c
--- linux.vanilla/drivers/video/pm2fb.c	Tue Mar 16 00:11:31 1999
+++ linux.14p6/drivers/video/pm2fb.c	Tue Nov 16 15:13:28 1999
@@ -1,6 +1,7 @@
 /*
  * Permedia2 framebuffer driver.
  * Copyright (c) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT)
+ * Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com)
  * Based on linux/drivers/video/skeletonfb.c by Geert Uytterhoeven.
  * --------------------------------------------------------------------------
  * $Id: pm2fb.c,v 1.163 1999/02/21 14:06:49 illo Exp $
@@ -38,17 +39,28 @@
 #include <video/fbcon-cfb32.h>
 #include "pm2fb.h"
 #include "cvisionppc.h"
+#ifdef __sparc__
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#endif
 
 #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
 #error	"The endianness of the target host has not been defined."
 #endif
 
+#if defined(__BIG_ENDIAN) && !defined(__sparc__)
+#define PM2FB_BE_APERTURE
+#endif
+
+/* Need to debug this some more */
+#undef PM2FB_HW_CURSOR
+
 #if defined(CONFIG_FB_PM2_PCI) && !defined(CONFIG_PCI)
 #undef CONFIG_FB_PM2_PCI
 #warning "support for Permedia2 PCI boards with no generic PCI support!"
 #endif
 
-#define PM2FB_MASTER_DEBUG
+#undef PM2FB_MASTER_DEBUG
 #ifdef PM2FB_MASTER_DEBUG
 #define DPRINTK(a,b...)	printk("pm2fb: %s: " a, __FUNCTION__ , ## b)
 #else
@@ -102,12 +114,20 @@
 #define OPTF_OLD_MEM		(1L<<0)
 #define OPTF_YPAN		(1L<<1)
 #define OPTF_VIRTUAL		(1L<<2)
+#define OPTF_USER		(1L<<3)
 static struct {
 	char font[40];
 	u32 flags;
 	struct pm2fb_par user_mode;
 } pm2fb_options =
+#ifdef __sparc__
+	/* For some reason Raptor is not happy with the low-end mode */
+	{"\0", 0L, {31499,640,480,4,20,50,209,0,3,20,499,80,0,8,121}};
+#else
 	{"\0", 0L, {25174,640,480,4,28,40,199,9,11,45,524,80,0,8,121}};
+#endif
+
+static char curblink __initdata = 1;
 
 static const struct {
 	char name[16];
@@ -185,12 +205,30 @@
 };
 #endif
 
+#define DEFAULT_CURSOR_BLINK_RATE       (20)
+#define CURSOR_DRAW_DELAY               (2)
+
+struct pm2_cursor {
+    int	enable;
+    int on;
+    int vbl_cnt;
+    int blink_rate;
+    struct {
+        u16 x, y;
+    } pos, hot, size;
+    u8 color[6];
+    u8 bits[8][64];
+    u8 mask[8][64];
+    struct timer_list *timer;
+};
+
 static const char permedia2_name[16]="Permedia2";
 
 static struct pm2fb_info {
 	struct fb_info_gen gen;
 	int board;			/* Permedia2 board index (see
 					   board_table[] below) */
+	pm2type_t type;
 	struct {
 		unsigned char* fb_base;	/* framebuffer memory base */
 		u32 fb_size;		/* framebuffer memory size */
@@ -233,6 +271,7 @@
 		u32 cmap32[16];
 #endif
 	} cmap;
+	struct pm2_cursor *cursor;
 } fb_info;
 
 #ifdef CONFIG_FB_PM2_CVPPC
@@ -245,6 +284,18 @@
 static void pm2pci_init(struct pm2fb_info*);
 #endif
 
+#ifdef PM2FB_HW_CURSOR
+static void pm2fb_cursor(struct display *p, int mode, int x, int y);
+static int pm2fb_set_font(struct display *d, int width, int height);
+static struct pm2_cursor *pm2_init_cursor(struct pm2fb_info *fb);
+static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue);
+static void pm2v_set_cursor_shape(struct pm2fb_info *fb);
+static u8 cursor_color_map[2] = { 0, 0xff };
+#else
+#define pm2fb_cursor NULL
+#define pm2fb_set_font NULL
+#endif
+
 /*
  * Table of the supported Permedia2 based boards.
  * Three hooks are defined for each board:
@@ -334,7 +385,7 @@
 static struct fb_ops pm2fb_ops={
 	pm2fb_open, pm2fb_release, fbgen_get_fix, fbgen_get_var,
 	fbgen_set_var, fbgen_get_cmap, fbgen_set_cmap, fbgen_pan_display,
-	NULL /* fb_ioctl() */, NULL /* fb_mmap() */
+	NULL /* fb_ioctl() */, NULL /* fb_mmap() */, NULL /* fb_rasterimg */,
 };
 
 /***************************************************************************
@@ -363,17 +414,50 @@
 
 inline static u32 pm2_RDAC_RD(struct pm2fb_info* p, s32 idx) {
 
-	pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx);
+	int index = PM2R_RD_INDEXED_DATA;
+	switch (p->type) {
+	case PM2_TYPE_PERMEDIA2:
+		pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx);
+		break;
+	case PM2_TYPE_PERMEDIA2V:
+		pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
+		index = PM2VR_RD_INDEXED_DATA;
+		break;
+	}	
 	DEFRW();
-	return pm2_RD(p, PM2R_RD_INDEXED_DATA);
+	return pm2_RD(p, index);
 }
 
 inline static void pm2_RDAC_WR(struct pm2fb_info* p, s32 idx,
 						u32 v) {
 
-	pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx);
-	DEFW();
-	pm2_WR(p, PM2R_RD_INDEXED_DATA, v);
+	int index = PM2R_RD_INDEXED_DATA;
+	switch (p->type) {
+	case PM2_TYPE_PERMEDIA2:
+		pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx);
+		break;
+	case PM2_TYPE_PERMEDIA2V:
+		pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
+		index = PM2VR_RD_INDEXED_DATA;
+		break;
+	}	
+	DEFRW();
+	pm2_WR(p, index, v);
+}
+
+inline static u32 pm2v_RDAC_RD(struct pm2fb_info* p, s32 idx) {
+
+	pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
+	DEFRW();
+	return pm2_RD(p, PM2VR_RD_INDEXED_DATA);
+}
+
+inline static void pm2v_RDAC_WR(struct pm2fb_info* p, s32 idx,
+						u32 v) {
+
+	pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
+	DEFRW();
+	pm2_WR(p, PM2VR_RD_INDEXED_DATA, v);
 }
 
 #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT
@@ -435,8 +519,8 @@
 	return timing;
 }
 
-static void mnp(u32 clk, unsigned char* mm, unsigned char* nn,
-							unsigned char* pp) {
+static void pm2_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
+		unsigned char* pp) {
 	unsigned char m;
 	unsigned char n;
 	unsigned char p;
@@ -463,6 +547,30 @@
 	}
 }
 
+static void pm2v_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
+		unsigned char* pp) {
+	unsigned char m;
+	unsigned char n;
+	unsigned char p;
+	u32 f;
+	s32 delta=1000;
+
+	*mm=*nn=*pp=0;
+	for (n=1; n; n++) {
+		for (m=1; m; m++) {
+			for (p=0; p<2; p++) {
+				f=PM2_REFERENCE_CLOCK*n/(m * (1<<(p+1)));
+				if (clk>f-delta && clk<f+delta) {
+					delta=clk>f?clk-f:f-clk;
+					*mm=m;
+					*nn=n;
+					*pp=p;
+				}
+			}
+		}
+	}
+}
+
 static void wait_pm2(struct pm2fb_info* i) {
 
 	WAIT_FIFO(i, 1);
@@ -474,12 +582,12 @@
 	} while (pm2_RD(i, PM2R_OUT_FIFO)!=PM2TAG(PM2R_SYNC));
 }
 
-static void set_memclock(struct pm2fb_info* info, u32 clk) {
+static void pm2_set_memclock(struct pm2fb_info* info, u32 clk) {
 	int i;
 	unsigned char m, n, p;
 
-	mnp(clk, &m, &n, &p);
-	WAIT_FIFO(info, 5);
+	pm2_mnp(clk, &m, &n, &p);
+	WAIT_FIFO(info, 10);
 	pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_3, 6);
 	DEFW();
 	pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_1, m);
@@ -493,23 +601,36 @@
 		!(pm2_RD(info, PM2R_RD_INDEXED_DATA)&PM2F_PLL_LOCKED); i--);
 }
 
-static void set_pixclock(struct pm2fb_info* info, u32 clk) {
+static void pm2_set_pixclock(struct pm2fb_info* info, u32 clk) {
 	int i;
 	unsigned char m, n, p;
 
-	mnp(clk, &m, &n, &p);
-	WAIT_FIFO(info, 5);
-	pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 0);
-	DEFW();
-	pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A1, m);
-	pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A2, n);
-	DEFW();
-	pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 8|p);
-	DEFW();
-	pm2_RDAC_RD(info, PM2I_RD_PIXEL_CLOCK_STATUS);
-	DEFR();
-	for (i=256; i &&
-		!(pm2_RD(info, PM2R_RD_INDEXED_DATA)&PM2F_PLL_LOCKED); i--);
+	switch (info->type) {
+	case PM2_TYPE_PERMEDIA2:
+		pm2_mnp(clk, &m, &n, &p);
+		WAIT_FIFO(info, 10);
+		pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 0);
+		DEFW();
+		pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A1, m);
+		pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A2, n);
+		DEFW();
+		pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 8|p);
+		DEFW();
+		pm2_RDAC_RD(info, PM2I_RD_PIXEL_CLOCK_STATUS);
+		DEFR();
+		for (i=256; i &&
+		     !(pm2_RD(info, PM2R_RD_INDEXED_DATA)&PM2F_PLL_LOCKED); i--);
+		break;
+	case PM2_TYPE_PERMEDIA2V:
+		pm2v_mnp(clk/2, &m, &n, &p);
+		WAIT_FIFO(info, 8);
+		pm2_WR(info, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CLK0_PRESCALE >> 8);
+		pm2v_RDAC_WR(info, PM2VI_RD_CLK0_PRESCALE, m);
+		pm2v_RDAC_WR(info, PM2VI_RD_CLK0_FEEDBACK, n);
+		pm2v_RDAC_WR(info, PM2VI_RD_CLK0_POSTSCALE, p);
+		pm2_WR(info, PM2VR_RD_INDEX_HIGH, 0);
+		break;
+	}
 }
 
 static void clear_palette(struct pm2fb_info* p) {
@@ -543,7 +664,7 @@
 
 	WAIT_FIFO(i, 2);
 #ifdef __LITTLE_ENDIAN
-	pm2_WR(i, PM2R_APERTURE_ONE, 0);	/* FIXME */
+	pm2_WR(i, PM2R_APERTURE_ONE, 0);
 	pm2_WR(i, PM2R_APERTURE_TWO, 0);
 #else
 	switch (p->depth) {
@@ -567,32 +688,46 @@
 static void set_screen(struct pm2fb_info* i, struct pm2fb_par* p) {
 	u32 clrmode=0;
 	u32 txtmap=0;
+	u32 pixsize=0;
+	u32 clrformat=0;
 	u32 xres;
+	u32 video, tmp;
 
+	if (i->type == PM2_TYPE_PERMEDIA2V) {
+		WAIT_FIFO(i, 1);
+		pm2_WR(i, PM2VR_RD_INDEX_HIGH, 0);
+	}
 	xres=(p->width+31)&~31;
 	set_aperture(i, p);
 	DEFRW();
-	WAIT_FIFO(i, 22);
+	WAIT_FIFO(i, 27);
 	pm2_RDAC_WR(i, PM2I_RD_COLOR_KEY_CONTROL, p->depth==8?0:
 						PM2F_COLOR_KEY_TEST_OFF);
 	switch (p->depth) {
 		case 8:
 			pm2_WR(i, PM2R_FB_READ_PIXEL, 0);
+			clrformat=0x0e;
 			break;
 		case 16:
 			pm2_WR(i, PM2R_FB_READ_PIXEL, 1);
 			clrmode=PM2F_RD_TRUECOLOR|0x06;
 			txtmap=PM2F_TEXTEL_SIZE_16;
+			pixsize=1;
+			clrformat=0x70;
 			break;
 		case 32:
 			pm2_WR(i, PM2R_FB_READ_PIXEL, 2);
 			clrmode=PM2F_RD_TRUECOLOR|0x08;
 			txtmap=PM2F_TEXTEL_SIZE_32;
+			pixsize=2;
+			clrformat=0x20;
 			break;
 		case 24:
 			pm2_WR(i, PM2R_FB_READ_PIXEL, 4);
 			clrmode=PM2F_RD_TRUECOLOR|0x09;
 			txtmap=PM2F_TEXTEL_SIZE_24;
+			pixsize=4;
+			clrformat=0x20;
 			break;
 	}
 	pm2_WR(i, PM2R_SCREEN_SIZE, (p->height<<16)|p->width);
@@ -613,10 +748,34 @@
 	pm2_WR(i, PM2R_SCREEN_STRIDE, p->stride);
 	DEFW();
 	pm2_WR(i, PM2R_SCREEN_BASE, p->base);
-	pm2_RDAC_WR(i, PM2I_RD_COLOR_MODE, PM2F_RD_COLOR_MODE_RGB|
-						PM2F_RD_GUI_ACTIVE|clrmode);
-	pm2_WR(i, PM2R_VIDEO_CONTROL, p->video);
-	set_pixclock(i, p->pixclock);
+	/* HW cursor needs /VSYNC for recognizing vert retrace */
+	video=p->video & ~(PM2F_HSYNC_ACT_LOW|PM2F_VSYNC_ACT_LOW);
+	video|=PM2F_HSYNC_ACT_HIGH|PM2F_VSYNC_ACT_HIGH;
+	switch (i->type) {
+	case PM2_TYPE_PERMEDIA2:
+		tmp = PM2F_RD_PALETTE_WIDTH_8;
+		pm2_RDAC_WR(i, PM2I_RD_COLOR_MODE, PM2F_RD_COLOR_MODE_RGB|
+						   PM2F_RD_GUI_ACTIVE|clrmode);
+		if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
+			tmp |= 4; /* invert hsync */
+		if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
+			tmp |= 8; /* invert vsync */
+		pm2_RDAC_WR(i, PM2I_RD_MISC_CONTROL, tmp);
+		break;
+	case PM2_TYPE_PERMEDIA2V:
+		tmp = 0;
+		pm2v_RDAC_WR(i, PM2VI_RD_PIXEL_SIZE, pixsize);
+		pm2v_RDAC_WR(i, PM2VI_RD_COLOR_FORMAT, clrformat);
+		if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
+			tmp |= 1; /* invert hsync */
+		if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
+			tmp |= 4; /* invert vsync */
+		pm2v_RDAC_WR(i, PM2VI_RD_SYNC_CONTROL, tmp);
+		pm2v_RDAC_WR(i, PM2VI_RD_MISC_CONTROL, 1);
+		break;
+	}
+	pm2_WR(i, PM2R_VIDEO_CONTROL, video);
+	pm2_set_pixclock(i, p->pixclock);
 };
 
 /*
@@ -682,6 +841,8 @@
 
 static void pm2fb_reset(struct pm2fb_info* p) {
 
+	if (p->type == PM2_TYPE_PERMEDIA2V)
+		pm2_WR(p, PM2VR_RD_INDEX_HIGH, 0);
 	pm2_WR(p, PM2R_RESET_STATUS, 0);
 	DEFRW();
 	while (pm2_RD(p, PM2R_RESET_STATUS)&PM2F_BEING_RESET);
@@ -729,8 +890,16 @@
 	pm2_WR(p, PM2R_LOGICAL_OP_MODE, 0);
 	pm2_WR(p, PM2R_STATISTICS_MODE, 0);
 	pm2_WR(p, PM2R_SCISSOR_MODE, 0);
-	pm2_RDAC_WR(p, PM2I_RD_CURSOR_CONTROL, 0);
-	pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, PM2F_RD_PALETTE_WIDTH_8);
+	switch (p->type) {
+	case PM2_TYPE_PERMEDIA2:
+		pm2_RDAC_WR(p, PM2I_RD_MODE_CONTROL, 0); /* no overlay */
+		pm2_RDAC_WR(p, PM2I_RD_CURSOR_CONTROL, 0);
+		pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, PM2F_RD_PALETTE_WIDTH_8);
+		break;
+	case PM2_TYPE_PERMEDIA2V:
+		pm2v_RDAC_WR(p, PM2VI_RD_MISC_CONTROL, 1); /* 8bit */
+		break;
+	}
 	pm2_RDAC_WR(p, PM2I_RD_COLOR_KEY_CONTROL, 0);
 	pm2_RDAC_WR(p, PM2I_RD_OVERLAY_KEY, 0);
 	pm2_RDAC_WR(p, PM2I_RD_RED_KEY, 0);
@@ -738,7 +907,7 @@
 	pm2_RDAC_WR(p, PM2I_RD_BLUE_KEY, 0);
 	clear_palette(p);
 	if (p->memclock)
-		set_memclock(p, p->memclock);
+		pm2_set_memclock(p, p->memclock);
 }
 
 __initfunc(static int pm2fb_conf(struct pm2fb_info* p)) {
@@ -752,12 +921,16 @@
 	DPRINTK("found board: %s\n", board_table[p->board].name);
 	p->regions.p_fb=p->regions.fb_base;
 	p->regions.v_fb=MMAP(p->regions.p_fb, p->regions.fb_size);
-#ifdef __LITTLE_ENDIAN
+#ifndef PM2FB_BE_APERTURE
 	p->regions.p_regs=p->regions.rg_base;
 #else
 	p->regions.p_regs=p->regions.rg_base+PM2_REGS_SIZE;
 #endif
 	p->regions.v_regs=MMAP(p->regions.p_regs, PM2_REGS_SIZE);
+
+#ifdef PM2FB_HW_CURSOR
+	p->cursor = pm2_init_cursor(p);
+#endif
 	return 1;
 }
 
@@ -805,10 +978,11 @@
 	return 1;
 }
 
-static int cvppc_detect(struct pm2fb_info* p) {
+static int __init cvppc_detect(struct pm2fb_info* p) {
 
 	if (!cvppc_PCI_init(&p->board_par.cvppc))
 		return 0;
+	p->type = PM2_TYPE_PERMEDIA2;
 	p->regions.fb_base=(unsigned char* )CVPPC_FB_APERTURE_ONE;
 	p->regions.fb_size=CVPPC_FB_SIZE;
 	p->regions.rg_base=(unsigned char* )CVPPC_REGS_REGION;
@@ -833,9 +1007,25 @@
  * Generic PCI detection routines
  */
 #ifdef CONFIG_FB_PM2_PCI
-static int pm2pci_detect(struct pm2fb_info* p) {
+struct {
+	unsigned short vendor, device;
+	char *name;
+	pm2type_t type;
+} pm2pci_cards[] __initdata = {
+{ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TVP4020, "Texas Instruments TVP4020", PM2_TYPE_PERMEDIA2 },
+{ PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2, "3dLabs Permedia 2", PM2_TYPE_PERMEDIA2 },
+{ PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2V, "3dLabs Permedia 2v", PM2_TYPE_PERMEDIA2V },
+{ 0, 0 }
+};
+
+static int __init pm2pci_detect(struct pm2fb_info* p) {
 	struct pm2pci_par* pci=&p->board_par.pci;
+	struct pci_dev* dev;
+	int i;
 	unsigned char* m;
+#ifdef __sparc__
+	struct pcidev_cookie *pcp;
+#endif
 
 	memset(pci, 0, sizeof(struct pm2pci_par));
 	if (!pci_present()) {
@@ -843,9 +1033,16 @@
 		return 0;
 	}
 	DPRINTK("scanning PCI bus for known chipsets...\n");
-	if ((pci->dev=pci_find_device(PCI_VENDOR_ID_TI,
-					PCI_DEVICE_ID_TI_TVP4020, NULL))) {
-		DPRINTK("... found Texas Instruments TVP4020\n");
+
+	for (dev = pci_devices; !pci->dev && dev; dev = dev->next) {
+		for (i = 0; pm2pci_cards[i].vendor; i++)
+			if (pm2pci_cards[i].vendor == dev->vendor &&
+			    pm2pci_cards[i].device == dev->device) {
+				pci->dev = dev;
+				p->type = pm2pci_cards[i].type;
+				DPRINTK("... found %s\n", pm2pci_cards[i].name);
+				break;
+			}
 	}
 	if (!pci->dev) {
 		DPRINTK("no PCI board found.\n");
@@ -861,6 +1058,41 @@
 		__pa(pci->dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK);
 	p->regions.fb_base=(unsigned char* )
 		__pa(pci->dev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK);
+	pcp = pci->dev->sysdata;
+	/* If the user has not asked for a particular mode, lets guess */
+	if (pcp->prom_node && !(pm2fb_options.flags & OPTF_USER)) {
+		char timing[256], *q, *r;
+		unsigned long w, h;
+		int i;
+		prom_getstring(pcp->prom_node, "timing-numbers", timing, 256);
+		/* FIXME: Find out what the actual pixclock is and other values as well */
+		if (timing[0]) {
+			w = simple_strtoul(timing, &q, 0);
+			h = 0;
+			if (q == timing) w = 0;
+			if (w) {
+				for (i = 0; i < 3; i++) {
+					for (r = q; *r && (*r < '0' || *r > '9'); r++);
+					simple_strtoul(r, &q, 0);
+					if (r == q) break;
+				}
+				if (i < 3) w = 0;
+			}
+			if (w) {
+				for (r = q; *r && (*r < '0' || *r > '9'); r++);
+				h = simple_strtoul(r, &q, 0);
+				if (r == q) w = 0;
+			}
+			if (w == 640 && h == 480) w = 0;
+			if (w) {
+				for (i=0; user_mode[i].name[0] &&
+					  (w != user_mode[i].par.width ||
+					   h != user_mode[i].par.height); i++);
+				if (user_mode[i].name[0])
+					memcpy(&p->current_par, &user_mode[i].par, sizeof(user_mode[i].par));
+			}
+		}
+	}
 #else
 	if (pm2fb_options.flags & OPTF_VIRTUAL) {
 		p->regions.rg_base=(unsigned char* )
@@ -875,7 +1107,7 @@
 			(pci->dev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK);
 	}
 #endif
-#ifdef __BIG_ENDIAN
+#ifdef PM2FB_BE_APERTURE
 	p->regions.rg_base += PM2_REGS_SIZE;
 #endif
 	if ((m=MMAP(p->regions.rg_base, PM2_REGS_SIZE))) {
@@ -1042,7 +1274,7 @@
 static struct display_switch pm2_cfb8 = {
 	fbcon_cfb8_setup, pm2fb_pp_bmove, pm2fb_clear8,
 	fbcon_cfb8_putc, fbcon_cfb8_putcs, fbcon_cfb8_revc,
-	NULL /* cursor() */, NULL /* set_font() */,
+	pm2fb_cursor, pm2fb_set_font,
 	pm2fb_clear_margins8,
 	FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) };
 #endif /* FBCON_HAS_CFB8 */
@@ -1082,7 +1314,7 @@
 static struct display_switch pm2_cfb16 = {
 	fbcon_cfb16_setup, pm2fb_pp_bmove, pm2fb_clear16,
 	fbcon_cfb16_putc, fbcon_cfb16_putcs, fbcon_cfb16_revc,
-	NULL /* cursor() */, NULL /* set_font() */,
+	pm2fb_cursor, pm2fb_set_font,
 	pm2fb_clear_margins16,
 	FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) };
 #endif /* FBCON_HAS_CFB16 */
@@ -1140,7 +1372,7 @@
 static struct display_switch pm2_cfb24 = {
 	fbcon_cfb24_setup, pm2fb_bmove, pm2fb_clear24,
 	fbcon_cfb24_putc, fbcon_cfb24_putcs, fbcon_cfb24_revc,
-	NULL /* cursor() */, NULL /* set_font() */,
+	pm2fb_cursor, pm2fb_set_font,
 	pm2fb_clear_margins24,
 	FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) };
 #endif /* FBCON_HAS_CFB24 */
@@ -1178,7 +1410,7 @@
 static struct display_switch pm2_cfb32 = {
 	fbcon_cfb32_setup, pm2fb_bmove, pm2fb_clear32,
 	fbcon_cfb32_putc, fbcon_cfb32_putcs, fbcon_cfb32_revc,
-	NULL /* cursor() */, NULL /* set_font() */,
+	pm2fb_cursor, pm2fb_set_font,
 	pm2fb_clear_margins32,
 	FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) };
 #endif /* FBCON_HAS_CFB32 */
@@ -1258,7 +1490,7 @@
 	p.height=var->yres_virtual;
 	p.depth=(var->bits_per_pixel+7)&~7;
 	p.depth=p.depth>32?32:p.depth;
-	data64=p.depth>8;
+	data64=p.depth>8 || i->type == PM2_TYPE_PERMEDIA2V;
 	xres=(var->xres+31)&~31;
 	if (p.width<xres+var->xoffset)
 		p.width=xres+var->xoffset;
@@ -1417,14 +1649,12 @@
 
 static void set_user_mode(struct pm2fb_info* i) {
 
-	memcpy(&i->current_par, &pm2fb_options.user_mode,
-						sizeof(i->current_par));
 	if (pm2fb_options.flags & OPTF_YPAN) {
+		int h = i->current_par.height;
 		i->current_par.height=i->regions.fb_size/
 			(i->current_par.width*i->current_par.depth/8);
 		i->current_par.height=MIN(i->current_par.height,2047);
-		i->current_par.height=MAX(i->current_par.height,
-						pm2fb_options.user_mode.height);
+		i->current_par.height=MAX(i->current_par.height,h);
 	}
 }
 
@@ -1456,6 +1686,12 @@
 	set_screen(i, p);
 	i->current_par=*p;
 	i->current_par_valid=1;
+#ifdef PM2FB_HW_CURSOR	
+	if (i->cursor) {
+		pm2v_set_cursor_color(i, cursor_color_map, cursor_color_map, cursor_color_map);
+		pm2v_set_cursor_shape(i);
+	}
+#endif
 }
 
 static int pm2fb_getcolreg(unsigned regno,
@@ -1577,6 +1813,210 @@
 	return 0;
 }
 
+#ifdef PM2FB_HW_CURSOR
+/***************************************************************************
+ * Hardware cursor support
+ ***************************************************************************/
+ 
+static u8 cursor_bits_lookup[16] = {
+	0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
+	0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
+};
+
+static u8 cursor_mask_lookup[16] = {
+	0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
+	0x02, 0x82, 0x22, 0xa2, 0x0a, 0x8a, 0x2a, 0xaa
+};
+
+static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue)
+{
+	struct pm2_cursor *c = fb->cursor;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		c->color[3*i] = red[i];
+		c->color[3*i+1] = green[i];
+		c->color[3*i+2] = blue[i];
+	}
+
+	WAIT_FIFO(fb, 14);
+	pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PALETTE >> 8);
+	for (i = 0; i < 6; i++)
+		pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PALETTE+i, c->color[i]);
+	pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0);
+}
+
+static void pm2v_set_cursor_shape(struct pm2fb_info *fb)
+{
+	struct pm2_cursor *c = fb->cursor;
+	u8 m, b;
+	int i, x, y;
+
+	WAIT_FIFO(fb, 1);
+	pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PATTERN >> 8);
+	for (y = 0, i = 0; y < c->size.y; y++) {
+		WAIT_FIFO(fb, 32);
+		for (x = 0; x < c->size.x >> 3; x++) {
+			m = c->mask[x][y];
+			b = c->bits[x][y];
+			pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i,
+				     cursor_mask_lookup[m >> 4] |
+				     cursor_bits_lookup[(b & m) >> 4]);
+			pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1,
+				     cursor_mask_lookup[m & 0x0f] |
+				     cursor_bits_lookup[(b & m) & 0x0f]);
+			i+=2;
+		}
+		for ( ; x < 8; x++) {
+			pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i, 0);
+			pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1, 0);
+			i+=2;
+		}
+	}
+	for (; y < 64; y++) {
+		WAIT_FIFO(fb, 32);
+		for (x = 0; x < 8; x++) {
+			pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i, 0);
+			pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1, 0);
+			i+=2;
+		}
+	}
+	WAIT_FIFO(fb, 1);
+	pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0);
+}
+
+static void pm2v_set_cursor(struct pm2fb_info *fb, int on)
+{
+	struct pm2_cursor *c = fb->cursor;
+	int x = c->pos.x;
+
+	if (!on) x = 4000;
+	WAIT_FIFO(fb, 14);
+	pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_LOW, x & 0xff);
+	pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_HIGH, (x >> 8) & 0x0f);
+	pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_LOW, c->pos.y & 0xff);
+	pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_HIGH, (c->pos.y >> 8) & 0x0f);
+	pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_HOT, c->hot.x & 0x3f);
+	pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_HOT, c->hot.y & 0x3f);
+	pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_MODE, 0x11);
+}
+
+static void pm2_cursor_timer_handler(unsigned long dev_addr)
+{
+	struct pm2fb_info *fb = (struct pm2fb_info *)dev_addr;
+
+	if (!fb->cursor->enable)
+		goto out;
+
+	if (fb->cursor->vbl_cnt && --fb->cursor->vbl_cnt == 0) {
+		fb->cursor->on ^= 1;
+		pm2v_set_cursor(fb, fb->cursor->on);
+		fb->cursor->vbl_cnt = fb->cursor->blink_rate;
+	}
+
+out:
+	fb->cursor->timer->expires = jiffies + (HZ / 50);
+	add_timer(fb->cursor->timer);
+}
+
+static void pm2fb_cursor(struct display *p, int mode, int x, int y)
+{
+	struct pm2fb_info *fb = (struct pm2fb_info *)p->fb_info;
+	struct pm2_cursor *c = fb->cursor;
+
+	if (!c) return;
+
+	x *= fontwidth(p);
+	y *= fontheight(p);
+	if (c->pos.x == x && c->pos.y == y && (mode == CM_ERASE) == !c->enable)
+		return;
+
+	c->enable = 0;
+	if (c->on)
+		pm2v_set_cursor(fb, 0);
+	c->pos.x = x;
+	c->pos.y = y;
+
+	switch (mode) {
+	case CM_ERASE:
+		c->on = 0;
+		break;
+
+	case CM_DRAW:
+	case CM_MOVE:
+		if (c->on)
+			pm2v_set_cursor(fb, 1);
+		else
+			c->vbl_cnt = CURSOR_DRAW_DELAY;
+		c->enable = 1;
+		break;
+	}
+}
+
+static struct pm2_cursor * __init pm2_init_cursor(struct pm2fb_info *fb)
+{
+	struct pm2_cursor *cursor;
+
+	if (fb->type != PM2_TYPE_PERMEDIA2V)
+		return 0; /* FIXME: Support hw cursor everywhere */
+
+	cursor = kmalloc(sizeof(struct pm2_cursor), GFP_ATOMIC);
+	if (!cursor)
+		return 0;
+	memset(cursor, 0, sizeof(*cursor));
+
+	cursor->timer = kmalloc(sizeof(*cursor->timer), GFP_KERNEL);
+	if (!cursor->timer) {
+		kfree(cursor);
+		return 0;
+	}
+	memset(cursor->timer, 0, sizeof(*cursor->timer));
+
+	cursor->blink_rate = DEFAULT_CURSOR_BLINK_RATE;
+
+	if (curblink) {
+		init_timer(cursor->timer);
+		cursor->timer->expires = jiffies + (HZ / 50);
+		cursor->timer->data = (unsigned long)fb;
+		cursor->timer->function = pm2_cursor_timer_handler;
+		add_timer(cursor->timer);
+	}
+
+	return cursor;
+}
+
+static int pm2fb_set_font(struct display *d, int width, int height)
+{
+	struct pm2fb_info *fb = (struct pm2fb_info *)d->fb_info;
+	struct pm2_cursor *c = fb->cursor;
+	int i, j;
+
+	if (c) {
+		if (!width || !height) {
+			width = 8;
+			height = 16;
+		}
+
+		c->hot.x = 0;
+		c->hot.y = 0;
+		c->size.x = width;
+		c->size.y = height;
+
+		memset(c->bits, 0xff, sizeof(c->bits));
+		memset(c->mask, 0, sizeof(c->mask));
+
+		for (i = 0, j = width; j >= 0; j -= 8, i++) {
+			c->mask[i][height-2] = (j >= 8) ? 0xff : (0xff << (8 - j));
+			c->mask[i][height-1] = (j >= 8) ? 0xff : (0xff << (8 - j));
+		}
+
+		pm2v_set_cursor_color(fb, cursor_color_map, cursor_color_map, cursor_color_map);
+		pm2v_set_cursor_shape(fb);
+	}
+	return 1;
+}
+#endif /* PM2FB_HW_CURSOR */
+
 /***************************************************************************
  * Begin of public functions
  ***************************************************************************/
@@ -1594,6 +2034,7 @@
 __initfunc(void pm2fb_init(void)) {
 
 	memset(&fb_info, 0, sizeof(fb_info));
+	memcpy(&fb_info.current_par, &pm2fb_options.user_mode, sizeof(fb_info.current_par));
 	if (!pm2fb_conf(&fb_info))
 		return;
 	pm2fb_reset(&fb_info);
@@ -1624,23 +2065,25 @@
 	MOD_INC_USE_COUNT;
 }
 
-__initfunc(void pm2fb_mode_setup(char* options)) {
+static void __init pm2fb_mode_setup(char* options) {
 	int i;
 
 	for (i=0; user_mode[i].name[0] &&
 		strcmp(options, user_mode[i].name); i++);
-	if (user_mode[i].name[0])
+	if (user_mode[i].name[0]) {
 		memcpy(&pm2fb_options.user_mode, &user_mode[i].par,
 					sizeof(pm2fb_options.user_mode));
+		pm2fb_options.flags |= OPTF_USER;
+	}
 }
 
-__initfunc(void pm2fb_font_setup(char* options)) {
+static void __init pm2fb_font_setup(char* options) {
 
 	strncpy(pm2fb_options.font, options, sizeof(pm2fb_options.font));
 	pm2fb_options.font[sizeof(pm2fb_options.font)-1]='\0';
 }
 
-__initfunc(void pm2fb_setup(char* options, int* ints)) {
+void __init pm2fb_setup(char* options, int* ints) {
 	char* next;
 
 	while (options) {
@@ -1656,6 +2099,8 @@
 			pm2fb_options.flags |= OPTF_OLD_MEM;
 		else if (!strcmp(options, "virtual"))
 			pm2fb_options.flags |= OPTF_VIRTUAL;
+		else if (!strcmp(options, "noblink"))
+			curblink = 0;
 		options=next;
 	}
 }
@@ -1665,9 +2110,14 @@
  ***************************************************************************/
 
 #ifdef MODULE
-int init_module(void) {
 
-	pm2fb_init();
+static char *mode = NULL;
+
+MODULE_PARM(mode, "s");
+
+int init_module(void) {
+	if (mode) pm2fb_mode_setup(mode);
+  	pm2fb_init();
 }
 
 void cleanup_module(void) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/pm2fb.h linux.14p6/drivers/video/pm2fb.h
--- linux.vanilla/drivers/video/pm2fb.h	Thu Feb 25 18:02:12 1999
+++ linux.14p6/drivers/video/pm2fb.h	Tue Nov 16 15:13:28 1999
@@ -108,6 +108,11 @@
 #define PM2R_FB_SOURCE_DELTA				0x8d88
 #define PM2R_CONFIG					0x8d90
 
+/* Permedia2v */
+#define PM2VR_RD_INDEX_LOW				0x4020
+#define PM2VR_RD_INDEX_HIGH				0x4028
+#define PM2VR_RD_INDEXED_DATA				0x4030
+
 /* Permedia2 RAMDAC indexed registers */
 #define PM2I_RD_CURSOR_CONTROL				0x06
 #define PM2I_RD_COLOR_MODE				0x18
@@ -127,6 +132,28 @@
 #define PM2I_RD_GREEN_KEY				0x43
 #define PM2I_RD_BLUE_KEY				0x44
 
+/* Permedia2v extensions */
+#define PM2VI_RD_MISC_CONTROL				0x000
+#define PM2VI_RD_SYNC_CONTROL				0x001
+#define PM2VI_RD_DAC_CONTROL				0x002
+#define PM2VI_RD_PIXEL_SIZE				0x003
+#define PM2VI_RD_COLOR_FORMAT				0x004
+#define PM2VI_RD_CURSOR_MODE				0x005
+#define PM2VI_RD_CURSOR_X_LOW				0x007
+#define PM2VI_RD_CURSOR_X_HIGH				0x008
+#define PM2VI_RD_CURSOR_Y_LOW				0x009
+#define PM2VI_RD_CURSOR_Y_HIGH				0x00A
+#define PM2VI_RD_CURSOR_X_HOT				0x00B
+#define PM2VI_RD_CURSOR_Y_HOT				0x00C
+#define PM2VI_RD_CLK0_PRESCALE				0x201
+#define PM2VI_RD_CLK0_FEEDBACK				0x202
+#define PM2VI_RD_CLK0_POSTSCALE				0x203
+#define PM2VI_RD_CLK1_PRESCALE				0x204
+#define PM2VI_RD_CLK1_FEEDBACK				0x205
+#define PM2VI_RD_CLK1_POSTSCALE				0x206
+#define PM2VI_RD_CURSOR_PALETTE				0x303
+#define PM2VI_RD_CURSOR_PATTERN				0x400
+
 /* Fields and flags */
 #define PM2F_RENDER_AREASTIPPLE				(1L<<0)
 #define PM2F_RENDER_FASTFILL				(1L<<3)
@@ -178,6 +205,11 @@
 #define PM2F_MEM_BANKS_2				(1L<<29)
 #define PM2F_MEM_BANKS_3				(2L<<29)
 #define PM2F_MEM_BANKS_4				(3L<<29)
+
+typedef enum {
+	PM2_TYPE_PERMEDIA2,
+	PM2_TYPE_PERMEDIA2V
+} pm2type_t;
 
 #endif /* PM2FB_H */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/tcxfb.c linux.14p6/drivers/video/tcxfb.c
--- linux.vanilla/drivers/video/tcxfb.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/drivers/video/tcxfb.c	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: tcxfb.c,v 1.7 1999/01/26 10:55:03 jj Exp $
+/* $Id: tcxfb.c,v 1.7.2.1 1999/09/28 16:00:14 davem Exp $
  * tcxfb.c: TCX 24/8bit frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/binfmt_aout.c linux.14p6/fs/binfmt_aout.c
--- linux.vanilla/fs/binfmt_aout.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/binfmt_aout.c	Wed Oct 27 18:44:04 1999
@@ -296,6 +296,8 @@
 	return sp;
 }
 
+static int warnings = 0;
+
 /*
  * These are the functions used to load a.out style executables and shared
  * libraries.  There is no binary dependent code anywhere else.
@@ -322,6 +324,14 @@
 
 	fd_offset = N_TXTOFF(ex);
 
+#ifdef __i386__
+	if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) {
+		if(warnings++<10)
+			printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n");
+		return -ENOEXEC;
+	}
+#endif
+
 	/* Check initial limits. This avoids letting people circumvent
 	 * size limits imposed on them by creating programs with large
 	 * arrays in the data or bss.
@@ -389,30 +399,22 @@
 		flush_icache_range((unsigned long) 0,
 				   (unsigned long) ex.a_text+ex.a_data);
 	} else {
-		static unsigned long error_time, error_time2;
 		if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
-		    (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ)
-		{
-			printk(KERN_NOTICE "executable not page aligned\n");
-			error_time2 = jiffies;
-		}
+		    (N_MAGIC(ex) != NMAGIC))
+			if(warnings++<10)
+				printk(KERN_NOTICE "executable not page aligned\n");
 
 		fd = open_dentry(bprm->dentry, O_RDONLY);
 		if (fd < 0)
 			return fd;
 		file = fget(fd);
 
-		if ((fd_offset & ~PAGE_MASK) != 0 &&
-		    (jiffies-error_time) > 5*HZ)
-		{
-			printk(KERN_WARNING 
-			       "fd_offset is not page aligned. Please convert program: %s\n",
-			       file->f_dentry->d_name.name
-			       );
-			error_time = jiffies;
-		}
-
-		if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) {
+		if (!file->f_op || !file->f_op->mmap ||
+		    fd_offset & (bprm->dentry->d_inode->i_sb->s_blocksize-1)) {
+			if (warnings++<10)
+				printk(KERN_NOTICE
+				       "fd_offset is not blocksize aligned. Loading %s in anonymous memory.\n",
+				       file->f_dentry->d_name.name);
 			fput(file);
 			sys_close(fd);
 			do_mmap(NULL, N_TXTADDR(ex), ex.a_text+ex.a_data,
@@ -530,16 +532,11 @@
 
 	start_addr =  ex.a_entry & 0xfffff000;
 
-	if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
-		static unsigned long error_time;
-
-		if ((jiffies-error_time) > 5*HZ)
-		{
-			printk(KERN_WARNING 
-			       "N_TXTOFF is not page aligned. Please convert library: %s\n",
+	if (N_TXTOFF(ex) & (inode->i_sb->s_blocksize-1)) {
+		if (warnings++<10)
+			printk(KERN_NOTICE
+			       "N_TXTOFF is not blocksize aligned. Loading library %s in anonymous memory.\n",
 			       file->f_dentry->d_name.name);
-			error_time = jiffies;
-		}
 		do_mmap(NULL, start_addr, ex.a_text + ex.a_data,
 			PROT_READ | PROT_WRITE | PROT_EXEC,
 			MAP_FIXED| MAP_PRIVATE, 0);
@@ -547,8 +544,7 @@
 			  (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;
+		goto map_bss;
 	}
 	/* Now use mmap to map the library into memory. */
 	error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
@@ -559,6 +555,7 @@
 	if (error != start_addr)
 		goto out_putf;
 
+ map_bss:
 	len = PAGE_ALIGN(ex.a_text + ex.a_data);
 	bss = ex.a_text + ex.a_data + ex.a_bss;
 	if (bss > len) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/buffer.c linux.14p6/fs/buffer.c
--- linux.vanilla/fs/buffer.c	Sat Aug 14 02:27:35 1999
+++ linux.14p6/fs/buffer.c	Fri Oct 29 23:52:14 1999
@@ -58,11 +58,12 @@
 /*
  * Hash table mask..
  */
-static unsigned long bh_hash_mask = 0;
+static unsigned int bh_hash_mask = 0;
+static unsigned int bh_hash_shift = 0;
+static struct buffer_head ** hash_table = NULL;
 
 static int grow_buffers(int size);
 
-static struct buffer_head ** hash_table;
 static struct buffer_head * lru_list[NR_LIST] = {NULL, };
 static struct buffer_head * free_list[NR_SIZES] = {NULL, };
 
@@ -420,8 +421,13 @@
 	}
 }
 
-#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask)
-#define hash(dev,block) hash_table[_hashfn(dev,block)]
+/* After several hours of tedious analysis, the following hash
+ * function won.  Do not mess with it... -DaveM
+ */
+#define _hashfn(dev,block)	\
+	((((dev)<<(bh_hash_shift - 6)) ^ ((dev)<<(bh_hash_shift - 9))) ^ \
+	 (((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ ((block) << (bh_hash_shift - 12))))
+#define hash(dev,block) hash_table[_hashfn(dev,block) & bh_hash_mask]
 
 static inline void remove_from_hash_queue(struct buffer_head * bh)
 {
@@ -1506,22 +1512,32 @@
 	   fsync times (ext2) manageable, is the following */
 
 	memory_size >>= 20;
-	for (order = 5; (1UL << order) < memory_size; order++);
+	for (order = 0; (1UL << order) < memory_size; order++);
 
 	/* try to allocate something until we get it or we're asking
            for something that is really too small */
 
 	do {
+		unsigned long tmp;
+
 		nr_hash = (1UL << order) * PAGE_SIZE /
 		    sizeof(struct buffer_head *);
+		bh_hash_mask = (nr_hash - 1);
+
+		tmp = nr_hash;
+		bh_hash_shift = 0;
+		while((tmp >>= 1UL) != 0UL)
+			bh_hash_shift++;
+
 		hash_table = (struct buffer_head **)
 		    __get_free_pages(GFP_ATOMIC, order);
-	} while (hash_table == NULL && --order > 4);
+	} while (hash_table == NULL && --order >= 0);
+	printk("Buffer-cache hash table entries: %d (order: %d, %ld bytes)\n",
+	       nr_hash, order, (1UL<<order) * PAGE_SIZE);
 	
 	if (!hash_table)
 		panic("Failed to allocate buffer hash table\n");
 	memset(hash_table, 0, nr_hash * sizeof(struct buffer_head *));
-	bh_hash_mask = nr_hash-1;
 
 	bh_cachep = kmem_cache_create("buffer_head",
 				      sizeof(struct buffer_head),
@@ -1563,7 +1579,6 @@
 		return;
 	wake_up(&bdflush_wait);
 	if (wait) {
-		run_task_queue(&tq_disk);
 		sleep_on(&bdflush_done);
 	}
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/dcache.c linux.14p6/fs/dcache.c
--- linux.vanilla/fs/dcache.c	Sat Aug 14 02:25:58 1999
+++ linux.14p6/fs/dcache.c	Sat Nov 13 17:46:36 1999
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 
 #include <asm/uaccess.h>
+#include <asm/cache.h>
 
 #define DCACHE_PARANOIA 1
 /* #define DCACHE_DEBUG 1 */
@@ -41,11 +42,12 @@
  * This hash-function tries to avoid losing too many bits of hash
  * information, yet avoid using a prime hash-size or similar.
  */
-#define D_HASHBITS     10
-#define D_HASHSIZE     (1UL << D_HASHBITS)
-#define D_HASHMASK     (D_HASHSIZE-1)
+#define D_HASHBITS     d_hash_shift
+#define D_HASHMASK     d_hash_mask
 
-static struct list_head dentry_hashtable[D_HASHSIZE];
+static unsigned int d_hash_mask;
+static unsigned int d_hash_shift;
+static struct list_head *dentry_hashtable;
 static LIST_HEAD(dentry_unused);
 
 struct {
@@ -69,18 +71,23 @@
  * Release the dentry's inode, using the fileystem
  * d_iput() operation if defined.
  */
-static inline void dentry_iput(struct dentry * dentry)
+static inline int dentry_iput(struct dentry * dentry)
 {
 	struct inode *inode = dentry->d_inode;
+	int ret = 0;
+
 	if (inode) {
 		dentry->d_inode = NULL;
 		list_del(&dentry->d_alias);
 		INIT_LIST_HEAD(&dentry->d_alias);
+		ret = inode->i_count == 1;
 		if (dentry->d_op && dentry->d_op->d_iput)
 			dentry->d_op->d_iput(dentry, inode);
 		else
 			iput(inode);
 	}
+
+	return ret;
 }
 
 /*
@@ -195,86 +202,23 @@
 }
 
 /*
- * Select less valuable dentries to be pruned when we need
- * inodes or memory. The selected dentries are moved to the
- * old end of the list where prune_dcache() can find them.
- * 
- * Negative dentries are included in the selection so that
- * they don't accumulate at the end of the list. The count
- * returned is the total number of dentries selected, which
- * may be much larger than the requested number of inodes.
- */
-int select_dcache(int inode_count, int page_count)
-{
-	struct list_head *next, *tail = &dentry_unused;
-	int found = 0;
-	int depth = dentry_stat.nr_unused >> 1;
-	unsigned long max_value = 4;
-
-	if (page_count)
-		max_value = -1;
-
-	next = tail->prev;
-	while (next != &dentry_unused && depth--) {
-		struct list_head *tmp = next;
-		struct dentry *dentry = list_entry(tmp, struct dentry, d_lru);
-		struct inode *inode = dentry->d_inode;
-		unsigned long value = 0;	
-
-		next = tmp->prev;
-		if (dentry->d_count) {
-			dentry_stat.nr_unused--;
-			list_del(tmp);
-			INIT_LIST_HEAD(tmp);
-			continue;
-		}
-
-		/*
-		 * Select dentries based on the page cache count ...
-		 * should factor in number of uses as well. We take
-		 * all negative dentries so that they don't accumulate.
-		 * (We skip inodes that aren't immediately available.)
-		 */
-		if (inode) {
-			value = inode->i_nrpages;	
-			if (value >= max_value)
-				continue;
-			if (inode->i_state || inode->i_count > 1)
-				continue;
-		}
-
-		/*
-		 * Move the selected dentries behind the tail.
-		 */
-		if (tmp != tail->prev) {
-			list_del(tmp);
-			list_add(tmp, tail->prev);
-		}
-		tail = tmp;
-		found++;
-		if (inode && --inode_count <= 0)
-			break;
-		if (page_count && (page_count -= value) <= 0)
-			break;
-	}
-	return found;
-}
-
-/*
  * Throw away a dentry - free the inode, dput the parent.
  * This requires that the LRU list has already been
  * removed.
  */
-static inline void prune_one_dentry(struct dentry * dentry)
+static inline int prune_one_dentry(struct dentry * dentry)
 {
 	struct dentry * parent;
+	int ret;
 
 	list_del(&dentry->d_hash);
 	list_del(&dentry->d_child);
-	dentry_iput(dentry);
+	ret = dentry_iput(dentry);
 	parent = dentry->d_parent;
 	d_free(dentry);
 	dput(parent);
+
+	return ret;
 }
 
 /*
@@ -282,9 +226,21 @@
  * more memory, or simply when we need to unmount
  * something (at which point we need to unuse
  * all dentries).
+ *
+ * If you don't want a limit on the number of
+ * inodes that will be released then call
+ * with i_nr = -1.
+ *
+ * If you don't want a limit on the number of
+ * dentries that will be released then call
+ * with d_nr = 0.
+ *
+ * Returns the number of inodes released.
  */
-void prune_dcache(int count)
+int prune_dcache(int d_nr, int i_nr)
 {
+	int __i_nr = i_nr;
+
 	for (;;) {
 		struct dentry *dentry;
 		struct list_head *tmp = dentry_unused.prev;
@@ -296,11 +252,15 @@
 		INIT_LIST_HEAD(tmp);
 		dentry = list_entry(tmp, struct dentry, d_lru);
 		if (!dentry->d_count) {
-			prune_one_dentry(dentry);
-			if (!--count)
+			i_nr -= prune_one_dentry(dentry);
+			if (!i_nr)
+				break;
+			if (!--d_nr)
 				break;
 		}
 	}
+
+	return __i_nr - i_nr;
 }
 
 /*
@@ -455,7 +415,7 @@
 	int found;
 
 	while ((found = select_parent(parent)) != 0)
-		prune_dcache(found);
+		prune_dcache(found, -1);
 }
 
 /*
@@ -475,7 +435,7 @@
 		int count = 0;
 		if (priority)
 			count = dentry_stat.nr_unused / priority;
-		prune_dcache(count);
+		prune_dcache(count, -1);
 	}
 }
 
@@ -563,7 +523,7 @@
 
 static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash)
 {
-	hash += (unsigned long) parent;
+	hash += (unsigned long) parent / L1_CACHE_BYTES;
 	hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2);
 	return dentry_hashtable + (hash & D_HASHMASK);
 }
@@ -902,8 +862,10 @@
 
 void __init dcache_init(void)
 {
-	int i;
-	struct list_head *d = dentry_hashtable;
+	int i, order;
+	struct list_head *d;
+	unsigned int nr_hash;
+	unsigned long memory_size;
 
 	/* 
 	 * A constructor could be added for stable state like the lists,
@@ -921,7 +883,34 @@
 	if (!dentry_cache)
 		panic("Cannot create dentry cache");
 
-	i = D_HASHSIZE;
+	memory_size = num_physpages << PAGE_SHIFT;
+	memory_size >>= 13;
+	memory_size *= 2 * sizeof(void *);
+	for (order = 0; ((1UL << order) << PAGE_SHIFT) < memory_size; order++);
+
+	do {
+		unsigned long tmp;
+
+		nr_hash = (1UL << order) * PAGE_SIZE /
+			sizeof(struct list_head);
+		d_hash_mask = (nr_hash - 1);
+
+		tmp = nr_hash;
+		d_hash_shift = 0;
+		while((tmp >>= 1UL) != 0UL)
+			d_hash_shift++;
+
+		dentry_hashtable = (struct list_head *)
+			__get_free_pages(GFP_ATOMIC, order);
+	} while(dentry_hashtable == NULL && --order >= 0);
+	printk("DENTRY hash table entries: %d (order: %d, %ld bytes)\n",
+	       nr_hash, order, (1UL<<order) * PAGE_SIZE);
+
+	if (!dentry_hashtable)
+		panic("Failed to allocate dcache hash table\n");
+
+	d = dentry_hashtable;
+	i = nr_hash;
 	do {
 		INIT_LIST_HEAD(d);
 		d++;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/dquot.c linux.14p6/fs/dquot.c
--- linux.vanilla/fs/dquot.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/dquot.c	Thu Nov 11 21:54:16 1999
@@ -31,6 +31,9 @@
  *		add_dquot_ref() restarts after blocking
  *		Added check for bogus uid and fixed check for group in quotactl.
  *		Jan Kara, <jack@atrey.karlin.mff.cuni.cz>, 4-6/99
+ *		Definitely (hopefully) fixed deadlock in write_dquot(). We no
+ *		longer account quota files.
+ *		Jan Kara, <jack@suse.cz>, 11/99, sposored by SuSE CR
  *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
@@ -245,6 +248,9 @@
 	wake_up(&dquot->dq_wait);
 }
 
+/*
+ *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
+ */
 static void write_dquot(struct dquot *dquot)
 {
 	short type = dquot->dq_type;
@@ -252,20 +258,14 @@
 	mm_segment_t fs;
 	loff_t offset;
 	ssize_t ret;
-	struct dqblk data;
 	struct semaphore *sem = &dquot->dq_mnt->mnt_dquot.dqio_sem;
 
-	/*
-	 * We copy our data to preserve consistency when dquot is not locked.
-	 * We can't lock it because it can cause deadlocks - think about
-	 * growing the quota file...
-	 */
-	memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk));
-	down(sem);
+	lock_dquot(dquot);
 	if (!dquot->dq_mnt) {	/* Invalidated quota? */
-		up(sem);
+		unlock_dquot(dquot);
 		return;
 	}
+	down(sem);
 	filp = dquot->dq_mnt->mnt_dquot.files[type];
 	offset = dqoff(dquot->dq_id);
 	fs = get_fs();
@@ -278,18 +278,15 @@
 	dquot->dq_flags &= ~DQ_MOD;
 	ret = 0;
 	if (filp)
-		ret = filp->f_op->write(filp, (char *)&data, 
+		ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb, 
 					sizeof(struct dqblk), &offset);
 	if (ret != sizeof(struct dqblk))
 		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
 			kdevname(dquot->dq_dev));
 
-	/*
-	 * Be sure that anybody didn't invalidated the dquot in the mean time...
-	 * Nasty but I don't see other choice when we don't want to lock dquot.
-	 */
-	up(sem);
 	set_fs(fs);
+	up(sem);
+	unlock_dquot(dquot);
 
 	dqstats.writes++;
 }
@@ -572,10 +569,9 @@
 	/*
 	 * Try pruning the dcache to free up some dquots ...
 	 */
-	count = select_dcache(128, 0);
-	if (count) {
-		printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count);
-		prune_dcache(count);
+	printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count);
+	if (prune_dcache(0, 128))
+	{
 		free_inode_memory(count);
 		goto repeat;
 	}
@@ -652,12 +648,31 @@
 	return dquot;
 }
 
+/* Check whether this inode is quota file */
+static inline int is_quotafile(struct inode *inode)
+{
+	int cnt;
+	struct vfsmount *vfsmnt;
+	struct file **files;
+
+	vfsmnt = lookup_vfsmnt(inode->i_dev);
+	if (!vfsmnt)
+		return 0;
+	files = vfsmnt->mnt_dquot.files;
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (files[cnt] && files[cnt]->f_dentry->d_inode == inode)
+			return 1;
+	return 0;
+}
+
 static int dqinit_needed(struct inode *inode, short type)
 {
 	int cnt;
 
         if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))
                 return 0;
+	if (is_quotafile(inode))
+		return 0;
 	if (type != -1)
 		return inode->i_dquot[type] == NODQUOT;
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
@@ -1070,6 +1085,9 @@
 	if (S_ISREG(inode->i_mode) ||
             S_ISDIR(inode->i_mode) ||
             S_ISLNK(inode->i_mode)) {
+		/* We don't want to have quotas on quota files - nasty deadlocks possible */
+		if (is_quotafile(inode))
+			return;
 		for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 			if (type != -1 && cnt != type)
 				continue;
@@ -1341,7 +1359,6 @@
 		dquot_incr_blocks(transfer_to[cnt], blocks);
 
 		unlock_dquot(transfer_from[cnt]);
-		dqput(transfer_from[cnt]);
 		if (inode->i_dquot[cnt] != NODQUOT) {
 			struct dquot *temp = inode->i_dquot[cnt];
 			inode->i_dquot[cnt] = transfer_to[cnt];
@@ -1351,20 +1368,20 @@
 			unlock_dquot(transfer_to[cnt]);
 			dqput(transfer_to[cnt]);
 		}
+		dqput(transfer_from[cnt]);
 	}
 
 	return 0;
 put_all:
 	for (disc = 0; disc < cnt; disc++) {
 		/* There should be none or both pointers set but... */
-		if (transfer_to[disc] != NODQUOT) {
+		if (transfer_to[disc] != NODQUOT)
 			unlock_dquot(transfer_to[disc]);
-			dqput(transfer_to[disc]);
-		}
-		if (transfer_from[disc] != NODQUOT) {
+		if (transfer_from[disc] != NODQUOT)
 			unlock_dquot(transfer_from[disc]);
-			dqput(transfer_from[disc]);
-		}
+		/* dqput() tests for NODQUOT itself... */
+		dqput(transfer_from[disc]);
+		dqput(transfer_to[disc]);
 	}
 	return error;
 }
@@ -1513,6 +1530,7 @@
 	error = -EINVAL;
 	if (inode->i_size == 0 || (inode->i_size % sizeof(struct dqblk)) != 0)
 		goto out_f;
+	dquot_drop(inode);	/* We don't want quota on quota files */
 
 	set_enable_flags(vfsmnt, type);
 	mnt_dquot->files[type] = f;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/ext2/balloc.c linux.14p6/fs/ext2/balloc.c
--- linux.vanilla/fs/ext2/balloc.c	Sat Aug 14 02:27:37 1999
+++ linux.14p6/fs/ext2/balloc.c	Sat Nov 13 15:19:43 1999
@@ -603,6 +603,8 @@
 		unlock_super (sb);
 		return 0;
 	}
+	if (!buffer_uptodate(bh))
+		wait_on_buffer(bh);
 	memset(bh->b_data, 0, sb->s_blocksize);
 	mark_buffer_uptodate(bh, 1);
 	mark_buffer_dirty(bh, 1);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/ext2/fsync.c linux.14p6/fs/ext2/fsync.c
--- linux.vanilla/fs/ext2/fsync.c	Wed May 20 21:09:12 1998
+++ linux.14p6/fs/ext2/fsync.c	Sat Nov 13 15:19:43 1999
@@ -45,10 +45,21 @@
 	if (!bh)
 		return 0;
 	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
-		brelse (bh);
-		return -1;
+		/* There can be a parallell read(2) that started read-I/O
+		   on the buffer so we can't assume that there's been
+		   an I/O error without first waiting I/O completation. */
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(bh))
+		{
+			brelse (bh);
+			return -1;
+		}
 	}
 	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
+		if (wait)
+			/* when we return from fsync all the blocks
+			   must be _just_ stored on disk */
+			wait_on_buffer(bh);
 		brelse (bh);
 		return 0;
 	}
@@ -68,10 +79,21 @@
 	if (!bh)
 		return 0;
 	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
-		brelse (bh);
-		return -1;
+		/* There can be a parallell read(2) that started read-I/O
+		   on the buffer so we can't assume that there's been
+		   an I/O error without first waiting I/O completation. */
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(bh))
+		{
+			brelse (bh);
+			return -1;
+		}
 	}
 	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
+		if (wait)
+			/* when we return from fsync all the blocks
+			   must be _just_ stored on disk */
+			wait_on_buffer(bh);
 		brelse (bh);
 		return 0;
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/ext2/ialloc.c linux.14p6/fs/ext2/ialloc.c
--- linux.vanilla/fs/ext2/ialloc.c	Tue Oct 20 22:08:14 1998
+++ linux.14p6/fs/ext2/ialloc.c	Fri Oct 22 21:27:20 1999
@@ -268,21 +268,6 @@
 }
 
 /*
- * This function increments the inode version number
- *
- * This may be used one day by the NFS server
- */
-static void inc_inode_version (struct inode * inode,
-			       struct ext2_group_desc *gdp,
-			       int mode)
-{
-	inode->u.ext2_i.i_version++;
-	mark_inode_dirty(inode);
-
-	return;
-}
-
-/*
  * There are two policies for allocating an inode.  If the new inode is
  * a directory, then a forward search is made for a block group with both
  * free space and a low directory-to-inode ratio; if that fails, then of
@@ -493,8 +478,9 @@
 	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
 		inode->i_flags |= MS_SYNCHRONOUS;
 	insert_inode_hash(inode);
+	inode->i_generation = inode_generation_count++;
+	inode->u.ext2_i.i_version = inode->i_generation;
 	mark_inode_dirty(inode);
-	inc_inode_version (inode, gdp, mode);
 
 	unlock_super (sb);
 	if(DQUOT_ALLOC_INODE(sb, inode)) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/ext2/inode.c linux.14p6/fs/ext2/inode.c
--- linux.vanilla/fs/ext2/inode.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/ext2/inode.c	Sat Nov 13 15:19:43 1999
@@ -51,6 +51,10 @@
 	    inode->i_ino == EXT2_ACL_DATA_INO)
 		return;
 	inode->u.ext2_i.i_dtime	= CURRENT_TIME;
+	/* When we delete an inode, we increment its i_version. If it
+	   is ever read in from disk again, it will have a different
+	   i_version. */
+	inode->u.ext2_i.i_version++;
 	mark_inode_dirty(inode);
 	ext2_update_inode(inode, IS_SYNC(inode));
 	inode->i_size = 0;
@@ -121,6 +125,8 @@
 				    "cannot get block %lu", result);
 			return 0;
 		}
+		if (!buffer_uptodate(bh))
+			wait_on_buffer(bh);
 		memset(bh->b_data, 0, inode->i_sb->s_blocksize);
 		mark_buffer_uptodate(bh, 1);
 		mark_buffer_dirty(bh, 1);
@@ -538,6 +544,7 @@
 #endif
 	}
 	inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version);
+	inode->i_generation = inode->u.ext2_i.i_version;
 	inode->u.ext2_i.i_block_group = block_group;
 	inode->u.ext2_i.i_next_alloc_block = 0;
 	inode->u.ext2_i.i_next_alloc_goal = 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/ext2/ioctl.c linux.14p6/fs/ext2/ioctl.c
--- linux.vanilla/fs/ext2/ioctl.c	Sat Aug 14 02:27:37 1999
+++ linux.14p6/fs/ext2/ioctl.c	Fri Oct 22 21:27:20 1999
@@ -76,6 +76,7 @@
 			return -EROFS;
 		if (get_user(inode->u.ext2_i.i_version, (int *) arg))
 			return -EFAULT;	
+		inode->i_generation = inode->u.ext2_i.i_version;
 		inode->i_ctime = CURRENT_TIME;
 		mark_inode_dirty(inode);
 		return 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/filesystems.c linux.14p6/fs/filesystems.c
--- linux.vanilla/fs/filesystems.c	Sat Aug 14 02:27:37 1999
+++ linux.14p6/fs/filesystems.c	Fri Oct 22 21:27:34 1999
@@ -37,6 +37,7 @@
 #endif
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
+#include <linux/lockd/syscall.h>
 #include <linux/init.h>
 #include <linux/nls.h>
 
@@ -159,28 +160,48 @@
 #ifdef CONFIG_NFSD_MODULE
 int (*do_nfsservctl)(int, void *, void *) = NULL;
 #endif
+
+#ifdef CONFIG_LOCKD_MODULE
+int (*do_lockdctl)(int, void *, void *) = NULL;
+#endif
+
 int
 asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp)
 {
-#ifndef CONFIG_NFSD_MODULE
-	return -ENOSYS;
-#else
 	int ret = -ENOSYS;
 	
+	if (cmd >= NFSCTL_LOCKD) {
+#if defined(CONFIG_LOCKD) || defined(CONFIG_LOCKD_MODULE)
+		lock_kernel();
+#ifdef CONFIG_LOCKD
+		ret = lockdctl(cmd, argp, resp);
+#else
+		if (do_lockdctl) 
+			ret = do_lockdctl(cmd, argp, resp);
+#ifdef CONFIG_KMOD
+		else if (request_module ("lockd") == 0) {
+			if (do_lockdctl)
+				ret = do_lockdctl(cmd, argp, resp);
+		}
+#endif
+#endif
+		unlock_kernel();
+#endif
+		return ret;
+	}
+
+#ifdef CONFIG_NFSD_MODULE
 	lock_kernel();
-	if (do_nfsservctl) {
+	if (do_nfsservctl)
 		ret = do_nfsservctl(cmd, argp, resp);
-		goto out;
-	}
 #ifdef CONFIG_KMOD
-	if (request_module ("nfsd") == 0) {
+	else if (request_module ("nfsd") == 0) {
 		if (do_nfsservctl)
 			ret = do_nfsservctl(cmd, argp, resp);
 	}
 #endif /* CONFIG_KMOD */
-out:
 	unlock_kernel();
-	return ret;
 #endif /* CONFIG_NFSD_MODULE */
+	return ret;
 }
 #endif /* CONFIG_NFSD */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/inode.c linux.14p6/fs/inode.c
--- linux.vanilla/fs/inode.c	Sat Aug 14 02:27:37 1999
+++ linux.14p6/fs/inode.c	Thu Nov 11 21:54:17 1999
@@ -10,6 +10,7 @@
 #include <linux/dcache.h>
 #include <linux/init.h>
 #include <linux/quotaops.h>
+#include <linux/random.h>
 
 /*
  * New inode.c implementation.
@@ -48,6 +49,8 @@
 static LIST_HEAD(inode_unused);
 static struct list_head inode_hashtable[HASH_SIZE];
 
+__u32 inode_generation_count = 0;
+
 /*
  * A simple spinlock to protect the list manipulations.
  *
@@ -340,12 +343,11 @@
 	(((inode)->i_count | (inode)->i_state) == 0)
 #define INODE(entry)	(list_entry(entry, struct inode, i_list))
 
-static int free_inodes(void)
+static int __free_inodes(struct list_head * freeable)
 {
-	struct list_head list, *entry, *freeable = &list;
+	struct list_head *entry;
 	int found = 0;
 
-	INIT_LIST_HEAD(freeable);
 	entry = inode_in_use.next;
 	while (entry != &inode_in_use) {
 		struct list_head *tmp = entry;
@@ -358,15 +360,19 @@
 		INIT_LIST_HEAD(&INODE(tmp)->i_hash);
 		list_add(tmp, freeable);
 		list_entry(tmp, struct inode, i_list)->i_state = I_FREEING;
-		found = 1;
+		found++;
 	}
 
-	if (found)
-		dispose_list(freeable);
-
 	return found;
 }
 
+static void free_inodes(void)
+{
+	LIST_HEAD(throw_away);
+	if (__free_inodes(&throw_away))
+		dispose_list(&throw_away);
+}
+
 /*
  * Searches the inodes list for freeable inodes,
  * shrinking the dcache before (and possible after,
@@ -374,6 +380,36 @@
  */
 static void try_to_free_inodes(int goal)
 {
+	static int block;
+	static struct wait_queue * wait_inode_freeing;
+	LIST_HEAD(throw_away);
+	
+	/* We must make sure to not eat the inodes
+	   while the blocker task sleeps otherwise
+	   the blocker task may find none inode
+	   available. */
+	if (block)
+	{
+		struct wait_queue __wait;
+
+		__wait.task = current;
+		add_wait_queue(&wait_inode_freeing, &__wait);
+		for (;;)
+		{
+			/* NOTE: we rely only on the inode_lock to be sure
+			   to not miss the unblock event */
+			current->state = TASK_UNINTERRUPTIBLE;
+			spin_unlock(&inode_lock);
+			schedule();
+			spin_lock(&inode_lock);
+			if (!block)
+				break;
+		}
+		remove_wait_queue(&wait_inode_freeing, &__wait);
+		current->state = TASK_RUNNING;
+	}
+
+	block = 1;
 	/*
 	 * First stry to just get rid of unused inodes.
 	 *
@@ -381,16 +417,18 @@
 	 * to try to shrink the dcache and sync existing
 	 * inodes..
 	 */
-	free_inodes();
-	goal -= inodes_stat.nr_free_inodes;
+	goal -= __free_inodes(&throw_away);
 	if (goal > 0) {
 		spin_unlock(&inode_lock);
-		select_dcache(goal, 0);
-		prune_dcache(goal);
+		prune_dcache(0, goal);
 		spin_lock(&inode_lock);
 		sync_all_inodes();
-		free_inodes();
+		__free_inodes(&throw_away);
 	}
+	if (!list_empty(&throw_away))
+		dispose_list(&throw_away);
+	block = 0;
+	wake_up(&wait_inode_freeing);
 }
 
 /*
@@ -435,6 +473,10 @@
 			inode = list_entry(tmp, struct inode, i_list);
 			return inode;
 		}
+		spin_unlock(&inode_lock);
+		printk(KERN_WARNING
+		       "grow_inodes: inode-max limit reached\n");
+		return NULL;
 	}
 		
 	spin_unlock(&inode_lock);
@@ -465,7 +507,7 @@
 	 * If the allocation failed, do an extensive pruning of 
 	 * the dcache and then try again to free some inodes.
 	 */
-	prune_dcache(inodes_stat.nr_inodes >> 2);
+	prune_dcache(0, inodes_stat.nr_inodes >> 2);
 
 	spin_lock(&inode_lock);
 	free_inodes();
@@ -809,6 +851,10 @@
 	if (max > MAX_INODE)
 		max = MAX_INODE;
 	max_inodes = max;
+
+	/* Get a random number. */
+	get_random_bytes (&inode_generation_count,
+			  sizeof (inode_generation_count));
 }
 
 /* This belongs in file_table.c, not here... */
@@ -844,3 +890,48 @@
     inode->i_atime = CURRENT_TIME;
     mark_inode_dirty (inode);
 }   /*  End Function update_atime  */
+
+/* This function is called by nfsd. */
+struct inode *iget_in_use(struct super_block *sb, unsigned long ino)
+{
+	struct list_head * head = inode_hashtable + hash(sb,ino);
+	struct inode * inode;
+
+	spin_lock(&inode_lock);
+	inode = find_inode(sb, ino, head);
+	if (inode) {
+		spin_unlock(&inode_lock);
+		wait_on_inode(inode);
+	}
+	else
+		inode = get_new_inode (sb, ino, head);
+
+	/* When we get the inode, we have to check if it is in use. We
+	   have to release it if it is not. */
+	if (inode) {
+		spin_lock(&inode_lock);
+		if (inode->i_nlink == 0 && inode->i_count == 1) {
+			--inode->i_count;
+			list_del(&inode->i_hash);
+			INIT_LIST_HEAD(&inode->i_hash);
+			list_del(&inode->i_list);
+			INIT_LIST_HEAD(&inode->i_list);
+			if (list_empty(&inode->i_hash)) {
+				list_del(&inode->i_list);
+				INIT_LIST_HEAD(&inode->i_list);
+				spin_unlock(&inode_lock);
+				clear_inode(inode);
+				spin_lock(&inode_lock);
+				list_add(&inode->i_list, &inode_unused);
+				inodes_stat.nr_free_inodes++;
+			}
+			else if (!(inode->i_state & I_DIRTY)) {
+				list_del(&inode->i_list);
+				list_add(&inode->i_list, &inode_in_use);
+			}
+			inode = NULL;
+		}
+		spin_unlock(&inode_lock);
+	}
+	return inode;
+}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/isofs/inode.c linux.14p6/fs/isofs/inode.c
--- linux.vanilla/fs/isofs/inode.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/isofs/inode.c	Sat Nov 13 15:18:40 1999
@@ -954,7 +954,7 @@
 	printk("isofs_bmap: mapped inode:block %x:%d to block %lu\n",
 		inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode));
 #endif
-	return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode);
+	return firstext + ((b_off - offset) >> ISOFS_BUFFER_BITS(inode));
 }
 
 
@@ -1162,8 +1162,7 @@
 	  iso_date(raw_inode->date, high_sierra);
 
 	inode->u.isofs_i.i_first_extent = (isonum_733 (raw_inode->extent) +
-					   isonum_711 (raw_inode->ext_attr_length))
-	  << inode -> i_sb -> u.isofs_sb.s_log_zone_size;
+					   isonum_711 (raw_inode->ext_attr_length));
 
 /* Now test for possible Rock Ridge extensions which will override some of
    these numbers in the inode structure. */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/isofs/rock.c linux.14p6/fs/isofs/rock.c
--- linux.vanilla/fs/isofs/rock.c	Sat Sep 26 00:27:13 1998
+++ linux.14p6/fs/isofs/rock.c	Sat Nov 13 15:16:52 1999
@@ -358,9 +358,10 @@
 #ifdef DEBUG
 	printk("RR CL (%x)\n",inode->i_ino);
 #endif
-	inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) <<
-		inode -> i_sb -> u.isofs_sb.s_log_zone_size;
-	reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent);
+	inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location);
+	reloc = iget(inode->i_sb,
+		     (inode->u.isofs_i.i_first_extent <<
+		      inode -> i_sb -> u.isofs_sb.s_log_zone_size));
 	if (!reloc)
 		goto out;
 	inode->i_mode = reloc->i_mode;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/lockd/lockd_syms.c linux.14p6/fs/lockd/lockd_syms.c
--- linux.vanilla/fs/lockd/lockd_syms.c	Mon Apr  7 19:35:30 1997
+++ linux.14p6/fs/lockd/lockd_syms.c	Fri Oct 22 21:28:11 1999
@@ -23,6 +23,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
+#include <linux/lockd/syscall.h>
 
 /* Start/stop the daemon */
 EXPORT_SYMBOL(lockd_up);
@@ -34,6 +35,7 @@
 /* NFS server entry points/hooks */
 EXPORT_SYMBOL(nlmsvc_invalidate_client);
 EXPORT_SYMBOL(nlmsvc_ops);
+EXPORT_SYMBOL(lockdctl);
 
 /* Configuration at insmod time */
 EXPORT_SYMBOL(nlmsvc_grace_period);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/lockd/mon.c linux.14p6/fs/lockd/mon.c
--- linux.vanilla/fs/lockd/mon.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/lockd/mon.c	Sat Nov 13 15:31:54 1999
@@ -195,6 +195,12 @@
 #define SM_my_id_sz	(3+1+SM_my_name_sz)
 #define SM_mon_id_sz	(1+XDR_QUADLEN(20)+SM_my_id_sz)
 #define SM_mon_sz	(SM_mon_id_sz+4)
+#define SM_monres_sz	2
+#define SM_unmonres_sz	1
+
+#ifndef MAX
+# define MAX(a, b)	(((a) > (b))? (a) : (b))
+#endif
 
 static struct rpc_procinfo	nsm_procedures[] = {
         { "sm_null",
@@ -205,10 +211,10 @@
 		(kxdrproc_t) xdr_error, 0, 0 },
         { "sm_mon",
 		(kxdrproc_t) xdr_encode_mon,
-		(kxdrproc_t) xdr_decode_stat_res, SM_mon_sz, 2 },
+		(kxdrproc_t) xdr_decode_stat_res, MAX(SM_mon_sz, SM_monres_sz) << 2, 0 },
         { "sm_unmon",
 		(kxdrproc_t) xdr_encode_mon,
-		(kxdrproc_t) xdr_decode_stat, SM_mon_id_sz, 1 },
+		(kxdrproc_t) xdr_decode_stat, MAX(SM_mon_id_sz, SM_unmonres_sz) << 2, 0 },
         { "sm_unmon_all",
 		(kxdrproc_t) xdr_error,
 		(kxdrproc_t) xdr_error, 0, 0 },
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/lockd/svc.c linux.14p6/fs/lockd/svc.c
--- linux.vanilla/fs/lockd/svc.c	Sat Feb  6 20:46:21 1999
+++ linux.14p6/fs/lockd/svc.c	Fri Oct 22 21:28:11 1999
@@ -32,6 +32,7 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/lockd/lockd.h>
+#include <linux/lockd/syscall.h>
 #include <linux/nfs.h>
 
 #define NLMDBG_FACILITY		NLMDBG_SVC
@@ -315,6 +316,9 @@
   MODULE_PARM(nlm_grace_period, "10-240l");
   MODULE_PARM(nlm_timeout, "3-20l");
 #endif
+
+extern int (*do_lockdctl)(int, void *, void *);
+
 int
 init_module(void)
 {
@@ -324,6 +328,7 @@
 	nlmsvc_pid = 0;
 	lockd_exit = NULL;
 	nlmxdr_init();
+	do_lockdctl = lockdctl;
 	return 0;
 }
 
@@ -332,6 +337,7 @@
 {
 	/* FIXME: delete all NLM clients */
 	nlm_shutdown_hosts();
+	do_lockdctl = NULL;
 }
 #endif
 
@@ -370,3 +376,23 @@
 	"lockd",		/* service name */
 	&nlmsvc_stats,		/* stats table */
 };
+
+int
+lockdctl(int cmd, void *opaque_argp, void *opaque_resp)
+{
+	int err;
+
+	MOD_INC_USE_COUNT;
+
+	switch(cmd) {
+	case LOCKDCTL_SVC:
+		err = lockd_up ();
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	MOD_DEC_USE_COUNT;
+
+	return err;
+}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/locks.c linux.14p6/fs/locks.c
--- linux.vanilla/fs/locks.c	Sat Aug 14 02:27:37 1999
+++ linux.14p6/fs/locks.c	Fri Oct 22 21:28:11 1999
@@ -688,8 +688,11 @@
 
 	if (((start += l->l_start) < 0) || (l->l_len < 0))
 		return (0);
+	fl->fl_end = start + l->l_len - 1;
+	if (l->l_len > 0 && fl->fl_end < 0)
+		return (0);
 	fl->fl_start = start;	/* we record the absolute position */
-	if ((l->l_len == 0) || ((fl->fl_end = start + l->l_len - 1) < 0))
+	if (l->l_len == 0)
 		fl->fl_end = OFFSET_MAX;
 	
 	fl->fl_file = filp;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfs/dir.c linux.14p6/fs/nfs/dir.c
--- linux.vanilla/fs/nfs/dir.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/nfs/dir.c	Tue Nov 16 15:16:06 1999
@@ -526,29 +526,6 @@
 	NULL			/* d_iput */
 };
 
-#ifdef NFS_PARANOIA
-/*
- * Display all dentries holding the specified inode.
- */
-static void show_dentry(struct list_head * dlist)
-{
-	struct list_head *tmp = dlist;
-
-	while ((tmp = tmp->next) != dlist) {
-		struct dentry * dentry = list_entry(tmp, struct dentry, d_alias);
-		const char * unhashed = "";
-
-		if (list_empty(&dentry->d_hash))
-			unhashed = "(unhashed)";
-
-		printk("show_dentry: %s/%s, d_count=%d%s\n",
-			dentry->d_parent->d_name.name,
-			dentry->d_name.name, dentry->d_count,
-			unhashed);
-	}
-}
-#endif
-
 static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
 {
 	struct inode *inode;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfs/inode.c linux.14p6/fs/nfs/inode.c
--- linux.vanilla/fs/nfs/inode.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/nfs/inode.c	Fri Oct 22 21:28:40 1999
@@ -134,8 +134,10 @@
 	if ((rpc = server->client) != NULL)
 		rpc_shutdown_client(rpc);
 
+#if 0
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_down();	/* release rpc.lockd */
+#endif
 	rpciod_down();		/* release rpciod */
 	/*
 	 * Invalidate the dircache for this superblock.
@@ -311,9 +313,11 @@
 	/* We're airborne */
 	unlock_super(sb);
 
+#if 0
 	/* Check whether to start the lockd process */
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_up();
+#endif
 	return sb;
 
 	/* Yargs. It didn't work out. */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfs/mount_clnt.c linux.14p6/fs/nfs/mount_clnt.c
--- linux.vanilla/fs/nfs/mount_clnt.c	Wed Aug 26 18:10:33 1998
+++ linux.14p6/fs/nfs/mount_clnt.c	Wed Nov  3 00:15:29 1999
@@ -23,6 +23,11 @@
 # define NFSDBG_FACILITY	NFSDBG_ROOT
 #endif
 
+#ifndef MAX
+# define MAX(a, b)	(((a) > (b))? (a) : (b))
+#endif
+
+
 /*
 #define MOUNT_PROGRAM		100005
 #define MOUNT_VERSION		1
@@ -119,7 +124,7 @@
 	{ "mnt_mount",
 		(kxdrproc_t) xdr_encode_dirpath,	
 		(kxdrproc_t) xdr_decode_fhstatus,
-		MNT_dirpath_sz, MNT_fhstatus_sz },
+		MAX(MNT_dirpath_sz, MNT_fhstatus_sz)<<2, 0},
 };
 
 static struct rpc_version	mnt_version1 = {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfsd/nfsctl.c linux.14p6/fs/nfsd/nfsctl.c
--- linux.vanilla/fs/nfsd/nfsctl.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/nfsd/nfsctl.c	Fri Oct 22 21:29:04 1999
@@ -11,7 +11,7 @@
 #include <linux/module.h>
 #include <linux/version.h>
 
-#include <linux/linkage.h>
+#include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -29,6 +29,7 @@
 #include <linux/nfsd/cache.h>
 #include <linux/nfsd/xdr.h>
 #include <linux/nfsd/syscall.h>
+#include <linux/lockd/syscall.h>
 
 #if LINUX_VERSION_CODE >= 0x020100
 #include <asm/uaccess.h>
@@ -40,7 +41,6 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 
-extern void nfsd_fh_init(void);
 extern long sys_call_table[];
 
 static int	nfsctl_svc(struct nfsctl_svc *data);
@@ -57,15 +57,72 @@
 int exp_procfs_exports(char *buffer, char **start, off_t offset,
                              int length, int *eof, void *data);
 
-void proc_export_init(void)
+struct long_maxmin
 {
-	struct proc_dir_entry *nfs_export_ent = NULL;
+	long *value;
+	long min_value;
+	long max_value;
+};
+
+/* In seconds. */
+#define TIME_DIFF_MARGIN	10
+#define MIN_TIME_DIFF_MARGIN	0
+#define MAX_TIME_DIFF_MARGIN	600
+
+long nfsd_time_diff_margin = TIME_DIFF_MARGIN;
+
+static struct long_maxmin time_diff_margin =
+{
+	&nfsd_time_diff_margin,
+	MIN_TIME_DIFF_MARGIN,
+	MAX_TIME_DIFF_MARGIN
+};
+
+static int
+nfsd_proc_read_long_maxmin(char *buffer, char **start, off_t offset,
+			  int length, int *eof, void *data)
+{
+	int len;
+	struct long_maxmin *x = (struct long_maxmin *) data;
+	len = sprintf(buffer, "%ld\n", *x->value) - offset;
+	if (len < length) {
+		*eof = 1;
+		if (len <= 0)
+			 return 0;
+	} else
+		len = length;
+	*start = buffer + offset;
+	return len;
+}
+
+static int
+nfsd_proc_write_long_maxmin(struct file *file, const char *buffer,
+			   unsigned long count, void *data)
+{
+	long v;
+	struct long_maxmin *x = (struct long_maxmin *) data;
+	v = simple_strtoul(buffer, NULL, 10);
+	if (v < x->min_value || v > x->max_value)
+		return -EINVAL;
+	*x->value = v;
+	return count;
+}
+
+static void nfsd_proc_init(void)
+{
+	struct proc_dir_entry *nfsd_ent = NULL;
 
-	if (!(nfs_export_ent = create_proc_entry("fs/nfs", S_IFDIR, 0)))
+	if (!(nfsd_ent = create_proc_entry("fs/nfs", S_IFDIR, 0)))
 		return;
-	if (!(nfs_export_ent = create_proc_entry("fs/nfs/exports", 0, 0)))
+	if (!(nfsd_ent = create_proc_entry("fs/nfs/exports", 0, 0)))
 		return;
-	nfs_export_ent->read_proc = exp_procfs_exports;
+	nfsd_ent->read_proc = exp_procfs_exports;
+	if (!(nfsd_ent = create_proc_entry("fs/nfs/time-diff-margin",
+					   S_IFREG|S_IRUGO|S_IWUSR, 0)))
+		return;
+	nfsd_ent->read_proc = nfsd_proc_read_long_maxmin;
+	nfsd_ent->write_proc = nfsd_proc_write_long_maxmin;
+	nfsd_ent->data = (void *) &time_diff_margin;
 }
 
 
@@ -80,8 +137,7 @@
 	nfsd_cache_init();	/* RPC reply cache */
 	nfsd_export_init();	/* Exports table */
 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
-	nfsd_fh_init();		/* FH table */
-	proc_export_init();
+	nfsd_proc_init();
 	initialized = 1;
 }
 
@@ -209,6 +265,11 @@
 		goto done;
 	}
 
+	if (cmd >= NFSCTL_LOCKD) {
+		err = lockdctl(cmd, argp, resp);
+		goto done;
+	}
+
 	switch(cmd) {
 	case NFSCTL_SVC:
 		err = nfsctl_svc(&arg->ca_svc);
@@ -303,6 +364,7 @@
 	nfsd_export_shutdown();
 	nfsd_cache_shutdown();
 	nfsd_fh_free();
+	remove_proc_entry("fs/nfs/time-diff-margin", NULL);
 	remove_proc_entry("fs/nfs/exports", NULL);
 	remove_proc_entry("fs/nfs", NULL);
 	nfsd_stat_shutdown();
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfsd/nfsfh.c linux.14p6/fs/nfsd/nfsfh.c
--- linux.vanilla/fs/nfsd/nfsfh.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/nfsd/nfsfh.c	Fri Oct 22 21:29:04 1999
@@ -22,6 +22,7 @@
 #define NFSDDBG_FACILITY		NFSDDBG_FH
 #define NFSD_PARANOIA 1
 /* #define NFSD_DEBUG_VERBOSE 1 */
+/* #define NFSD_DEBUG_VERY_VERBOSE 1 */
 
 extern unsigned long max_mapnr;
 
@@ -34,16 +35,16 @@
 	kdev_t	dev;
 };
 
-#define NFSD_MAXFH PAGE_SIZE/sizeof(struct fh_entry)
-static struct fh_entry filetable[NFSD_MAXFH];
-static struct fh_entry dirstable[NFSD_MAXFH];
+#define NFSD_MAXFH \
+  (((nfsd_nservers + 1) >> 1) * PAGE_SIZE/sizeof(struct fh_entry))
+static struct fh_entry *filetable = NULL;
+static struct fh_entry *dirstable = NULL;
 
 static int nfsd_nr_verified = 0;
 static int nfsd_nr_put = 0;
 static unsigned long nfsd_next_expire = 0;
 
 static int add_to_fhcache(struct dentry *, int);
-static int nfsd_d_validate(struct dentry *);
 struct dentry * lookup_inode(kdev_t, ino_t, ino_t);
 
 static LIST_HEAD(fixup_head);
@@ -51,15 +52,16 @@
 static int nfsd_nr_fixups = 0;
 static int nfsd_nr_paths = 0;
 #define NFSD_MAX_PATHS 500
-#define NFSD_MAX_FIXUPAGE 60*HZ
+#define NFSD_MAX_FIXUPS 500
+#define NFSD_MAX_FIXUP_AGE 30*HZ
 
 struct nfsd_fixup {
 	struct list_head lru;
-	ino_t	dir;
+	unsigned long reftime;
+	ino_t	dirino;
 	ino_t	ino;
 	kdev_t	dev;
-	struct dentry *dentry;
-	unsigned long reftime;
+	ino_t	new_dirino;
 };
 
 struct nfsd_path {
@@ -71,7 +73,8 @@
 	char	name[1];
 };
 
-static struct nfsd_fixup * find_cached_lookup(kdev_t dev, ino_t dir, ino_t ino)
+static struct nfsd_fixup *
+find_cached_lookup(kdev_t dev, ino_t dirino, ino_t ino)
 {
 	struct list_head *tmp = fixup_head.next;
 
@@ -79,32 +82,43 @@
 		struct nfsd_fixup *fp;
 
 		fp = list_entry(tmp, struct nfsd_fixup, lru);
+#ifdef NFSD_DEBUG_VERY_VERBOSE
+printk("fixup %lu %lu, %lu %lu %s %s\n",
+        fp->ino, ino,
+	fp->dirino, dirino,
+	kdevname(fp->dev), kdevname(dev));
+#endif
 		if (fp->ino != ino)
 			continue;
-		if (fp->dir != dir)
+		if (fp->dirino != dirino)
 			continue;
 		if (fp->dev != dev)
 			continue;
+		fp->reftime = jiffies;	
 		list_del(tmp);
 		list_add(tmp, &fixup_head);
-		fp->reftime = jiffies;
 		return fp;
 	}
 	return NULL;
 }
 
 /*
- * Save the dentry pointer from a successful lookup.
+ * Save the dirino from a rename.
  */
-static void add_to_lookup_cache(struct dentry *dentry, struct knfs_fh *fh)
+void
+add_to_rename_cache(ino_t new_dirino,
+                    kdev_t dev, ino_t dirino, ino_t ino)
 {
 	struct nfsd_fixup *fp;
 
-	fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev), 
-				u32_to_ino_t(fh->fh_dirino),
-				u32_to_ino_t(fh->fh_ino));
+	if (dirino == new_dirino)
+		return;
+
+	fp = find_cached_lookup(dev, 
+				dirino,
+				ino);
 	if (fp) {
-		fp->dentry = dentry;
+		fp->new_dirino = new_dirino;
 		return;
 	}
 
@@ -115,19 +129,30 @@
 	 */
 	fp = kmalloc(sizeof(struct nfsd_fixup), GFP_KERNEL);
 	if (fp) {
-		fp->dir = u32_to_kdev_t(fh->fh_dirino);
-		fp->ino = u32_to_ino_t(fh->fh_ino);
-		fp->dev = u32_to_ino_t(fh->fh_dev);
-		fp->dentry = dentry;
-		fp->reftime = jiffies;
+		fp->dirino = dirino;
+		fp->ino = ino;
+		fp->dev = dev;
+		fp->new_dirino = new_dirino;
 		list_add(&fp->lru, &fixup_head);
 		nfsd_nr_fixups++;
 	}
 }
 
+/*
+ * Save the dentry pointer from a successful lookup.
+ */
+
 static void free_fixup_entry(struct nfsd_fixup *fp)
 {
 	list_del(&fp->lru);
+#ifdef NFSD_DEBUG_VERY_VERBOSE
+printk("free_rename_entry: %lu->%lu %lu/%s\n",
+		fp->dirino,
+		fp->new_dirino,
+		fp->ino,
+		kdevname(fp->dev),
+		(jiffies - fp->reftime));
+#endif
 	kfree(fp);
 	nfsd_nr_fixups--;
 }
@@ -212,9 +237,9 @@
 	if (new) {
 		new->users = 0;	
 		new->reftime = jiffies;	
-		new->ino = inode->i_ino;	
-		new->dev = inode->i_dev;	
-		result = copy_path(new->name, dentry, len);	
+		new->ino = inode->i_ino;
+		new->dev = inode->i_dev;
+		result = copy_path(new->name, dentry, len);
 		if (!result)
 			goto retry;
 		list_add(&new->lru, &path_inuse);
@@ -301,8 +326,8 @@
 	int result = 0;
 
 	buf->sequence++;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
+#ifdef NFSD_DEBUG_VERY_VERBOSE
+printk("filldir_one: seq=%d, ino=%lu, name=%s\n", buf->sequence, ino, name);
 #endif
 	if (buf->sequence == 2) {
 		buf->dirino = ino;
@@ -311,7 +336,7 @@
 	if (dirent->ino == ino) {
 		dirent->len = len;
 		memcpy(dirent->name, name, len);
-		dirent->name[len] = 0;
+		dirent->name[len] = '\0';
 		buf->found = 1;
 		result = -1;
 	}
@@ -431,7 +456,7 @@
 		}
 
 		result = ERR_PTR(-ENOENT);
-		dir = iget(sb, dirino);
+		dir = iget_in_use(sb, dirino);
 		if (!dir)
 			goto out_root;
 		dentry = d_alloc_root(dir, NULL);
@@ -483,7 +508,7 @@
 	iput(dir);
 out_root:
 	dput(root);
-	goto out;
+	goto out_page;
 }
 
 /*
@@ -521,7 +546,8 @@
 	struct dentry *dentry = empty->dentry;
 
 #ifdef NFSD_DEBUG_VERBOSE
-printk("expire_fhe: expiring %s/%s, d_count=%d, ino=%ld\n",
+printk("expire_fhe: expiring %s %s/%s, d_count=%d, ino=%lu\n",
+(cache == NFSD_FILE_CACHE) ? "file" : "dir",
 dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
 #endif
 	empty->dentry = NULL;	/* no dentry */
@@ -576,11 +602,10 @@
  */
 static void expire_old(int cache, int age)
 {
-	struct list_head *tmp;
 	struct fh_entry *fhe;
 	int i;
 
-#ifdef NFSD_DEBUG_VERBOSE
+#ifdef NFSD_DEBUG_VERY_VERBOSE
 printk("expire_old: expiring %s older than %d\n",
 (cache == NFSD_FILE_CACHE) ? "file" : "dir", age);
 #endif
@@ -593,12 +618,12 @@
 	}
 
 	/*
-	 * Remove old entries from the patch-up cache.
+	 * Trim the fixup cache ...
 	 */
-	while ((tmp = fixup_head.prev) != &fixup_head) {
+	while (nfsd_nr_fixups > NFSD_MAX_FIXUPS) {
 		struct nfsd_fixup *fp;
-		fp = list_entry(tmp, struct nfsd_fixup, lru);
-		if ((jiffies - fp->reftime) < NFSD_MAX_FIXUPAGE)
+		fp = list_entry(fixup_head.prev, struct nfsd_fixup, lru);
+		if ((jiffies - fp->reftime) < NFSD_MAX_FIXUP_AGE)
 			break;
 		free_fixup_entry(fp);
 	}
@@ -736,15 +761,21 @@
 					dget(dentry);
 					nfsd_nr_verified++;
 				}
+				put_path(pe);
 			} else {
 				dput(res);
+				put_path(pe);
+				/* We should delete it from the cache. */
+				free_path_entry(pe);
 			}
 		} else {
 #ifdef NFSD_PARANOIA
 printk("find_dentry_by_ino: %s lookup failed\n", pe->name);
 #endif
+			put_path(pe);
+			/* We should delete it from the cache. */
+			free_path_entry(pe);
 		}
-		put_path(pe);
 	}
 out:
 	return dentry;
@@ -757,6 +788,8 @@
  */
 static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh)
 {
+/* FIXME: this must use the dev/ino/dir_ino triple. */ 
+#if 0
 	struct fh_entry * fhe;
 
 	fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL);
@@ -794,6 +827,7 @@
 		return dentry;
 	}
 out:
+#endif
 	return NULL;
 }
 
@@ -822,7 +856,7 @@
 printk("lookup_by_inode: found %s\n", dirent.name);
 #endif
 
-	dentry = lookup_dentry(dirent.name, dget(parent), 0);
+	dentry = lookup_dentry(dirent.name, parent, 0);
 	if (!IS_ERR(dentry)) {
 		if (dentry->d_inode && dentry->d_inode->i_ino == ino)
 			goto out;
@@ -842,13 +876,12 @@
 	dentry = NULL;
 out:
 	return dentry;
-
 }
 
 /*
  * Search the fix-up list for a dentry from a prior lookup.
  */
-static struct dentry *nfsd_cached_lookup(struct knfs_fh *fh)
+static ino_t nfsd_cached_lookup(struct knfs_fh *fh)
 {
 	struct nfsd_fixup *fp;
 
@@ -856,8 +889,8 @@
 				u32_to_ino_t(fh->fh_dirino),
 				u32_to_ino_t(fh->fh_ino));
 	if (fp)
-		return fp->dentry;
-	return NULL;
+		return fp->new_dirino;
+	return 0;
 }
 
 void
@@ -916,8 +949,12 @@
 static struct dentry *
 find_fh_dentry(struct knfs_fh *fh)
 {
+	struct super_block *sb;
 	struct dentry *dentry, *parent;
+	struct inode * inode;
+	struct list_head *lst;
 	int looked_up = 0, retry = 0;
+	ino_t dirino;
 
 	/*
 	 * Stage 1: Look for the dentry in the short-term fhcache.
@@ -927,50 +964,73 @@
 		nfsdstats.fh_cached++;
 		goto out;
 	}
-
 	/*
-	 * Stage 2: Attempt to validate the dentry in the file handle.
+	 * Stage 2: Attempt to find the inode.
 	 */
-	dentry = fh->fh_dcookie;
+	sb = get_super(fh->fh_dev);
+	if (NULL == sb) {
+		printk("find_fh_dentry: No SuperBlock for device %s.",
+		       kdevname(fh->fh_dev));
+		dentry = NULL;
+		goto out;
+	}
+
+	dirino = u32_to_ino_t(fh->fh_dirino);
+	inode = iget_in_use(sb, fh->fh_ino);
+	if (!inode) {
+		dprintk("find_fh_dentry: No inode found.\n");
+		goto out_five;
+	}
+	goto check;
 recheck:
-	if (nfsd_d_validate(dentry)) {
-		struct inode * dir = dentry->d_parent->d_inode;
+	if (!inode) {
+		dprintk("find_fh_dentry: No inode found.\n");
+		goto out_three;
+	}
+check:
+	for (lst = inode->i_dentry.next;
+	     lst != &inode->i_dentry;
+	     lst = lst->next) {
+		dentry = list_entry(lst, struct dentry, d_alias);
+
+/* if we are looking up a directory then we don't need the parent! */
+		if (!dentry ||
+		    !dentry->d_parent ||
+		    !dentry->d_parent->d_inode) {
+printk("find_fh_dentry: Found a useless inode %lu\n", inode->i_ino);
+			continue;
+		}
+		if (dentry->d_parent->d_inode->i_ino != dirino)
+			continue;
 
-		if (dir->i_ino == u32_to_ino_t(fh->fh_dirino) && 
-		    dir->i_dev == u32_to_kdev_t(fh->fh_dev)) {
-			struct inode * inode = dentry->d_inode;
-			/*
-			 * NFS file handles must always have an inode,
-			 * so we won't accept a negative dentry.
-			 */
-			if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) {
-				dget(dentry);
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: validated %s/%s, ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino);
-#endif
-				if (!retry)
-					nfsdstats.fh_valid++;
-				else {
-					nfsdstats.fh_fixup++;
+		dget(dentry);
+		iput(inode);
 #ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: retried validation successful\n");
+		printk("find_fh_dentry: Found%s %s/%s filehandle dirino = %lu, %lu\n",
+		       retry ? " Renamed" : "",
+		       dentry->d_parent->d_name.name,
+		       dentry->d_name.name,
+		       dentry->d_parent->d_inode->i_ino,
+		       dirino);
 #endif
-				}
-				goto out;
-			}
-		}
-	}
+		goto out;
+	} /* for inode->i_dentry */
 
 	/*
-	 * Before proceeding to a lookup, check whether we cached a
-	 * prior lookup. If so, try to validate that dentry ...
+	 * Before proceeding to a lookup, check for a rename
 	 */
-	if (!retry && (dentry = nfsd_cached_lookup(fh)) != NULL) {
+	if (!retry && (dirino = nfsd_cached_lookup(fh))) {
+		dprintk("find_fh_dentry: retry with %lu\n", dirino);
 		retry = 1;
 		goto recheck;
 	}
 
+	iput(inode);
+
+	dprintk("find_fh_dentry: dirino not found %lu\n", dirino);
+
+out_three:
+
 	/*
 	 * Stage 3: Look up the dentry based on the inode and parent inode
 	 * numbers. This should work for all Unix-like filesystems.
@@ -983,7 +1043,7 @@
 		struct inode * inode = dentry->d_inode;
 #ifdef NFSD_DEBUG_VERBOSE
 printk("find_fh_dentry: looked up %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 		if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) {
 			nfsdstats.fh_lookup++;
@@ -991,7 +1051,7 @@
 		}
 #ifdef NFSD_PARANOIA
 printk("find_fh_dentry: %s/%s lookup mismatch!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 		dput(dentry);
 	}
@@ -1005,29 +1065,27 @@
 		/*
 		 * ... then search for the inode in the parent directory.
 		 */
+		dget(parent);
 		dentry = lookup_by_inode(parent, u32_to_ino_t(fh->fh_ino));
 		dput(parent);
 		if (dentry)
 			goto out;
 	}
 
+out_five:
+
 	/*
-	 * Stage 5: Search the whole volume.
+	 * Stage 5: Search the whole volume, Yea Right.
 	 */
 #ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s, %u/%u not found -- need full search!\n",
-kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_dirino, fh->fh_ino);
+printk("find_fh_dentry: %s/%u dir/%u not found!\n",
+       kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino, fh->fh_dirino);
 #endif
 	dentry = NULL;
 	nfsdstats.fh_stale++;
 	
 out:
-	if (looked_up && dentry) {
-		add_to_lookup_cache(dentry, fh);
-	}
-
 	expire_all();
-
 	return dentry;
 }
 
@@ -1046,8 +1104,12 @@
 	struct inode	*inode;
 	u32		error = 0;
 
-	dprintk("nfsd: fh_verify(exp %x/%u cookie %p)\n",
-		fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
+	dprintk("nfsd: fh_verify(exp %s/%u file (%s/%u dir %u)\n",
+		kdevname(fh->fh_xdev),
+		fh->fh_xino,
+		kdevname(fh->fh_dev),
+		fh->fh_ino,
+		fh->fh_dirino);
 
 	if (fhp->fh_dverified)
 		goto check_type;
@@ -1056,10 +1118,13 @@
 	 */
 	error = nfserr_stale;
 	exp = exp_get(rqstp->rq_client,
-			u32_to_kdev_t(fh->fh_xdev),
-			u32_to_ino_t(fh->fh_xino));
-	if (!exp) /* export entry revoked */
+		      u32_to_kdev_t(fh->fh_xdev),
+		      u32_to_ino_t(fh->fh_xino));
+	if (!exp) {
+		/* export entry revoked */
+		nfsdstats.fh_stale++;
 		goto out;
+	}
 
 	/* Check if the request originated from a secure port. */
 	error = nfserr_perm;
@@ -1079,8 +1144,13 @@
 	 */
 	error = nfserr_noent;
 	dentry = find_fh_dentry(fh);
-	if (!dentry)
+	if (!dentry) {
+		goto out;
+	}
+	if (IS_ERR(dentry)) {
+		error = nfserrno(-PTR_ERR(dentry));
 		goto out;
+	}
 
 	/*
 	 * Note:  it's possible the returned dentry won't be the one in the
@@ -1102,6 +1172,35 @@
 check_type:
 	dentry = fhp->fh_dentry;
 	inode = dentry->d_inode;
+	error = nfserr_stale;
+	/* On a heavily loaded SMP machine, more than one identical
+	   requests may run at the same time on different processors.
+	   One thread may get here with unfinished fh after another
+	   thread just fetched the inode. It doesn't make any senses
+	   to check fh->fh_generation here since it has not been set
+	   yet. In that case, we shouldn't send back the stale
+	   filehandle to the client. We use fh->fh_dcookie to indicate
+	   if fh->fh_generation is set or not. If fh->fh_dcookie is
+	   not set, don't return stale filehandle. */
+	if (inode->i_generation != fh->fh_generation) {
+		if (fh->fh_dcookie) {
+			dprintk("fh_verify: Bad version %u %u %u: 0x%x, 0x%x\n",
+				inode->i_ino,
+				inode->i_generation,
+				fh->fh_generation,
+				type, access);
+			nfsdstats.fh_stale++;
+			goto out;
+		}
+		else {
+			/* We get here when inode is fetched by other
+			   threads. We just use what is in there. */
+			fh->fh_ino = ino_t_to_u32(inode->i_ino);
+			fh->fh_generation = inode->i_generation;
+			fh->fh_dcookie = (struct dentry *)0xfeebbaca;
+			nfsdstats.fh_concurrent++;
+		}
+	}
 	exp = fhp->fh_export;
 	if (type > 0 && (inode->i_mode & S_IFMT) != type) {
 		error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
@@ -1117,9 +1216,10 @@
 	 */
 	error = 0;
 	if (fh->fh_dev != fh->fh_xdev) {
-		printk("fh_verify: Security: export on other device"
-		       " (%d, %d).\n", fh->fh_dev, fh->fh_xdev);
+		printk("fh_verify: Security: export on other device (%s, %s).\n",
+		       kdevname(fh->fh_dev), kdevname(fh->fh_xdev));
 		error = nfserr_stale;
+		nfsdstats.fh_stale++;
 	} else if (exp->ex_dentry != dentry) {
 		struct dentry *tdentry = dentry;
 
@@ -1128,19 +1228,21 @@
 			if (exp->ex_dentry == tdentry)
 				break;
 			/* executable only by root and we can't be root */
-			if (current->fsuid &&
-			    !(tdentry->d_inode->i_uid  &&
-			        (tdentry->d_inode->i_mode & S_IXUSR)) &&
-			    !(tdentry->d_inode->i_gid &&
-			        (tdentry->d_inode->i_mode & S_IXGRP)) &&
-			    !(tdentry->d_inode->i_mode & S_IXOTH) && 
-			    (exp->ex_flags & NFSEXP_ROOTSQUASH)) {
+			if (current->fsuid
+			    && !(tdentry->d_inode->i_uid
+			         && (tdentry->d_inode->i_mode & S_IXUSR))
+			    && !(tdentry->d_inode->i_gid
+				 && (tdentry->d_inode->i_mode & S_IXGRP))
+			    && !(tdentry->d_inode->i_mode & S_IXOTH)
+			    && (exp->ex_flags & NFSEXP_ROOTSQUASH)) {
 				error = nfserr_stale;
+				nfsdstats.fh_stale++;
 dprintk("fh_verify: no root_squashed access.\n");
 			}
 		} while ((tdentry != tdentry->d_parent));
 		if (exp->ex_dentry != tdentry) {
 			error = nfserr_stale;
+			nfsdstats.fh_stale++;
 			printk("nfsd Security: %s/%s bad export.\n",
 			       dentry->d_parent->d_name.name,
 			       dentry->d_name.name);
@@ -1194,6 +1296,7 @@
 	if (inode) {
 		fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
 		fhp->fh_handle.fh_generation = inode->i_generation;
+		fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
 	}
 	fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
 	fhp->fh_handle.fh_dev	 = kdev_t_to_u32(parent->d_inode->i_dev);
@@ -1226,7 +1329,8 @@
 		goto out_negative;
 	fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
 	fhp->fh_handle.fh_generation = inode->i_generation;
-out:
+	fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
+ out:
 	return;
 
 out_bad:
@@ -1265,51 +1369,6 @@
 }
 
 /*
- * Verify that the FH dentry is still a valid dentry pointer.
- * After making some preliminary checks, we ask VFS to verify
- * that it is indeed a dentry.
- */
-static int nfsd_d_validate(struct dentry *dentry)
-{
-	unsigned long dent_addr = (unsigned long) dentry;
-	unsigned long min_addr = PAGE_OFFSET;
-	unsigned long max_addr = min_addr + (max_mapnr << PAGE_SHIFT);
-	unsigned long align_mask = 0x0F;
-	unsigned int len;
-	int valid = 0;
-
-	if (dent_addr < min_addr)
-		goto bad_addr;
-	if (dent_addr > max_addr - sizeof(struct dentry))
-		goto bad_addr;
-	if ((dent_addr & ~align_mask) != dent_addr)
-		goto bad_align;
-	if (!kern_addr_valid(dent_addr))
-		goto bad_addr;
-	/*
-	 * Looks safe enough to dereference ...
-	 */
-	len = dentry->d_name.len;
-	if (len > NFS_MAXNAMLEN)
-		goto out;
-	/*
-	 * Note: d_validate doesn't dereference the parent pointer ...
-	 * just combines it with the name hash to find the hash chain.
-	 */
-	valid = d_validate(dentry, dentry->d_parent, dentry->d_name.hash, len);
-
-out:
-	return valid;
-
-bad_addr:
-	printk(KERN_DEBUG "nfsd_d_validate: invalid address %lx\n", dent_addr);
-	goto out;
-bad_align:
-	printk(KERN_DEBUG "nfsd_d_validate: unaligned address %lx\n", dent_addr);
-	goto out;
-}
-
-/*
  * Flush any cached dentries for the specified device
  * or for all devices.
  *
@@ -1338,7 +1397,7 @@
 }
 
 /*
- * Free the dentry and path caches.
+ * Free the rename and path caches.
  */
 void nfsd_fh_free(void)
 {
@@ -1375,18 +1434,33 @@
 
 void nfsd_fh_init(void)
 {
-	/* Sanity check */ 
 	extern void __my_nfsfh_is_too_big(void); 
+
+	if (filetable)
+		return;
+
+	/* Sanity check */ 
 	if (sizeof(struct nfs_fhbase) > 32) 
 		__my_nfsfh_is_too_big(); 
 
+	filetable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH,
+			    GFP_KERNEL);
+	dirstable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH,
+			    GFP_KERNEL);
+
+	if (filetable == NULL || dirstable == NULL) {
+		printk(KERN_WARNING "nfsd_fh_init : Could not allocate fhcache\n");
+		nfsd_nservers = 0;
+		return;
+	}
+
 	memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
 	memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
 	INIT_LIST_HEAD(&path_inuse);
 	INIT_LIST_HEAD(&fixup_head);
 
 	printk(KERN_DEBUG 
-		"nfsd_init: initialized fhcache, entries=%lu\n", NFSD_MAXFH);
+		"nfsd_fh_init : initialized fhcache, entries=%lu\n", NFSD_MAXFH);
 	/*
 	 * Display a warning if the ino_t is larger than 32 bits.
 	 */
@@ -1394,4 +1468,16 @@
 		printk(KERN_INFO 
 			"NFSD: ino_t is %d bytes, using lower 4 bytes\n",
 			sizeof(ino_t));
+}
+
+void
+nfsd_fh_shutdown(void)
+{
+	if (!filetable)
+		return;
+	printk(KERN_DEBUG 
+		"nfsd_fh_shutdown : freeing %d fhcache entries.\n", NFSD_MAXFH);
+	kfree(filetable);
+	kfree(dirstable);
+	filetable = dirstable = NULL;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfsd/nfsproc.c linux.14p6/fs/nfsd/nfsproc.c
--- linux.vanilla/fs/nfsd/nfsproc.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/nfsd/nfsproc.c	Fri Oct 22 21:29:04 1999
@@ -270,7 +270,7 @@
 		rdev = (dev_t) size;
 		if (type != S_IFBLK && type != S_IFCHR) {
 			rdev = 0;
-		} else if (type == S_IFCHR && size == ~(u32) 0) {
+		} else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) {
 			/* If you think you've seen the worst, grok this. */
 			attr->ia_mode = S_IFIFO | mode;
 			type = S_IFIFO;
@@ -288,6 +288,10 @@
 		if (inode && (type != (inode->i_mode & S_IFMT) || 
 		    (is_borc && inode->i_rdev != rdev)))
 			goto out_unlock;
+
+		/* invalidate size because only (type == S_IFREG) has
+		   size. */
+		attr->ia_valid &= ~ATTR_SIZE;
 	}
 	
 	nfserr = 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfsd/nfssvc.c linux.14p6/fs/nfsd/nfssvc.c
--- linux.vanilla/fs/nfsd/nfssvc.c	Sat Mar 20 20:28:14 1999
+++ linux.14p6/fs/nfsd/nfssvc.c	Fri Oct 22 21:29:05 1999
@@ -5,7 +5,7 @@
  *
  * Authors:	Olaf Kirch (okir@monad.swb.de)
  *
- * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
  */
 
 #define __NO_VERSION__
@@ -64,6 +64,11 @@
 	nfsd_nservers = nrservs;
 
 	error = -ENOMEM;
+	nfsd_fh_init();		/* NFS dentry cache */
+	if (nfsd_nservers == 0)
+		goto out;
+	  
+	error = -ENOMEM;
 	nfsd_racache_init();     /* Readahead param cache */
 	if (nfsd_nservers == 0)
 		goto out;
@@ -119,7 +124,9 @@
 		nfssvc_boot = xtime;		/* record boot time */
 		first = 1;
 	}
+#if 0
 	lockd_up();				/* start lockd */
+#endif
 
 	/*
 	 * The main request loop
@@ -177,12 +184,16 @@
 		printk(KERN_WARNING "nfsd: terminating on signal %d\n", signo);
 	}
 
+#if 0
 	/* Release lockd */
 	lockd_down();
+#endif
 	if (!--nfsd_active) {
 		printk("nfsd: last server exiting\n");
 		/* revoke all exports */
 		nfsd_export_shutdown();
+		/* release fhcache */
+		nfsd_fh_shutdown ();
 		/* release read-ahead cache */
 	        nfsd_racache_shutdown();
 	}
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfsd/stats.c linux.14p6/fs/nfsd/stats.c
--- linux.vanilla/fs/nfsd/stats.c	Thu Jan  7 17:28:47 1999
+++ linux.14p6/fs/nfsd/stats.c	Fri Oct 22 21:29:05 1999
@@ -32,7 +32,7 @@
 {
 	int	len;
 
-	len = sprintf(buffer, "rc %d %d %d  %d %d %d %d %d\n",
+	len = sprintf(buffer, "rc %d %d %d  %d %d %d %d %d %d\n",
 			nfsdstats.rchits,
 			nfsdstats.rcmisses,
 			nfsdstats.rcnocache,
@@ -40,7 +40,8 @@
 			nfsdstats.fh_valid,
 			nfsdstats.fh_fixup,
 			nfsdstats.fh_lookup,
-			nfsdstats.fh_stale);
+			nfsdstats.fh_stale,
+			nfsdstats.fh_concurrent);
 
 	/* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
 	*eof = 0;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/nfsd/vfs.c linux.14p6/fs/nfsd/vfs.c
--- linux.vanilla/fs/nfsd/vfs.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/nfsd/vfs.c	Fri Oct 22 21:29:05 1999
@@ -21,7 +21,6 @@
 #include <linux/locks.h>
 #include <linux/fs.h>
 #include <linux/major.h>
-#include <linux/ext2_fs.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
@@ -48,9 +47,15 @@
 
 /* Hack until we have a macro check for mandatory locks. */
 #ifndef IS_ISMNDLK
-#define IS_ISMNDLK(i)	(((i)->i_mode & (S_ISGID|S_IXGRP)) == S_ISGID)
+#define IS_ISMNDLK(i)	(((i)->i_mode & (S_ISGID|S_IXGRP|S_IFMT)) \
+			 == (S_ISGID|S_IFREG))
 #endif
 
+/* Time difference margin in seconds for comparison. It is a
+   dynamically-tunable parameter via /proc/fs/nfs/time-diff-margin.
+ */
+extern long nfsd_time_diff_margin;
+
 /* Check for dir entries '.' and '..' */
 #define isdotent(n, l)	(l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
 
@@ -236,8 +241,30 @@
 	inode = dentry->d_inode;
 
 	err = inode_change_ok(inode, iap);
-	if (err)
+	if (err) {
+		/* It is very tricky. When you are not the file owner,
+		   but have the write permission, you should be allowed
+		   to set atime and mtime to the current time on the
+		   server. However, the NFS V2 protocol doesn't support
+		   it. It has been fixed in V3. Here we do this: if the
+		   current server time and atime/mtime are close enough,
+		   we use the current server time. */
+#define CURRENT_TIME_SET	(ATTR_ATIME_SET | ATTR_MTIME_SET)
+		if (iap->ia_mtime == iap->ia_atime
+		    && ((iap->ia_valid & (CURRENT_TIME_SET))
+			== CURRENT_TIME_SET)) {
+			time_t now = CURRENT_TIME;
+			time_t delta = iap->ia_atime - now;
+			if (delta < 0) delta = -delta;
+			if (delta <= nfsd_time_diff_margin) {
+				iap->ia_valid &= ~CURRENT_TIME_SET;
+				goto current_time_ok;
+			}
+		}
 		goto out_nfserr;
+	}
+
+current_time_ok:
 
 	/* The size case is special... */
 	if (iap->ia_valid & ATTR_SIZE) {
@@ -248,15 +275,19 @@
 			if (err)
 				goto out;
 		}
+		DQUOT_INIT(inode);
 		err = get_write_access(inode);
-		if (err)
+		if (err) {
+			DQUOT_DROP(inode);
 			goto out_nfserr;
+		}
 		/* N.B. Should we update the inode cache here? */
 		inode->i_size = iap->ia_size;
 		if (inode->i_op && inode->i_op->truncate)
 			inode->i_op->truncate(inode);
 		mark_inode_dirty(inode);
 		put_write_access(inode);
+		DQUOT_DROP(inode);
 		iap->ia_valid &= ~ATTR_SIZE;
 		iap->ia_valid |= ATTR_MTIME;
 		iap->ia_mtime = CURRENT_TIME;
@@ -554,7 +585,7 @@
 	/* clear setuid/setgid flag after write */
 	if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) {
 		struct iattr	ia;
-		kernel_cap_t	saved_cap;
+		kernel_cap_t	saved_cap = 0;
 
 		ia.ia_valid = ATTR_MODE;
 		ia.ia_mode  = inode->i_mode & ~(S_ISUID | S_ISGID);
@@ -658,10 +689,6 @@
 		if (IS_ERR(dchild))
 			goto out_nfserr;
 		fh_compose(resfhp, fhp->fh_export, dchild);
-		/* Lock the parent and check for errors ... */
-		err = fh_lock_parent(fhp, dchild);
-		if (err)
-			goto out;
 	} else {
 		dchild = resfhp->fh_dentry;
 		if (!fhp->fh_locked)
@@ -670,6 +697,15 @@
 				dentry->d_parent->d_name.name,
 				dentry->d_name.name);
 	}
+	err = nfserr_exist;
+	if (dchild->d_inode)
+		goto out;
+	if (!fhp->fh_locked) {
+		/* Lock the parent and check for errors ... */
+		err = fh_lock_parent(fhp, dchild);
+		if (err)
+			goto out;
+	}
 	/*
 	 * Make sure the child dentry is still negative ...
 	 */
@@ -695,8 +731,7 @@
 	case S_IFCHR:
 	case S_IFBLK:
 		/* The client is _NOT_ required to do security enforcement */
-		if(!capable(CAP_SYS_ADMIN))
-		{
+		if(!capable(CAP_SYS_ADMIN)) {
 			err = -EPERM;
 			goto out;
 		}
@@ -759,7 +794,7 @@
 	struct inode	*inode;
 	struct iattr	newattrs;
 	int		err;
-	kernel_cap_t	saved_cap;
+	kernel_cap_t	saved_cap = 0;
 
 	err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE | MAY_TRUNC);
 	if (err)
@@ -899,6 +934,7 @@
 	/* Compose the fh so the dentry will be freed ... */
 out_compose:
 	fh_compose(resfhp, fhp->fh_export, dnew);
+
 out:
 	return err;
 
@@ -1091,6 +1127,15 @@
 	DQUOT_DROP(tdir);
 
 	nfsd_double_up(&tdir->i_sem, &fdir->i_sem);
+
+	if (!err && odentry->d_inode) {
+		add_to_rename_cache(tdir->i_ino,
+				    odentry->d_inode->i_dev,
+				    fdir->i_ino,
+				    odentry->d_inode->i_ino);
+	} else {
+		printk(": no inode in rename or err: %d.\n", err);
+	}
 	dput(ndentry);
 
 out_dput_old:
@@ -1132,7 +1177,6 @@
 	err = PTR_ERR(rdentry);
 	if (IS_ERR(rdentry))
 		goto out_nfserr;
-
 	if (!rdentry->d_inode) {
 		dput(rdentry);
 		err = nfserr_noent;
@@ -1154,7 +1198,7 @@
 		fh_unlock(fhp);
 
 		dput(rdentry);
-
+		expire_by_dentry(rdentry);
 	} else {
 		/* It's RMDIR */
 		/* See comments in fs/namei.c:do_rmdir */
@@ -1183,6 +1227,7 @@
 		goto out_nfserr;
 	if (EX_ISSYNC(fhp->fh_export))
 		write_inode_now(dirp);
+
 out:
 	return err;
 
@@ -1314,7 +1359,7 @@
 {
 	struct inode	*inode = dentry->d_inode;
 	int		err;
-	kernel_cap_t	saved_cap;
+	kernel_cap_t	saved_cap = 0;
 
 	if (acc == MAY_NOP)
 		return 0;
@@ -1334,7 +1379,7 @@
 		inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
 #endif
 #ifndef CONFIG_NFSD_SUN
-        if (dentry->d_mounts != dentry) {
+	if (dentry->d_mounts != dentry) {
 		return nfserr_perm;
 	}
 #endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/open.c linux.14p6/fs/open.c
--- linux.vanilla/fs/open.c	Wed Oct 20 01:12:49 1999
+++ linux.14p6/fs/open.c	Wed Nov  3 00:49:31 1999
@@ -712,7 +712,7 @@
 
 	/* Do we need to expand the fdset array? */
 	if (fd >= current->files->max_fdset) {
-		error = expand_fdset(files, 0);
+		error = expand_fdset(files, fd + 1);
 		if (!error)
 			goto repeat;
 		goto out;
@@ -722,7 +722,7 @@
 	 * Check whether we need to expand the fd array.
 	 */
 	if (fd >= files->max_fds) {
-		error = expand_fd_array(files, 0);
+		error = expand_fd_array(files, fd + 1);
 		if (!error)
 			goto repeat;
 		goto out;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/proc/mem.c linux.14p6/fs/proc/mem.c
--- linux.vanilla/fs/proc/mem.c	Wed Sep 23 23:24:37 1998
+++ linux.14p6/fs/proc/mem.c	Tue Nov 16 15:18:13 1999
@@ -288,11 +288,15 @@
 		if (!dest_table)
 			return -ENOMEM;
 
-		if (!pte_present(*src_table))
-			handle_mm_fault(tsk, src_vma, stmp, 1);
+		if (!pte_present(*src_table)) {
+			if (handle_mm_fault(tsk, src_vma, stmp, 1) < 0)
+				return -ENOMEM;
+		}
 
-		if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
-			handle_mm_fault(tsk, src_vma, stmp, 1);
+		if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table)) {
+			if (handle_mm_fault(tsk, src_vma, stmp, 1) < 0)
+				return -ENOMEM;
+		}
 
 		set_pte(src_table, pte_mkdirty(*src_table));
 		set_pte(dest_table, *src_table);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/smbfs/proc.c linux.14p6/fs/smbfs/proc.c
--- linux.vanilla/fs/smbfs/proc.c	Sat Aug 14 02:26:51 1999
+++ linux.14p6/fs/smbfs/proc.c	Tue Nov 16 15:25:20 1999
@@ -7,6 +7,11 @@
  *  28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
  *  28/09/97 - Fixed smb_d_path [now smb_build_path()] to be non-recursive
  *             by Riccardo Facchetti
+ *  16/11/99 (tridge) 
+ *           - use level 260 for most conns, or level 1 for <NT1
+ *           - don't sleep every time with win95 on a FINDNEXT
+ *           - fixed loop_count bug
+ *           - got rid of resume_key
  */
 
 #include <linux/types.h>
@@ -1449,13 +1454,12 @@
 
 /*
  * Interpret a long filename structure using the specified info level:
- *   level 1   -- Win NT, Win 95, OS/2
- *   level 259 -- File name and length only, Win NT, Win 95
+ *   level 1 for anything below NT1 protocol
+ *   level 260 for NT1 protocol
  *
  * We return a reference to the name string to avoid copying, and perform
- * any needed upper/lower casing in place.  Note!! Level 259 entries may
- * not have any space beyond the name, so don't try to write a null byte!
- *
+ * any needed upper/lower casing in place.  
+
  * Bugs Noted:
  * (1) Win NT 4.0 appends a null byte to names and counts it in the length!
  */
@@ -1474,30 +1478,24 @@
 	switch (level)
 	{
 	case 1:
-		len = *((unsigned char *) p + 26);
+		len = *((unsigned char *) p + 22);
 		entry->len = len;
-		entry->name = p + 27;
-		result = p + 28 + len;
+		entry->name = p + 23;
+		result = p + 24 + len;
 		break;
 
-	case 259: /* SMB_FIND_FILE_NAMES_INFO = 0x103 */
-		result = p + DVAL(p, 0);
-		/* DVAL(p, 4) should be resume key? Seems to be 0 .. */
-		len = DVAL(p, 8);
-		if (len > 255)
-			len = 255;
-		entry->name = p + 12;
-		/*
-		 * Kludge alert: Win NT 4.0 adds a trailing null byte and
-		 * counts it in the name length, but Win 95 doesn't.  Hence
-		 * we test for a trailing null and decrement the length ...
-		 */
+	case 260: /* SMB_FIND_FILE_BOTH_DIRECTORY_INFO = 0x104 */
+		result = p + WVAL(p, 0);
+		len = DVAL(p, 60);
+		if (len > 255) len = 255;
+		/* NT4 null terminates */
+		entry->name = p + 94;
 		if (len && entry->name[len-1] == '\0')
 			len--;
 		entry->len = len;
 #ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_decode_long_dirent: info 259 at %p, len=%d, name=%s\n",
-p, len, entry->name);
+printk("smb_decode_long_dirent: info 260 at %p, len=%d, name=%s\n",
+       p, entry->len, entry->name);
 #endif
 		break;
 
@@ -1528,40 +1526,40 @@
  * is completely reproducible and can be toggled by the creation of a
  * single file. (E.g. echo hi >foo breaks, rm -f foo works.)
  */
+
+#define SMB_CLOSE_AFTER_FIRST (1<<0)
+#define SMB_CLOSE_IF_END (1<<1)
+#define SMB_REQUIRE_RESUME_KEY (1<<2)
+#define SMB_CONTINUE_BIT (1<<3)
+
 static int
 smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
 		      void *cachep)
 {
-	char *p, *mask, *lastname, *param = server->temp_buf;
+	char *p, *mask, *param = server->temp_buf;
 	__u16 command;
 	int first, entries, entries_seen;
 
 	/* Both NT and OS/2 accept info level 1 (but see note below). */
-	int info_level = 1;
+	int info_level = 260;
 	const int max_matches = 512;
 
 	unsigned char *resp_data = NULL;
 	unsigned char *resp_param = NULL;
 	int resp_data_len = 0;
 	int resp_param_len = 0;
-	int ff_resume_key = 0; /* this isn't being used */
 	int ff_searchcount = 0;
 	int ff_eos = 0;
-	int ff_lastname = 0;
 	int ff_dir_handle = 0;
 	int loop_count = 0;
 	int mask_len, i, result;
 	static struct qstr star = { "*", 1, 0 };
 
 	/*
-	 * Check whether to change the info level.  There appears to be
-	 * a bug in Win NT 4.0's handling of info level 1, whereby it
-	 * truncates the directory scan for certain patterns of files.
-	 * Hence we use level 259 for NT.
+	 * use info level 1 for older servers that don't do 260
 	 */
-	if (server->opt.protocol >= SMB_PROTOCOL_NT1 &&
-	    !(server->mnt->version & SMB_FIX_WIN95))
-		info_level = 259;
+	if (server->opt.protocol < SMB_PROTOCOL_NT1)
+		info_level = 1;
 
 	smb_lock_server(server);
 
@@ -1586,7 +1584,7 @@
 	while (ff_eos == 0)
 	{
 		loop_count += 1;
-		if (loop_count > 200)
+		if (loop_count > 10)
 		{
 			printk(KERN_WARNING "smb_proc_readdir_long: "
 			       "Looping in FIND_NEXT??\n");
@@ -1599,33 +1597,27 @@
 			command = TRANSACT2_FINDFIRST;
 			WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
 			WSET(param, 2, max_matches);	/* max count */
-			WSET(param, 4, 8 + 4 + 2);	/* resume required +
-							   close on end +
-							   continue */
+			WSET(param, 4, 
+			     SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
 			WSET(param, 6, info_level);
 			DSET(param, 8, 0);
 		} else
 		{
+			/* we don't need the mask after the first bit */
+			mask_len = 0;
+			mask[0] = 0;
+
 			command = TRANSACT2_FINDNEXT;
 #ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_proc_readdir_long: handle=0x%X, resume=%d, lastname=%d, mask=%s\n",
-ff_dir_handle, ff_resume_key, ff_lastname, mask);
+printk("smb_proc_readdir_long: handle=0x%X, mask=%s\n",
+ff_dir_handle, mask);
 #endif
 			WSET(param, 0, ff_dir_handle);	/* search handle */
 			WSET(param, 2, max_matches);	/* max count */
 			WSET(param, 4, info_level);
-			DSET(param, 6, ff_resume_key);	/* ff_resume_key */
-			WSET(param, 10, 8 + 4 + 2);	/* resume required +
-							   close on end +
-							   continue */
-			if (server->mnt->version & SMB_FIX_WIN95)
-			{
-				/* Windows 95 is not able to deliver answers
-				 * to FIND_NEXT fast enough, so sleep 0.2 sec
-				 */
-				current->state = TASK_INTERRUPTIBLE;
-				schedule_timeout(HZ/5);
-			}
+			DSET(param, 6, 0);       	/* ff_resume_key */
+			WSET(param, 10,
+			     SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
 		}
 
 		result = smb_trans2_request(server, command,
@@ -1648,6 +1640,16 @@
 			entries = result;
 			break;
 		}
+
+
+		if (server->rcls == ERRSRV && server->err == ERRerror) {
+			/* a damn Win95 bug - sometimes it clags if you 
+			   ask it too fast */
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(HZ/5);
+			continue;
+		}
+
 		if (server->rcls != 0)
 		{ 
 #ifdef SMBFS_PARANOIA
@@ -1664,12 +1666,10 @@
 			ff_dir_handle = WVAL(resp_param, 0);
 			ff_searchcount = WVAL(resp_param, 2);
 			ff_eos = WVAL(resp_param, 4);
-			ff_lastname = WVAL(resp_param, 8);
 		} else
 		{
 			ff_searchcount = WVAL(resp_param, 0);
 			ff_eos = WVAL(resp_param, 2);
-			ff_lastname = WVAL(resp_param, 6);
 		}
 
 		if (ff_searchcount == 0)
@@ -1677,38 +1677,6 @@
 			break;
 		}
 
-		/* we might need the lastname for continuations */
-		mask_len = 0;
-		if (ff_lastname > 0)
-		{
-			lastname = resp_data + ff_lastname;
-			switch (info_level)
-			{
-			case 259:
- 				if (ff_lastname < resp_data_len)
-					mask_len = resp_data_len - ff_lastname;
-				break;
-			case 1:
-				/* Win NT 4.0 doesn't set the length byte */
-				lastname++;
- 				if (ff_lastname + 2 < resp_data_len)
-					mask_len = strlen(lastname);
-				break;
-			}
-			/*
-			 * Update the mask string for the next message.
-			 */
-			if (mask_len > 255)
-				mask_len = 255;
-			if (mask_len)
-				strncpy(mask, lastname, mask_len);
-			ff_resume_key = 0;
-		}
-		mask[mask_len] = 0;
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_proc_readdir_long: new mask, len=%d@%d, mask=%s\n",
-mask_len, ff_lastname, mask);
-#endif
 		/* Now we are ready to parse smb directory entries. */
 
 		/* point to the data bytes */
@@ -1720,8 +1688,6 @@
 			p = smb_decode_long_dirent(server, p, entry,
 							info_level);
 
-			pr_debug("smb_readdir_long: got %s\n", entry->name);
-
 			/* ignore . and .. from the server */
 			if (entries_seen == 2 && entry->name[0] == '.')
 			{
@@ -1739,10 +1705,11 @@
 		}
 
 #ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_proc_readdir_long: received %d entries, eos=%d, resume=%d\n",
-ff_searchcount, ff_eos, ff_resume_key);
+printk("smb_proc_readdir_long: received %d entries, eos=%d\n",
+       ff_searchcount, ff_eos);
 #endif
 		first = 0;
+		loop_count = 0;
 	}
 
 	smb_unlock_server(server);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-alpha/delay.h linux.14p6/include/asm-alpha/delay.h
--- linux.vanilla/include/asm-alpha/delay.h	Tue Dec 29 21:56:15 1998
+++ linux.14p6/include/asm-alpha/delay.h	Fri Oct 22 21:29:12 1999
@@ -9,12 +9,18 @@
  * Delay routines, using a pre-computed "loops_per_second" value.
  */
 
+/* We can make the delay loop inline, but we have to be very careful
+ * WRT scheduling for EV6 machines, to keep it consistent for all locations
+ * of invocations. This is a reasonable compromise.
+ */
+
 extern __inline__ void
 __delay(unsigned long loops)
 {
-	__asm__ __volatile__(".align 3\n"
-		"1:\tsubq %0,1,%0\n\t"
-		"bge %0,1b": "=r" (loops) : "0" (loops));
+	__asm__ __volatile__(".align 4\n"
+		     "1:\tsubq %0,1,%0\n\t"
+		     "bge %0,1b\n\t"
+		     "nop": "=r" (loops) : "0" (loops));
 }
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-alpha/io.h linux.14p6/include/asm-alpha/io.h
--- linux.vanilla/include/asm-alpha/io.h	Sat Aug 14 02:27:37 1999
+++ linux.14p6/include/asm-alpha/io.h	Fri Oct 22 21:29:12 1999
@@ -261,6 +261,8 @@
 {
 }
 
+#define ioremap_nocache(offset, size) ioremap((offset),(size))
+
 #ifndef readb
 # define readb(a)	_readb((unsigned long)(a))
 #endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-i386/fixmap.h linux.14p6/include/asm-i386/fixmap.h
--- linux.vanilla/include/asm-i386/fixmap.h	Sun Mar 28 19:03:26 1999
+++ linux.14p6/include/asm-i386/fixmap.h	Fri Oct 22 23:25:13 1999
@@ -45,7 +45,10 @@
 	FIX_APIC_BASE,	/* local (CPU) APIC) -- required for SMP or not */
 #endif
 #ifdef CONFIG_X86_IO_APIC
-	FIX_IO_APIC_BASE,
+	FIX_IO_APIC_BASE_0,	/* IF YOU CHANGE THIS, PLEASE ALSO CHANGE */
+	FIX_IO_APIC_BASE_1,     /* MAX_IO_APICS in arch/i386/irq.h        */
+	FIX_IO_APIC_BASE_2,
+	FIX_IO_APIC_BASE_3,
 #endif
 #ifdef CONFIG_X86_VISWS_APIC
 	FIX_CO_CPU,	/* Cobalt timer */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-i386/sgi-cobalt.h linux.14p6/include/asm-i386/sgi-cobalt.h
--- linux.vanilla/include/asm-i386/sgi-cobalt.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/include/asm-i386/sgi-cobalt.h	Wed Oct 27 18:50:24 1999
@@ -0,0 +1,119 @@
+#include <linux/config.h>
+#ifndef __I386_SGI_COBALT_H
+#define __I386_SGI_COBALT_H
+
+/*
+ * Cobalt SGI Visual Workstation system ASIC
+ */ 
+
+#define	CO_CPU_PHYS		0xc2000000
+#define	CO_APIC_PHYS		0xc4000000
+
+/* see set_fixmap() and asm/fixmap.h */
+#define	CO_CPU_VADDR		(fix_to_virt(FIX_CO_CPU))
+#define	CO_APIC_VADDR		(fix_to_virt(FIX_CO_APIC))
+
+/* Cobalt CPU registers -- relative to CO_CPU_VADDR, use co_cpu_*() */
+#define	CO_CPU_REV		0x08
+#define	CO_CPU_CTRL		0x10
+#define	CO_CPU_STAT		0x20
+#define	CO_CPU_TIMEVAL		0x30
+
+/* CO_CPU_CTRL bits */
+#define	CO_CTRL_TIMERUN		0x04		/* 0 == disabled */
+#define	CO_CTRL_TIMEMASK	0x08		/* 0 == unmasked */
+
+/* CO_CPU_STATUS bits */
+#define	CO_STAT_TIMEINTR	0x02	/* (r) 1 == int pend, (w) 0 == clear */
+
+/* CO_CPU_TIMEVAL value */
+#define	CO_TIME_HZ		100000000	/* Cobalt core rate */
+
+/* Cobalt APIC registers -- relative to CO_APIC_VADDR, use co_apic_*() */
+#define	CO_APIC_HI(n)		(((n) * 0x10) + 4)
+#define	CO_APIC_LO(n)		((n) * 0x10)
+#define	CO_APIC_ID		0x0ffc
+
+/* CO_APIC_ID bits */
+#define	CO_APIC_ENABLE		0x00000100
+
+/* CO_APIC_LO bits */
+#define	CO_APIC_MASK		0x00010000	/* 0 = enabled */
+#define	CO_APIC_LEVEL		0x00008000	/* 0 = edge */
+
+/*
+ * Where things are physically wired to Cobalt
+ * #defines with no board _<type>_<rev>_ are common to all (thus far)
+ */
+#define	CO_APIC_IDE0		4
+#define CO_APIC_IDE1		2		/* Only on 320 */
+
+#define	CO_APIC_8259		12		/* serial, floppy, par-l-l */
+
+/* Lithium PCI Bridge A -- "the one with 82557 Ethernet" */
+#define	CO_APIC_PCIA_BASE0	0 /* and 1 */	/* slot 0, line 0 */
+#define	CO_APIC_PCIA_BASE123	5 /* and 6 */	/* slot 0, line 1 */
+
+#define	CO_APIC_PIIX4_USB	7		/* this one is wierd */
+
+/* Lithium PCI Bridge B -- "the one with PIIX4" */
+#define	CO_APIC_PCIB_BASE0	8 /* and 9-12 *//* slot 0, line 0 */
+#define	CO_APIC_PCIB_BASE123	13 /* 14.15 */	/* slot 0, line 1 */
+
+#define	CO_APIC_VIDOUT0		16
+#define	CO_APIC_VIDOUT1		17
+#define	CO_APIC_VIDIN0		18
+#define	CO_APIC_VIDIN1		19
+
+#define	CO_APIC_LI_AUDIO	22
+
+#define	CO_APIC_AS		24
+#define	CO_APIC_RE		25
+
+#define CO_APIC_CPU		28		/* Timer and Cache interrupt */
+#define	CO_APIC_NMI		29
+#define	CO_APIC_LAST		CO_APIC_NMI
+
+/*
+ * This is how irqs are assigned on the Visual Workstation.
+ * Legacy devices get irq's 1-15 (system clock is 0 and is CO_APIC_CPU).
+ * All other devices (including PCI) go to Cobalt and are irq's 16 on up.
+ */
+#define	CO_IRQ_APIC0	16			/* irq of apic entry 0 */
+#define	IS_CO_APIC(irq)	((irq) >= CO_IRQ_APIC0)
+#define	CO_IRQ(apic)	(CO_IRQ_APIC0 + (apic))	/* apic ent to irq */
+#define	CO_APIC(irq)	((irq) - CO_IRQ_APIC0)	/* irq to apic ent */
+#define CO_IRQ_IDE0	14			/* knowledge of... */
+#define CO_IRQ_IDE1	15			/* ... ide driver defaults! */
+#define	CO_IRQ_8259	CO_IRQ(CO_APIC_8259)
+
+#ifdef CONFIG_X86_VISWS_APIC
+extern __inline void co_cpu_write(unsigned long reg, unsigned long v)
+{
+	*((volatile unsigned long *)(CO_CPU_VADDR+reg))=v;
+}
+
+extern __inline unsigned long co_cpu_read(unsigned long reg)
+{
+	return *((volatile unsigned long *)(CO_CPU_VADDR+reg));
+}            
+             
+extern __inline void co_apic_write(unsigned long reg, unsigned long v)
+{
+	*((volatile unsigned long *)(CO_APIC_VADDR+reg))=v;
+}            
+             
+extern __inline unsigned long co_apic_read(unsigned long reg)
+{
+	return *((volatile unsigned long *)(CO_APIC_VADDR+reg));
+}
+#endif
+
+extern char visws_board_type;
+
+#define	VISWS_320	0
+#define	VISWS_540	1
+
+extern char visws_board_rev;
+
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-ppc/gemini.h linux.14p6/include/asm-ppc/gemini.h
--- linux.vanilla/include/asm-ppc/gemini.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/include/asm-ppc/gemini.h	Sat Nov 13 15:14:57 1999
@@ -0,0 +1,166 @@
+/*
+ *  include/asm-ppc/gemini.h
+ *
+ *
+ *  Onboard registers and descriptions for Synergy Microsystems' 
+ *  "Gemini" boards.
+ *
+ */
+#ifndef __PPC_GEMINI_H
+#define __PPC_GEMINI_H
+
+/*  Registers  */
+
+#define GEMINI_SERIAL_B     (0xffeffb00)
+#define GEMINI_SERIAL_A     (0xffeffb08)
+#define GEMINI_USWITCH      (0xffeffd00)
+#define GEMINI_BREV         (0xffeffe00)
+#define GEMINI_BECO         (0xffeffe08)
+#define GEMINI_FEAT         (0xffeffe10)
+#define GEMINI_BSTAT        (0xffeffe18)
+#define GEMINI_CPUSTAT      (0xffeffe20)
+#define GEMINI_L2CFG        (0xffeffe30)
+#define GEMINI_MEMCFG       (0xffeffe38)
+#define GEMINI_FLROM        (0xffeffe40)
+#define GEMINI_P0PCI        (0xffeffe48)
+#define GEMINI_FLWIN        (0xffeffe50)
+#define GEMINI_P0INTMASK    (0xffeffe60)
+#define GEMINI_P0INTAP      (0xffeffe68)
+#define GEMINI_PCIERR       (0xffeffe70)
+#define GEMINI_LEDBASE      (0xffeffe80)
+#define GEMINI_RTC          (0xffe9fff8)
+#define GEMINI_LEDS         8
+#define GEMINI_SWITCHES     8
+
+
+/* Flash ROM bit definitions */
+#define GEMINI_FLS_WEN      (1<<0)
+#define GEMINI_FLS_JMP      (1<<6)
+#define GEMINI_FLS_BOOT     (1<<7)
+
+/* Memory bit definitions */
+#define GEMINI_MEM_TYPE_MASK 0xc0
+#define GEMINI_MEM_SIZE_MASK 0x38
+#define GEMINI_MEM_BANK_MASK 0x07
+
+/* L2 cache bit definitions */
+#define GEMINI_L2_SIZE_MASK  0xc0
+#define GEMINI_L2_RATIO_MASK 0x03
+
+/* Timebase register bit definitons */
+#define GEMINI_TIMEB0_EN     (1<<0)
+#define GEMINI_TIMEB1_EN     (1<<1)
+#define GEMINI_TIMEB2_EN     (1<<2)
+#define GEMINI_TIMEB3_EN     (1<<3)
+
+/* CPU status bit definitions */
+#define GEMINI_CPU_ID_MASK   0x03
+#define GEMINI_CPU_COUNT_MASK 0x0c
+#define GEMINI_CPU0_HALTED   (1<<4)
+#define GEMINI_CPU1_HALTED   (1<<5)
+#define GEMINI_CPU2_HALTED   (1<<6)
+#define GEMINI_CPU3_HALTED   (1<<7)
+
+/* Board status bit definitions */
+#define GEMINI_BRD_FAIL      (1<<0)   /* FAIL led is lit */
+#define GEMINI_BRD_BUS_MASK  0x0c     /* PowerPC bus speed */
+
+/* Board family/feature bit descriptions */
+#define GEMINI_FEAT_HAS_FLASH (1<<0)
+#define GEMINI_FEAT_HAS_ETH   (1<<1)
+#define GEMINI_FEAT_HAS_SCSI  (1<<2)
+#define GEMINI_FEAT_HAS_P0    (1<<3)
+#define GEMINI_FEAT_FAM_MASK  0xf0
+
+/* Mod/ECO bit definitions */
+#define GEMINI_ECO_LEVEL_MASK 0x0f
+#define GEMINI_MOD_MASK       0xf0
+
+/* Type/revision bit definitions */
+#define GEMINI_REV_MASK       0x0f
+#define GEMINI_TYPE_MASK      0xf0
+
+/* User switch definitions */
+#define GEMINI_SWITCH_VERBOSE    1     /* adds "debug" to boot cmd line */
+#define GEMINI_SWITCH_SINGLE_USER 7    /* boots into "single-user" mode */
+
+#define SGS_RTC_CONTROL  0
+#define SGS_RTC_SECONDS  1
+#define SGS_RTC_MINUTES  2
+#define SGS_RTC_HOURS    3
+#define SGS_RTC_DAY      4
+#define SGS_RTC_DAY_OF_MONTH 5
+#define SGS_RTC_MONTH    6
+#define SGS_RTC_YEAR     7
+
+#define SGS_RTC_SET  0x80
+#define SGS_RTC_IS_STOPPED 0x80
+
+#define GRACKLE_CONFIG_ADDR_ADDR  (0xfec00000)
+#define GRACKLE_CONFIG_DATA_ADDR  (0xfee00000)
+
+#define GEMINI_BOOT_INIT  (0xfff00100)
+
+#ifndef __ASSEMBLY__
+
+static inline void grackle_write( unsigned long addr, unsigned long data )
+{
+  __asm__ __volatile__(
+  " stwbrx %1, 0, %0\n \
+    sync\n \
+    stwbrx %3, 0, %2\n \
+    sync "
+  : /* no output */
+  : "r" (GRACKLE_CONFIG_ADDR_ADDR), "r" (addr),
+    "r" (GRACKLE_CONFIG_DATA_ADDR), "r" (data));
+}
+
+static inline unsigned long grackle_read( unsigned long addr )
+{
+  unsigned long val;
+
+  __asm__ __volatile__(
+  " stwbrx %1, 0, %2\n \
+    sync\n \
+    lwbrx %0, 0, %3\n \
+    sync "
+  : "=r" (val)
+  : "r" (addr), "r" (GRACKLE_CONFIG_ADDR_ADDR),
+    "r" (GRACKLE_CONFIG_DATA_ADDR));
+
+  return val;
+}
+
+static inline void gemini_led_on( int led )
+{
+  if (led >= 0 && led < GEMINI_LEDS)
+    *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 1;
+}
+
+static inline void gemini_led_off(int led)
+{
+  if (led >= 0 && led < GEMINI_LEDS)
+    *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 0;
+}
+
+static inline int gemini_led_val(int led)
+{
+  int val = 0;
+  if (led >= 0 && led < GEMINI_LEDS)
+    val = *(unsigned char *)(GEMINI_LEDBASE + (led<<3));
+  return (val & 0x1);
+}
+
+/* returns processor id from the board */
+static inline int gemini_processor(void)
+{
+  unsigned char cpu = *(unsigned char *)(GEMINI_CPUSTAT);
+  return (int) ((cpu == 0) ? 4 : (cpu & GEMINI_CPU_ID_MASK));
+}
+
+
+extern void _gemini_reboot(void);
+extern void gemini_prom_init(void);
+extern void gemini_init_l2(void);
+#endif /* __ASSEMBLY__ */
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-ppc/gemini_serial.h linux.14p6/include/asm-ppc/gemini_serial.h
--- linux.vanilla/include/asm-ppc/gemini_serial.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/include/asm-ppc/gemini_serial.h	Sat Nov 13 15:14:57 1999
@@ -0,0 +1,32 @@
+#ifndef __ASMPPC_GEMINI_SERIAL_H
+#define __ASMPPC_GEMINI_SERIAL_H
+
+#include <asm/gemini.h>
+
+/* Rate for the 24.576 Mhz clock for the onboard serial chip */
+#define BASE_BAUD  (24576000 / 16)
+
+#ifdef CONFIG_SERIAL_DETECT_IRQ
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
+#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ)
+#else
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST)
+#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF)
+#endif
+
+#define STD_SERIAL_PORT_DEFNS \
+        { 0, BASE_BAUD, GEMINI_SERIAL_A, 15, STD_COM_FLAGS }, /* ttyS0 */ \
+        { 0, BASE_BAUD, GEMINI_SERIAL_B, 14, STD_COM_FLAGS }, /* ttyS1 */ \
+
+#ifdef CONFIG_GEMINI_PU32
+#define PU32_SERIAL_PORT_DEFNS \
+        { 0, BASE_BAUD, NULL, 0, STD_COM_FLAGS },
+#else
+#define PU32_SERIAL_PORT_DEFNS
+#endif
+
+#define SERIAL_PORT_DFNS \
+        STD_SERIAL_PORT_DEFNS \
+        PU32_SERIAL_PORT_DEFNS
+
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-ppc/keyboard.h linux.14p6/include/asm-ppc/keyboard.h
--- linux.vanilla/include/asm-ppc/keyboard.h	Sat Aug 28 20:00:58 1999
+++ linux.14p6/include/asm-ppc/keyboard.h	Sat Nov 13 15:14:57 1999
@@ -15,73 +15,68 @@
 
 #ifdef __KERNEL__
 
-#include <linux/ioport.h>
-#include <asm/io.h>
-
 #include <linux/config.h>
-#include <asm/adb.h>
 #include <asm/machdep.h>
-#ifdef CONFIG_APUS
-#include <asm-m68k/keyboard.h>
-#else
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
 
 #define KEYBOARD_IRQ			1
 #define DISABLE_KBD_DURING_INTERRUPTS	0
 #define INIT_KBD
 
-#ifdef CONFIG_PREP
-extern int prep_kbd_present;
-#endif /* CONFIG_PREP */
-
 static inline int kbd_setkeycode(unsigned int scancode, unsigned int keycode)
 {
-	return ppc_md.kbd_setkeycode(scancode, keycode);
+	if ( ppc_md.kbd_setkeycode )
+		return ppc_md.kbd_setkeycode(scancode, keycode);
+	else
+		return 0;
 }
   
 static inline int kbd_getkeycode(unsigned int scancode)
 {
-	return ppc_md.kbd_getkeycode(scancode);
+	if ( ppc_md.kbd_getkeycode )
+		return ppc_md.kbd_getkeycode(scancode);
+	else
+		return 0;
 }
   
 static inline int kbd_translate(unsigned char keycode, unsigned char *keycodep,
 				char raw_mode)
 {
-	return ppc_md.kbd_translate(keycode, keycodep, raw_mode);
+	if ( ppc_md.kbd_translate )
+		return ppc_md.kbd_translate(keycode, keycodep, raw_mode);
+	else
+		return 0;
 }
   
 static inline int kbd_unexpected_up(unsigned char keycode)
 {
-	return ppc_md.kbd_unexpected_up(keycode);
+	if ( ppc_md.kbd_unexpected_up )
+		return ppc_md.kbd_unexpected_up(keycode);
+	else
+		return 0;
 }
   
 static inline void kbd_leds(unsigned char leds)
 {
-#ifdef CONFIG_PREP
-	if (prep_kbd_present)
-#endif /* CONFIG_PREP */
+	if ( ppc_md.kbd_leds )
 		ppc_md.kbd_leds(leds);
 }
   
 static inline void kbd_init_hw(void)
 {
-#ifdef CONFIG_PREP
-	if (prep_kbd_present)
-#endif /* CONFIG_PREP */
+	if ( ppc_md.kbd_init_hw )
 		ppc_md.kbd_init_hw();
 }
 
-#define kbd_sysrq_xlate	(ppc_md.kbd_sysrq_xlate)
-
-#ifdef CONFIG_MAC_KEYBOARD
-# define SYSRQ_KEY 0x69
-#else
-# define SYSRQ_KEY 0x54
-#endif
+#define kbd_sysrq_xlate	(ppc_md.ppc_kbd_sysrq_xlate)
 
-#endif /* CONFIG_APUS */
+extern unsigned long SYSRQ_KEY;
 
 /* resource allocation */
-#define kbd_request_region() request_region(0x60, 16, "keyboard")
+#define kbd_request_region()
 #define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
                                              "keyboard", NULL)
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-ppc/m48t35.h linux.14p6/include/asm-ppc/m48t35.h
--- linux.vanilla/include/asm-ppc/m48t35.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/include/asm-ppc/m48t35.h	Sat Nov 13 15:14:57 1999
@@ -0,0 +1,30 @@
+/*
+ *  Registers for the SGS-Thomson M48T35 Timekeeper RAM chip
+ */
+
+#ifndef __PPC_M48T35_H
+#define __PPC_M48T35_H
+
+/* RTC offsets */
+#define M48T35_RTC_CONTROL  0
+#define M48T35_RTC_SECONDS  1
+#define M48T35_RTC_MINUTES  2
+#define M48T35_RTC_HOURS    3
+#define M48T35_RTC_DAY      4
+#define M48T35_RTC_DOM      5
+#define M48T35_RTC_MONTH    6
+#define M48T35_RTC_YEAR     7
+
+#define M48T35_RTC_SET      0x80
+#define M48T35_RTC_STOPPED  0x80
+#define M48T35_RTC_READ     0x40
+
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(x)   ((x)=((x)&15) + ((x)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(x)   ((x)=(((x)/10)<<4) + (x)%10)
+#endif
+
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-ppc/processor.h linux.14p6/include/asm-ppc/processor.h
--- linux.vanilla/include/asm-ppc/processor.h	Sat Aug 28 20:00:58 1999
+++ linux.14p6/include/asm-ppc/processor.h	Sat Nov 13 15:14:57 1999
@@ -69,6 +69,7 @@
 #define _MACH_rpxlite 64  /* RPCG RPX-Lite 8xx board */
 #define _MACH_bseip   128 /* Bright Star Engineering ip-Engine */
 #define _MACH_yk      256 /* Motorola Yellowknife */
+#define _MACH_gemini  512 /* Synergy Microsystems gemini board */
 
 /* see residual.h for these */
 #define _PREP_Motorola 0x01  /* motorola prep */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-ppc/serial.h linux.14p6/include/asm-ppc/serial.h
--- linux.vanilla/include/asm-ppc/serial.h	Wed Aug  5 00:06:36 1998
+++ linux.14p6/include/asm-ppc/serial.h	Sat Nov 13 15:14:57 1999
@@ -4,7 +4,10 @@
 
 #include <linux/config.h>
 
-#ifdef CONFIG_APUS
+#if defined(CONFIG_GEMINI)
+#include <asm/gemini_serial.h>
+#else
+#if defined(CONFIG_APUS)
 #include <asm-m68k/serial.h>
 #else
 
@@ -119,5 +122,5 @@
 	EXTRA_SERIAL_PORT_DEFNS		\
 	HUB6_SERIAL_PORT_DFNS		\
 	MCA_SERIAL_PORT_DFNS
-
-#endif /* CONFIG_APUS */
+#endif
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-sparc/asm_offsets.h linux.14p6/include/asm-sparc/asm_offsets.h
--- linux.vanilla/include/asm-sparc/asm_offsets.h	Thu Mar 11 00:53:37 1999
+++ linux.14p6/include/asm-sparc/asm_offsets.h	Tue Nov 16 15:13:28 1999
@@ -182,6 +182,10 @@
 #define ASIZ_task_sas_ss_sp	0x00000004
 #define AOFF_task_sas_ss_size	0x00000598
 #define ASIZ_task_sas_ss_size	0x00000004
+#define AOFF_task_parent_exec_id	0x0000059c
+#define ASIZ_task_parent_exec_id	0x00000004
+#define AOFF_task_self_exec_id	0x000005a0
+#define ASIZ_task_self_exec_id	0x00000004
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000004
 #define AOFF_mm_mmap_avl	0x00000004
@@ -457,6 +461,10 @@
 #define ASIZ_task_sas_ss_sp	0x00000004
 #define AOFF_task_sas_ss_size	0x00000694
 #define ASIZ_task_sas_ss_size	0x00000004
+#define AOFF_task_parent_exec_id	0x00000698
+#define ASIZ_task_parent_exec_id	0x00000004
+#define AOFF_task_self_exec_id	0x0000069c
+#define ASIZ_task_self_exec_id	0x00000004
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000004
 #define AOFF_mm_mmap_avl	0x00000004
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-sparc/shmparam.h linux.14p6/include/asm-sparc/shmparam.h
--- linux.vanilla/include/asm-sparc/shmparam.h	Sun Oct  4 18:22:44 1998
+++ linux.14p6/include/asm-sparc/shmparam.h	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: shmparam.h,v 1.4 1998/09/28 07:15:01 jj Exp $ */
+/* $Id: shmparam.h,v 1.4.2.1 1999/09/29 18:00:44 davem Exp $ */
 #ifndef _ASMSPARC_SHMPARAM_H
 #define _ASMSPARC_SHMPARAM_H
 
@@ -39,7 +39,12 @@
 #define SHMMNI (1<<_SHM_ID_BITS)	/* max num of segs system wide */
 #define SHMALL				/* max shm system wide (pages) */ \
 	(1<<(_SHM_IDX_BITS+_SHM_ID_BITS))
-#define	SHMLBA PAGE_SIZE		/* attach addr a multiple of this */
+
+extern int vac_cache_size;
+#define SHMLBA (vac_cache_size ? vac_cache_size : \
+		(sparc_cpu_model == sun4c ? (64 * 1024) : \
+		 (sparc_cpu_model == sun4 ? (128 * 1024) : PAGE_SIZE)))
+
 #define SHMSEG SHMMNI			/* max shared segs per process */
 
 #endif /* _ASMSPARC_SHMPARAM_H */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-sparc64/asm_offsets.h linux.14p6/include/asm-sparc64/asm_offsets.h
--- linux.vanilla/include/asm-sparc64/asm_offsets.h	Thu Mar 11 00:53:38 1999
+++ linux.14p6/include/asm-sparc64/asm_offsets.h	Tue Nov 16 15:13:28 1999
@@ -182,6 +182,10 @@
 #define ASIZ_task_sas_ss_sp	0x00000008
 #define AOFF_task_sas_ss_size	0x00000820
 #define ASIZ_task_sas_ss_size	0x00000008
+#define AOFF_task_parent_exec_id	0x00000828
+#define ASIZ_task_parent_exec_id	0x00000004
+#define AOFF_task_self_exec_id	0x0000082c
+#define ASIZ_task_self_exec_id	0x00000004
 #define ASIZ_task	0x00000830
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
@@ -468,6 +472,10 @@
 #define ASIZ_task_sas_ss_sp	0x00000008
 #define AOFF_task_sas_ss_size	0x00000a10
 #define ASIZ_task_sas_ss_size	0x00000008
+#define AOFF_task_parent_exec_id	0x00000a18
+#define ASIZ_task_parent_exec_id	0x00000004
+#define AOFF_task_self_exec_id	0x00000a1c
+#define ASIZ_task_self_exec_id	0x00000004
 #define ASIZ_task	0x00000a20
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
@@ -752,7 +760,11 @@
 #define ASIZ_task_sas_ss_sp	0x00000008
 #define AOFF_task_sas_ss_size	0x00000a18
 #define ASIZ_task_sas_ss_size	0x00000008
-#define ASIZ_task	0x00000a20
+#define AOFF_task_parent_exec_id	0x00000a20
+#define ASIZ_task_parent_exec_id	0x00000004
+#define AOFF_task_self_exec_id	0x00000a24
+#define ASIZ_task_self_exec_id	0x00000004
+#define ASIZ_task	0x00000a30
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
 #define AOFF_mm_mmap_avl	0x00000008
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-sparc64/mmu_context.h linux.14p6/include/asm-sparc64/mmu_context.h
--- linux.vanilla/include/asm-sparc64/mmu_context.h	Sat Aug 14 02:26:21 1999
+++ linux.14p6/include/asm-sparc64/mmu_context.h	Tue Nov 16 15:13:28 1999
@@ -1,9 +1,10 @@
-/* $Id: mmu_context.h,v 1.35 1999/05/08 03:03:20 davem Exp $ */
+/* $Id: mmu_context.h,v 1.35.2.1 1999/09/29 20:22:02 davem Exp $ */
 #ifndef __SPARC64_MMU_CONTEXT_H
 #define __SPARC64_MMU_CONTEXT_H
 
 /* Derived heavily from Linus's Alpha/AXP ASN code... */
 
+#include <asm/spinlock.h>
 #include <asm/system.h>
 #include <asm/spitfire.h>
 #include <asm/spinlock.h>
@@ -12,6 +13,7 @@
 
 #ifndef __ASSEMBLY__
 
+extern spinlock_t ctx_alloc_lock;
 extern unsigned long tlb_context_cache;
 extern unsigned long mmu_context_bmap[];
 
@@ -35,11 +37,13 @@
  * the final reference to the address space.
  */
 #define destroy_context(__mm)	do { 						\
+	spin_lock(&ctx_alloc_lock);						\
 	if ((__mm)->context != NO_CONTEXT &&					\
 	    atomic_read(&(__mm)->count) == 1) { 				\
 		if (!(((__mm)->context ^ tlb_context_cache) & CTX_VERSION_MASK))\
-			clear_bit((__mm)->context & ~(CTX_VERSION_MASK),	\
-				  mmu_context_bmap);				\
+		{	unsigned long nr = (__mm)->context & ~CTX_VERSION_MASK;	\
+			mmu_context_bmap[nr>>6] &= ~(1UL << (nr & 63));		\
+		}								\
 		(__mm)->context = NO_CONTEXT; 					\
 		if(current->mm == (__mm)) {					\
 			current->tss.ctx = 0;					\
@@ -47,6 +51,7 @@
 			__asm__ __volatile__("flush %g6");			\
 		}								\
 	} 									\
+	spin_unlock(&ctx_alloc_lock);						\
 } while (0)
 
 /* This routine must called with interrupts off,
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-sparc64/oplib.h linux.14p6/include/asm-sparc64/oplib.h
--- linux.vanilla/include/asm-sparc64/oplib.h	Wed Oct 20 01:12:50 1999
+++ linux.14p6/include/asm-sparc64/oplib.h	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.10.2.1 1999/08/19 01:11:21 davem Exp $
+/* $Id: oplib.h,v 1.10.2.2 1999/10/24 17:29:38 davem Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -222,6 +222,24 @@
 extern long prom_dtlb_load(unsigned long index,
 			   unsigned long tte_data,
 			   unsigned long vaddr);
+
+/* Map/Unmap client program address ranges.  First the format of
+ * the mapping mode argument.
+ */
+#define PROM_MAP_WRITE	0x0001 /* Writable */
+#define PROM_MAP_READ	0x0002 /* Readable - sw */
+#define PROM_MAP_EXEC	0x0004 /* Executable - sw */
+#define PROM_MAP_LOCKED	0x0010 /* Locked, use i/dtlb load calls for this instead */
+#define PROM_MAP_CACHED	0x0020 /* Cacheable in both L1 and L2 caches */
+#define PROM_MAP_SE	0x0040 /* Side-Effects */
+#define PROM_MAP_GLOB	0x0080 /* Global */
+#define PROM_MAP_IE	0x0100 /* Invert-Endianness */
+#define PROM_MAP_DEFAULT (PROM_MAP_WRITE | PROM_MAP_READ | PROM_MAP_EXEC | PROM_MAP_CACHED)
+
+extern int prom_map(int mode, unsigned long size,
+		    unsigned long vaddr, unsigned long paddr);
+extern void prom_unmap(unsigned long size, unsigned long vaddr);
+
 
 /* PROM device tree traversal functions... */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/asm-sparc64/ttable.h linux.14p6/include/asm-sparc64/ttable.h
--- linux.vanilla/include/asm-sparc64/ttable.h	Wed Oct 20 01:12:50 1999
+++ linux.14p6/include/asm-sparc64/ttable.h	Tue Nov 16 15:13:28 1999
@@ -1,4 +1,4 @@
-/* $Id: ttable.h,v 1.11.2.2 1999/09/22 11:37:47 jj Exp $ */
+/* $Id: ttable.h,v 1.11.2.3 1999/10/07 20:48:25 davem Exp $ */
 #ifndef _SPARC64_TTABLE_H
 #define _SPARC64_TTABLE_H
 
@@ -163,6 +163,20 @@
 	        
 /* Before touching these macros, you owe it to yourself to go and
  * see how arch/sparc64/kernel/winfixup.S works... -DaveM
+ *
+ * For the user cases we used to use the %asi register, but
+ * it turns out that the "wr xxx, %asi" costs ~30 cycles, so
+ * now we use immediate ASI loads and stores instead.  Kudos
+ * to Greg Onufer for pointing out this performance anomaly.
+ *
+ * Further note that we cannot use the g2, g4, g5, and g7 alternate
+ * globals in the spill routines, check out the save instruction in
+ * arch/sparc64/kernel/etrap.S to see what I mean about g2, and
+ * g4/g5 are the globals which are preserved by etrap processing
+ * for the caller of it.  The g7 register is the return pc for
+ * etrap.  Finally, g6 is the current thread register so we cannot
+ * us it in the spill handlers either.  Most of these rules do not
+ * apply to fill processing, only g6 is not usable.
  */
 
 /* Normal kernel spill */
@@ -187,52 +201,67 @@
 	nop; nop; nop; nop; nop; nop; nop; nop;
 
 /* Normal 64bit spill */
-#define SPILL_1_GENERIC(xxx)				\
-	wr	%g0, xxx, %asi;				\
-	stxa	%l0, [%sp + STACK_BIAS + 0x00] %asi;	\
-	stxa	%l1, [%sp + STACK_BIAS + 0x08] %asi;	\
-	stxa	%l2, [%sp + STACK_BIAS + 0x10] %asi;	\
-	stxa	%l3, [%sp + STACK_BIAS + 0x18] %asi;	\
-	stxa	%l4, [%sp + STACK_BIAS + 0x20] %asi;	\
-	stxa	%l5, [%sp + STACK_BIAS + 0x28] %asi;	\
-	stxa	%l6, [%sp + STACK_BIAS + 0x30] %asi;	\
-	stxa	%l7, [%sp + STACK_BIAS + 0x38] %asi;	\
-	stxa	%i0, [%sp + STACK_BIAS + 0x40] %asi;	\
-	stxa	%i1, [%sp + STACK_BIAS + 0x48] %asi;	\
-	stxa	%i2, [%sp + STACK_BIAS + 0x50] %asi;	\
-	stxa	%i3, [%sp + STACK_BIAS + 0x58] %asi;	\
-	stxa	%i4, [%sp + STACK_BIAS + 0x60] %asi;	\
-	stxa	%i5, [%sp + STACK_BIAS + 0x68] %asi;	\
-	stxa	%i6, [%sp + STACK_BIAS + 0x70] %asi;	\
-	stxa	%i7, [%sp + STACK_BIAS + 0x78] %asi;	\
-	saved; retry; nop; nop; nop; nop; nop; nop;	\
-	nop; nop; nop; nop;				\
+#define SPILL_1_GENERIC(ASI)				\
+	add	%sp, STACK_BIAS + 0x00, %g1;		\
+	stxa	%l0, [%g1 + %g0] ASI;			\
+	mov	0x08, %g3;				\
+	stxa	%l1, [%g1 + %g3] ASI;			\
+	add	%g1, 0x10, %g1;				\
+	stxa	%l2, [%g1 + %g0] ASI;			\
+	stxa	%l3, [%g1 + %g3] ASI;			\
+	add	%g1, 0x10, %g1;				\
+	stxa	%l4, [%g1 + %g0] ASI;			\
+	stxa	%l5, [%g1 + %g3] ASI;			\
+	add	%g1, 0x10, %g1;				\
+	stxa	%l6, [%g1 + %g0] ASI;			\
+	stxa	%l7, [%g1 + %g3] ASI;			\
+	add	%g1, 0x10, %g1;				\
+	stxa	%i0, [%g1 + %g0] ASI;			\
+	stxa	%i1, [%g1 + %g3] ASI;			\
+	add	%g1, 0x10, %g1;				\
+	stxa	%i2, [%g1 + %g0] ASI;			\
+	stxa	%i3, [%g1 + %g3] ASI;			\
+	add	%g1, 0x10, %g1;				\
+	stxa	%i4, [%g1 + %g0] ASI;			\
+	stxa	%i5, [%g1 + %g3] ASI;			\
+	add	%g1, 0x10, %g1;				\
+	stxa	%i6, [%g1 + %g0] ASI;			\
+	stxa	%i7, [%g1 + %g3] ASI;			\
+	saved;						\
+	retry; nop; nop;				\
 	b,a,pt	%xcc, spill_fixup_dax;			\
 	b,a,pt	%xcc, spill_fixup_mna;			\
 	b,a,pt	%xcc, spill_fixup;
 
 /* Normal 32bit spill */
-#define SPILL_2_GENERIC(xxx)				\
-	wr	%g0, xxx, %asi;				\
+#define SPILL_2_GENERIC(ASI)				\
 	srl	%sp, 0, %sp;				\
-	stwa	%l0, [%sp + 0x00] %asi;			\
-	stwa	%l1, [%sp + 0x04] %asi;			\
-	stwa	%l2, [%sp + 0x08] %asi;			\
-	stwa	%l3, [%sp + 0x0c] %asi;			\
-	stwa	%l4, [%sp + 0x10] %asi;			\
-	stwa	%l5, [%sp + 0x14] %asi;			\
-	stwa	%l6, [%sp + 0x18] %asi;			\
-	stwa	%l7, [%sp + 0x1c] %asi;			\
-	stwa	%i0, [%sp + 0x20] %asi;			\
-	stwa	%i1, [%sp + 0x24] %asi;			\
-	stwa	%i2, [%sp + 0x28] %asi;			\
-	stwa	%i3, [%sp + 0x2c] %asi;			\
-	stwa	%i4, [%sp + 0x30] %asi;			\
-	stwa	%i5, [%sp + 0x34] %asi;			\
-	stwa	%i6, [%sp + 0x38] %asi;			\
-	stwa	%i7, [%sp + 0x3c] %asi;			\
-	saved; retry; nop; nop; nop; nop;		\
-	nop; nop; nop; nop; nop;			\
+	stwa	%l0, [%sp + %g0] ASI;			\
+	mov	0x04, %g3;				\
+	stwa	%l1, [%sp + %g3] ASI;			\
+	add	%sp, 0x08, %g1;				\
+	stwa	%l2, [%g1 + %g0] ASI;			\
+	stwa	%l3, [%g1 + %g3] ASI;			\
+	add	%g1, 0x08, %g1;				\
+	stwa	%l4, [%g1 + %g0] ASI;			\
+	stwa	%l5, [%g1 + %g3] ASI;			\
+	add	%g1, 0x08, %g1;				\
+	stwa	%l6, [%g1 + %g0] ASI;			\
+	stwa	%l7, [%g1 + %g3] ASI;			\
+	add	%g1, 0x08, %g1;				\
+	stwa	%i0, [%g1 + %g0] ASI;			\
+	stwa	%i1, [%g1 + %g3] ASI;			\
+	add	%g1, 0x08, %g1;				\
+	stwa	%i2, [%g1 + %g0] ASI;			\
+	stwa	%i3, [%g1 + %g3] ASI;			\
+	add	%g1, 0x08, %g1;				\
+	stwa	%i4, [%g1 + %g0] ASI;			\
+	stwa	%i5, [%g1 + %g3] ASI;			\
+	add	%g1, 0x08, %g1;				\
+	stwa	%i6, [%g1 + %g0] ASI;			\
+	stwa	%i7, [%g1 + %g3] ASI;			\
+	saved;						\
+        retry; nop; nop;				\
 	b,a,pt	%xcc, spill_fixup_dax;			\
 	b,a,pt	%xcc, spill_fixup_mna;			\
 	b,a,pt	%xcc, spill_fixup;
@@ -276,52 +305,63 @@
 	nop; nop; nop; nop; nop; nop; nop; nop;
 
 /* Normal 64bit fill */
-#define FILL_1_GENERIC(xxx)				\
-	wr	%g0, xxx, %asi;				\
-	ldxa	[%sp + STACK_BIAS + 0x00] %asi, %l0;	\
-	ldxa	[%sp + STACK_BIAS + 0x08] %asi, %l1;	\
-	ldxa	[%sp + STACK_BIAS + 0x10] %asi, %l2;	\
-	ldxa	[%sp + STACK_BIAS + 0x18] %asi, %l3;	\
-	ldxa	[%sp + STACK_BIAS + 0x20] %asi, %l4;	\
-	ldxa	[%sp + STACK_BIAS + 0x28] %asi, %l5;	\
-	ldxa	[%sp + STACK_BIAS + 0x30] %asi, %l6;	\
-	ldxa	[%sp + STACK_BIAS + 0x38] %asi, %l7;	\
-	ldxa	[%sp + STACK_BIAS + 0x40] %asi, %i0;	\
-	ldxa	[%sp + STACK_BIAS + 0x48] %asi, %i1;	\
-	ldxa	[%sp + STACK_BIAS + 0x50] %asi, %i2;	\
-	ldxa	[%sp + STACK_BIAS + 0x58] %asi, %i3;	\
-	ldxa	[%sp + STACK_BIAS + 0x60] %asi, %i4;	\
-	ldxa	[%sp + STACK_BIAS + 0x68] %asi, %i5;	\
-	ldxa	[%sp + STACK_BIAS + 0x70] %asi, %i6;	\
-	ldxa	[%sp + STACK_BIAS + 0x78] %asi, %i7;	\
-	restored; retry; nop; nop; nop; nop; nop; nop;	\
-	nop; nop; nop; nop;				\
+#define FILL_1_GENERIC(ASI)				\
+	add	%sp, STACK_BIAS + 0x00, %g1;		\
+	ldxa	[%g1 + %g0] ASI, %l0;			\
+	mov	0x08, %g2;				\
+	mov	0x10, %g3;				\
+	ldxa	[%g1 + %g2] ASI, %l1;			\
+	mov	0x18, %g5;				\
+	ldxa	[%g1 + %g3] ASI, %l2;			\
+	ldxa	[%g1 + %g5] ASI, %l3;			\
+	add	%g1, 0x20, %g1;				\
+	ldxa	[%g1 + %g0] ASI, %l4;			\
+	ldxa	[%g1 + %g2] ASI, %l5;			\
+	ldxa	[%g1 + %g3] ASI, %l6;			\
+	ldxa	[%g1 + %g5] ASI, %l7;			\
+	add	%g1, 0x20, %g1;				\
+	ldxa	[%g1 + %g0] ASI, %i0;			\
+	ldxa	[%g1 + %g2] ASI, %i1;			\
+	ldxa	[%g1 + %g3] ASI, %i2;			\
+	ldxa	[%g1 + %g5] ASI, %i3;			\
+	add	%g1, 0x20, %g1;				\
+	ldxa	[%g1 + %g0] ASI, %i4;			\
+	ldxa	[%g1 + %g2] ASI, %i5;			\
+	ldxa	[%g1 + %g3] ASI, %i6;			\
+	ldxa	[%g1 + %g5] ASI, %i7;			\
+	restored;					\
+	retry; nop; nop; nop; nop;			\
 	b,a,pt	%xcc, fill_fixup_dax;			\
 	b,a,pt	%xcc, fill_fixup_mna;			\
 	b,a,pt	%xcc, fill_fixup;
 
 /* Normal 32bit fill */
-#define FILL_2_GENERIC(xxx)				\
-	wr	%g0, xxx, %asi;				\
+#define FILL_2_GENERIC(ASI)				\
 	srl	%sp, 0, %sp;				\
-	lduwa	[%sp + 0x00] %asi, %l0;			\
-	lduwa	[%sp + 0x04] %asi, %l1;			\
-	lduwa	[%sp + 0x08] %asi, %l2;			\
-	lduwa	[%sp + 0x0c] %asi, %l3;			\
-	lduwa	[%sp + 0x10] %asi, %l4;			\
-	lduwa	[%sp + 0x14] %asi, %l5;			\
-	lduwa	[%sp + 0x18] %asi, %l6;			\
-	lduwa	[%sp + 0x1c] %asi, %l7;			\
-	lduwa	[%sp + 0x20] %asi, %i0;			\
-	lduwa	[%sp + 0x24] %asi, %i1;			\
-	lduwa	[%sp + 0x28] %asi, %i2;			\
-	lduwa	[%sp + 0x2c] %asi, %i3;			\
-	lduwa	[%sp + 0x30] %asi, %i4;			\
-	lduwa	[%sp + 0x34] %asi, %i5;			\
-	lduwa	[%sp + 0x38] %asi, %i6;			\
-	lduwa	[%sp + 0x3c] %asi, %i7;			\
-	restored; retry; nop; nop; nop; nop;		\
-	nop; nop; nop; nop; nop;			\
+	lduwa	[%sp + %g0] ASI, %l0;			\
+	mov	0x04, %g2;				\
+	mov	0x08, %g3;				\
+	lduwa	[%sp + %g2] ASI, %l1;			\
+	mov	0x0c, %g5;				\
+	lduwa	[%sp + %g3] ASI, %l2;			\
+	lduwa	[%sp + %g5] ASI, %l3;			\
+	add	%sp, 0x10, %g1;				\
+	lduwa	[%g1 + %g0] ASI, %l4;			\
+	lduwa	[%g1 + %g2] ASI, %l5;			\
+	lduwa	[%g1 + %g3] ASI, %l6;			\
+	lduwa	[%g1 + %g5] ASI, %l7;			\
+	add	%g1, 0x10, %g1;				\
+	lduwa	[%g1 + %g0] ASI, %i0;			\
+	lduwa	[%g1 + %g2] ASI, %i1;			\
+	lduwa	[%g1 + %g3] ASI, %i2;			\
+	lduwa	[%g1 + %g5] ASI, %i3;			\
+	add	%g1, 0x10, %g1;				\
+	lduwa	[%g1 + %g0] ASI, %i4;			\
+	lduwa	[%g1 + %g2] ASI, %i5;			\
+	lduwa	[%g1 + %g3] ASI, %i6;			\
+	lduwa	[%g1 + %g5] ASI, %i7;			\
+	restored;					\
+	retry; nop; nop; nop; nop;			\
 	b,a,pt	%xcc, fill_fixup_dax;			\
 	b,a,pt	%xcc, fill_fixup_mna;			\
 	b,a,pt	%xcc, fill_fixup;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/apm_bios.h linux.14p6/include/linux/apm_bios.h
--- linux.vanilla/include/linux/apm_bios.h	Wed Oct 20 01:12:50 1999
+++ linux.14p6/include/linux/apm_bios.h	Fri Oct 29 23:39:48 1999
@@ -3,7 +3,7 @@
 
 /*
  * Include file for the interface to an APM BIOS
- * Copyright 1994-1998 Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
+ * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.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
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/dcache.h linux.14p6/include/linux/dcache.h
--- linux.vanilla/include/linux/dcache.h	Sat Aug 14 02:26:21 1999
+++ linux.14p6/include/linux/dcache.h	Thu Nov 11 21:54:17 1999
@@ -133,15 +133,14 @@
 
 /* allocate/de-allocate */
 extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
-extern void prune_dcache(int);
+extern int prune_dcache(int, int);
 extern void shrink_dcache_sb(struct super_block *);
 extern void shrink_dcache_parent(struct dentry *);
 extern int d_invalidate(struct dentry *);
 
-#define shrink_dcache() prune_dcache(0)
+#define shrink_dcache() prune_dcache(0, -1)
 
 /* dcache memory management */
-extern int  select_dcache(int, int);
 extern void shrink_dcache_memory(int, unsigned int);
 extern void check_dcache_memory(void);
 extern void free_inode_memory(int);	/* defined in fs/inode.c */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/fb.h linux.14p6/include/linux/fb.h
--- linux.vanilla/include/linux/fb.h	Sat Aug 14 02:26:01 1999
+++ linux.14p6/include/linux/fb.h	Tue Nov 16 15:13:28 1999
@@ -26,6 +26,7 @@
 /* #define FBIOSWITCH_MONIBIT	0x460E */
 #define FBIOGET_CON2FBMAP	0x460F
 #define FBIOPUT_CON2FBMAP	0x4610
+#define FBIOBLANK		0x4611		/* arg: 0 or vesa level + 1 */
 
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
@@ -72,6 +73,7 @@
 #define FB_ACCEL_SUN_BWTWO	23	/* Sun bwtwo			 */
 #define FB_ACCEL_SUN_CGTHREE	24	/* Sun cgthree			 */
 #define FB_ACCEL_SUN_TCX	25	/* Sun tcx			 */
+#define FB_ACCEL_MATROX_MGAG400 26	/* Matrox G400                   */
 
 struct fb_fix_screeninfo {
 	char id[16];			/* identification string eg "TT Builtin" */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/fs.h linux.14p6/include/linux/fs.h
--- linux.vanilla/include/linux/fs.h	Wed Oct 20 01:12:50 1999
+++ linux.14p6/include/linux/fs.h	Thu Nov 11 21:58:48 1999
@@ -837,6 +837,7 @@
 extern struct inode * igrab(struct inode *inode);
 extern ino_t iunique(struct super_block *, ino_t);
 extern struct inode * iget(struct super_block *, unsigned long);
+extern struct inode * iget_in_use (struct super_block *, unsigned long);
 extern void clear_inode(struct inode *);
 extern struct inode * get_empty_inode(void);
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/genhd.h linux.14p6/include/linux/genhd.h
--- linux.vanilla/include/linux/genhd.h	Sat Aug 14 02:27:39 1999
+++ linux.14p6/include/linux/genhd.h	Fri Oct 22 23:25:10 1999
@@ -42,7 +42,7 @@
 #endif
 
 #define DM6_PARTITION		0x54	/* has DDO: use xlated geom & offset */
-#define EZD_PARTITION		0x55	/* EZ-DRIVE:  same as DM6 (we think) */
+#define EZD_PARTITION		0x55	/* EZ-DRIVE: remap sector 1 to 0 */
 #define DM6_AUX1PARTITION	0x51	/* no DDO:  use xlated geom */
 #define DM6_AUX3PARTITION	0x53	/* no DDO:  use xlated geom */
 	
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/i2c.h linux.14p6/include/linux/i2c.h
--- linux.vanilla/include/linux/i2c.h	Wed Oct 20 01:12:50 1999
+++ linux.14p6/include/linux/i2c.h	Tue Nov 16 16:14:44 1999
@@ -40,6 +40,7 @@
 	/* 2 is used in 2.3.x */
 #define I2C_BUSID_BUZ		3	/* I2C bus on a BUZ */
 #define I2C_BUSID_ZORAN		4	/* I2C bus on a Zoran */
+#define I2C_BUSID_SGIVWFB	5	/* Moved to be unique */
 
 /*
  * struct for a driver for a i2c chip (tuner, soundprocessor,
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/isdn.h linux.14p6/include/linux/isdn.h
--- linux.vanilla/include/linux/isdn.h	Wed Oct 20 01:12:50 1999
+++ linux.14p6/include/linux/isdn.h	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.76 1999/09/14 10:16:21 keil Exp $
+/* $Id: isdn.h,v 1.81 1999/10/27 21:21:18 detabc Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +21,31 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.81  1999/10/27 21:21:18  detabc
+ * Added support for building logically-bind-group's per interface.
+ * usefull for outgoing call's with more then one isdn-card.
+ *
+ * Switchable support to dont reset the hangup-timeout for
+ * receive frames. Most part's of the timru-rules for receiving frames
+ * are now obsolete. If the input- or forwarding-firewall deny
+ * the frame, the line will be not hold open.
+ *
+ * Revision 1.80  1999/10/26 21:09:29  armin
+ * New bufferlen for phonenumber only with kernel 2.3.x
+ *
+ * Revision 1.79  1999/10/16 17:52:38  keil
+ * Changing the MSN length need new data versions
+ *
+ * Revision 1.78  1999/10/08 18:59:33  armin
+ * Bugfix of too small MSN buffer and checking phone number
+ * in isdn_tty_getdial()
+ *
+ * Revision 1.77  1999/09/23 22:22:42  detabc
+ * added tcp-keepalive-detect with local response (ipv4 only)
+ * added host-only-interface support
+ * (source ipaddr == interface ipaddr) (ipv4 only)
+ * ok with kernel 2.3.18 and 2.2.12
+ *
  * Revision 1.76  1999/09/14 10:16:21  keil
  * change ABC include
  *
@@ -301,9 +326,13 @@
 #undef CONFIG_ISDN_WITH_ABC_CALLB
 #undef CONFIG_ISDN_WITH_ABC_UDP_CHECK
 #undef CONFIG_ISDN_WITH_ABC_UDP_CHECK_HANGUP
+#undef CONFIG_ISDN_WITH_ABC_UDP_CHECK_DIAL
 #undef CONFIG_ISDN_WITH_ABC_OUTGOING_EAZ
-#undef CONFIG_ISDN_WITH_ABC_CALL_CHECK_SYNCRO
 #undef CONFIG_ISDN_WITH_ABC_LCR_SUPPORT
+#undef CONFIG_ISDN_WITH_ABC_IPV4_TCP_KEEPALIVE
+#undef CONFIG_ISDN_WITH_ABC_IPV4_DYNADDR
+#undef CONFIG_ISDN_WITH_ABC_RCV_NO_HUPTIMER
+#undef CONFIG_ISDN_WITH_ABC_ICALL_BIND
 
 
 /* New ioctl-codes */
@@ -363,10 +392,15 @@
 #define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing  */
 
 #define ISDN_MODEM_ANZREG    24        /* 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 */
 
+#define ISDN_MSNLEN          20
+#define NET_DV 0x05  /* Data version for isdn_net_ioctl_cfg   */
+#define TTY_DV 0x05  /* Data version for iprofd etc.          */
+
+#define INF_DV 0x01  /* Data version for /dev/isdninfo        */
+
 typedef struct {
   char drvid[25];
   unsigned long arg;
@@ -384,10 +418,6 @@
   int  outgoing;
 } isdn_net_ioctl_phone;
 
-#define NET_DV 0x05  /* Data version for isdn_net_ioctl_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                     */
   char master[10];   /* Name of Master for Bundling           */
@@ -896,6 +926,7 @@
 } isdn_dev;
 
 extern isdn_dev *dev;
+
 
 
 /* Utility-Macros */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/isdnif.h linux.14p6/include/linux/isdnif.h
--- linux.vanilla/include/linux/isdnif.h	Wed Oct 20 01:12:50 1999
+++ linux.14p6/include/linux/isdnif.h	Tue Nov 16 15:10:48 1999
@@ -1,4 +1,4 @@
-/* $Id: isdnif.h,v 1.31 1999/09/06 07:29:36 fritz Exp $
+/* $Id: isdnif.h,v 1.32 1999/10/11 22:03:00 keil Exp $
  *
  * Linux ISDN subsystem
  *
@@ -22,6 +22,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.32  1999/10/11 22:03:00  keil
+ * COMPAT_NEED_UACCESS (no include in isdn_compat.h)
+ *
  * Revision 1.31  1999/09/06 07:29:36  fritz
  * Changed my mail-address.
  *
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/lockd/syscall.h linux.14p6/include/linux/lockd/syscall.h
--- linux.vanilla/include/linux/lockd/syscall.h	Thu Jan  1 01:00:00 1970
+++ linux.14p6/include/linux/lockd/syscall.h	Thu Nov 11 22:01:29 1999
@@ -0,0 +1,26 @@
+/*
+ * include/linux/lockd/syscall.h
+ *
+ * This file holds all declarations for the knfsd/lockd syscall
+ * interface.
+ *
+ */
+
+#ifndef LOCKD_SYSCALL_H
+#define LOCKD_SYSCALL_H
+
+#include <linux/nfsd/syscall.h>
+
+#define LOCKDCTL_SVC NFSCTL_LOCKD
+
+#ifdef __KERNEL__
+/*
+ * Kernel syscall implementation.
+ */
+#if defined(CONFIG_LOCKD) || defined(CONFIG_LOCKD_MODULE)
+extern int	lockdctl(int, void *, void *);
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* LOCKD_SYSCALL_H */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/mm.h linux.14p6/include/linux/mm.h
--- linux.vanilla/include/linux/mm.h	Sat Aug 28 20:00:59 1999
+++ linux.14p6/include/linux/mm.h	Thu Nov 11 21:58:48 1999
@@ -292,7 +292,7 @@
 
 extern void vmtruncate(struct inode * inode, unsigned long offset);
 extern int handle_mm_fault(struct task_struct *tsk,struct vm_area_struct *vma, unsigned long address, int write_access);
-extern void make_pages_present(unsigned long addr, unsigned long end);
+extern int make_pages_present(unsigned long addr, unsigned long end);
 
 extern int pgt_cache_water[2];
 extern int check_pgt_cache(void);
@@ -300,7 +300,6 @@
 extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
 extern void mem_init(unsigned long start_mem, unsigned long end_mem);
 extern void show_mem(void);
-extern void oom(struct task_struct * tsk);
 extern void si_meminfo(struct sysinfo * val);
 
 /* mmap.c */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/msg.h linux.14p6/include/linux/msg.h
--- linux.vanilla/include/linux/msg.h	Mon Dec 28 06:19:28 1998
+++ linux.14p6/include/linux/msg.h	Thu Nov 11 21:52:41 1999
@@ -49,6 +49,7 @@
 #define MSGMNI   128   /* <= 1K */     /* max # of msg queue identifiers */
 #define MSGMAX  4056   /* <= 4056 */   /* max size of message (bytes) */
 #define MSGMNB 16384   /* ? */        /* default max size of a message queue */
+#define MSGQNUM 1024   /* <=65535 */	  /* Max messages in flight / queue */
 
 /* unused */
 #define MSGPOOL (MSGMNI*MSGMNB/1024)  /* size in kilobytes of message pool */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/netdevice.h linux.14p6/include/linux/netdevice.h
--- linux.vanilla/include/linux/netdevice.h	Sat Aug 28 20:00:59 1999
+++ linux.14p6/include/linux/netdevice.h	Thu Nov 11 22:00:00 1999
@@ -266,6 +266,9 @@
 	struct Qdisc		*qdisc_list;
 	unsigned long		tx_queue_len;	/* Max frames per queue allowed */
 
+	/* Bridge stuff */
+	int			bridge_port_id;		
+	
 	/* Pointers to interface service routines.	*/
 	int			(*open)(struct device *dev);
 	int			(*stop)(struct device *dev);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/nfsd/nfsfh.h linux.14p6/include/linux/nfsd/nfsfh.h
--- linux.vanilla/include/linux/nfsd/nfsfh.h	Wed Oct 20 01:12:51 1999
+++ linux.14p6/include/linux/nfsd/nfsfh.h	Thu Nov 11 22:01:29 1999
@@ -107,6 +107,7 @@
 void	fh_put(struct svc_fh *);
 void	nfsd_fh_flush(kdev_t);
 void	nfsd_fh_init(void);
+void	nfsd_fh_shutdown(void);
 void	nfsd_fh_free(void);
 
 void	expire_all(void);
@@ -181,6 +182,11 @@
 		up(&inode->i_sem);
 	}
 }
+
+/*
+ * This is a long term cache to help find renamed files.
+ */
+void add_to_rename_cache(ino_t new_dirino, kdev_t dev, ino_t dirino, ino_t ino);
 
 #endif /* __KERNEL__ */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/nfsd/stats.h linux.14p6/include/linux/nfsd/stats.h
--- linux.vanilla/include/linux/nfsd/stats.h	Wed Nov 26 21:08:38 1997
+++ linux.14p6/include/linux/nfsd/stats.h	Fri Oct 22 21:31:59 1999
@@ -18,6 +18,7 @@
 	unsigned int	fh_fixup;	/* dentry fixup validated */
 	unsigned int	fh_lookup;	/* new lookup required */
 	unsigned int	fh_stale;	/* FH stale error */
+	unsigned int    fh_concurrent;	/* concurrent request */
 };
 
 #ifdef __KERNEL__
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/nfsd/syscall.h linux.14p6/include/linux/nfsd/syscall.h
--- linux.vanilla/include/linux/nfsd/syscall.h	Mon Dec 28 22:10:01 1998
+++ linux.14p6/include/linux/nfsd/syscall.h	Thu Nov 11 22:01:29 1999
@@ -35,6 +35,9 @@
 #define NFSCTL_GETFH		6	/* get an fh by ino (used by mountd) */
 #define NFSCTL_GETFD		7	/* get an fh by path (used by mountd) */
 
+/* Above this is for lockd. */
+#define NFSCTL_LOCKD		0x10000
+
 /* SVC */
 struct nfsctl_svc {
 	unsigned short		svc_port;
@@ -121,11 +124,7 @@
 /*
  * Kernel syscall implementation.
  */
-#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
 extern asmlinkage int	sys_nfsservctl(int, void *, void *);
-#else
-#define sys_nfsservctl		sys_ni_syscall
-#endif
 extern int		exp_addclient(struct nfsctl_client *ncp);
 extern int		exp_delclient(struct nfsctl_client *ncp);
 extern int		exp_export(struct nfsctl_export *nxp);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/pagemap.h linux.14p6/include/linux/pagemap.h
--- linux.vanilla/include/linux/pagemap.h	Wed Oct 20 01:12:51 1999
+++ linux.14p6/include/linux/pagemap.h	Thu Nov 11 21:58:48 1999
@@ -38,11 +38,15 @@
  */
 #define page_cache_entry(x)	(mem_map + MAP_NR(x))
 
-#define PAGE_HASH_BITS 12
-#define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS)
+#define PAGE_HASH_BITS page_hash_bits
+#define PAGE_HASH_MASK page_hash_mask
 
 extern unsigned long page_cache_size; /* # of pages currently in the hash table */
-extern struct page * page_hash_table[PAGE_HASH_SIZE];
+extern unsigned int page_hash_bits;
+extern unsigned int page_hash_mask;
+extern struct page **page_hash_table;
+
+extern void page_cache_init(unsigned long);
 
 /*
  * We use a power-of-two hash table to avoid a modulus,
@@ -53,12 +57,10 @@
 static inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset)
 {
 #define i (((unsigned long) inode)/(sizeof(struct inode) & ~ (sizeof(struct inode) - 1)))
-#define o (offset >> PAGE_SHIFT)
-#define s(x) ((x)+((x)>>PAGE_HASH_BITS))
-	return s(i+o) & (PAGE_HASH_SIZE-1);
+#define o ((offset >> PAGE_SHIFT) + (offset & ~PAGE_MASK))
+	return ((i+o) & PAGE_HASH_MASK);
 #undef i
 #undef o
-#undef s
 }
 
 #define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset))
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/parport.h linux.14p6/include/linux/parport.h
--- linux.vanilla/include/linux/parport.h	Sun Mar 28 19:04:25 1999
+++ linux.14p6/include/linux/parport.h	Thu Nov 11 21:59:48 1999
@@ -209,6 +209,9 @@
 	spinlock_t pardevice_lock;
 	spinlock_t waitlist_lock;
 	rwlock_t cad_lock;
+
+	/* PCI parallel I/O card support. */
+	unsigned long base_hi;  /* base address (hi - ECR) */
 };
 
 /* parport_register_port registers a new parallel port at the given address (if
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/parport_pc.h linux.14p6/include/linux/parport_pc.h
--- linux.vanilla/include/linux/parport_pc.h	Sat Aug 14 02:26:21 1999
+++ linux.14p6/include/linux/parport_pc.h	Thu Nov 11 21:59:48 1999
@@ -5,14 +5,15 @@
 
 /* --- register definitions ------------------------------- */
 
-#define ECONTROL 0x402
-#define CONFIGB  0x401
-#define CONFIGA  0x400
-#define EPPDATA  0x4
-#define EPPADDR  0x3
-#define CONTROL  0x2
-#define STATUS   0x1
-#define DATA     0
+#define ECONTROL(p) ((p)->base_hi + 0x2)
+#define CONFIGB(p)  ((p)->base_hi + 0x1)
+#define CONFIGA(p)  ((p)->base_hi + 0x0)
+#define FIFO(p)     ((p)->base_hi + 0x0)
+#define EPPDATA(p)  ((p)->base    + 0x4)
+#define EPPADDR(p)  ((p)->base    + 0x3)
+#define CONTROL(p)  ((p)->base    + 0x2)
+#define STATUS(p)   ((p)->base    + 0x1)
+#define DATA(p)     ((p)->base    + 0x0)
 
 /* Private data for PC low-level driver. */
 struct parport_pc_private {
@@ -26,27 +27,27 @@
 
 extern __inline__ void parport_pc_write_epp(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+EPPDATA);
+	outb(d, EPPDATA(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_epp(struct parport *p)
 {
-	return inb(p->base+EPPDATA);
+	return inb(EPPDATA(p));
 }
 
 extern __inline__ void parport_pc_write_epp_addr(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+EPPADDR);
+	outb(d, EPPADDR(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_epp_addr(struct parport *p)
 {
-	return inb(p->base+EPPADDR);
+	return inb(EPPADDR(p));
 }
 
 extern __inline__ int parport_pc_check_epp_timeout(struct parport *p)
 {
-	if (!(inb(p->base+STATUS) & 1))
+	if (!(inb(STATUS(p)) & 1))
 		return 0;
 	parport_pc_epp_clear_timeout(p);
 	return 1;
@@ -54,24 +55,24 @@
 
 extern __inline__ unsigned char parport_pc_read_configb(struct parport *p)
 {
-	return inb(p->base+CONFIGB);
+	return inb(CONFIGB(p));
 }
 
 extern __inline__ void parport_pc_write_data(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+DATA);
+	outb(d, DATA(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_data(struct parport *p)
 {
-	return inb(p->base+DATA);
+	return inb(DATA(p));
 }
 
 extern __inline__ void parport_pc_write_control(struct parport *p, unsigned char d)
 {
 	struct parport_pc_private *priv = p->private_data;
 	priv->ctr = d;/* update soft copy */
-	outb(d, p->base+CONTROL);
+	outb(d, CONTROL(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_control(struct parport *p)
@@ -85,34 +86,34 @@
 	struct parport_pc_private *priv = p->private_data;
 	unsigned char ctr = priv->ctr;
 	ctr = (ctr & ~mask) ^ val;
-	outb (ctr, p->base+CONTROL);
+	outb (ctr, CONTROL(p));
 	return priv->ctr = ctr; /* update soft copy */
 }
 
 extern __inline__ void parport_pc_write_status(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+STATUS);
+	outb(d, STATUS(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_status(struct parport *p)
 {
-	return inb(p->base+STATUS);
+	return inb(STATUS(p));
 }
 
 extern __inline__ void parport_pc_write_econtrol(struct parport *p, unsigned char d)
 {
-	outb(d, p->base+ECONTROL);
+	outb(d, ECONTROL(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_econtrol(struct parport *p)
 {
-	return inb(p->base+ECONTROL);
+	return inb(ECONTROL(p));
 }
 
 extern __inline__ unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned char old = inb(p->base+ECONTROL);
-	outb(((old & ~mask) ^ val), p->base+ECONTROL);
+	unsigned char old = inb(ECONTROL(p));
+	outb(((old & ~mask) ^ val), ECONTROL(p));
 	return old;
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/pci.h linux.14p6/include/linux/pci.h
--- linux.vanilla/include/linux/pci.h	Wed Oct 20 01:12:51 1999
+++ linux.14p6/include/linux/pci.h	Tue Nov 16 15:13:28 1999
@@ -993,9 +993,35 @@
 #define PCI_VENDOR_ID_CBOARDS		0x1307
 #define PCI_DEVICE_ID_CBOARDS_DAS1602_16 0x0001
 
+#define PCI_VENDOR_ID_SIIG		0x131f
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_550	0x1010
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_650	0x1011
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_850	0x1012
+#define PCI_DEVICE_ID_SIIG_1P_10x	0x1020
+#define PCI_DEVICE_ID_SIIG_2P_10x	0x1021
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_550	0x1034
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_650	0x1035
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_850	0x1036
+#define PCI_DEVICE_ID_SIIG_1P_20x	0x2020
+#define PCI_DEVICE_ID_SIIG_2P_20x	0x2021
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_550	0x2040
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_650	0x2041
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_850	0x2042
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_550	0x2010
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_650	0x2011
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_850	0x2012
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_550	0x2060
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_650	0x2061
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_850	0x2062
+
 #define PCI_VENDOR_ID_NETGEAR		0x1385
 #define PCI_DEVICE_ID_NETGEAR_GA620	0x620a
 
+#define PCI_VENDOR_ID_LAVA		0x1407
+#define PCI_DEVICE_ID_LAVA_PARALLEL	0x8000
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A	0x8002 /* The Lava Dual Parallel is */
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B	0x8003 /* two PCI devices on a card */
+
 #define PCI_VENDOR_ID_SYMPHONY		0x1c1c
 #define PCI_DEVICE_ID_SYMPHONY_101	0x0001
 
@@ -1008,6 +1034,9 @@
 #define PCI_DEVICE_ID_3DLABS_DELTA	0x0003
 #define PCI_DEVICE_ID_3DLABS_PERMEDIA	0x0004
 #define PCI_DEVICE_ID_3DLABS_MX		0x0006
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2	0x0007
+#define PCI_DEVICE_ID_3DLABS_GAMMA	0x0008
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V	0x0009
 
 #define PCI_VENDOR_ID_AVANCE		0x4005
 #define PCI_DEVICE_ID_AVANCE_ALG2064	0x2064
@@ -1067,6 +1096,7 @@
 #define PCI_DEVICE_ID_INTEL_82441	0x1237
 #define PCI_DEVICE_ID_INTEL_82380FB	0x124b
 #define PCI_DEVICE_ID_INTEL_82439	0x1250
+#define PCI_DEVICE_ID_INTEL_MEGARAID	0x1960
 #define PCI_DEVICE_ID_INTEL_82371SB_0	0x7000
 #define PCI_DEVICE_ID_INTEL_82371SB_1	0x7010
 #define PCI_DEVICE_ID_INTEL_82371SB_2	0x7020
@@ -1076,17 +1106,19 @@
 #define PCI_DEVICE_ID_INTEL_82371AB	0x7111
 #define PCI_DEVICE_ID_INTEL_82371AB_2	0x7112
 #define PCI_DEVICE_ID_INTEL_82371AB_3	0x7113
-#define PCI_VENDOR_ID_COMPUTONE		0x8e0e
-#define PCI_DEVICE_ID_COMPUTONE_IP2EX	0x0291
-
 #define PCI_DEVICE_ID_INTEL_82443LX_0	0x7180
 #define PCI_DEVICE_ID_INTEL_82443LX_1	0x7181
 #define PCI_DEVICE_ID_INTEL_82443BX_0	0x7190
 #define PCI_DEVICE_ID_INTEL_82443BX_1	0x7191
 #define PCI_DEVICE_ID_INTEL_82443BX_2	0x7192
 #define PCI_DEVICE_ID_INTEL_P6		0x84c4
-#define PCI_DEVICE_ID_INTEL_82450GX	0x84c5
+#define PCI_DEVICE_ID_INTEL_82450GX	0x84c4
+#define PCI_DEVICE_ID_INTEL_82453GX	0x84c5
 #define PCI_DEVICE_ID_INTEL_82451NX	0x84ca
+#define PCI_DEVICE_ID_INTEL_82454NX	0x84cb
+
+#define PCI_VENDOR_ID_COMPUTONE		0x8e0e
+#define PCI_DEVICE_ID_COMPUTONE_IP2EX	0x0291
 
 #define PCI_VENDOR_ID_KTI		0x8e2e
 #define PCI_DEVICE_ID_KTI_ET32P2	0x3000
@@ -1317,6 +1349,9 @@
 
 extern inline struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn)
 { return NULL; }
+
+extern inline void pci_set_master(struct pci_dev *dev)
+{ return; }
 
 #endif /* !CONFIG_PCI */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/proc_fs.h linux.14p6/include/linux/proc_fs.h
--- linux.vanilla/include/linux/proc_fs.h	Wed Oct 20 01:12:51 1999
+++ linux.14p6/include/linux/proc_fs.h	Thu Nov 11 21:58:48 1999
@@ -466,6 +466,7 @@
 extern inline void proc_tty_register_driver(struct tty_driver *driver) {};
 extern inline void proc_tty_unregister_driver(struct tty_driver *driver) {};
 
+extern struct proc_dir_entry proc_root;
 
-#endif
+#endif /* CONFIG_PROC_FS */
 #endif /* _LINUX_PROC_FS_H */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/sched.h linux.14p6/include/linux/sched.h
--- linux.vanilla/include/linux/sched.h	Wed Oct 20 01:12:51 1999
+++ linux.14p6/include/linux/sched.h	Thu Nov 11 21:58:48 1999
@@ -657,12 +657,12 @@
 	
 	if (nr >= files->max_fdset) {
 		expand = 1;
-		if ((err = expand_fdset(files, nr)))
+		if ((err = expand_fdset(files, nr + 1)))
 			goto out;
 	}
 	if (nr >= files->max_fds) {
 		expand = 1;
-		if ((err = expand_fd_array(files, nr)))
+		if ((err = expand_fd_array(files, nr + 1)))
 			goto out;
 	}
 	err = expand;
@@ -737,6 +737,7 @@
 	add_wait_queue(&wq, &__wait);					\
 	for (;;) {							\
 		current->state = TASK_UNINTERRUPTIBLE;			\
+		mb();							\
 		if (condition)						\
 			break;						\
 		schedule();						\
@@ -760,6 +761,7 @@
 	add_wait_queue(&wq, &__wait);					\
 	for (;;) {							\
 		current->state = TASK_INTERRUPTIBLE;			\
+		mb();							\
 		if (condition)						\
 			break;						\
 		if (!signal_pending(current)) {				\
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/sunrpc/svc.h linux.14p6/include/linux/sunrpc/svc.h
--- linux.vanilla/include/linux/sunrpc/svc.h	Sun Mar 28 19:03:50 1999
+++ linux.14p6/include/linux/sunrpc/svc.h	Fri Oct 22 23:27:44 1999
@@ -13,6 +13,7 @@
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/svcauth.h>
+#include <asm/atomic.h>
 
 /*
  * RPC service.
@@ -29,7 +30,7 @@
 	struct svc_sock *	sv_sockets;	/* pending sockets */
 	struct svc_program *	sv_program;	/* RPC program */
 	struct svc_stat *	sv_stats;	/* RPC statistics */
-	unsigned int		sv_nrthreads;	/* # of server threads */
+	atomic_t		sv_nrthreads;	/* # of server threads */
 	unsigned int		sv_bufsz;	/* datagram buffer size */
 	unsigned int		sv_xdrsize;	/* XDR buffer size */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/sunrpc/svcsock.h linux.14p6/include/linux/sunrpc/svcsock.h
--- linux.vanilla/include/linux/sunrpc/svcsock.h	Sun Mar 28 19:03:53 1999
+++ linux.14p6/include/linux/sunrpc/svcsock.h	Fri Oct 22 23:27:48 1999
@@ -10,6 +10,7 @@
 #define SUNRPC_SVCSOCK_H
 
 #include <linux/sunrpc/svc.h>
+#include <asm/atomic.h>
 
 /*
  * RPC server socket.
@@ -23,7 +24,7 @@
 	struct sock *		sk_sk;		/* INET layer */
 
 	struct svc_serv *	sk_server;	/* service for this socket */
-	unsigned char		sk_inuse;	/* use count */
+	atomic_t		sk_inuse;	/* use count */
 	unsigned char		sk_busy;	/* enqueued/receiving */
 	unsigned char		sk_conn;	/* conn pending */
 	unsigned char		sk_close;	/* dead or dying */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/sunrpc/xprt.h linux.14p6/include/linux/sunrpc/xprt.h
--- linux.vanilla/include/linux/sunrpc/xprt.h	Sun Mar 28 19:03:27 1999
+++ linux.14p6/include/linux/sunrpc/xprt.h	Tue Nov 16 15:15:34 1999
@@ -124,8 +124,10 @@
 struct rpc_xprt {
 	struct rpc_xprt *	link;		/* list of all clients */
 	struct rpc_xprt *	rx_pending;	/* receive pending list */
+	struct rpc_xprt *	tx_pending;	/* transmit pending list */
 	
-	int 			rx_pending_flag;/* are we on the pending list ? */
+	int 			rx_pending_flag;/* are we on the rcv pending list ? */
+	int 			tx_pending_flag;/* are we on the xmit pending list ? */
 
 	struct file *		file;		/* VFS layer */
 	struct socket *		sock;		/* BSD socket layer */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/swap.h linux.14p6/include/linux/swap.h
--- linux.vanilla/include/linux/swap.h	Sun Mar 28 19:03:26 1999
+++ linux.14p6/include/linux/swap.h	Sat Oct 23 23:02:34 1999
@@ -91,8 +91,8 @@
 extern void swap_after_unlock_page (unsigned long entry);
 
 /* linux/mm/page_alloc.c */
-extern void swap_in(struct task_struct *, struct vm_area_struct *,
-		    pte_t *, unsigned long, int);
+extern int swap_in(struct task_struct *, struct vm_area_struct *,
+		   pte_t *, unsigned long, int);
 
 
 /* linux/mm/swap_state.c */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/sysctl.h linux.14p6/include/linux/sysctl.h
--- linux.vanilla/include/linux/sysctl.h	Wed Oct 20 01:12:51 1999
+++ linux.14p6/include/linux/sysctl.h	Tue Nov 16 15:13:48 1999
@@ -104,7 +104,8 @@
 	KERN_MSGMNB=36,         /* int: Maximum message queue size */
 	KERN_MSGPOOL=37,         /* int: Maximum system message pool size */
 	KERN_SYSRQ=38,		/* int: Sysreq enable */
-	KERN_SHMALL=41		/* int: maximum size of shared memory */
+	KERN_SHMALL=41,		/* int: maximum size of shared memory */
+	KERN_SPARC_STOP_A=44,	/* int: Sparc Stop-A enable */
 };
 
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/wireless.h linux.14p6/include/linux/wireless.h
--- linux.vanilla/include/linux/wireless.h	Sat Aug 14 02:27:40 1999
+++ linux.14p6/include/linux/wireless.h	Wed Nov  3 00:22:30 1999
@@ -1,7 +1,7 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :	8	28.7.99
+ * Version :	9	16.10.99
  *
  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
  */
@@ -63,7 +63,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	8
+#define WIRELESS_EXT	9
 
 /*
  * Changes :
@@ -96,6 +96,14 @@
  *	- Changed my e-mail address
  *	- More 802.11 support (nickname, rate, rts, frag)
  *	- List index in frequencies
+ *
+ * V8 to V9
+ * --------
+ *	- Support for 'mode of operation' (ad-hoc, managed...)
+ *	- Support for unicast and multicast power saving
+ *	- Change encoding to support larger tokens (>64 bits)
+ *	- Updated iw_params (disable, flags) and use it for NWID
+ *	- Extracted iw_point from iwreq for clarity
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
@@ -103,12 +111,12 @@
 /* Basic operations */
 #define SIOCSIWNAME	0x8B00		/* Unused ??? */
 #define SIOCGIWNAME	0x8B01		/* get name */
-#define SIOCSIWNWID	0x8B02		/* set network id */
+#define SIOCSIWNWID	0x8B02		/* set network id (the cell) */
 #define SIOCGIWNWID	0x8B03		/* get network id */
 #define SIOCSIWFREQ	0x8B04		/* set channel/frequency */
 #define SIOCGIWFREQ	0x8B05		/* get channel/frequency */
-#define SIOCSIWENCODE	0x8B06		/* set encoding info */
-#define SIOCGIWENCODE	0x8B07		/* get encoding info */
+#define SIOCSIWMODE	0x8B06		/* set operation mode */
+#define SIOCGIWMODE	0x8B07		/* get operation mode */
 #define SIOCSIWSENS	0x8B08		/* set sensitivity */
 #define SIOCGIWSENS	0x8B09		/* get sensitivity */
 
@@ -146,11 +154,18 @@
 #define SIOCSIWFRAG	0x8B24		/* set fragmentation thr (bytes) */
 #define SIOCGIWFRAG	0x8B25		/* get fragmentation thr (bytes) */
 
+/* Encoding stuff (scrambling, hardware security, WEP...) */
+#define SIOCSIWENCODE	0x8B2A		/* set encoding token & mode */
+#define SIOCGIWENCODE	0x8B2B		/* get encoding token & mode */
+/* Power saving stuff (power management, unicast and multicast) */
+#define SIOCSIWPOWER	0x8B2C		/* set Power Management settings */
+#define SIOCGIWPOWER	0x8B2D		/* get Power Management settings */
+
 /* ------------------------- IOCTL STUFF ------------------------- */
 
 /* The first and the last (range) */
 #define SIOCIWFIRST	0x8B00
-#define SIOCIWLAST	0x8B25
+#define SIOCIWLAST	0x8B30
 
 /* Even : get (world access), odd : set (root access) */
 #define IW_IS_SET(cmd)	(!((cmd) & 0x1))
@@ -200,10 +215,67 @@
 /* Maximum size of the ESSID and NICKN strings */
 #define IW_ESSID_MAX_SIZE	32
 
+/* Modes of operation */
+#define IW_MODE_AUTO	0	/* Let the driver decides */
+#define IW_MODE_ADHOC	1	/* Single cell network */
+#define IW_MODE_INFRA	2	/* Multi cell network, roaming, ... */
+#define IW_MODE_MASTER	3	/* Synchronisation master or Access Point */
+#define IW_MODE_REPEAT	4	/* Wireless Repeater (forwarder) */
+#define IW_MODE_SECOND	5	/* Secondary master/repeater (backup) */
+
+/* Maximum number of size of encoding token available
+ * they are listed in the range structure */
+#define IW_MAX_ENCODING_SIZES	8
+
+/* Maximum size of the encoding token in bytes */
+#define IW_ENCODING_TOKEN_MAX	32	/* 256 bits (for now) */
+
+/* Flags for encoding (along with the token) */
+#define IW_ENCODE_INDEX		0x00FF	/* Token index (if needed) */
+#define IW_ENCODE_FLAGS		0xF000	/* Flags defined below */
+#define IW_ENCODE_DISABLED	0x8000	/* Encoding disabled */
+#define IW_ENCODE_ENABLED	0x0000	/* Encoding enabled */
+#define IW_ENCODE_RESTRICTED	0x4000	/* Refuse non-encoded packets */
+#define IW_ENCODE_OPEN		0x2000	/* Accept non-encoded packets */
+
+/* Power management flags available (along with the value, if any) */
+#define IW_POWER_ON		0x0000	/* No details... */
+#define IW_POWER_TYPE		0xF000	/* Type of parameter */
+#define IW_POWER_PERIOD		0x1000	/* Value is a period/duration of  */
+#define IW_POWER_TIMEOUT	0x2000	/* Value is a timeout (to go asleep) */
+#define IW_POWER_MODE		0x0F00	/* Power Management mode */
+#define IW_POWER_UNICAST_R	0x0100	/* Receive only unicast messages */
+#define IW_POWER_MULTICAST_R	0x0200	/* Receive only multicast messages */
+#define IW_POWER_ALL_R		0x0300	/* Receive all messages though PM */
+#define IW_POWER_FORCE_S	0x0400	/* Force PM procedure for sending unicast */
+#define IW_POWER_REPEATER	0x0800	/* Repeat broadcast messages in PM period */
+
 /****************************** TYPES ******************************/
 
 /* --------------------------- SUBTYPES --------------------------- */
 /*
+ *	Generic format for most parameters that fit in an int
+ */
+struct	iw_param
+{
+  __s32		value;		/* The value of the parameter itself */
+  __u8		fixed;		/* Hardware should not use auto select */
+  __u8		disabled;	/* Disable the feature */
+  __u16		flags;		/* Various specifc flags (if any) */
+};
+
+/*
+ *	For all data larger than 16 octets, we need to use a
+ *	pointer to memory alocated in user space.
+ */
+struct	iw_point
+{
+  caddr_t	pointer;	/* Pointer to the data  (in user space) */
+  __u16		length;		/* number of fields or size in bytes */
+  __u16		flags;		/* Optional params */
+};
+
+/*
  *	A frequency
  *	For numbers lower than 10^9, we encode the number in 'm' and
  *	set 'e' to 0
@@ -223,7 +295,7 @@
  */
 struct	iw_quality
 {
-	__u8		qual;		/* link quality (SNR or better...) */
+	__u8		qual;		/* link quality (%retries, SNR or better...) */
 	__u8		level;		/* signal level */
 	__u8		noise;		/* noise level */
 	__u8		updated;	/* Flags to know if updated */
@@ -240,33 +312,13 @@
 	__u32		misc;		/* Others cases */
 };
 
-/*
- *	Encoding information (setting and so on)
- *	Encoding might be hardware encryption, scrambing or others
- */
-struct	iw_encoding
-{
-  __u8	method;			/* Algorithm number / key used */
-  __u64	code;			/* Data/key used for algorithm */
-};
-
-/*
- *	Generic format for parameters
- */
-struct	iw_param
-{
-  __s32		value;		/* The value of the parameter itself */
-  __u8		fixed;		/* Hardware should not use auto select */
-};
-
-
 /* ------------------------ WIRELESS STATS ------------------------ */
 /*
  * Wireless statistics (used for /proc/net/wireless)
  */
 struct	iw_statistics
 {
-	__u8		status;		/* Status
+	__u16		status;		/* Status
 					 * - device dependent for now */
 
 	struct iw_quality	qual;		/* Quality of the link
@@ -295,37 +347,28 @@
 	union
 	{
 		/* Config - generic */
-		char	name[IFNAMSIZ];
+		char		name[IFNAMSIZ];
 		/* Name : used to verify the presence of  wireless extensions.
 		 * Name of the protocol/provider... */
 
-		struct		/* network id (or domain) : used to to */
-		{		/* create logical channels on the air */
-			__u32	nwid;		/* value */
-			__u8	on;		/* active/unactive nwid */
-		}	nwid;
-
+		struct iw_point	essid;	/* Extended network name */
+		struct iw_param	nwid;	/* network id (or domain - the cell) */
 		struct iw_freq	freq;	/* frequency or channel :
 					 * 0-1000 = channel
 					 * > 1000 = frequency in Hz */
 
-		struct iw_encoding	encoding;	/* Encoding stuff */
-
-		__u32	sensitivity;		/* Obsolete, but compatible */
 		struct iw_param	sens;		/* signal level threshold */
 		struct iw_param	bitrate;	/* default bit rate */
 		struct iw_param	rts;		/* RTS threshold threshold */
 		struct iw_param	frag;		/* Fragmentation threshold */
+		__u32		mode;		/* Operation mode */
+
+		struct iw_point	encoding;	/* Encoding stuff : tokens */
+		struct iw_param	power;		/* PM duration/timeout */
 
 		struct sockaddr	ap_addr;	/* Access point address */
 
-		struct		/* For all data bigger than 16 octets */
-		{
-			caddr_t	pointer;	/* Pointer to the data
-						 * (in user space) */
-			__u16	length;		/* fields or byte size */
-			__u16	flags;		/* Optional params */
-		}	data;
+		struct iw_point	data;		/* Other large parameters */
 	}	u;
 };
 
@@ -366,9 +409,6 @@
 	/* Quality of link & SNR stuff */
 	struct iw_quality	max_qual;	/* Quality of the link */
 
-	/* Encoder stuff */
-	struct iw_encoding	max_encoding;	/* Encoding max range */
-
 	/* Rates */
 	__u8		num_bitrates;	/* Number of entries in the list */
 	__s32		bitrate[IW_MAX_BITRATES];	/* list, in bps */
@@ -380,6 +420,17 @@
 	/* Frag threshold */
 	__s32		min_frag;	/* Minimal frag threshold */
 	__s32		max_frag;	/* Maximal frag threshold */
+
+	/* Power Management duration & timeout */
+	__s32		min_pmd;	/* Minimal PM duration */
+	__s32		max_pmd;	/* Maximal PM duration */
+	__s32		min_pmt;	/* Minimal PM timeout */
+	__s32		max_pmt;	/* Maximal PM timeout */
+
+	/* Encoder stuff */
+	__u16	encoding_size[IW_MAX_ENCODING_SIZES];	/* Different token sizes */
+	__u8	num_encoding_sizes;	/* Number of entry in the list */
+	__u8	max_encoding_tokens;	/* Max number of tokens */
 };
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/net/br.h linux.14p6/include/net/br.h
--- linux.vanilla/include/net/br.h	Sat Aug 14 02:27:40 1999
+++ linux.14p6/include/net/br.h	Fri Oct 22 23:28:35 1999
@@ -19,7 +19,16 @@
 #define Forwarding	3			  /* (4 4 4)	 */
 #define Blocking	4			  /* (4.4.1)	 */
 
-#define No_of_ports 8
+
+/* MAG Yich! Easiest way of giving a configurable number of ports
+ * If you want more than 32, change BR_MAX_PORTS and recompile brcfg!
+ */
+#define BR_MAX_PORTS (32)
+#if CONFIG_BRIDGE_NUM_PORTS > BR_MAX_PORTS
+#undef CONFIG_BRIDGE_NUM_PORTS
+#define CONFIG_BRIDGE_NUM_PORTS BR_MAX_PORTS
+#endif
+#define No_of_ports CONFIG_BRIDGE_NUM_PORTS
 /* arbitrary choice, to allow the code below to compile */
 
 #define All_ports (No_of_ports + 1)
@@ -147,6 +156,7 @@
 	unsigned int     top_change;		  /* (4.5.3.12)	 */
 	unsigned short   topology_change_time;	  /* (4.5.3.13)	 */
 	unsigned short   hold_time;		  /* (4.5.3.14)	 */
+	unsigned int	 instance;
 } Bridge_data;
 
 /** Port Parameters (4.5.5) **/
@@ -160,7 +170,10 @@
 	unsigned short   designated_port;	  /* (4.5.5.7)	 */
 	unsigned int     top_change_ack;	  /* (4.5.5.8)	 */
 	unsigned int     config_pending;	  /* (4.5.5.9)	 */
-	struct device *dev;	
+        bridge_id_t      ifmac; 
+        unsigned int     admin_state;
+	char             ifname[IFNAMSIZ]; /* Make life easier for brcfg */
+        struct device *dev;	
 	struct fdb *fdb;	/* head of per port fdb chain */
 } Port_data;
 
@@ -245,13 +258,14 @@
 struct br_stat {
 	unsigned int flags;
 	Bridge_data bridge_data;
-	Port_data port_data[No_of_ports];
 	unsigned int policy;
 	unsigned int exempt_protocols;
 	unsigned short protocols[BR_MAX_PROTOCOLS];
 	unsigned short prot_id[BR_MAX_PROT_STATS];	/* Protocol encountered */
 	unsigned int prot_counter[BR_MAX_PROT_STATS];	/* How many packets ? */
 	br_stats_counter packet_cnts;
+	unsigned int	num_ports;
+	Port_data port_data[BR_MAX_PORTS + 1];
 };
 
 /* defined flags for br_stat.flags */
@@ -274,7 +288,7 @@
 #define	BRCMD_SET_BRIDGE_PRIORITY	5	/* arg1 = priority */
 #define	BRCMD_SET_PORT_PRIORITY	6	/* arg1 = port, arg2 = priority */
 #define	BRCMD_SET_PATH_COST	7	/* arg1 = port, arg2 = cost */
-#define	BRCMD_DISPLAY_FDB	8	/* arg1 = port */
+#define	BRCMD_DISPLAY_FDB	8
 #define	BRCMD_ENABLE_DEBUG	9
 #define	BRCMD_DISABLE_DEBUG	10
 #define BRCMD_SET_POLICY	11	/* arg1 = default policy (1==bridge all) */
@@ -283,14 +297,19 @@
 #define BRCMD_DISABLE_PROT_STATS 14
 #define BRCMD_ZERO_PROT_STATS	15
 #define BRCMD_TOGGLE_STP	16
+#define BRCMD_IF_ENABLE		17	/* arg1 = if_index */
+#define BRCMD_IF_DISABLE	18	/* arg1 = if_index */
+#define BRCMD_SET_IF_PRIORITY	19	/* arg1 = if_index, arg2 = priority */
+#define	BRCMD_SET_IF_PATH_COST	20	/* arg1 = if_index, arg2 = cost */
 
 /* prototypes of exported bridging functions... */
 
+#ifdef __KERNEL__
 void br_init(void);
 int br_receive_frame(struct sk_buff *skb);	/* 3.5 */
 int br_tx_frame(struct sk_buff *skb);
+int brg_init(void);
 int br_ioctl(unsigned int cmd, void *arg);
-int br_protocol_ok(unsigned short protocol);
 void requeue_fdb(struct fdb *node, int new_port);
 
 struct fdb *br_avl_find_addr(unsigned char addr[6]);
@@ -298,8 +317,15 @@
 void sprintf_avl (char **pbuffer, struct fdb * tree, off_t *pos,int* len, off_t offset, int length);
 int br_tree_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 void br_avl_delete_by_port(int port);
+int br_call_bridge(struct sk_buff *skb, unsigned short type);
+void br_spacedevice_register(void);
+
 /* externs */
 
 extern struct br_stat br_stats;
 extern Port_data port_info[];
 
+#endif
+
+
+ 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/net/tcp.h linux.14p6/include/net/tcp.h
--- linux.vanilla/include/net/tcp.h	Sat Aug 28 20:00:59 1999
+++ linux.14p6/include/net/tcp.h	Tue Nov 16 16:15:55 1999
@@ -24,22 +24,18 @@
 #include <net/checksum.h>
 
 /* This is for all connections with a full identity, no wildcards.
- * New scheme, half the table is for TIME_WAIT, the other half is
- * for the rest.  I'll experiment with dynamic table growth later.
+ * Half of the table is for TIME_WAIT, the other half is for the
+ * rest.
+ *
+ * This needs 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.
  */
-#define TCP_HTABLE_SIZE		512
+extern unsigned 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
@@ -81,7 +77,8 @@
 	struct tcp_bind_bucket	**pprev;
 };
 
-extern struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE];
+extern unsigned int tcp_bhash_size;
+extern struct tcp_bind_bucket **tcp_bhash;
 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);
@@ -109,7 +106,7 @@
 /* These are AF independent. */
 static __inline__ int tcp_bhashfn(__u16 lport)
 {
-	return (lport & (TCP_BHTABLE_SIZE - 1));
+	return (lport & (tcp_bhash_size - 1));
 }
 
 /* This is a TIME_WAIT bucket.  It works around the memory consumption
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/scsi/sg.h linux.14p6/include/scsi/sg.h
--- linux.vanilla/include/scsi/sg.h	Sat Aug 14 02:26:52 1999
+++ linux.14p6/include/scsi/sg.h	Wed Nov  3 00:27:23 1999
@@ -12,10 +12,17 @@
 *       Copyright (C) 1998, 1999 Douglas Gilbert
 
 
-    Version: 2.1.34 (990603)
-    This version for later 2.1.x and 2.2.x series kernels
+    Version: 2.1.36 (991008)
+    This version for 2.2.x series kernels
     D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au)
 
+    Changes since 2.1.34 (990603)
+        - skipped 2.1.35 (never fully released)
+        - add queuing info into struct sg_scsi_id
+        - block negative timeout and reply_len values
+        - protect scsi_allocate_device() call
+        - fix pack_id input to sg_read()
+        - add wake_up to mid-level on command release
     Changes since 2.1.33 (990521)
         - implement SG_SET_RESERVED_SIZE and associated memory re-org.
         - add SG_NEXT_CMD_LEN to override SCSI command lengths
@@ -144,9 +151,10 @@
     int scsi_id;        /* scsi id of target device */
     int lun;
     int scsi_type;      /* TYPE_... defined in scsi/scsi.h */
+    short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
+    short d_queue_depth;/* device (or adapter) maximum queue length */
     int unused1;        /* probably find a good use, set 0 for now */
     int unused2;        /* ditto */
-    int unused3;  
 } Sg_scsi_id;
 
 /* IOCTLs: ( _GET_s yield result via 'int *' 3rd argument unless 
@@ -204,6 +212,10 @@
 #define SG_GET_VERSION_NUM 0x2282 /* Example: version 2.1.34 yields 20134 */
 #define SG_NEXT_CMD_LEN 0x2283  /* override SCSI command length with given
                    number on the next write() on this file descriptor */
+
+/* Returns -EBUSY if occupied else takes as input: 0 -> do nothing,
+   1 -> device reset or  2 -> bus reset (not operational yet) */
+#define SG_SCSI_RESET 0x2284
 
 
 #define SG_SCATTER_SZ (8 * 4096)  /* PAGE_SIZE not available to user */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/init/main.c linux.14p6/init/main.c
--- linux.vanilla/init/main.c	Wed Oct 20 01:12:51 1999
+++ linux.14p6/init/main.c	Fri Oct 22 23:31:19 1999
@@ -1211,6 +1211,7 @@
 	dcache_init();
 	vma_init();
 	buffer_init(memory_end-memory_start);
+ 	page_cache_init(memory_end-memory_start);
 	signals_init();
 	inode_init();
 	file_table_init();
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/ipc/msg.c linux.14p6/ipc/msg.c
--- linux.vanilla/ipc/msg.c	Fri Nov 20 19:43:19 1998
+++ linux.14p6/ipc/msg.c	Thu Nov 11 21:52:41 1999
@@ -50,6 +50,7 @@
 	struct ipc_perm *ipcp;
 	struct msg *msgh;
 	long mtype;
+	int err;
 	
 	if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
 		return -EINVAL;
@@ -70,34 +71,38 @@
 	if (ipcperms(ipcp, S_IWUGO)) 
 		return -EACCES;
 	
-	if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
-		if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
-			/* still no space in queue */
-			if (msgflg & IPC_NOWAIT)
-				return -EAGAIN;
-			if (signal_pending(current))
-				return -EINTR;
-			interruptible_sleep_on (&msq->wwait);
-			goto slept;
-		}
+	if (msgsz + msq->msg_cbytes > msq->msg_qbytes || msq->msg_qnum>= MSGQNUM) { 
+		/* still no space in queue */
+		if (msgflg & IPC_NOWAIT)
+			return -EAGAIN;
+		if (signal_pending(current))
+			return -EINTR;
+		interruptible_sleep_on (&msq->wwait);
+		goto slept;
 	}
-	
-	/* allocate message header and text space*/ 
+
+	/* Charge first to avoid races */
+	msgbytes  += msgsz;
+	msghdrs++;
+	msq->msg_qnum++;
+	msq->msg_cbytes += msgsz;
+
+	/* allocate message header and text space */ 
+	err = -ENOMEM;
 	msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_KERNEL);
-	if (!msgh)
-		return -ENOMEM;
+	if (!msgh) 
+		goto uncharge;
 	msgh->msg_spot = (char *) (msgh + 1);
 
-	if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz))
-	{
-		kfree(msgh);
-		return -EFAULT;
+	err = -EFAULT; 
+	if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz))	{
+		goto uncharge;
 	}
 	
+	err = -EIDRM; 
 	if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
 		|| msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
-		kfree(msgh);
-		return -EIDRM;
+		goto uncharge;
 	}
 
 	msgh->msg_next = NULL;
@@ -111,14 +116,19 @@
 		msq->msg_last->msg_next = msgh;
 		msq->msg_last = msgh;
 	}
-	msq->msg_cbytes += msgsz;
-	msgbytes  += msgsz;
-	msghdrs++;
-	msq->msg_qnum++;
 	msq->msg_lspid = current->pid;
 	msq->msg_stime = CURRENT_TIME;
 	wake_up (&msq->rwait);
 	return 0;
+
+uncharge:
+	msgbytes  -= msgsz;
+	msghdrs--;
+	msq->msg_qnum--;
+	msq->msg_cbytes -= msgsz;
+	if (msgh)
+		kfree(msgh); 
+	return err;
 }
 
 static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/ipc/sem.c linux.14p6/ipc/sem.c
--- linux.vanilla/ipc/sem.c	Sun Jan 10 03:16:44 1999
+++ linux.14p6/ipc/sem.c	Fri Oct 22 22:15:21 1999
@@ -694,7 +694,7 @@
                 interruptible_sleep_on(&queue.sleeper);
 
                 /*
-                 * If queue.status == 1 we where woken up and
+                 * If queue.status == 1 we were woken up and
                  * have to retry else we simply return.
                  * If an interrupt occurred we have to clean up the
                  * queue
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/ipc/shm.c linux.14p6/ipc/shm.c
--- linux.vanilla/ipc/shm.c	Wed Oct 20 01:12:51 1999
+++ linux.14p6/ipc/shm.c	Sat Oct 23 23:02:35 1999
@@ -639,10 +639,8 @@
 	pte = __pte(shp->shm_pages[idx]);
 	if (!pte_present(pte)) {
 		unsigned long page = get_free_page(GFP_USER);
-		if (!page) {
-			oom(current);
-			return 0;
-		}
+		if (!page)
+			return -1;
 		pte = __pte(shp->shm_pages[idx]);
 		if (pte_present(pte)) {
 			free_page (page); /* doesn't sleep */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/kernel/exit.c linux.14p6/kernel/exit.c
--- linux.vanilla/kernel/exit.c	Wed Oct 20 01:12:51 1999
+++ linux.14p6/kernel/exit.c	Wed Nov  3 00:13:02 1999
@@ -232,18 +232,17 @@
 static inline void __exit_sighand(struct task_struct *tsk)
 {
 	struct signal_struct * sig = tsk->sig;
+	unsigned long flags;
 
+	spin_lock_irqsave(&tsk->sigmask_lock, flags);
 	if (sig) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&tsk->sigmask_lock, flags);
 		tsk->sig = NULL;
-		spin_unlock_irqrestore(&tsk->sigmask_lock, flags);
 		if (atomic_dec_and_test(&sig->count))
 			kfree(sig);
 	}
 
 	flush_signals(tsk);
+	spin_unlock_irqrestore(&tsk->sigmask_lock, flags);
 }
 
 void exit_sighand(struct task_struct *tsk)
@@ -444,6 +443,13 @@
 	add_wait_queue(&current->wait_chldexit,&wait);
 repeat:
 	flag = 0;
+
+	/* The interruptible state must be set before looking at the
+	   children. This because we want to catch any racy exit from
+	   the children as do_exit() may run under us. The following
+	   read_lock will enforce SMP ordering at the CPU level. */
+	current->state = TASK_INTERRUPTIBLE;
+
 	read_lock(&tasklist_lock);
  	for (p = current->p_cptr ; p ; p = p->p_osptr) {
 		if (pid>0) {
@@ -510,13 +516,13 @@
 		retval = -ERESTARTSYS;
 		if (signal_pending(current))
 			goto end_wait4;
-		current->state=TASK_INTERRUPTIBLE;
 		schedule();
 		goto repeat;
 	}
 	retval = -ECHILD;
 end_wait4:
 	remove_wait_queue(&current->wait_chldexit,&wait);
+	current->state = TASK_RUNNING;
 	return retval;
 }
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/kernel/ksyms.c linux.14p6/kernel/ksyms.c
--- linux.vanilla/kernel/ksyms.c	Wed Oct 20 01:12:51 1999
+++ linux.14p6/kernel/ksyms.c	Tue Nov 16 15:37:07 1999
@@ -54,6 +54,9 @@
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 extern int (*do_nfsservctl)(int, void *, void *);
 #endif
+#if !defined(CONFIG_LOCKD) && defined(CONFIG_LOCKD_MODULE)
+extern int (*do_lockdctl)(int, void *, void *);
+#endif
 
 extern void *sys_call_table;
 
@@ -121,6 +124,7 @@
 EXPORT_SYMBOL(igrab);
 EXPORT_SYMBOL(iunique);
 EXPORT_SYMBOL(iget);
+EXPORT_SYMBOL(iget_in_use);
 EXPORT_SYMBOL(iput);
 EXPORT_SYMBOL(__namei);
 EXPORT_SYMBOL(lookup_dentry);
@@ -189,9 +193,13 @@
 EXPORT_SYMBOL(vfs_rename);
 EXPORT_SYMBOL(__pollwait);
 EXPORT_SYMBOL(ROOT_DEV);
+EXPORT_SYMBOL(inode_generation_count);
 
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 EXPORT_SYMBOL(do_nfsservctl);
+#endif
+#if !defined(CONFIG_LOCKD) && defined(CONFIG_LOCKD_MODULE)
+EXPORT_SYMBOL(do_lockdctl);
 #endif
 
 /* device registration */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/kernel/panic.c linux.14p6/kernel/panic.c
--- linux.vanilla/kernel/panic.c	Sat Aug 14 02:27:40 1999
+++ linux.14p6/kernel/panic.c	Tue Nov 16 15:13:28 1999
@@ -74,7 +74,12 @@
 		machine_restart(NULL);
 	}
 #ifdef __sparc__
-	printk("Press L1-A to return to the boot prom\n");
+	{
+		extern int stop_a_enabled;
+		/* Make sure the user can actually press L1-A */
+		stop_a_enabled = 1;
+		printk("Press L1-A to return to the boot prom\n");
+	}
 #endif
 #ifdef __alpha__
 	if (alpha_using_srm)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/kernel/signal.c linux.14p6/kernel/signal.c
--- linux.vanilla/kernel/signal.c	Wed Oct 20 01:12:51 1999
+++ linux.14p6/kernel/signal.c	Wed Nov  3 00:13:02 1999
@@ -44,6 +44,8 @@
 
 /*
  * Flush all pending signals for a task.
+ * Callers must hold the sigmask_lock so that we do not race
+ * with dequeue_signal or send_sig_info.
  */
 
 void
@@ -270,12 +272,16 @@
 		goto out_nolock;
 
 	/* The null signal is a permissions and process existance probe.
-	   No signal is actually delivered.  Same goes for zombies. */
+	   No signal is actually delivered.  Same goes for zombies.
+	   We have to grab the spinlock now so that we do not race
+	   with flush_signals. */
 	ret = 0;
-	if (!sig || !t->sig)
+	spin_lock_irqsave(&t->sigmask_lock, flags);
+	if (!sig || !t->sig) {
+		spin_unlock_irqrestore(&t->sigmask_lock, flags);
 		goto out_nolock;
+	}
 
-	spin_lock_irqsave(&t->sigmask_lock, flags);
 	switch (sig) {
 	case SIGKILL: case SIGCONT:
 		/* Wake up the process if stopped.  */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/kernel/sysctl.c linux.14p6/kernel/sysctl.c
--- linux.vanilla/kernel/sysctl.c	Wed Oct 20 01:12:51 1999
+++ linux.14p6/kernel/sysctl.c	Tue Nov 16 15:13:28 1999
@@ -53,6 +53,7 @@
 
 #ifdef __sparc__
 extern char reboot_command [];
+extern int stop_a_enabled;
 #endif
 #ifdef __powerpc__
 extern unsigned long htab_reclaim_on, zero_paged_on, powersave_nap;
@@ -169,8 +170,10 @@
 	 0644, NULL, &proc_doutsstring, &sysctl_string},
 	{KERN_PANIC, "panic", &panic_timeout, sizeof(int),
 	 0644, NULL, &proc_dointvec},
+#ifdef CONFIG_PROC_FS	 
 	{KERN_CAP_BSET, "cap-bound", &cap_bset, sizeof(kernel_cap_t),
 	 0600, NULL, &proc_dointvec_bset},
+#endif	 
 #ifdef CONFIG_BLK_DEV_INITRD
 	{KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int),
 	 0644, NULL, &proc_dointvec},
@@ -184,6 +187,8 @@
 #ifdef __sparc__
 	{KERN_SPARC_REBOOT, "reboot-cmd", reboot_command,
 	 256, 0644, NULL, &proc_dostring, &sysctl_string },
+	{KERN_SPARC_STOP_A, "stop-a", &stop_a_enabled, sizeof (int),
+	 0644, NULL, &proc_dointvec},
 #endif
 #ifdef __powerpc__
 	{KERN_PPC_HTABRECLAIM, "htab-reclaim", &htab_reclaim_on, sizeof(int),
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/mm/filemap.c linux.14p6/mm/filemap.c
--- linux.vanilla/mm/filemap.c	Wed Oct 20 01:12:51 1999
+++ linux.14p6/mm/filemap.c	Sat Oct 23 23:02:35 1999
@@ -20,6 +20,7 @@
 #include <linux/file.h>
 #include <linux/swapctl.h>
 #include <linux/slab.h>
+#include <linux/init.h>
 
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
@@ -32,7 +33,8 @@
  */
 
 unsigned long page_cache_size = 0;
-struct page * page_hash_table[PAGE_HASH_SIZE];
+unsigned int page_hash_bits, page_hash_mask;
+struct page **page_hash_table;
 
 /* 
  * Define a request structure for outstanding page write requests
@@ -985,7 +987,7 @@
 	if (no_share && !new_page) {
 		new_page = page_cache_alloc();
 		if (!new_page)
-			goto failure;
+			goto release_and_oom;
 	}
 
 	if (PageLocked(page))
@@ -1033,7 +1035,7 @@
 	if (!new_page)
 		new_page = page_cache_alloc();
 	if (!new_page)
-		goto no_page;
+		goto oom;
 
 	/*
 	 * During getting the above page we might have slept,
@@ -1087,6 +1089,11 @@
 		page_cache_free(new_page);
 no_page:
 	return 0;
+
+release_and_oom:
+	page_cache_release(page);
+oom:
+	return -1;
 }
 
 /*
@@ -1756,4 +1763,35 @@
 			kmem_cache_free(pio_request_cache, p);
 		}
 	}
+}
+
+void __init page_cache_init(unsigned long memory_size)
+{
+	unsigned long htable_size;
+	long order;
+
+	htable_size  = memory_size >> PAGE_SHIFT;
+	htable_size *= sizeof(struct page *);
+	for(order = 0; (PAGE_SIZE << order) < htable_size; order++)
+		;
+
+	do {
+		unsigned long tmp = (PAGE_SIZE << order) / sizeof(struct page *);
+
+		page_hash_mask = (tmp - 1UL);
+
+		page_hash_bits = 0;
+		while((tmp >>= 1UL) != 0UL)
+			page_hash_bits++;
+
+		page_hash_table = (struct page **)
+			__get_free_pages(GFP_ATOMIC, order);
+	} while(page_hash_table == NULL && --order >= 0L);
+
+	printk("Page-cache hash table entries: %d (order: %ld, %ld bytes)\n",
+	       (1 << page_hash_bits), order, (PAGE_SIZE << order));
+	if (!page_hash_table)
+		panic("Failed to allocate page hash table\n");
+	memset(page_hash_table, 0,
+	       (PAGE_HASH_MASK + 1UL) * sizeof(struct page *));
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/mm/memory.c linux.14p6/mm/memory.c
--- linux.vanilla/mm/memory.c	Sat Aug 14 02:27:40 1999
+++ linux.14p6/mm/memory.c	Sat Oct 23 23:02:35 1999
@@ -62,16 +62,6 @@
 mem_map_t * mem_map = NULL;
 
 /*
- * oom() prints a message (so that the user knows why the process died),
- * and gives the process an untrappable SIGKILL.
- */
-void oom(struct task_struct * task)
-{
-	printk("\nOut of memory for %s.\n", task->comm);
-	force_sig(SIGKILL, task);
-}
-
-/*
  * Note: this doesn't free the actual pages themselves. That
  * has been handled earlier when unmapping all the memory regions.
  */
@@ -577,13 +567,13 @@
 	pmd = pmd_alloc(pgd, address);
 	if (!pmd) {
 		free_page(page);
-		oom(tsk);
+		force_sig(SIGKILL, tsk);
 		return 0;
 	}
 	pte = pte_alloc(pmd, address);
 	if (!pte) {
 		free_page(page);
-		oom(tsk);
+		force_sig(SIGKILL, tsk);
 		return 0;
 	}
 	if (!pte_none(*pte)) {
@@ -687,12 +677,11 @@
 
 bad_wp_page:
 	printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
-	send_sig(SIGKILL, tsk, 1);
 no_new_page:
 	unlock_kernel();
 	if (new_page)
 		free_page(new_page);
-	return 0;
+	return -1;
 }
 
 /*
@@ -789,8 +778,9 @@
 	struct vm_area_struct * vma, unsigned long address,
 	pte_t * page_table, pte_t entry, int write_access)
 {
+	int ret = 1;
 	if (!vma->vm_ops || !vma->vm_ops->swapin) {
-		swap_in(tsk, vma, page_table, pte_val(entry), write_access);
+		ret = swap_in(tsk, vma, page_table, pte_val(entry), write_access);
 		flush_page_to_ram(pte_page(*page_table));
 	} else {
 		pte_t page = vma->vm_ops->swapin(vma, address - vma->vm_start + vma->vm_offset, pte_val(entry));
@@ -807,7 +797,7 @@
 		}
 	}
 	unlock_kernel();
-	return 1;
+	return ret;
 }
 
 /*
@@ -819,7 +809,7 @@
 	if (write_access) {
 		unsigned long page = __get_free_page(GFP_USER);
 		if (!page)
-			return 0;
+			return -1;
 		clear_page(page);
 		entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
 		vma->vm_mm->rss++;
@@ -865,6 +855,8 @@
 	unlock_kernel();
 	if (!page)
 		return 0;
+	if (page == -1)
+		return -1;
 
 	++tsk->maj_flt;
 	++vma->vm_mm->rss;
@@ -937,25 +929,26 @@
 {
 	pgd_t *pgd;
 	pmd_t *pmd;
+	pte_t * pte;
+	int ret;
 
 	pgd = pgd_offset(vma->vm_mm, address);
 	pmd = pmd_alloc(pgd, address);
-	if (pmd) {
-		pte_t * pte = pte_alloc(pmd, address);
-		if (pte) {
-			if (handle_pte_fault(tsk, vma, address, write_access, pte)) {
-				update_mmu_cache(vma, address, *pte);
-				return 1;
-			}
-		}
-	}
-	return 0;
+	if (!pmd)
+		return -1;
+	pte = pte_alloc(pmd, address);
+	if (!pte)
+		return -1;
+	ret = handle_pte_fault(tsk, vma, address, write_access, pte);
+	if (ret > 0)
+		update_mmu_cache(vma, address, *pte);
+	return ret;
 }
 
 /*
  * Simplistic page force-in..
  */
-void make_pages_present(unsigned long addr, unsigned long end)
+int make_pages_present(unsigned long addr, unsigned long end)
 {
 	int write;
 	struct vm_area_struct * vma;
@@ -963,7 +956,9 @@
 	vma = find_vma(current->mm, addr);
 	write = (vma->vm_flags & VM_WRITE) != 0;
 	while (addr < end) {
-		handle_mm_fault(current, vma, addr, write);
+		if (handle_mm_fault(current, vma, addr, write) < 0)
+			return -1;
 		addr += PAGE_SIZE;
 	}
+	return 0;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/mm/page_alloc.c linux.14p6/mm/page_alloc.c
--- linux.vanilla/mm/page_alloc.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/mm/page_alloc.c	Sat Oct 23 23:02:35 1999
@@ -388,7 +388,7 @@
  * Also, don't bother to add to the swap cache if this page-in
  * was due to a write access.
  */
-void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
+int swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
 	pte_t * page_table, unsigned long entry, int write_access)
 {
 	unsigned long page;
@@ -401,14 +401,10 @@
 	if (pte_val(*page_table) != entry) {
 		if (page_map)
 			free_page_and_swap_cache(page_address(page_map));
-		return;
-	}
-	if (!page_map) {
-		set_pte(page_table, BAD_PAGE);
-		swap_free(entry);
-		oom(tsk);
-		return;
+		return 1;
 	}
+	if (!page_map)
+		return -1;
 
 	page = page_address(page_map);
 	vma->vm_mm->rss++;
@@ -417,7 +413,7 @@
 
 	if (!write_access || is_page_shared(page_map)) {
 		set_pte(page_table, mk_pte(page, vma->vm_page_prot));
-		return;
+		return 1;
 	}
 
 	/*
@@ -427,5 +423,5 @@
 	 */
 	delete_from_swap_cache(page_map);
 	set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
-  	return;
+  	return 1;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/mm/swapfile.c linux.14p6/mm/swapfile.c
--- linux.vanilla/mm/swapfile.c	Sat Aug 14 02:27:41 1999
+++ linux.14p6/mm/swapfile.c	Sat Oct 23 23:02:35 1999
@@ -130,15 +130,17 @@
 	offset = SWP_OFFSET(entry);
 	if (offset >= p->max)
 		goto bad_offset;
-	if (offset < p->lowest_bit)
-		p->lowest_bit = offset;
-	if (offset > p->highest_bit)
-		p->highest_bit = offset;
 	if (!p->swap_map[offset])
 		goto bad_free;
 	if (p->swap_map[offset] < SWAP_MAP_MAX) {
 		if (!--p->swap_map[offset])
+		{
+			if (offset < p->lowest_bit)
+				p->lowest_bit = offset;
+			if (offset > p->highest_bit)
+				p->highest_bit = offset;
 			nr_swap_pages++;
+		}
 	}
 #ifdef DEBUG_SWAP
 	printk("DebugVM: swap_free(entry %08lx, count now %d)\n",
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/mm/vmscan.c linux.14p6/mm/vmscan.c
--- linux.vanilla/mm/vmscan.c	Sat Aug 14 02:26:01 1999
+++ linux.14p6/mm/vmscan.c	Sat Oct 23 23:02:35 1999
@@ -308,7 +308,8 @@
 static int swap_out(unsigned int priority, int gfp_mask)
 {
 	struct task_struct * p, * pbest;
-	int counter, assign, max_cnt;
+	int assign = 0, counter;
+	unsigned long max_cnt;
 
 	/* 
 	 * We make one or two passes through the task list, indexed by 
@@ -327,11 +328,8 @@
 	counter = nr_tasks / (priority+1);
 	if (counter < 1)
 		counter = 1;
-	if (counter > nr_tasks)
-		counter = nr_tasks;
 
 	for (; counter >= 0; counter--) {
-		assign = 0;
 		max_cnt = 0;
 		pbest = NULL;
 	select:
@@ -343,7 +341,7 @@
 	 		if (p->mm->rss <= 0)
 				continue;
 			/* Refresh swap_cnt? */
-			if (assign)
+			if (assign == 1)
 				p->mm->swap_cnt = p->mm->rss;
 			if (p->mm->swap_cnt > max_cnt) {
 				max_cnt = p->mm->swap_cnt;
@@ -351,6 +349,8 @@
 			}
 		}
 		read_unlock(&tasklist_lock);
+		if (assign == 1)
+			assign = 2;
 		if (!pbest) {
 			if (!assign) {
 				assign = 1;
@@ -435,7 +435,7 @@
        printk ("Starting kswapd v%.*s\n", i, s);
 }
 
-static struct task_struct *kswapd_process;
+static struct wait_queue * kswapd_wait = NULL;
 
 /*
  * The background pageout daemon, started as a kernel thread
@@ -455,7 +455,6 @@
 {
 	struct task_struct *tsk = current;
 
-	kswapd_process = tsk;
 	tsk->session = 1;
 	tsk->pgrp = 1;
 	strcpy(tsk->comm, "kswapd");
@@ -484,16 +483,18 @@
 		 * the processes needing more memory will wake us
 		 * up on a more timely basis.
 		 */
-		do {
-			if (nr_free_pages >= freepages.high)
-				break;
-
-			if (!do_try_to_free_pages(GFP_KSWAPD))
-				break;
-		} while (!tsk->need_resched);
-		run_task_queue(&tq_disk);
-		tsk->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(HZ);
+		interruptible_sleep_on_timeout(&kswapd_wait, HZ);
+		while (nr_free_pages < freepages.high)
+		{
+			if (do_try_to_free_pages(GFP_KSWAPD))
+			{
+				if (tsk->need_resched)
+					schedule();
+				continue;
+			}
+			tsk->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(10*HZ);
+		}
 	}
 }
 
@@ -516,7 +517,7 @@
 {
 	int retval = 1;
 
-	wake_up_process(kswapd_process);
+	wake_up_interruptible(&kswapd_wait);
 	if (gfp_mask & __GFP_WAIT)
 		retval = do_try_to_free_pages(gfp_mask);
 	return retval;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/802/fc.c linux.14p6/net/802/fc.c
--- linux.vanilla/net/802/fc.c	Sat Aug 28 20:00:59 1999
+++ linux.14p6/net/802/fc.c	Fri Oct 22 22:17:19 1999
@@ -90,7 +90,10 @@
 		printk("fc_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(fcllc->ethertype));
 		return 0;
 	}
-
+#ifdef CONFIG_INET
 	return arp_find(fch->daddr, skb);
+#else
+	return 0; /* Cannot happen because of ETH_P_IP test */
+#endif	
 }
 	
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/Config.in linux.14p6/net/Config.in
--- linux.vanilla/net/Config.in	Thu Feb 25 18:46:47 1999
+++ linux.14p6/net/Config.in	Fri Oct 22 22:39:35 1999
@@ -39,6 +39,9 @@
   tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
   tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
   bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
+  if [ "$CONFIG_BRIDGE" != "n" ]; then
+	int '  Maximum number of bridged interfaces' CONFIG_BRIDGE_NUM_PORTS 8
+  fi
   bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
 #  if [ "$CONFIG_LLC" = "y" ]; then
 #   bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/bridge/br.c linux.14p6/net/bridge/br.c
--- linux.vanilla/net/bridge/br.c	Sat Aug 14 02:27:41 1999
+++ linux.14p6/net/bridge/br.c	Wed Nov  3 00:33:35 1999
@@ -40,10 +40,19 @@
  *			so blame me first if its broken ;)
  *
  *	Robert Pintarelli:	fixed bug in bpdu time values
+ *
+ *      Matthew Grant:  start ports disabled.  
+ *                      auto-promiscuous mode on port enable/disable
+ *                      fleshed out interface event handling, interfaces 
+ *                        now register with bridge on module load as well as ifup
+ *                      port control ioctls with ifindex support
+ *                      brg0 logical ethernet interface
+ *                      reworked brcfg to take interface arguments
+ *                      added support for changing the hardware address
+ *                      generally made bridge a lot more usable.
  *	
  *	Todo:
- *		Don't bring up devices automatically. Start ports disabled
- *	and use a netlink notifier so a daemon can maintain the bridge
+ *	Use a netlink notifier so a daemon can maintain the bridge
  *	port group (could we also do multiple groups ????).
  *		A nice /proc file interface.
  *		Put the path costs in the port info and devices.
@@ -53,6 +62,7 @@
  */
  
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -61,10 +71,13 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
+#include <linux/malloc.h>
 #include <linux/string.h>
 #include <linux/net.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
 #include <linux/ip.h>
@@ -72,6 +85,7 @@
 #include <linux/init.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <linux/rtnetlink.h>
 #include <net/br.h>
 #include <linux/proc_fs.h>
 
@@ -145,11 +159,20 @@
 static int br_flood(struct sk_buff *skb, int port);
 static int br_drop(struct sk_buff *skb);
 static int br_learn(struct sk_buff *skb, int port);	/* 3.8 */
+static int br_protocol_ok(unsigned short protocol);
+static int br_find_port(int ifindex);
+static void br_get_ifnames(void);
+static int brg_rx(struct sk_buff *skb, int port);
 
 static unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
 static Bridge_data     bridge_info;			  /* (4.5.3)	 */
 Port_data       port_info[All_ports];		  /* (4.5.5)	 */
 
+/* MAG: Maximum port registered - used to speed up flooding and to make
+ * have a large ports array more efficient
+ */
+static int max_port_used = 0; 
+
 /* JRP: fdb cache 1/port save kmalloc/kfree on every frame */
 struct fdb	*newfdb[All_ports];
 int allocated_fdb_cnt = 0;
@@ -190,6 +213,30 @@
 	0
 };
 
+
+/* 
+ * the following data is for the bridge network device
+ */
+struct brg_if {
+  struct device dev;
+  char name[IFNAMSIZ];
+};
+static struct brg_if brg_if;
+
+/* 
+ * Here to save linkage? problems
+ */
+
+static inline int find_port(struct device *dev)
+{
+	int i;
+
+	for (i = One; i <= No_of_ports; i++)
+		if (port_info[i].dev == dev)
+			return(i);
+	return(0);
+}
+
 /*
  * Implementation of Protocol specific bridging
  *
@@ -202,7 +249,7 @@
 
 /* Checks if that protocol type is to be bridged */
 
-int br_protocol_ok(unsigned short protocol)
+static int inline br_protocol_ok(unsigned short protocol)
 {
 	unsigned x;
 	
@@ -843,8 +890,14 @@
 {						  /* (4.8.1)	 */
 	int port_no;
 
-	printk(KERN_INFO "NET4: Ethernet Bridge 005 for NET4.0\n");
+	printk(KERN_INFO "NET4: Ethernet Bridge 007 for NET4.0\n");
+
+	/* Set up brg device information */
+	bridge_info.instance = 0;
+	brg_init();
 
+	max_port_used = 0;
+	
 	/*
 	 * Form initial topology change time.
 	 * The topology change timer is only used if this is the root bridge.
@@ -874,8 +927,8 @@
 	stop_topology_change_timer();
 	memset(newfdb, 0, sizeof(newfdb));
 	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.8.1.4) */
-		/* initial state = Enable */
-		user_port_state[port_no] = ~Disabled;
+		/* initial state = Disable */
+		user_port_state[port_no] = Disabled;
 		port_priority[port_no] = 128;
 		br_init_port(port_no);
 		disable_port(port_no);
@@ -1205,7 +1258,7 @@
   	memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
  
   	if (br_stats.flags & BR_DEBUG)
- 		printk("send_%s_bpdu: port %i src %02x:%02x:%02x:%02x:%02x:%02x\n",
+ 		printk(KERN_DEBUG "send_%s_bpdu: port %i src %02x:%02x:%02x:%02x:%02x:%02x\n",
  			pdu_name,
   			port_no,
   			eth->h_source[0],
@@ -1306,6 +1359,9 @@
 	if (dev->flags & IFF_LOOPBACK)
 		return(NOTIFY_DONE);
 
+	if (dev == &brg_if.dev)
+	  return(NOTIFY_DONE);	/* Don't attach the brg device to a port! */
+	
 	switch (event) 
 	{
 		case NETDEV_DOWN:
@@ -1335,6 +1391,9 @@
 				{
 					port_info[i].dev = dev;
 					port_info[i].port_id = i;
+					dev->bridge_port_id = i;
+					if( i > max_port_used ) 
+						max_port_used = i;
 					/* set bridge addr from 1st device addr */
 					if (((htonl(bridge_info.bridge_id.BRIDGE_ID[0])&0xffff) == 0) &&
 						(bridge_info.bridge_id.BRIDGE_ID[1] == 0)) 
@@ -1344,7 +1403,10 @@
 							bridge_info.bridge_id.BRIDGE_PRIORITY = htons(32768);
 						set_bridge_priority(&bridge_info.bridge_id);
 					}
+					/* Add local MAC address */
 					br_add_local_mac(dev->dev_addr);
+					/* Save MAC address for latter change address events */
+					memcpy(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6);
 					if((br_stats.flags & BR_UP) &&
 				   		(user_port_state[i] != Disabled)) 
 				   	{
@@ -1362,19 +1424,122 @@
 				}
 			}
 			break;
+	        case NETDEV_REGISTER:
+		        if (br_stats.flags & BR_DEBUG) 
+				printk(KERN_DEBUG "br_device_event: NETDEV_REGISTER...\n");
+			/* printk(KERN_ERR "br_device_event: NETDEV_REGISTER...\n"); */
+			/* printk(KERN_ERR "br_device_event: dev->type: 0x%X\n", dev->type); */
+			/* Only handle ethernet ports */
+			if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK)
+				return NOTIFY_DONE;
+			/* printk(KERN_ERR "br_device_event: Looking for port...\n"); */
+			for (i = One; i <= No_of_ports; i++) 
+			{
+				if (port_info[i].dev == NULL || port_info[i].dev == dev) 
+				{
+				        /* printk(KERN_ERR "br_device_event: Found port %d\n", i); */
+					port_info[i].dev = dev;
+					port_info[i].port_id = i;
+					dev->bridge_port_id = i;
+					if( i > max_port_used )
+						max_port_used = i;
+					/* handle local MAC address minuplations */
+					br_add_local_mac(dev->dev_addr);
+					memcpy(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6);
+					return NOTIFY_DONE;
+					break;
+				}
+			}
+			break;
 		case NETDEV_UNREGISTER:
 			if (br_stats.flags & BR_DEBUG)
 				printk(KERN_DEBUG "br_device_event: NETDEV_UNREGISTER...\n");
                         i = find_port(dev);
                         if (i > 0) {
 				br_avl_delete_by_port(i);
+				memset(port_info[i].ifmac.BRIDGE_ID_ULA, 0, 6);
 				port_info[i].dev = NULL;
 			}
 			break;
+		case NETDEV_CHANGEADDR:
+			if (br_stats.flags & BR_DEBUG)
+				printk(KERN_DEBUG "br_device_event: NETDEV_CHANGEADDR...\n");
+                        i = find_port(dev);
+                        if (i <= 0)  
+			  break;
+			if (memcmp(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6) != 0)
+			  break; /* Don't worry about a change of hardware broadcast address! */
+			if (dev->start) {
+			  printk(KERN_CRIT "br_device_event: NETDEV_CHANGEADDR on busy device %s - FIX DRIVER!\n", 
+				 dev->name);
+			/*  return NOTIFY_BAD;  It SHOULD be this, but I want to be friendly... */
+			  return NOTIFY_DONE;
+			}
+			br_avl_delete_by_port(i);
+			memset(port_info[i].ifmac.BRIDGE_ID_ULA, 0, 6);
+			break;
 	}
 	return NOTIFY_DONE;
 }
 
+/* Routine to loop over device list and register 
+ * interfaces to bridge.  Called from last part of net_dev_init just before
+ * bootp/rarp interface setup
+ */
+void br_spacedevice_register(void) 
+{
+	struct device *dev;
+	for( dev = dev_base; dev != NULL; dev = dev->next)
+	{
+		br_device_event(NULL, NETDEV_REGISTER, dev);
+	}
+}	
+
+
+/* This is for SPEED in the kernel in net_bh.c */
+
+int br_call_bridge(struct sk_buff *skb, unsigned short type)
+{
+	int port;
+	struct device *dev;
+  
+#if 0  /* Checked first in handle_bridge to save expense of function call */ 
+	if(!(br_stats.flags & BR_UP))
+		return 0;
+#endif
+  
+	dev = skb->dev;
+
+	/* Check for brg0 device
+	 */
+	if (dev == &brg_if.dev)
+		return 0;
+		 
+	port = dev->bridge_port_id;
+
+	if(!port)
+		return 0;
+
+	/* Sanity - make sure we are not leaping off into fairy space! */
+	if ( port < 0 || port > max_port_used || port_info[port].dev != dev) {
+		if (net_ratelimit())
+			printk(KERN_CRIT "br_call_bridge: device %s has invalid port ID %d!\n",
+				dev->name,
+				dev->bridge_port_id);
+		return 0;
+	}
+
+	if(user_port_state[port] == Disabled)
+		return 0;
+  
+	if (!br_protocol_ok(ntohs(type)))
+		return 0;
+
+	return 1;
+
+}
+
+
 /*
  * following routine is called when a frame is received
  * from an interface, it returns 1 when it consumes the
@@ -1383,9 +1548,10 @@
 
 int br_receive_frame(struct sk_buff *skb)	/* 3.5 */
 {
-	int port;
+	int port, ret;
 	Port_data  *p;
 	struct ethhdr *eth;
+	struct device *dev;
 	
 	/* sanity */
 	if (!skb) {
@@ -1393,17 +1559,32 @@
 		return(1);
 	}
 
+	dev = skb->dev;
+
+	/* Check for brg0 device 
+	 */
+	if (dev == &brg_if.dev)
+		return 0;
+
 	skb->pkt_bridged = IS_BRIDGED;
 
 	/* check for loopback */
-	if (skb->dev->flags & IFF_LOOPBACK)
+	if (dev->flags & IFF_LOOPBACK)
 		return 0 ;
 
-	port = find_port(skb->dev);
+#if 0
+	port = find_port(dev);
+#else
+	port = dev->bridge_port_id;
+#endif
 	
 	if(!port)
 		return 0;
 	
+	/* Hand off to brg_rx BEFORE we screw up the skb */
+	if(brg_rx(skb, port))
+	  return(1);
+
 	skb->nh.raw = skb->mac.raw;
 	eth = skb->mac.ethernet;
 	p = &port_info[port];
@@ -1435,9 +1616,9 @@
 	switch (p->state) 
 	{
 		case Learning:
-			if(br_learn(skb, port)) 
+			if((ret = br_learn(skb, port))) 
 			{	/* 3.8 */
-				++br_stats_cnt.drop_multicast;
+				if (ret > 0) ++br_stats_cnt.drop_multicast;
 				return br_drop(skb);
 			}
 			/* fall through */
@@ -1452,8 +1633,8 @@
 		 */
 			break;
 		case Forwarding:
-			if(br_learn(skb, port)) {	/* 3.8 */
-				++br_stats_cnt.drop_multicast;
+			if((ret = br_learn(skb, port))) {	/* 3.8 */
+				if (ret > 0) ++br_stats_cnt.drop_multicast;
 				return br_drop(skb);
 			}
 			/* Now this frame came from one of bridged
@@ -1512,7 +1693,7 @@
 	eth = skb->mac.ethernet;
 	port = 0;	/* an impossible port (locally generated) */	
 	if (br_stats.flags & BR_DEBUG)
-		printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x"
+		printk(KERN_DEBUG "br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x"
 	  		" dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
 			port,
 			eth->h_source[0],
@@ -1577,7 +1758,11 @@
 
 /*
  * this routine returns 0 when it learns (or updates) from the
- * frame, and 1 if we must dropped the frame.
+ * frame, and 1 if we must dropped the frame due to multicast
+ * limitations, or -1 because of not enough memory.
+ *
+ * NB Can be called when skb->nh.raw is NOT set up when
+ * receiving frames on brg0 via brg_rx
  */
 
 static int br_learn(struct sk_buff *skb, int port)	/* 3.8 */
@@ -1600,8 +1785,8 @@
 		newfdb[port] = f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC);
 		if (!f) 
 		{
-			printk(KERN_DEBUG "br_learn: unable to malloc fdb\n");
-			return(-1); /* this drop the frame */
+			printk(KERN_WARNING "br_learn: unable to malloc fdb\n");
+			return(-1); /* this drops the frame */
 		}
 	}
 	f->port = port;	/* source port */
@@ -1750,7 +1935,7 @@
 				/* timer expired, invalidate entry */
 				f->flags &= ~FDB_ENT_VALID;
 				if (br_stats.flags & BR_DEBUG)
-					printk("fdb entry expired...\n");
+					printk(KERN_DEBUG "fdb entry expired...\n");
 				/*
 				 *	Send flood and drop original
 				 */
@@ -1792,7 +1977,7 @@
 				/* timer expired, invalidate entry */
 				f->flags &= ~FDB_ENT_VALID;
 				if (br_stats.flags & BR_DEBUG)
-					printk("fdb entry expired...\n");
+					printk(KERN_DEBUG "fdb entry expired...\n");
 				++br_stats_cnt.drop_same_port_aged;
 			}
 			else ++br_stats_cnt.drop_same_port;
@@ -1819,6 +2004,9 @@
 	{
 		if (i == port)	/* don't send back where we got it */
 			continue;
+		if (i > max_port_used)
+			/* Don't go scanning empty port entries */
+			break;
 		if (port_info[i].state == Forwarding) 
 		{
 			nskb = skb_clone(skb, GFP_ATOMIC);
@@ -1831,7 +2019,7 @@
 			/* To get here we must have done ARP already,
 			   or have a received valid MAC header */
 			
-/*			printk("Flood to port %d\n",i);*/
+/*			printk(KERN_DEBUG "Flood to port %d\n",i);*/
 			nskb->nh.raw = nskb->data + ETH_HLEN;
 			nskb->priority = 1;
 			dev_queue_xmit(nskb);
@@ -1840,16 +2028,6 @@
 	return(0);
 }
 
-static int find_port(struct device *dev)
-{
-	int i;
-
-	for (i = One; i <= No_of_ports; i++)
-		if (port_info[i].dev == dev)
-			return(i);
-	return(0);
-}
-
 /*
  *	FIXME: This needs to come from the device structs, eg for
  *	10,100,1Gbit ethernet.
@@ -1956,17 +2134,62 @@
 	return fdbis;
 }
 
+
+/* Fill in interface names in port_info structure
+ */
+static void br_get_ifdata(void) {
+	int i;
+
+  	for(i=One;i<=No_of_ports; i++) {
+
+		port_info[i].admin_state = user_port_state[i]; 
+		
+		/* memset IS needed.  Kernel strncpy does NOT NULL terminate 
+		 * strings when limit reached 
+	 	 */
+		memset(port_info[i].ifname, 0, IFNAMSIZ); 
+		if( port_info[i].dev == 0 )
+			continue;
+		strncpy(port_info[i].ifname, port_info[i].dev->name, IFNAMSIZ-1);
+		/* Being paranoid */
+		port_info[i].ifname[IFNAMSIZ-1] = '\0';
+	}
+}
+
+/* Given an interface index, loop over port array to see if configured.  If
+   so, return port number, otherwise error */ 
+static int br_find_port(int ifindex) 
+{
+  int i;
+  
+  for(i=1; i <= No_of_ports; i++) {
+    if (port_info[i].dev == 0)
+      continue;
+    if (port_info[i].dev->ifindex == ifindex)
+      return(i);
+  }
+  
+  return -EUNATCH;  /* Tell me if this is incorrect error code for this case */
+} 
+
+
 int br_ioctl(unsigned int cmd, void *arg)
 {
-	int err, i;
+	int err, i, ifflags;
 	struct br_cf bcf;
 	bridge_id_t new_id;
-
+	struct device *dev;
+	
 	switch(cmd)
 	{
 		case SIOCGIFBR:	/* get bridging control blocks */
 			memcpy(&br_stats.bridge_data, &bridge_info, sizeof(Bridge_data));
-			memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*No_of_ports);
+
+			/* Fill in interface names in port_info*/
+			br_get_ifdata();
+			
+			br_stats.num_ports = No_of_ports;
+			memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*All_ports);
 
 			err = copy_to_user(arg, &br_stats, sizeof(struct br_stat));
 			if (err)
@@ -2032,16 +2255,28 @@
 					}
 					br_stats.flags ^= BR_STP_DISABLED;
 					break;
+			        case BRCMD_IF_ENABLE:
+				        bcf.arg1 = br_find_port(bcf.arg1);
+					if (bcf.arg1 < 0)
+						return(bcf.arg1);
 				case BRCMD_PORT_ENABLE:
 					if (port_info[bcf.arg1].dev == 0)
 						return(-EINVAL);
 					if (user_port_state[bcf.arg1] != Disabled)
 						return(-EALREADY);
 					printk(KERN_DEBUG "br: enabling port %i\n",bcf.arg1);
+					dev = port_info[bcf.arg1].dev;
+					ifflags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI))
+					  |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); 
+					dev_change_flags(dev, ifflags|IFF_PROMISC);
 					user_port_state[bcf.arg1] = ~Disabled;
 					if(br_stats.flags & BR_UP)
 						enable_port(bcf.arg1);
 					break;
+			        case BRCMD_IF_DISABLE:
+				        bcf.arg1 = br_find_port(bcf.arg1);
+					if (bcf.arg1 < 0)
+						return(bcf.arg1);
 				case BRCMD_PORT_DISABLE:
 					if (port_info[bcf.arg1].dev == 0)
 						return(-EINVAL);
@@ -2051,12 +2286,20 @@
 					user_port_state[bcf.arg1] = Disabled;
 					if(br_stats.flags & BR_UP)
 						disable_port(bcf.arg1);
+					dev = port_info[bcf.arg1].dev;
+					ifflags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI))
+					  |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); 
+					dev_change_flags(port_info[bcf.arg1].dev, ifflags & ~IFF_PROMISC);
 					break;
 				case BRCMD_SET_BRIDGE_PRIORITY:
 					new_id = bridge_info.bridge_id;
 					new_id.BRIDGE_PRIORITY = htons(bcf.arg1);
 					set_bridge_priority(&new_id);
 					break;
+			        case BRCMD_SET_IF_PRIORITY:
+				        bcf.arg1 = br_find_port(bcf.arg1);
+					if (bcf.arg1 < 0)
+						return(bcf.arg1);
 				case BRCMD_SET_PORT_PRIORITY:
 					if((port_info[bcf.arg1].dev == 0)
 					    || (bcf.arg2 & ~0xff))
@@ -2064,6 +2307,10 @@
 					port_priority[bcf.arg1] = bcf.arg2;
 					set_port_priority(bcf.arg1);
 					break;
+			        case BRCMD_SET_IF_PATH_COST:
+				        bcf.arg1 = br_find_port(bcf.arg1);
+					if (bcf.arg1 < 0)
+						return(bcf.arg1);
 				case BRCMD_SET_PATH_COST:
 					if (port_info[bcf.arg1].dev == 0)
 						return(-EINVAL);
@@ -2145,3 +2392,385 @@
 	}
 	return(0);
 }
+
+
+
+
+/* --------------------------------------------------------------------------------
+ *
+ *
+ *  Bridge network device here for future modularization - device structures
+ *  must be 'static' inside bridge instance
+ *  Modelled after sch_teql.c
+ * 
+ */
+
+
+
+/*
+ *	Index to functions.
+ */
+
+int	    brg_probe(struct device *dev);
+static int  brg_open(struct device *dev);
+static int  brg_start_xmit(struct sk_buff *skb, struct device *dev);
+static int  brg_close(struct device *dev);
+static struct net_device_stats *brg_get_stats(struct device *dev);
+static void brg_set_multicast_list(struct device *dev);
+
+/*
+ *	Board-specific info in dev->priv.
+ */
+
+struct net_local
+{
+	__u32		groups;
+	struct net_device_stats stats;
+};
+
+
+
+
+/*
+ *	To call this a probe is a bit misleading, however for real
+ *	hardware it would have to check what was present.
+ */
+ 
+__initfunc(int brg_probe(struct device *dev))
+{
+  unsigned int bogomips;
+  struct timeval utime;
+
+  printk(KERN_INFO "%s: network interface for Ethernet Bridge 007/NET4.0\n", dev->name);
+
+  /*
+   *	Initialize the device structure.
+   */
+  
+  dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+  if (dev->priv == NULL)
+    return -ENOMEM;
+  memset(dev->priv, 0, sizeof(struct net_local));
+
+  /* Set up MAC address based on BogoMIPs figure for first CPU and time
+   */ 
+  bogomips = (boot_cpu_data.loops_per_sec+2500)/500000 ;
+  get_fast_time(&utime);
+
+  /* Ummmm....  YES! */
+  dev->dev_addr[0] = '\xFE';
+  dev->dev_addr[1] = '\xFD';
+  dev->dev_addr[2] = (bridge_info.instance & 0x0F) << 4;
+  dev->dev_addr[2] |= ((utime.tv_sec & 0x000F0000) >> 16);
+  dev->dev_addr[3] = bogomips & 0xFF;
+  dev->dev_addr[4] = (utime.tv_sec & 0x0000FF00) >> 8;
+  dev->dev_addr[5] = (utime.tv_sec & 0x000000FF);
+  
+  printk(KERN_INFO "%s: generated MAC address %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", 
+	 dev->name,
+	 dev->dev_addr[0],
+	 dev->dev_addr[1],
+	 dev->dev_addr[2],
+	 dev->dev_addr[3],
+	 dev->dev_addr[4],
+	 dev->dev_addr[5]);
+
+  
+  printk(KERN_INFO "%s: attached to bridge instance %lu\n", dev->name, dev->base_addr);
+
+  /*
+   *	The brg specific entries in the device structure.
+   */
+
+  dev->open = brg_open;
+  dev->hard_start_xmit = brg_start_xmit;
+  dev->stop = brg_close;
+  dev->get_stats = brg_get_stats;
+  dev->set_multicast_list = brg_set_multicast_list;
+
+  /*
+   *	Setup the generic properties
+   */
+
+  ether_setup(dev);
+
+  dev->tx_queue_len = 0;
+
+  return 0;
+}
+
+/*
+ *	Open/initialize the board.
+ */
+
+static int brg_open(struct device *dev)
+{
+        if (br_stats.flags & BR_DEBUG)
+		printk(KERN_DEBUG "%s: Doing brg_open()...", dev->name);
+
+	if (memcmp(dev->dev_addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0)
+	  return -EFAULT;
+
+	dev->start = 1;
+	dev->tbusy = 0;
+	return 0;
+}
+
+static unsigned brg_mc_hash(__u8 *dest)
+{
+	unsigned idx = 0;
+	idx ^= dest[0];
+	idx ^= dest[1];
+	idx ^= dest[2];
+	idx ^= dest[3];
+	idx ^= dest[4];
+	idx ^= dest[5];
+	return 1U << (idx&0x1F);
+}
+
+static void brg_set_multicast_list(struct device *dev)
+{
+	unsigned groups = ~0;
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+		struct dev_mc_list *dmi;
+
+		groups = brg_mc_hash(dev->broadcast);
+
+		for (dmi=dev->mc_list; dmi; dmi=dmi->next) {
+			if (dmi->dmi_addrlen != 6)
+				continue;
+			groups |= brg_mc_hash(dmi->dmi_addr);
+		}
+	}
+	lp->groups = groups;
+}
+
+/*
+ *	We transmit by throwing the packet at the bridge.
+ */
+ 
+static int brg_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	struct ethhdr *eth = (struct ethhdr*)skb->data;
+	int port;
+
+	/* Deal with the bridge being disabled */
+	if(!(br_stats.flags & BR_UP)) {
+		/* Either this */
+		/* lp->stats.tx_errors++; */ /* this condition is NOT an error */
+		/* or this  (implied by RFC 2233) */
+		lp->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	lp->stats.tx_bytes+=skb->len;
+	lp->stats.tx_packets++;
+
+#if 0
+	++br_stats_cnt.port_not_disable;
+#endif
+	skb->mac.raw = skb->nh.raw = skb->data;
+	eth = skb->mac.ethernet;
+	port = 0;	/* an impossible port (locally generated) */	
+
+	 if (br_stats.flags & BR_DEBUG)
+		printk(KERN_DEBUG "%s: brg_start_xmit - src %02x:%02x:%02x:%02x:%02x:%02x"
+	  		" dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
+		       dev->name,
+		       eth->h_source[0],
+		       eth->h_source[1],
+		       eth->h_source[2],
+		       eth->h_source[3],
+		       eth->h_source[4],
+		       eth->h_source[5],
+		       eth->h_dest[0],
+		       eth->h_dest[1],
+		       eth->h_dest[2],
+		       eth->h_dest[3],
+		       eth->h_dest[4],
+		       eth->h_dest[5]);
+	
+	/* Forward the packet ! */
+	if(br_forward(skb, port))
+	  return(0);
+    
+	/* Throw packet initially */
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+
+/*
+ *	The typical workload of the driver:
+ *	Handle the ether interface interrupts.
+ *
+ *	(In this case handle the packets posted from the bridge)
+ */
+
+static int brg_rx(struct sk_buff *skb, int port)
+{
+        struct device *dev = &brg_if.dev;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	struct ethhdr *eth = (struct ethhdr*)(skb->data);
+	int len = skb->len;
+	int clone = 0;
+
+	if (br_stats.flags & BR_DEBUG)
+		printk(KERN_DEBUG "%s: brg_rx()\n", dev->name);
+
+	/* Get out of here if the bridge interface is not up
+	 */
+	if(!(dev->flags & IFF_UP))
+	  return(0);
+	
+	/* Check that the port that this thing came off is in the forwarding state 
+	 * We sould only listen to the same address scope we will transmit to.
+	 */
+	if(port_info[port].state != Forwarding)
+	  return(0);
+
+	/* Is this for us? - broadcast/mulitcast/promiscuous packets need cloning,
+         * with uni-cast we eat the packet
+	 */
+	clone = 0;
+	if (dev->flags & IFF_PROMISC) {
+	  clone = 1;
+	}
+	else if (eth->h_dest[0]&1) {
+	  if (!(dev->flags&(IFF_ALLMULTI))
+	      && !(brg_mc_hash(eth->h_dest)&lp->groups))
+	    return(0);
+	  clone = 1;
+	}
+	else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN) != 0) {
+	  return(0);
+	}
+
+	/* Clone things here - we want to be transparent before we check packet data 
+	 * integrity
+	 */
+	if(clone) {
+	  struct sk_buff *skb2 = skb;
+	  skb = skb_clone(skb2, GFP_KERNEL);
+	  if (skb == NULL) {
+	    return(0);
+	  }
+	  
+	}
+	else {
+		/* Learn source addresses for unicast non-promiscuous 
+		 * frames for brg0
+		 */
+		if(br_learn(skb, port)) {
+			return br_drop(skb);
+		}
+	}
+
+	/* Check packet length 
+	 */
+	if (len < 16) {
+		printk(KERN_DEBUG "%s : rx len = %d\n", dev->name, len);
+		kfree_skb(skb);
+		return(!clone);
+	}
+
+	if (br_stats.flags & BR_DEBUG)
+	  printk(KERN_DEBUG "%s: brg_rx - src %02x:%02x:%02x:%02x:%02x:%02x"
+		 " dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
+		 dev->name,
+		 eth->h_source[0],
+		 eth->h_source[1],
+		 eth->h_source[2],
+		 eth->h_source[3],
+		 eth->h_source[4],
+		 eth->h_source[5],
+		 eth->h_dest[0],
+		 eth->h_dest[1],
+		 eth->h_dest[2],
+		 eth->h_dest[3],
+		 eth->h_dest[4],
+		 eth->h_dest[5]);
+
+	/* Do it */
+	skb->pkt_type = PACKET_HOST;
+	skb->dev = dev;
+	skb->protocol=eth_type_trans(skb,dev);
+	memset(skb->cb, 0, sizeof(skb->cb));
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes+=len;
+	netif_rx(skb);
+	return(!clone);
+}
+
+static int brg_close(struct device *dev)
+{
+	if (br_stats.flags & BR_DEBUG)
+		printk(KERN_DEBUG "%s: Shutting down.\n", dev->name);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	return 0;
+}
+
+static struct net_device_stats *brg_get_stats(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	return &lp->stats;
+}
+
+/* 
+ *      
+ */
+
+__initfunc(int brg_init(void))
+{
+        int err;
+
+	memset(&brg_if, 0, sizeof(brg_if));
+
+        rtnl_lock();
+
+	brg_if.dev.base_addr = bridge_info.instance;
+	sprintf (brg_if.name, "brg%d", bridge_info.instance);
+	brg_if.dev.name = (void*)&brg_if.name;
+        if(dev_get(brg_if.name)) {
+               printk(KERN_INFO "%s already loaded.\n", brg_if.name);
+	       return -EBUSY;
+	}
+        brg_if.dev.init = brg_probe;
+
+        err = register_netdevice(&brg_if.dev);
+        rtnl_unlock();
+        return err;
+}
+
+
+#if 0				/* Its here if we ever need it... */
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+
+  /* 
+   * Unregister the device
+   */
+  rtnl_lock();
+  unregister_netdevice(&the_master.dev);
+  rtnl_unlock();
+
+  /*
+   *	Free up the private structure.
+   */
+  
+  kfree(brg_if.dev.priv);
+  brg_if.dev.priv = NULL;	/* gets re-allocated by brg_probe */
+}
+
+#endif /* MODULE */
+
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/bridge/br_tree.c linux.14p6/net/bridge/br_tree.c
--- linux.vanilla/net/bridge/br_tree.c	Sun Mar  7 23:25:23 1999
+++ linux.14p6/net/bridge/br_tree.c	Fri Oct 22 22:39:35 1999
@@ -492,7 +492,8 @@
         port_info[port].fdb = NULL;
 
         /* remove the local mac too */
-        next = br_avl_find_addr(port_info[port].dev->dev_addr);
+/*        next = br_avl_find_addr(port_info[port].dev->dev_addr); */
+        next = br_avl_find_addr(port_info[port].ifmac.BRIDGE_ID_ULA);
         if (next != NULL)
                 br_avl_remove(next);
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/core/dev.c linux.14p6/net/core/dev.c
--- linux.vanilla/net/core/dev.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/core/dev.c	Wed Nov  3 00:22:30 1999
@@ -57,6 +57,8 @@
  *					A network device unload needs to purge
  *					the backlog queue.
  *	Paul Rusty Russel	:	SIOCSIFNAME
+ *	Andrea Arcangeli	:	dev_clear_backlog() needs the
+ *					skb_queue_lock held.
  */
 
 #include <asm/uaccess.h>
@@ -711,7 +713,8 @@
 
 static void dev_clear_backlog(struct device *dev)
 {
-	struct sk_buff *prev, *curr;
+	struct sk_buff *curr;
+	unsigned long flags;
 
 	/*
 	 *
@@ -719,27 +722,24 @@
 	 *
 	 *  We are competing here both with netif_rx() and net_bh().
 	 *  We don't want either of those to mess with skb ptrs
-	 *  while we work on them, thus cli()/sti().
-	 *
-	 *  It looks better to use net_bh trick, at least
-	 *  to be sure, that we keep interrupt latency really low. --ANK (980727)
+	 *  while we work on them, thus we must grab the
+	 *  skb_queue_lock.
 	 */ 
 
 	if (backlog.qlen) {
-		start_bh_atomic();
-		curr = backlog.next;
-		while ( curr != (struct sk_buff *)(&backlog) ) {
-			unsigned long flags;
-			curr=curr->next;
-			if ( curr->prev->dev == dev ) {
-				prev = curr->prev;
-				spin_lock_irqsave(&skb_queue_lock, flags);
-				__skb_unlink(prev, &backlog);
+	repeat:
+		spin_lock_irqsave(&skb_queue_lock, flags);
+		for (curr = backlog.next;
+		     curr != (struct sk_buff *)(&backlog);
+		     curr = curr->next)
+			if (curr->dev == dev)
+			{
+				__skb_unlink(curr, &backlog);
 				spin_unlock_irqrestore(&skb_queue_lock, flags);
-				kfree_skb(prev);
+				kfree_skb(curr);
+				goto repeat;
 			}
-		}
-		end_bh_atomic();
+		spin_unlock_irqrestore(&skb_queue_lock, flags);
 #ifdef CONFIG_NET_HW_FLOWCONTROL
 		if (netdev_dropping)
 			netdev_wakeup();
@@ -796,7 +796,11 @@
 #ifdef CONFIG_BRIDGE
 static inline void handle_bridge(struct sk_buff *skb, unsigned short type)
 {
-	if (br_stats.flags & BR_UP && br_protocol_ok(ntohs(type)))
+	/* 
+	 * The br_stats.flags is checked here to save the expense of a 
+	 * function call.
+	 */
+	if ((br_stats.flags & BR_UP) && br_call_bridge(skb, type))
 	{
 		/*
 		 *	We pass the bridge a complete frame. This means
@@ -820,7 +824,6 @@
 }
 #endif
 
-
 /*
  *	When we are called the queue is ready to grab, the interrupts are
  *	on and hardware can interrupt and queue to the receive queue as we
@@ -1275,8 +1278,9 @@
 	int size;
 
 	if(stats != (struct iw_statistics *) NULL)
+	{
 		size = sprintf(buffer,
-			       "%6s: %02x  %3d%c %3d%c  %3d%c %5d %5d %5d\n",
+			       "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d\n",
 			       dev->name,
 			       stats->status,
 			       stats->qual.qual,
@@ -1288,6 +1292,8 @@
 			       stats->discard.nwid,
 			       stats->discard.code,
 			       stats->discard.misc);
+		stats->qual.updated = 0;
+	}
 	else
 		size = 0;
 
@@ -1309,8 +1315,9 @@
 	struct device *	dev;
 
 	size = sprintf(buffer,
-		       "Inter-|sta|  Quality       |  Discarded packets\n"
-		       " face |tus|link level noise| nwid crypt  misc\n");
+		       "Inter-| sta-|   Quality        |   Discarded packets\n"
+		       " face | tus | link level noise |  nwid  crypt   misc\n"
+			);
 	
 	pos+=size;
 	len+=size;
@@ -1855,6 +1862,7 @@
 extern int baycom_par_init(void);
 
 extern int lapbeth_init(void);
+extern int comx_init(void);
 extern void arcnet_init(void);
 extern void ip_auto_config(void);
 #ifdef CONFIG_8xx
@@ -1948,6 +1956,9 @@
 #if defined(CONFIG_8xx)
         cpm_enet_init();
 #endif
+#if defined(CONFIG_COMX)
+	comx_init();
+#endif
 	/*
 	 *	SLHC if present needs attaching so other people see it
 	 *	even if not opened.
@@ -2017,6 +2028,13 @@
 	dev_boot_phase = 0;
 
 	dev_mcast_init();
+
+#ifdef CONFIG_BRIDGE
+	/*
+	 * Register any statically linked ethernet devices with the bridge
+	 */
+	br_spacedevice_register();
+#endif
 
 #ifdef CONFIG_IP_PNP
 	ip_auto_config();
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ethernet/eth.c linux.14p6/net/ethernet/eth.c
--- linux.vanilla/net/ethernet/eth.c	Mon Jan 12 23:28:25 1998
+++ linux.14p6/net/ethernet/eth.c	Tue Nov 16 15:16:27 1999
@@ -201,7 +201,7 @@
 	 *	seems to set IFF_PROMISC.
 	 */
 	 
-	else if(dev->flags&(IFF_PROMISC/*|IFF_ALLMULTI*/))
+	else if(1 /*dev->flags&IFF_PROMISC*/)
 	{
 		if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
 			skb->pkt_type=PACKET_OTHERHOST;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/arp.c linux.14p6/net/ipv4/arp.c
--- linux.vanilla/net/ipv4/arp.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/ipv4/arp.c	Tue Nov 16 15:16:27 1999
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.77.2.1 1999/06/28 10:39:23 davem Exp $
+ * Version:	$Id: arp.c,v 1.77.2.4 1999/09/23 19:03:36 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/icmp.c linux.14p6/net/ipv4/icmp.c
--- linux.vanilla/net/ipv4/icmp.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/ipv4/icmp.c	Tue Nov 16 15:16:27 1999
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@redhat.com>
  *
- *	Version: $Id: icmp.c,v 1.52.2.3 1999/09/22 16:33:02 davem Exp $
+ *	Version: $Id: icmp.c,v 1.52.2.4 1999/11/16 02:28:40 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 --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/ip_forward.c linux.14p6/net/ipv4/ip_forward.c
--- linux.vanilla/net/ipv4/ip_forward.c	Sun Mar 21 15:22:00 1999
+++ linux.14p6/net/ipv4/ip_forward.c	Tue Nov 16 15:16:27 1999
@@ -5,11 +5,12 @@
  *
  *		The IP forwarding functionality.
  *		
- * Version:	$Id: ip_forward.c,v 1.43 1999/03/21 05:22:37 davem Exp $
+ * Version:	$Id: ip_forward.c,v 1.43.2.1 1999/11/16 06:33:43 davem Exp $
  *
  * Authors:	see ip.c
  *
  * Fixes:
+ *		Joseph Gooch	:	Removed maddr selection for ip_masq, now done in ip_masq.c
  *		Many		:	Split from ip.c , see ip_input.c for 
  *					history.
  *		Dave Gregorich	:	NULL ip_rt_put fix for multicast 
@@ -168,7 +169,6 @@
 		 *	and skip the firewall checks
 		 */
 		if (iph->protocol == IPPROTO_ICMP) {
-			__u32 maddr;
 #ifdef CONFIG_IP_MASQUERADE_ICMP
 			struct icmphdr *icmph = (struct icmphdr *)((char*)iph + (iph->ihl << 2));
 			if ((icmph->type==ICMP_DEST_UNREACH)||
@@ -176,8 +176,7 @@
 			    (icmph->type==ICMP_TIME_EXCEEDED))
 			{
 #endif
-				maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE);
-				fw_res = ip_fw_masq_icmp(&skb, maddr);
+				fw_res = ip_fw_masquerade(&skb, 0);
 			        if (fw_res < 0) {
 					kfree_skb(skb);
 					return -1;
@@ -187,7 +186,7 @@
 					/* ICMP matched - skip firewall */
 					goto skip_call_fw_firewall;
 #ifdef CONFIG_IP_MASQUERADE_ICMP
-			       }
+			}
 #endif				
 		}
 		if (rt->rt_flags&RTCF_MASQ)
@@ -219,15 +218,11 @@
 	 */
 	if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
 	    (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
-		u32 maddr;
+		u32 maddr = 0;
 
 #ifdef CONFIG_IP_ROUTE_NAT
 		maddr = (rt->rt_flags&RTCF_MASQ) ? rt->rt_src_map : 0;
-
-		if (maddr == 0)
 #endif
-			maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE);
-
 			if (ip_fw_masquerade(&skb, maddr) < 0) {
 				kfree_skb(skb);
 				return -1;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/ip_fw.c linux.14p6/net/ipv4/ip_fw.c
--- linux.vanilla/net/ipv4/ip_fw.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/ipv4/ip_fw.c	Wed Nov  3 00:59:58 1999
@@ -40,11 +40,13 @@
  * 23-Jul-1999: Fixed small fragment security exposure opened on 15-May-1998.
  *              John McDonald <jm@dataprotect.com>
  *              Thomas Lopatic <tl@dataprotect.com>
+ * 21-Oct-1999: Use bh, not interrupt locking. --RR
+ *              Applied count fix by Emanuele Caratti <wiz@iol.it>
  */
 
 /*
  *
- * The origina Linux port was done Alan Cox, with changes/fixes from
+ * The original Linux port was done Alan Cox, with changes/fixes from
  * Pauline Middlelink, Jos Vos, Thomas Quinot, Wouter Gadeyne, Juan
  * Jose Ciarlante, Bernd Eckenfels, Keith Owens and others.
  * 
@@ -86,6 +88,7 @@
 #include <net/udp.h>
 #include <net/sock.h>
 #include <net/icmp.h>
+#include <net/ip_masq.h>
 #include <linux/netlink.h>
 #include <linux/init.h>
 #include <linux/firewall.h>
@@ -150,6 +153,7 @@
 static struct sock *ipfwsk;
 #endif
 
+/* Don't call SLOT_NUMBER when you have a write lock. */
 #ifdef __SMP__
 #define SLOT_NUMBER() (cpu_number_map[smp_processor_id()]*2 + !in_interrupt())
 #else
@@ -190,21 +194,26 @@
 	       __FILE__, __LINE__, SLOT_NUMBER());	\
 } while (0)
 
+#define FWC_NOINT()							    \
+do {									    \
+	if (in_interrupt())						    \
+		printk("Rusty, you promised! %s %u\n", __FILE__, __LINE__); \
+} while(0)
 #else
 #define FWC_DEBUG_LOCK(d) do { } while(0)
 #define FWC_DEBUG_UNLOCK(d) do { } while(0)
 #define FWC_DONT_HAVE_LOCK(d) do { } while(0)
 #define FWC_HAVE_LOCK(d) do { } while(0)
+#define FWC_NOINT() do { } while(0)
 #endif /*DEBUG_IP_FIRWALL_LOCKING*/
 
+/* We never to a write lock in bh, so we only need write_lock_bh */
 #define FWC_READ_LOCK(l) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock(l); } while (0)
-#define FWC_WRITE_LOCK(l) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock(l); } while (0)
-#define FWC_READ_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock_irqsave(l,f); } while (0)
-#define FWC_WRITE_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock_irqsave(l,f); } while (0)
+/* Debug after lock obtained, (in_interrupt() will be true there), so
+   SLOT_NUMBER consistent. */
+#define FWC_WRITE_LOCK(l) do { FWC_NOINT(); write_lock_bh(l); FWC_DEBUG_LOCK(fwc_wlocks); } while (0)
 #define FWC_READ_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock(l); } while (0)
-#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock(l); } while (0)
-#define FWC_READ_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock_irqrestore(l,f); } while (0)
-#define FWC_WRITE_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_irqrestore(l,f); } while (0)
+#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_bh(l); } while (0)
 
 struct ip_chain;
 
@@ -228,6 +237,7 @@
 {
 	struct ip_chain *prevchain;	/* Pointer to referencing chain */
 	struct ip_fwkernel *prevrule;	/* Pointer to referencing rule */
+	unsigned int count;
 	struct ip_counters counters;
 };
 
@@ -488,7 +498,10 @@
                 static int enabled = 0;
 
                 if(!enabled)
+                {
+                	enabled=1;
                         sysctl_ip_always_defrag++;
+                }
 		*answer = FW_REDIRECT;
 		return 1;
 #endif
@@ -729,8 +742,8 @@
 	else FWC_HAVE_LOCK(fwc_rlocks);
 
 	f = chain->chain;
+	count = 0;
 	do {
-		count = 0;
 		for (; f; f = f->next) {
 			count++;
 			if (ip_rule_match(f,rif,ip,
@@ -768,10 +781,12 @@
 				else {
 					f->branch->reent[slot].prevchain 
 						= chain;
+					f->branch->reent[slot].count = count;
 					f->branch->reent[slot].prevrule 
 						= f->next;
 					chain = f->branch;
 					f = chain->chain;
+					count = 0;
 				}
 			}
 			else if (f->simplebranch == FW_SKIP) 
@@ -790,6 +805,7 @@
 			if (chain->reent[slot].prevchain) {
 				struct ip_chain *tmp = chain;
 				f = chain->reent[slot].prevrule;
+				count = chain->reent[slot].count;
 				chain = chain->reent[slot].prevchain;
 				tmp->reent[slot].prevchain = NULL;
 			}
@@ -1300,9 +1316,8 @@
 {
 	int ret;
 	struct ip_chain *chain;
-	unsigned long flags;
 
-	FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+	FWC_WRITE_LOCK(&ip_fw_lock);
 
 	switch (cmd) {
 	case IP_FW_FLUSH:
@@ -1326,7 +1341,7 @@
 		struct iphdr *ip;
 
 		/* Don't need write lock. */
-		FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+		FWC_WRITE_UNLOCK(&ip_fw_lock);
 		
 		if (len != sizeof(struct ip_fwtest) || !check_label(m))
 			return EINVAL;
@@ -1521,7 +1536,7 @@
 		ret = EINVAL;
 	}
 
-	FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+	FWC_WRITE_UNLOCK(&ip_fw_lock);
 	return ret;
 }
 
@@ -1582,7 +1597,6 @@
 {
 	struct ip_chain *i;
 	struct ip_fwkernel *j = ip_fw_chains->chain;
-	unsigned long flags;
 	int len = 0;
 	int last_len = 0;
 	off_t upto = 0;
@@ -1591,7 +1605,7 @@
 	duprintf("ip_fw_chains is 0x%0lX\n", (unsigned long int)ip_fw_chains);
 
 	/* Need a write lock to lock out ``readers'' which update counters. */
-	FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+	FWC_WRITE_LOCK(&ip_fw_lock);
 
 	for (i = ip_fw_chains; i; i = i->next) {
 	    for (j = i->chain; j; j = j->next) {
@@ -1622,7 +1636,7 @@
 		}
 	}
 outside:
-	FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+	FWC_WRITE_UNLOCK(&ip_fw_lock);
 	buffer[len] = '\0';
 
 	duprintf("ip_chain_procinfo: Length = %i (of %i).  Offset = %li.\n",
@@ -1638,10 +1652,9 @@
 	struct ip_chain *i;
 	int len = 0,last_len = 0;
 	off_t pos = 0,begin = 0;
-	unsigned long flags;
 
 	/* Need a write lock to lock out ``readers'' which update counters. */
-	FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+	FWC_WRITE_LOCK(&ip_fw_lock);
 
 	for (i = ip_fw_chains; i; i = i->next)
 	{
@@ -1673,7 +1686,7 @@
 		
 		last_len = len;
 	}
-	FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+	FWC_WRITE_UNLOCK(&ip_fw_lock);
 
 	*start = buffer+(offset-begin);
 	len-=(offset-begin);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/ip_masq.c linux.14p6/net/ipv4/ip_masq.c
--- linux.vanilla/net/ipv4/ip_masq.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/ipv4/ip_masq.c	Wed Nov  3 00:31:56 1999
@@ -10,6 +10,9 @@
  *	See ip_fw.c for original log
  *
  * Fixes:
+ *	Joseph Gooch		:	Modified ip_fw_masquerade() to do a ip_route_output()
+ *	 (help by Dan Drown)	:	to choose the proper local address.
+ *	 (and Alexey)		:
  *	Juan Jose Ciarlante	:	Modularized application masquerading (see ip_masq_app.c)
  *	Juan Jose Ciarlante	:	New struct ip_masq_seq that holds output/input delta seq.
  *	Juan Jose Ciarlante	:	Added hashed lookup by proto,maddr,mport and proto,saddr,sport
@@ -1139,6 +1142,23 @@
 		 *	invalid packets.
 		 */
 		return -1;
+	}
+
+	/* Lets determine our maddr now, shall we? */
+	if (maddr == 0) {
+		struct rtable *rt;
+		struct rtable *skb_rt = (struct rtable*)skb->dst;
+		struct device *skb_dev = skb_rt->u.dst.dev;
+
+		if (ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos)|RTO_CONN, skb_dev?skb_dev->ifindex:0)) {
+			/* Fallback on old method */
+			/* This really shouldn't happen... */
+			maddr = inet_select_addr(skb_dev, skb_rt->rt_gateway, RT_SCOPE_UNIVERSE);
+		} else {
+			/* Route lookup succeeded */
+			maddr = rt->rt_src;
+			ip_rt_put(rt);
+		}
 	}
 
 	switch (iph->protocol) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/ip_masq_user.c linux.14p6/net/ipv4/ip_masq_user.c
--- linux.vanilla/net/ipv4/ip_masq_user.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/ipv4/ip_masq_user.c	Tue Nov 16 15:16:27 1999
@@ -2,7 +2,7 @@
  *	IP_MASQ_USER user space control module
  *
  *
- *	$Id: ip_masq_user.c,v 1.1.2.2 1999/08/13 18:26:33 davem Exp $
+ *	$Id: ip_masq_user.c,v 1.1.2.3 1999/11/16 06:33:51 davem Exp $
  */
 
 #include <linux/config.h>
@@ -100,7 +100,7 @@
 		return ret;
 	}
 	dev = rt->u.dst.dev;
-	ums->maddr = ip_masq_select_addr(dev, rt->rt_gateway, RT_SCOPE_UNIVERSE);
+	ums->maddr = rt->rt_src;  /* Per Alexey */
 
 	IP_MASQ_DEBUG(1-debug, "did setup maddr=%lX\n", ntohl(ums->maddr));
 	ip_rt_put(rt);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/route.c linux.14p6/net/ipv4/route.c
--- linux.vanilla/net/ipv4/route.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/ipv4/route.c	Tue Nov 16 15:16:27 1999
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.67.2.3 1999/08/08 08:43:12 davem Exp $
+ * Version:	$Id: route.c,v 1.67.2.4 1999/11/16 02:28:43 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/tcp.c linux.14p6/net/ipv4/tcp.c
--- linux.vanilla/net/ipv4/tcp.c	Sat Aug 14 02:27:41 1999
+++ linux.14p6/net/ipv4/tcp.c	Tue Nov 16 15:16:27 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.140.2.4 1999/08/09 03:13:12 davem Exp $
+ * Version:	$Id: tcp.c,v 1.140.2.5 1999/09/23 19:21:16 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1798,6 +1798,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),
@@ -1823,4 +1825,43 @@
 						NULL, NULL);
 	if(!tcp_timewait_cachep)
 		panic("tcp_init: Cannot alloc tcp_tw_bucket cache.");
+
+	/* Size and allocate TCP hash tables. */
+	goal = num_physpages >> (20 - PAGE_SHIFT);
+	for (order = 0; (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 >= 0);
+
+	if (!tcp_ehash)
+		panic("Failed to allocate TCP established hash table\n");
+	memset(tcp_ehash, 0, tcp_ehash_size * sizeof(struct sock *));
+
+	goal = (((1UL << order) * PAGE_SIZE) / sizeof(struct tcp_bind_bucket *));
+	if (goal > (64 * 1024)) {
+		/* Don't size the bind-hash larger than the port
+		 * space, that is just silly.
+		 */
+		goal = (((64 * 1024) * sizeof(struct tcp_bind_bucket *)) / PAGE_SIZE);
+		for (order = 0; (1UL << order) < goal; order++)
+			;
+	}
+
+	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 >= 0);
+
+	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 (ehash %d bhash %d)\n",
+	       tcp_ehash_size, tcp_bhash_size);
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/tcp_input.c linux.14p6/net/ipv4/tcp_input.c
--- linux.vanilla/net/ipv4/tcp_input.c	Sat Aug 28 20:00:59 1999
+++ linux.14p6/net/ipv4/tcp_input.c	Tue Nov 16 15:16:27 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.164.2.7 1999/08/13 16:14:27 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.164.2.8 1999/09/23 19:21:23 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1043,7 +1043,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/2)];
 	sktw = (struct sock *)tw;
 	if((sktw->next = *head) != NULL)
 		(*head)->pprev = &sktw->next;
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/tcp_ipv4.c linux.14p6/net/ipv4/tcp_ipv4.c
--- linux.vanilla/net/ipv4/tcp_ipv4.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/ipv4/tcp_ipv4.c	Tue Nov 16 15:16:27 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.175.2.10 1999/08/13 16:14:35 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.175.2.13 1999/11/16 06:33:53 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];
+unsigned int tcp_ehash_size;
+struct sock **tcp_ehash;
 
 /* 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];
+unsigned int tcp_bhash_size;
+struct tcp_bind_bucket **tcp_bhash;
 
 /* 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/2) - 1);
 }
 
 static __inline__ int tcp_sk_hashfn(struct sock *sk)
@@ -140,7 +142,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->fastreuse = 0;
 		tb->owners = NULL;
@@ -162,7 +164,7 @@
 {
 	struct tcp_bind_bucket *tb;
 
-	tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	tb = tcp_bhash[tcp_bhashfn(snum)];
 	for( ; (tb && (tb->port != snum)); tb = tb->next)
 		;
 	if (tb == NULL) {
@@ -180,8 +182,8 @@
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if (child->num != sk->num) {
-		unsigned short snum = ntohs(child->num);
-		for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+		unsigned short snum = child->num;
+		for(tb = tcp_bhash[tcp_bhashfn(snum)];
 		    tb && tb->port != snum;
 		    tb = tb->next)
 			;
@@ -220,7 +222,7 @@
 		do {	rover++;
 			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;
@@ -237,7 +239,7 @@
 		snum = rover;
 		tb = NULL;
 	} else {
-		for (tb = tcp_bound_hash[tcp_bhashfn(snum)];
+		for (tb = tcp_bhash[tcp_bhashfn(snum)];
 		     tb != NULL;
 		     tb = tb->next)
 			if (tb->port == snum)
@@ -328,7 +330,7 @@
 	if(sk->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;
@@ -421,7 +423,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;
@@ -429,7 +431,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/2)]; sk; sk = sk->next)
 		if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif))
 			goto hit;
 	sk = tcp_v4_lookup_listener(daddr, hnum, dif);
@@ -469,7 +471,7 @@
 
 	/* This code must run only from NET_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)
@@ -510,7 +512,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) {
@@ -547,7 +549,7 @@
 
 	/* Freeze the hash while we snoop around. */
 	SOCKHASH_LOCK();
-	tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	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
@@ -1615,7 +1617,8 @@
 			sk = tcp_check_req(sk, skb, req);
 		}
 #ifdef CONFIG_SYN_COOKIES
-		else if (flg == __constant_htonl(0x00120000))  {
+		else if ((flg & __constant_htonl(0x00120000))==__constant_htonl(0x00100000))
+		{
 			sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
 		}
 #endif
@@ -1793,7 +1796,7 @@
 
 static void __tcp_v4_rehash(struct sock *sk)
 {
-	struct sock **skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))];
+	struct sock **skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))];
 
 	SOCKHASH_LOCK();
 	if(sk->pprev) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/tcp_timer.c linux.14p6/net/ipv4/tcp_timer.c
--- linux.vanilla/net/ipv4/tcp_timer.c	Sat Aug 14 02:27:41 1999
+++ linux.14p6/net/ipv4/tcp_timer.c	Tue Nov 16 15:16:27 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.62.2.3 1999/06/20 20:14:30 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.62.2.4 1999/09/23 19:21:39 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -363,8 +363,8 @@
 	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];
+	for(i = chain_start; i < (chain_start + ((tcp_ehash_size/2) >> 2)); i++) {
+		struct sock *sk = tcp_ehash[i];
 		while(sk) {
 			if(!atomic_read(&sk->sock_readers) && sk->keepopen) {
 				count += tcp_keepopen_proc(sk);
@@ -375,8 +375,8 @@
 		}
 	}
 out:
-	chain_start = ((chain_start + ((TCP_HTABLE_SIZE/2)>>2)) &
-		       ((TCP_HTABLE_SIZE/2) - 1));
+	chain_start = ((chain_start + ((tcp_ehash_size/2)>>2)) &
+		       ((tcp_ehash_size/2) - 1));
 }
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv6/README linux.14p6/net/ipv6/README
--- linux.vanilla/net/ipv6/README	Sat Apr 20 10:01:37 1996
+++ linux.14p6/net/ipv6/README	Wed Nov  3 00:25:58 1999
@@ -1,6 +1,6 @@
 To join in the work on Linux IPv6 send mail to:
 
-        majordomo@nuclecu.unam.mx
+        majordomo@oss.sgi.com
 
 and in the body of the message include:
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv6/tcp_ipv6.c linux.14p6/net/ipv6/tcp_ipv6.c
--- linux.vanilla/net/ipv6/tcp_ipv6.c	Sat Aug 28 20:00:59 1999
+++ linux.14p6/net/ipv6/tcp_ipv6.c	Tue Nov 16 15:16:27 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.104.2.9 1999/08/13 18:49:56 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.104.2.10 1999/09/23 19:21:46 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/2) - 1));
 }
 
 static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
@@ -98,7 +98,7 @@
 		do {	rover++;
 			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;
@@ -115,7 +115,7 @@
 		snum = rover;
 		tb = NULL;
 	} else {
-		for (tb = tcp_bound_hash[tcp_bhashfn(snum)];
+		for (tb = tcp_bhash[tcp_bhashfn(snum)];
 		     tb != NULL;
 		     tb = tb->next)
 			if (tb->port == snum)
@@ -194,7 +194,7 @@
 		if(sk->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))];
 
 		SOCKHASH_LOCK();
 		if((sk->next = *skp) != NULL)
@@ -276,7 +276,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)
@@ -285,7 +285,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/2)]; sk; sk = sk->next) {
 		if(*((__u32 *)&(sk->dport))	== ports	&&
 		   sk->family			== PF_INET6) {
 			struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
@@ -336,7 +336,7 @@
 
 	/* Freeze the hash while we snoop around. */
 	SOCKHASH_LOCK();
-	tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	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
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/irda/irda_device.c linux.14p6/net/irda/irda_device.c
--- linux.vanilla/net/irda/irda_device.c	Sat Aug 14 02:26:52 1999
+++ linux.14p6/net/irda/irda_device.c	Wed Nov  3 00:22:30 1999
@@ -526,13 +526,13 @@
 		break;
 	case SIOCSIWNWID:
 		/* Set domain */
-		if (wrq->u.nwid.on) {
+		if (!wrq->u.nwid.disabled) {
 			
 		} break;
 	case SIOCGIWNWID:
 		/* Read domain*/
 /* 		wrq->u.nwid.nwid = domain; */
-/* 		wrq->u.nwid.on = 1; */
+/* 		wrq->u.nwid.disabled = 0; */
 		break;
 	case SIOCGIWENCODE:
 		/* Get scramble key */
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/irda/irlan/irlan_eth.c linux.14p6/net/irda/irlan/irlan_eth.c
--- linux.vanilla/net/irda/irlan/irlan_eth.c	Sat Aug 14 02:26:52 1999
+++ linux.14p6/net/irda/irlan/irlan_eth.c	Fri Oct 22 22:17:19 1999
@@ -345,6 +345,7 @@
 	 * is useful if we have changed access points on the same
 	 * subnet.  
 	 */
+#ifdef CONFIG_INET	 
 	DEBUG(4, "IrLAN: Sending gratuitous ARP\n");
 	in_dev = dev->ip_ptr;
 	arp_send(ARPOP_REQUEST, ETH_P_ARP, 
@@ -352,6 +353,7 @@
 		 dev, 
 		 in_dev->ifa_list->ifa_address,
 		 NULL, dev->dev_addr, NULL);
+#endif /* CONFIG_INET */		 
 }
 
 /*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/irda/irmod.c linux.14p6/net/irda/irmod.c
--- linux.vanilla/net/irda/irmod.c	Sat Aug 14 02:26:54 1999
+++ linux.14p6/net/irda/irmod.c	Wed Oct 27 18:50:24 1999
@@ -119,7 +119,9 @@
 EXPORT_SYMBOL(irda_notify_init);
 EXPORT_SYMBOL(irmanager_notify);
 EXPORT_SYMBOL(irda_lock);
+#ifdef CONFIG_PROC_FS
 EXPORT_SYMBOL(proc_irda);
+#endif
 
 /* IrIAP/IrIAS */
 EXPORT_SYMBOL(iriap_getvaluebyclass_request);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/netsyms.c linux.14p6/net/netsyms.c
--- linux.vanilla/net/netsyms.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/netsyms.c	Fri Oct 22 22:17:19 1999
@@ -277,9 +277,11 @@
 EXPORT_SYMBOL(inet_recvmsg);
 
 /* Socket demultiplexing. */
-EXPORT_SYMBOL(tcp_established_hash);
+EXPORT_SYMBOL(tcp_ehash_size);
+EXPORT_SYMBOL(tcp_ehash);
 EXPORT_SYMBOL(tcp_listening_hash);
-EXPORT_SYMBOL(tcp_bound_hash);
+EXPORT_SYMBOL(tcp_bhash_size);
+EXPORT_SYMBOL(tcp_bhash);
 EXPORT_SYMBOL(udp_hash);
 
 EXPORT_SYMBOL(destroy_sock);
@@ -496,8 +498,10 @@
 EXPORT_SYMBOL(unregister_hipdev);
 #endif
 
+#ifdef CONFIG_INET
 EXPORT_SYMBOL(sysctl_wmem_max);
 EXPORT_SYMBOL(sysctl_rmem_max);
+#endif
 
 #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) 
 #include<linux/if_ltalk.h>
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/sunrpc/clnt.c linux.14p6/net/sunrpc/clnt.c
--- linux.vanilla/net/sunrpc/clnt.c	Tue Oct  6 17:39:44 1998
+++ linux.14p6/net/sunrpc/clnt.c	Fri Oct 22 22:46:31 1999
@@ -467,6 +467,9 @@
 		return;
 	}
 
+	/* Zero buffer so we have automatic zero-padding of opaque & string */
+	memset(task->tk_buffer, 0, bufsiz);
+
 	/* Encode header and provided arguments */
 	encode = rpcproc_encode(clnt, task->tk_proc);
 	if (!(p = call_header(task))) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/sunrpc/svc.c linux.14p6/net/sunrpc/svc.c
--- linux.vanilla/net/sunrpc/svc.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/sunrpc/svc.c	Fri Oct 22 21:36:09 1999
@@ -38,7 +38,7 @@
 
 	memset(serv, 0, sizeof(*serv));
 	serv->sv_program   = prog;
-	serv->sv_nrthreads = 1;
+	atomic_set(&serv->sv_nrthreads, 1);
 	serv->sv_stats     = prog->pg_stats;
 	serv->sv_bufsz	   = bufsize? bufsize : 4096;
 	serv->sv_xdrsize   = xdrsize;
@@ -61,10 +61,10 @@
 
 	dprintk("RPC: svc_destroy(%s, %d)\n",
 				serv->sv_program->pg_name,
-				serv->sv_nrthreads);
+				atomic_read (&serv->sv_nrthreads));
 
-	if (serv->sv_nrthreads) {
-		if (--(serv->sv_nrthreads) != 0)
+	if (atomic_read (&serv->sv_nrthreads)) {
+		if (!atomic_dec_and_test (&serv->sv_nrthreads))
 			return;
 	} else
 		printk("svc_destroy: no threads for serv=%p!\n", serv);
@@ -128,7 +128,7 @@
 	 || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz))
 		goto out_thread;
 
-	serv->sv_nrthreads++;
+	atomic_inc(&serv->sv_nrthreads);
 	rqstp->rq_server = serv;
 	error = kernel_thread((int (*)(void *)) func, rqstp, 0);
 	if (error < 0)
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/sunrpc/svcsock.c linux.14p6/net/sunrpc/svcsock.c
--- linux.vanilla/net/sunrpc/svcsock.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/sunrpc/svcsock.c	Fri Oct 22 21:36:09 1999
@@ -124,7 +124,7 @@
 				"svc_sock_enqueue: server %p, rq_sock=%p!\n",
 				rqstp, rqstp->rq_sock);
 		rqstp->rq_sock = svsk;
-		svsk->sk_inuse++;
+		atomic_inc(&svsk->sk_inuse);
 		wake_up(&rqstp->rq_wait);
 	} else {
 		dprintk("svc: socket %p put into queue\n", svsk->sk_sk);
@@ -148,7 +148,7 @@
 
 	if (svsk) {
 		dprintk("svc: socket %p dequeued, inuse=%d\n",
-			svsk->sk_sk, svsk->sk_inuse);
+			svsk->sk_sk, atomic_read(&svsk->sk_inuse));
 		svsk->sk_qued = 0;
 	}
 
@@ -206,7 +206,7 @@
 		return;
 	svc_release_skb(rqstp);
 	rqstp->rq_sock = NULL;
-	if (!--(svsk->sk_inuse) && svsk->sk_dead) {
+	if (atomic_dec_and_test(&svsk->sk_inuse) && svsk->sk_dead) {
 		dprintk("svc: releasing dead socket\n");
 		sock_release(svsk->sk_sock);
 		kfree(svsk);
@@ -766,7 +766,7 @@
 	start_bh_atomic();
 	if ((svsk = svc_sock_dequeue(serv)) != NULL) {
 		rqstp->rq_sock = svsk;
-		svsk->sk_inuse++;
+		atomic_inc(&svsk->sk_inuse);
 	} else {
 		/* No data pending. Go to sleep */
 		svc_serv_enqueue(serv, rqstp);
@@ -793,7 +793,7 @@
 	end_bh_atomic();
 
 	dprintk("svc: server %p, socket %p, inuse=%d\n",
-		 rqstp, svsk, svsk->sk_inuse);
+		 rqstp, svsk, atomic_read(&svsk->sk_inuse));
 	len = svsk->sk_recvfrom(rqstp);
 	dprintk("svc: got len=%d\n", len);
 
@@ -988,11 +988,12 @@
 		rpc_remove_list(&serv->sv_sockets, svsk);
 	svsk->sk_dead = 1;
 
-	if (!svsk->sk_inuse) {
+	if (!atomic_read(&svsk->sk_inuse)) {
 		sock_release(svsk->sk_sock);
 		kfree(svsk);
 	} else {
-		printk(KERN_NOTICE "svc: server socket destroy delayed\n");
+		printk(KERN_NOTICE "svc: server socket destroy delayed (sk_inuse: %d)\n",
+		       atomic_read(&svsk->sk_inuse));
 		/* svsk->sk_server = NULL; */
 	}
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/sunrpc/xprt.c linux.14p6/net/sunrpc/xprt.c
--- linux.vanilla/net/sunrpc/xprt.c	Wed Oct 20 01:12:52 1999
+++ linux.14p6/net/sunrpc/xprt.c	Tue Nov 16 15:15:35 1999
@@ -743,11 +743,29 @@
 	return result;
 }
 
+static __inline__ void tcp_output_record(struct rpc_xprt *xprt)
+{
+	if(xprt->snd_sent && xprt->snd_task)
+		dprintk("RPC: write space\n");
+	if(xprt->write_space == 0)
+	{
+		xprt->write_space = 1;
+		if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task))
+		{
+			if(xprt->snd_sent)
+				dprintk("RPC: Write wakeup snd_sent =%d\n",
+					xprt->snd_sent);
+			rpc_wake_up_task(xprt->snd_task);			
+		}
+	}
+}
+
 /*
  *	TCP task queue stuff
  */
  
-static struct rpc_xprt *rpc_xprt_pending = NULL;	/* Chain by rx_pending of rpc_xprt's */
+static struct rpc_xprt *rpc_rx_xprt_pending = NULL;	/* Chain by rx_pending of rpc_xprt's */
+static struct rpc_xprt *rpc_tx_xprt_pending = NULL;	/* Chain by tx_pending of rpc_xprt's */
 
 /*
  *	This is protected from tcp_data_ready and the stack as its run
@@ -765,11 +783,11 @@
 	 *	Empty each pending socket
 	 */
 	 
-	while((xprt=rpc_xprt_pending)!=NULL)
+	while((xprt=rpc_rx_xprt_pending)!=NULL)
 	{
 		int safe_retry=0;
 		
-		rpc_xprt_pending=xprt->rx_pending;
+		rpc_rx_xprt_pending=xprt->rx_pending;
 		xprt->rx_pending_flag=0;
 		
 		dprintk("rpciod_tcp_dispatcher: Processing %p\n", xprt);
@@ -794,6 +812,13 @@
 					result);
 		}
 	}
+
+	while((xprt=rpc_tx_xprt_pending)!=NULL)
+	{
+		rpc_tx_xprt_pending = xprt->tx_pending;
+		xprt->tx_pending_flag = 0;
+		tcp_output_record(xprt);
+	}
 }
 
 
@@ -831,12 +856,12 @@
 	{
 		int start_queue=0;
 
-		dprintk("RPC:     xprt queue %p\n", rpc_xprt_pending);
-		if(rpc_xprt_pending==NULL)
+		dprintk("RPC:     xprt queue %p\n", rpc_rx_xprt_pending);
+		if(rpc_rx_xprt_pending==NULL)
 			start_queue=1;
 		xprt->rx_pending_flag=1;
-		xprt->rx_pending=rpc_xprt_pending;
-		rpc_xprt_pending=xprt;
+		xprt->rx_pending=rpc_rx_xprt_pending;
+		rpc_rx_xprt_pending=xprt;
 		if (start_queue)
 		  {
 		    tcp_rpciod_queue();
@@ -878,18 +903,16 @@
 
 	if (!(xprt = xprt_from_sock(sk)))
 		return;
-	if(xprt->snd_sent && xprt->snd_task)
-		dprintk("RPC: write space\n");
-	if(xprt->write_space == 0)
-	{
-		xprt->write_space = 1;
-		if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task))
-		{
-			if(xprt->snd_sent)
-				dprintk("RPC: Write wakeup snd_sent =%d\n",
-					xprt->snd_sent);
-			rpc_wake_up_task(xprt->snd_task);			
-		}
+	if (!xprt->tx_pending_flag) {
+		int start_queue = 0;
+
+		if (rpc_tx_xprt_pending == NULL)
+			start_queue = 1;
+		xprt->tx_pending_flag = 1;
+		xprt->tx_pending = rpc_tx_xprt_pending;
+		rpc_tx_xprt_pending = xprt;
+		if (start_queue)
+			tcp_rpciod_queue();
 	}
 }
 
