diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/CREDITS linux.14p3/CREDITS
--- linux.vanilla/CREDITS	Wed Oct 20 01:12:32 1999
+++ linux.14p3/CREDITS	Fri Oct 29 23:39:48 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
 
@@ -1713,8 +1713,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 +2122,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.14p3/Documentation/Changes
--- linux.vanilla/Documentation/Changes	Sat Aug 14 02:27:12 1999
+++ linux.14p3/Documentation/Changes	Fri Oct 22 22:44:33 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,19 @@
 - 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
 
 Upgrade notes
 *************
@@ -226,7 +226,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.
@@ -284,8 +284,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
 ===================
@@ -519,23 +519,15 @@
 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 +539,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 +586,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 +613,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 +633,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 +648,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 +695,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 +708,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 +727,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 +742,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,9 +780,9 @@
 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
 =====
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/Configure.help linux.14p3/Documentation/Configure.help
--- linux.vanilla/Documentation/Configure.help	Wed Oct 20 01:12:32 1999
+++ linux.14p3/Documentation/Configure.help	Fri Oct 29 23:39:48 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
@@ -8884,9 +8867,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 +8888,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 +9409,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 +9688,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
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/kernel-docs.txt linux.14p3/Documentation/kernel-docs.txt
--- linux.vanilla/Documentation/kernel-docs.txt	Sat Aug 14 02:26:08 1999
+++ linux.14p3/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.14p3/Documentation/kernel-parameters.txt
--- linux.vanilla/Documentation/kernel-parameters.txt	Sat Aug 14 02:26:45 1999
+++ linux.14p3/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/proc.txt linux.14p3/Documentation/proc.txt
--- linux.vanilla/Documentation/proc.txt	Sat Feb  6 20:46:20 1999
+++ linux.14p3/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/MAINTAINERS linux.14p3/MAINTAINERS
--- linux.vanilla/MAINTAINERS	Wed Oct 20 01:12:33 1999
+++ linux.14p3/MAINTAINERS	Fri Oct 29 23:39:48 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
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Makefile linux.14p3/Makefile
--- linux.vanilla/Makefile	Wed Oct 20 01:12:33 1999
+++ linux.14p3/Makefile	Fri Oct 29 23:39:02 1999
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
-SUBLEVEL = 13
-EXTRAVERSION =
+SUBLEVEL = 14
+EXTRAVERSION = pre3
 
 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.14p3/arch/alpha/kernel/process.c
--- linux.vanilla/arch/alpha/kernel/process.c	Sat Aug 28 20:00:53 1999
+++ linux.14p3/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.14p3/arch/alpha/kernel/proto.h
--- linux.vanilla/arch/alpha/kernel/proto.h	Sat Aug 14 02:27:12 1999
+++ linux.14p3/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.14p3/arch/alpha/kernel/ptrace.c
--- linux.vanilla/arch/alpha/kernel/ptrace.c	Wed Dec 30 00:17:00 1998
+++ linux.14p3/arch/alpha/kernel/ptrace.c	Sat Oct 23 23:02:34 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, current);
+		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, current);
+		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, current);
+		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, current);
+		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, current);
+		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, current);
+		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, current);
+		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.14p3/arch/alpha/kernel/signal.c
--- linux.vanilla/arch/alpha/kernel/signal.c	Sat Aug 14 02:27:12 1999
+++ linux.14p3/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.14p3/arch/alpha/math-emu/fp-emul.c
--- linux.vanilla/arch/alpha/math-emu/fp-emul.c	Sat Aug 28 20:00:53 1999
+++ linux.14p3/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.14p3/arch/alpha/math-emu/sfp-machine.h
--- linux.vanilla/arch/alpha/math-emu/sfp-machine.h	Sat Aug 28 20:00:53 1999
+++ linux.14p3/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.14p3/arch/alpha/mm/fault.c
--- linux.vanilla/arch/alpha/mm/fault.c	Sat Aug 14 02:27:13 1999
+++ linux.14p3/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.14p3/arch/i386/boot/compressed/Makefile
--- linux.vanilla/arch/i386/boot/compressed/Makefile	Wed Oct 20 01:12:33 1999
+++ linux.14p3/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.14p3/arch/i386/boot/compressed/misc.c
--- linux.vanilla/arch/i386/boot/compressed/misc.c	Wed Jun 24 22:30:08 1998
+++ linux.14p3/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.14p3/arch/i386/kernel/apm.c
--- linux.vanilla/arch/i386/kernel/apm.c	Wed Oct 20 01:12:33 1999
+++ linux.14p3/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.14p3/arch/i386/kernel/bios32.c
--- linux.vanilla/arch/i386/kernel/bios32.c	Sat Aug 14 02:25:55 1999
+++ linux.14p3/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.14p3/arch/i386/kernel/io_apic.c
--- linux.vanilla/arch/i386/kernel/io_apic.c	Wed Oct 20 01:12:33 1999
+++ linux.14p3/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.14p3/arch/i386/kernel/irq.h
--- linux.vanilla/arch/i386/kernel/irq.h	Sat Aug 14 02:27:13 1999
+++ linux.14p3/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.14p3/arch/i386/kernel/ptrace.c
--- linux.vanilla/arch/i386/kernel/ptrace.c	Wed Mar 24 21:18:46 1999
+++ linux.14p3/arch/i386/kernel/ptrace.c	Sat Oct 23 23:02:34 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, current);
+		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, current);
+		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, current);
+		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, current);
+		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, current);
+		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, current);
+		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, current);
+		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.14p3/arch/i386/kernel/setup.c
--- linux.vanilla/arch/i386/kernel/setup.c	Wed Oct 20 01:12:33 1999
+++ linux.14p3/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.14p3/arch/i386/kernel/signal.c
--- linux.vanilla/arch/i386/kernel/signal.c	Sat Aug 14 02:26:46 1999
+++ linux.14p3/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.14p3/arch/i386/kernel/smp.c
--- linux.vanilla/arch/i386/kernel/smp.c	Wed Oct 20 01:12:33 1999
+++ linux.14p3/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/mm/fault.c linux.14p3/arch/i386/mm/fault.c
--- linux.vanilla/arch/i386/mm/fault.c	Sun Nov 22 17:38:19 1998
+++ linux.14p3/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/common_defconfig linux.14p3/arch/ppc/common_defconfig
--- linux.vanilla/arch/ppc/common_defconfig	Sat Aug 14 02:27:15 1999
+++ linux.14p3/arch/ppc/common_defconfig	Fri Oct 22 22:26:41 1999
@@ -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
@@ -226,10 +228,19 @@
 # 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
@@ -238,11 +249,12 @@
 # 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
@@ -261,7 +273,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 +287,22 @@
 #
 # 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_DLCI is not set
 
 #
 # Amateur Radio support
@@ -311,7 +337,6 @@
 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
@@ -407,6 +432,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 +484,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
 
@@ -468,6 +495,7 @@
 CONFIG_DMASOUND=y
 # CONFIG_SOUND_ES1370 is not set
 # CONFIG_SOUND_ES1371 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
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/defconfig linux.14p3/arch/ppc/defconfig
--- linux.vanilla/arch/ppc/defconfig	Wed Oct 20 01:12:33 1999
+++ linux.14p3/arch/ppc/defconfig	Fri Oct 22 22:26:41 1999
@@ -9,14 +9,13 @@
 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_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
@@ -200,14 +191,14 @@
 # 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_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 +206,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
@@ -262,7 +253,7 @@
 # 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
@@ -289,7 +280,6 @@
 # CONFIG_LTPC is not set
 # CONFIG_COPS is not set
 # CONFIG_IPDDP is not set
-# CONFIG_PLIP is not set
 CONFIG_PPP=y
 
 #
@@ -338,12 +328,15 @@
 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_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
@@ -365,13 +358,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 +394,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 +416,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 +485,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
 
 #
@@ -489,7 +499,34 @@
 # 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_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_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
+
+#
+# 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/kernel/chrp_pci.c linux.14p3/arch/ppc/kernel/chrp_pci.c
--- linux.vanilla/arch/ppc/kernel/chrp_pci.c	Wed Oct 20 01:12:33 1999
+++ linux.14p3/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/head.S linux.14p3/arch/ppc/kernel/head.S
--- linux.vanilla/arch/ppc/kernel/head.S	Sat Aug 28 20:00:53 1999
+++ linux.14p3/arch/ppc/kernel/head.S	Fri Oct 22 22:26:41 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)
@@ -112,6 +112,10 @@
 	
 /* 601 only have IBAT cr0.eq is set on 601 when using this macro */ 
 #define LOAD_BAT(n, offset, reg, RA, RB) \
+	/* see the comment for clear_bats() -- Cort */ \
+	li	RA,0;			\
+	mtspr	IBAT##n##U,RA;		\
+	mtspr	DBAT##n##U,RA;		\
 	lwz	RA,offset+0(reg);	\
 	lwz	RB,offset+4(reg);	\
 	mtspr	IBAT##n##U,RA;		\
@@ -285,6 +289,14 @@
 	clrldi	r16,r16,63
 	mtsdr1	r16
 #else /* CONFIG_PPC64 */
+	/*
+	 * If the MMU is off clear the bats.  See clear_bat() -- Cort
+	 */
+	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
@@ -1312,7 +1324,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 */
@@ -2733,3 +2744,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/setup.c linux.14p3/arch/ppc/kernel/setup.c
--- linux.vanilla/arch/ppc/kernel/setup.c	Wed Oct 20 01:12:33 1999
+++ linux.14p3/arch/ppc/kernel/setup.c	Fri Oct 22 22:26:41 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.
  */
 
@@ -497,6 +497,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 +544,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 +554,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 +572,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/mm/init.c linux.14p3/arch/ppc/mm/init.c
--- linux.vanilla/arch/ppc/mm/init.c	Wed Oct 20 01:12:33 1999
+++ linux.14p3/arch/ppc/mm/init.c	Fri Oct 22 22:26:41 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)
@@ -149,6 +149,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! */
 
@@ -1296,7 +1299,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 +1332,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 +1350,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;
 	}
 
 	/*
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/xmon/xmon.c linux.14p3/arch/ppc/xmon/xmon.c
--- linux.vanilla/arch/ppc/xmon/xmon.c	Sat Aug 28 20:00:54 1999
+++ linux.14p3/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/drivers/block/Config.in linux.14p3/drivers/block/Config.in
--- linux.vanilla/drivers/block/Config.in	Sat Aug 14 02:27:16 1999
+++ linux.14p3/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.14p3/drivers/block/genhd.c
--- linux.vanilla/drivers/block/genhd.c	Wed Oct 20 01:12:34 1999
+++ linux.14p3/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.14p3/drivers/block/ide-cd.c
--- linux.vanilla/drivers/block/ide-cd.c	Wed Oct 20 01:12:34 1999
+++ linux.14p3/drivers/block/ide-cd.c	Wed Oct 27 18:59:00 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.14p3/drivers/block/ide-disk.c
--- linux.vanilla/drivers/block/ide-disk.c	Thu Mar 11 01:49:43 1999
+++ linux.14p3/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.14p3/drivers/block/ide-dma.c
--- linux.vanilla/drivers/block/ide-dma.c	Sat Aug 14 02:26:27 1999
+++ linux.14p3/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.14p3/drivers/block/ide-floppy.c
--- linux.vanilla/drivers/block/ide-floppy.c	Sat Aug 14 02:27:16 1999
+++ linux.14p3/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.14p3/drivers/block/ide-probe.c
--- linux.vanilla/drivers/block/ide-probe.c	Mon Mar 22 20:44:18 1999
+++ linux.14p3/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.14p3/drivers/block/ide-proc.c
--- linux.vanilla/drivers/block/ide-proc.c	Wed May  6 22:42:53 1998
+++ linux.14p3/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.14p3/drivers/block/ide-tape.c
--- linux.vanilla/drivers/block/ide-tape.c	Sat Aug 14 02:27:16 1999
+++ linux.14p3/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.14p3/drivers/block/ide.c
--- linux.vanilla/drivers/block/ide.c	Wed Oct 20 01:12:34 1999
+++ linux.14p3/drivers/block/ide.c	Wed Oct 27 18:56:41 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,34 @@
 				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.
+				 *
+				 * Mmmm.. note we also do hwgroup->busy=0 below, which means
+				 * we will also be woken up if somebody enqueues another
+				 * request against this hwgroup while we're snoozing.
+				 * We could "fix" that by setting hwgroup->busy=1 instead,
+				 * but that would make the error handling more complicated
+				 * in ide_timer_expiry() -- this is good enough for now.
+				 */
 				if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) 
 					sleep = jiffies + WAIT_MIN_SLEEP;
-#if 1
-				if (hwgroup->timer.next || hwgroup->timer.prev)
-					printk("ide_set_handler: timer already active\n");
-#endif
+				hwgroup->sleeping = 1;	/* so that ide_timer_expiry knows what to do */
 				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 +1207,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 +1227,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 +1392,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 +1419,63 @@
 			 */
 			(void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]);
 			unexpected_intr(irq, hwgroup);
+		} 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);
+		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 +1558,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 +1573,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 +1602,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 +1995,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 +2008,7 @@
 				val = *((u32 *) setting->data);
 				break;
 		}
-		spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 	}
 	return val;
 }
@@ -1985,19 +2018,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 +2072,7 @@
 				*p = val;
 			break;
 	}
-	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 	return 0;
 }
 
@@ -2844,9 +2886,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 +2927,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.14p3/drivers/block/ide.h
--- linux.vanilla/drivers/block/ide.h	Sun Mar 28 19:03:35 1999
+++ linux.14p3/drivers/block/ide.h	Sat Oct 30 00:02:20 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/pdc4030.c linux.14p3/drivers/block/pdc4030.c
--- linux.vanilla/drivers/block/pdc4030.c	Tue Dec 29 19:24:57 1998
+++ linux.14p3/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/Makefile linux.14p3/drivers/char/Makefile
--- linux.vanilla/drivers/char/Makefile	Wed Oct 20 01:12:34 1999
+++ linux.14p3/drivers/char/Makefile	Wed Oct 27 18:50:24 1999
@@ -380,9 +380,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.14p3/drivers/char/bttv.c
--- linux.vanilla/drivers/char/bttv.c	Wed Oct 20 01:12:35 1999
+++ linux.14p3/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/buz.c linux.14p3/drivers/char/buz.c
--- linux.vanilla/drivers/char/buz.c	Wed Oct 20 01:12:35 1999
+++ linux.14p3/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/hfmodem/refclock.c linux.14p3/drivers/char/hfmodem/refclock.c
--- linux.vanilla/drivers/char/hfmodem/refclock.c	Sat Jul 18 19:47:49 1998
+++ linux.14p3/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/radio-aimslab.c linux.14p3/drivers/char/radio-aimslab.c
--- linux.vanilla/drivers/char/radio-aimslab.c	Sat Aug 14 02:25:39 1999
+++ linux.14p3/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.14p3/drivers/char/radio-cadet.c
--- linux.vanilla/drivers/char/radio-cadet.c	Sat Aug 14 02:27:17 1999
+++ linux.14p3/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.14p3/drivers/char/radio-rtrack2.c
--- linux.vanilla/drivers/char/radio-rtrack2.c	Sat Aug 14 02:25:40 1999
+++ linux.14p3/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.14p3/drivers/fc4/Makefile
--- linux.vanilla/drivers/fc4/Makefile	Tue Mar 16 00:11:29 1999
+++ linux.14p3/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/avmb1/capi.c linux.14p3/drivers/isdn/avmb1/capi.c
--- linux.vanilla/drivers/isdn/avmb1/capi.c	Wed Oct 20 01:12:36 1999
+++ linux.14p3/drivers/isdn/avmb1/capi.c	Fri Oct 22 22:18:35 1999
@@ -109,6 +109,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_ppp.c linux.14p3/drivers/isdn/isdn_ppp.c
--- linux.vanilla/drivers/isdn/isdn_ppp.c	Wed Oct 20 01:12:38 1999
+++ linux.14p3/drivers/isdn/isdn_ppp.c	Fri Oct 29 23:19:29 1999
@@ -1540,7 +1540,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;
@@ -2667,9 +2673,10 @@
 	}
 
 	/* Allow for at least 150 % expansion (for now) */
-	skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32);
+	skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32 + skb_headroom(skb_in));
 	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) {
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/macintosh/macserial.c linux.14p3/drivers/macintosh/macserial.c
--- linux.vanilla/drivers/macintosh/macserial.c	Wed Oct 20 01:12:38 1999
+++ linux.14p3/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.14p3/drivers/macintosh/macserial.h
--- linux.vanilla/drivers/macintosh/macserial.h	Wed Oct 20 01:12:38 1999
+++ linux.14p3/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.14p3/drivers/macintosh/mediabay.c
--- linux.vanilla/drivers/macintosh/mediabay.c	Wed Oct 20 01:12:38 1999
+++ linux.14p3/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.14p3/drivers/misc/parport_pc.c
--- linux.vanilla/drivers/misc/parport_pc.c	Sat Aug 14 02:27:27 1999
+++ linux.14p3/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.14p3/drivers/misc/parport_share.c
--- linux.vanilla/drivers/misc/parport_share.c	Sat Aug 14 02:26:48 1999
+++ linux.14p3/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.14p3/drivers/net/Config.in
--- linux.vanilla/drivers/net/Config.in	Wed Oct 20 01:12:38 1999
+++ linux.14p3/drivers/net/Config.in	Fri Oct 22 22:16:43 1999
@@ -165,7 +165,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
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/arlan-proc.c linux.14p3/drivers/net/arlan-proc.c
--- linux.vanilla/drivers/net/arlan-proc.c	Sat Aug 14 02:27:28 1999
+++ linux.14p3/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/hamradio/Config.in linux.14p3/drivers/net/hamradio/Config.in
--- linux.vanilla/drivers/net/hamradio/Config.in	Sat Aug 14 02:27:29 1999
+++ linux.14p3/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/pcnet32.c linux.14p3/drivers/net/pcnet32.c
--- linux.vanilla/drivers/net/pcnet32.c	Wed Oct 20 01:12:39 1999
+++ linux.14p3/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/sis900.c linux.14p3/drivers/net/sis900.c
--- linux.vanilla/drivers/net/sis900.c	Wed Oct 20 01:12:39 1999
+++ linux.14p3/drivers/net/sis900.c	Fri Oct 29 23:30:25 1999
@@ -1,58 +1,28 @@
-/*****************************************************************************/
-/*      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.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,338 +35,94 @@
 #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.05.05  10/29/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 struct mii_chip_info {
+	const char * name;
+	u16 phy_id0;
+	u16 phy_id1;
+} mii_chip_table[] = {
+	{"SiS 900 Internal MII PHY", 0x001d, 0x8000},
+	{"SiS 7014 Physical Layer Solution", 0x0016, 0xf830},
+	{"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9},
+	{"AMD 79C901 HomePNA PHY",  0x0000, 0x35c8},
+	{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.             */
+	struct pci_dev * pci_dev;
+
+	struct mac_chip_info * mac;
+	struct mii_phy * mii;
+
+        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 MediaSpeed;                         	/* user force speed         */
+	int MediaDuplex;                        	/* user force duplex        */
+        int full_duplex;                        	/* Full/Half-duplex.        */
+        int speeds;                             	/* 100/10 Mbps.             */
         u16 LinkOn;
         u16 LinkChange;
 };
@@ -414,14 +140,18 @@
 #endif
 
 static int sis900_open(struct device *dev);
+static int sis900_mii_probe (struct device * dev);
+static void sis900_init_rxfilter (struct device * dev);
 static u16 read_eeprom(long ioaddr, int location);
-static int mdio_read(struct device *dev, int phy_id, int location);
+static u16 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 void sis900_timer(unsigned long data);
 static void sis900_tx_timeout(struct device *dev);
-static void sis900_init_ring(struct device *dev);
+static void sis900_init_tx_ring(struct device *dev);
+static void sis900_init_rx_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_finish_xmit (struct device *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);
@@ -431,314 +161,205 @@
 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 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);
 
 /* 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;
-
-        tp->next_module = root_sis900_dev;
-        root_sis900_dev = dev;
-
-        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;
-                        }
-                }
+	/* 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;
+	}
 
-                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 = net_dev->priv;
+	memset(sis_priv, 0, sizeof(struct sis900_private));
 
-        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);
-			}
-		}
+	/* 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 (tp->pmd_status & MIISTAT_LINK) 
-               	        tp->LinkOn = TRUE;
-	        else
-                        tp->LinkOn = FALSE;
+	sis_priv->next_module = root_sis900_dev;
+	root_sis900_dev = net_dev;
 
-		tp->LinkChange = FALSE;
+	/* 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;
+
+	return net_dev;
+}
+
+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, 
+				   quit searching (table) */
+				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. */
-
-/* Delay between EEPROM clock transitions.
-   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
- */
+	/* FIXME: AMD stuff should be added */
+	/* auto negotiate FIXME: not completed */
+	elSetCapability(net_dev, sis_priv->mii->phy_addr, 1, 100);
+	sis_priv->mii->status = elAutoNegotiate(net_dev, 
+						sis_priv->mii->phy_addr,
+						&sis_priv->full_duplex, 
+						&sis_priv->speeds);
+	
+	if (sis_priv->mii->status & MIISTAT_LINK) 
+		sis_priv->LinkOn = TRUE;
+	else
+		sis_priv->LinkOn = FALSE;
+	
+	sis_priv->LinkChange = FALSE;
+	
+	return 1;
+}
 
+/* Delay between EEPROM clock transitions. */
 #define eeprom_delay()  inl(ee_addr)
 
-/* 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
-
+/* 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 +369,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 +380,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 +389,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 +424,11 @@
         return;
 }
 
-static int mdio_read(struct device *dev, int phy_id, int location)
+static u16 mdio_read(struct device *dev, int phy_id, int location)
 {
         long mdio_addr = 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 +437,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();
@@ -858,7 +463,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 +471,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);
@@ -879,110 +495,30 @@
 static int
 sis900_open(struct device *dev)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        struct sis900_private *sis_priv = (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);
-
         /* Soft reset the chip. */
-        outl(0, ioaddr + imr);
-        outl(0, ioaddr + ier);
-        outl(0, ioaddr + rfcr);
-        outl(RESET | RxRESET | TxRESET, ioaddr + cr);
+	sis900_reset(dev);
 
-        if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev))
-        {
+        if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, 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;
-        }
+	/* FIXME: should this be move to set_rx_mode() ? */
+	sis900_init_rxfilter(dev);
 
-        {
-                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_tx_ring(dev);
+	sis900_init_rx_ring(dev);
 
-        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);
-                }
-        }
-
-        outl(PESEL, ioaddr + cfg);
+	/* FIXME: should be removed, and replaced by AutoNeogotiate stuff */
+        outl((RX_DMA_BURST << RxMXDMA_shift) | (RxDRNT_10 << RxDRNT_shift), 
+	     ioaddr + rxcfg);
+        outl(TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift) | TxDRNT_10,
+	     ioaddr + txcfg);
 
-        /* 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);
 
         dev->tbusy = 0;
@@ -990,186 +526,180 @@
         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);
-
         /* 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)dev;
+        sis_priv->timer.function = &sis900_timer;
+        add_timer(&sis_priv->timer);
 
         return 0;
 }
 
+/* set receive filter address to our MAC address */
+static void
+sis900_init_rxfilter (struct device * net_dev)
+{
+	long ioaddr = net_dev->base_addr;
+	u32 rfcrSave;
+	u32 i;
+	
+	rfcrSave = inl(rfcr + ioaddr);
+
+	/* 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));
+		}
+	}
+
+	/* enable packet filitering */
+	outl(rfcrSave | RFEN, rfcr + ioaddr);
+}
+
+/* Initialize the Tx ring. */
+static void
+sis900_init_tx_ring(struct device *dev)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = dev->base_addr; 
+        int i;
+	
+        tp->tx_full = 0;
+        tp->dirty_tx = tp->cur_tx = 0;
+	
+        for (i = 0; i < NUM_TX_DESC; i++) {
+		tp->tx_skbuff[i] = NULL;
+
+		tp->tx_ring[i].link = (u32) virt_to_bus(&tp->tx_ring[i+1]);
+                tp->tx_ring[i].cmdsts = 0;
+		tp->tx_ring[i].bufptr = 0;
+        }
+	tp->tx_ring[i-1].link = (u32) virt_to_bus(&tp->tx_ring[0]);
+
+	/* load Transmit Descriptor Register */
+        outl(virt_to_bus(&tp->tx_ring[0]), ioaddr + txdp); 
+	if (sis900_debug > 2)
+                printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", 
+		       dev->name, inl(ioaddr + txdp));
+}
+
+/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */ 
+static void 
+sis900_init_rx_ring(struct device *dev) 
+{ 
+        struct sis900_private *tp = (struct sis900_private *)dev->priv; 
+        long ioaddr = dev->base_addr; 
+        int i;
+ 
+        tp->cur_rx = 0; 
+	tp->dirty_rx = 0;
+
+	/* init RX descriptor */
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		tp->rx_skbuff[i] = NULL;
+
+		tp->rx_ring[i].link = (u32) virt_to_bus(&tp->rx_ring[i+1]);
+		tp->rx_ring[i].cmdsts = 0;
+		tp->rx_ring[i].bufptr = 0;
+	}
+	tp->rx_ring[i-1].link = (u32) virt_to_bus(&tp->rx_ring[0]);
+
+        /* 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 = dev;
+		tp->rx_skbuff[i] = skb;
+		tp->rx_ring[i].cmdsts = RX_BUF_SIZE;
+		tp->rx_ring[i].bufptr = virt_to_bus(skb->tail);
+	}
+	tp->dirty_rx = (unsigned int) (i - NUM_RX_DESC);
+
+	/* load Receive Descriptor Register */
+        outl(virt_to_bus(&tp->rx_ring[0]), ioaddr + rxdp);
+	if (sis900_debug > 2)
+                printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", 
+		       dev->name, inl(ioaddr + rxdp));
+}
+
 static void sis900_timer(unsigned long data)
 {
         struct device *dev = (struct device *)data;
         struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        int next_tick = 0;
+        int next_tick = 2*HZ;
         u16 status;
 
+	/* FIXME: Should we check transmission time out here ? */
+	/* FIXME: call auto negotiate routine to detect link status */
         if (!tp->LinkOn) {
-                status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS);
+                status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS);
 		if (status & MIISTAT_LINK) {
-                	elPMDreadMode(dev, tp->phys[tp->phy_idx],
-                                        &tp->speeds, &tp->full_duplex);
+                	elPMDreadMode(dev, tp->mii->phy_addr,
+				      &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");
+                        printk(KERN_INFO "%s: Media Link On %s%s-duplex \n", 
+			       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);
+                status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS);
 		if (!(status & MIISTAT_LINK)) {
 			tp->LinkOn = FALSE;
                         printk(KERN_INFO "%s: Media Link Off\n", dev->name);
 		}
         }
-        next_tick = 2*HZ;
 
-        if (next_tick) {
-                tp->timer.expires = RUN_AT(next_tick);
-                add_timer(&tp->timer);
-        }
+	tp->timer.expires = jiffies + next_tick;
+	add_timer(&tp->timer);
 }
 
 static void sis900_tx_timeout(struct device *dev)
 {
         struct sis900_private *tp = (struct sis900_private *)dev->priv;
         long ioaddr = dev->base_addr;
-        int i;
 
-        if (sis900_debug > 0)
+        if (sis900_debug > 2)
                 printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n",
-                           dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
-
+		       dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
+	
         /* Disable interrupts by clearing the interrupt mask. */
         outl(0x0000, ioaddr + imr);
 
-        /* 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)" : "");
-	}
-
-        /* 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;
-        */
-
-        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;
-                }
-        }
-
+        tp->cur_rx = 0;
         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;
-}
-
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-sis900_init_ring(struct device *dev)
-{
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        int i;
 
-        tp->tx_full = 0;
-        tp->cur_rx = 0;
-        tp->dirty_tx = tp->cur_tx = 0;
+	/* FIXME: Should we restart the transmission thread here  ?? */
 
-        /* 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 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;
-        }
-
-        /* 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]);
-        }
-
-        /* 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;
-        }
+        /* Enable all known interrupts by setting the interrupt mask. */
+        outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
+        return;
 }
 
 static int
@@ -1177,54 +707,39 @@
 {
         struct sis900_private *tp = (struct sis900_private *)dev->priv;
         long ioaddr = dev->base_addr;
-        int entry;
+        unsigned 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. */
+	/* test tbusy to see if we have timeout situation then set it */
         if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
-                if (jiffies - dev->trans_start < TX_TIMEOUT)
-                        return 1;
-                sis900_tx_timeout(dev);
+                if (jiffies - dev->trans_start > TX_TIMEOUT)
+			sis900_tx_timeout(dev);
                 return 1;
         }
 
-        /* Calculate the next Tx descriptor entry. ????? */
+        /* Calculate the next Tx descriptor entry. */
         entry = tp->cur_tx % NUM_TX_DESC;
-
         tp->tx_skbuff[entry] = skb;
 
-        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");
-        }
-
-        memcpy(tp->tx_buf[entry].buf,
-                                skb->data, skb->len);
-
-        tp->tx_buf[entry].cmdsts=(OWN | skb->len);
-
-        //tp->tx_buf[entry].plink = 0;
+	/* set the transmit buffer descriptor and enable Transmit State Machine */
+	tp->tx_ring[entry].bufptr = virt_to_bus(skb->data);
+        tp->tx_ring[entry].cmdsts = (OWN | skb->len);
         outl(TxENA, ioaddr + cr);
-        if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
+
+        if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {
+		/* Typical path, clear tbusy to indicate more 
+		   transmission is possible */
                 clear_bit(0, (void*)&dev->tbusy);
         } else {
+		/* no more transmit descriptor avaiable, tbusy remain set */
                 tp->tx_full = 1;
         }
 
-        /* Note: the chip doesn't have auto-pad! */
-
         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",
+		       dev->name, skb->data, (int)skb->len, entry);
 
         return 0;
 }
@@ -1234,23 +749,22 @@
 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;
         int boguscnt = max_interrupt_work;
-        int status;
-        long ioaddr = dev->base_addr;
+	long ioaddr = 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)) {
                 printk(KERN_INFO "%s: SMP simultaneous entry of "
-                                "an interrupt handler.\n", dev->name);
+		       "an interrupt handler.\n", dev->name);
                 dev->interrupt = 0;     /* Avoid halting machine. */
                 return;
         }
 #else
         if (dev->interrupt) {
-                printk(KERN_INFO "%s: Re-entering the "
-                                "interrupt handler.\n", dev->name);
+                printk(KERN_INFO "%s: Re-entering the interrupt handler.\n", 
+		       dev->name);
                 return;
         }
         dev->interrupt = 1;
@@ -1258,159 +772,45 @@
 
         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",
+			       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 */
+		/* why dow't we break after Tx/Rx case ?? keyword: full-duplex */
+                if (status & (RxORN | RxERR | RxOK))
+			/* 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++;
-                        }
-                }
+                if (status & (TxURN | TxERR | TxOK))
+			/* Tx interrupt */
+			sis900_finish_xmit(dev);
+
+                /* something strange happened !!! */
+                if (status & HIBERR) {
+			printk(KERN_INFO "%s: Abnormal interrupt,"
+			       "status %#8.8x.\n", 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",
+			       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",
+		       dev->name, inl(ioaddr + isr));
+	
 #if defined(__i386__)
         clear_bit(0, (void*)&dev->interrupt);
 #else
@@ -1419,123 +819,160 @@
         return;
 }
 
-/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
-   field alignments and semantics. */
+/* 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 *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);
+	unsigned int entry = tp->cur_rx % NUM_RX_DESC;
+        u32 rx_status = tp->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",
+		        tp->cur_rx, tp->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);
+                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",
+				       dev->name, rx_status);
                         tp->stats.rx_errors++;
+			if (rx_status & OVERRUN)
+				tp->stats.rx_over_errors++;
+			if (rx_status & (TOOLONG|RUNT))
+                                tp->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++;
+			if (rx_status & CRCERR) 
+				tp->stats.rx_crc_errors++;
+			/* reset buffer descriptor state */
+			tp->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
-                        }
+			struct sk_buff * skb;
+			
+			if (tp->rx_skbuff[entry] == NULL) {
+				printk(KERN_INFO "%s: NULL pointer " 
+				       "encountered in Rx ring, skipping\n",
+				       dev->name);
+				break;			
+			}
+			skb = tp->rx_skbuff[entry];
+			tp->rx_skbuff[entry] = NULL;
+			/* reset buffer descriptor state */
+			tp->rx_ring[entry].cmdsts = 0;
+			tp->rx_ring[entry].bufptr = 0;
+
+			skb_put(skb, rx_size);
                         skb->protocol = eth_type_trans(skb, dev);
                         netif_rx(skb);
-#if LINUX_VERSION_CODE > 0x20119
+
+			if (rx_status & MCAST)
+				tp->stats.multicast++;
                         tp->stats.rx_bytes += rx_size;
-#endif
                         tp->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;
+                tp->cur_rx++;
+		entry = tp->cur_rx % NUM_RX_DESC;
+                rx_status = tp->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 (;tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
+		struct sk_buff *skb;
+
+		entry = tp->dirty_rx % NUM_RX_DESC;
+
+		if (tp->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",
+				       dev->name);
+				tp->stats.rx_dropped++;
+				break;
+			}
+			skb->dev = dev;
+			tp->rx_skbuff[entry] = skb;
+			tp->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+			tp->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 *dev)
+{
+	struct sis900_private *tp = (struct sis900_private *)dev->priv;
+
+	for (; tp->dirty_tx < tp->cur_tx; tp->dirty_tx++) {
+		unsigned int entry;
+		u32 tx_status;
+
+		entry = tp->dirty_tx % NUM_TX_DESC;
+	        tx_status = tp->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",
+				       dev->name, tx_status);
+			tp->stats.tx_errors++;
+			if (tx_status & UNDERRUN)
+				tp->stats.tx_fifo_errors++;
+			if (tx_status & ABORT)
+				tp->stats.tx_aborted_errors++;
+			if (tx_status & NOCARRIER)
+				tp->stats.tx_carrier_errors++;
+			if (tx_status & OWCOLL)
+				tp->stats.tx_window_errors++;
+		} else {
+			/* packet successfully transmited */
+			if (sis900_debug > 3)
+				printk(KERN_INFO "Tx Transmit OK\n");
+			tp->stats.collisions += (tx_status & COLCNT) >> 16;
+			tp->stats.tx_bytes += tx_status & DSIZE;
+			tp->stats.tx_packets++;
+		}
+		/* Free the original skb. */
+		dev_kfree_skb(tp->tx_skbuff[entry]);
+		tp->tx_skbuff[entry] = NULL;
+		tp->tx_ring[entry].bufptr = 0;
+		tp->tx_ring[entry].cmdsts = 0;
+	}
+
+	if (tp->tx_full && dev->tbusy && 
+	    tp->cur_tx - tp->dirty_tx < NUM_TX_DESC - 4) {
+		/* The ring is no longer full, clear tbusy, tx_full and schedule 
+		   more transmission by marking NET_BH */
+		tp->tx_full = 0;
+		clear_bit(0, (void *)&dev->tbusy);
+		mark_bh(NET_BH);
+	}
+}
+
 static int
 sis900_close(struct device *dev)
 {
@@ -1546,27 +983,28 @@
         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));
-
         /* 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);
 
         free_irq(dev->irq, dev);
 
+	/* Free Tx and RX skbuff */
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		if (tp->rx_skbuff[i] != NULL)
+			dev_kfree_skb(tp->rx_skbuff[i]);
+		tp->rx_skbuff[i] = 0;
+	}
         for (i = 0; i < NUM_TX_DESC; i++) {
-                if (tp->tx_skbuff[i])
-                        dev_free_skb(tp->tx_skbuff[i]);
+                if (tp->tx_skbuff[i] != NULL)
+                        dev_kfree_skb(tp->tx_skbuff[i]);
                 tp->tx_skbuff[i] = 0;
         }
-        kfree(tp->rx_bufs);
-        kfree(tp->tx_bufs);
 
         /* Green! Put the chip in low-power mode. */
 
@@ -1581,13 +1019,13 @@
         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] = tp->mii->phy_addr;
                 /* Fall Through */
-        case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
+        case SIOCDEVPRIVATE+1:          	/* Read the specified MII register. */
                 data[3] = mdio_read(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]);
@@ -1663,7 +1101,7 @@
         *duplex = FDX_CAPABLE_HALF_SELECTED;
 
         status = mdio_read(dev, phy_id, MII_ANLPAR);
-        OurCap = mdio_read(dev, phy_id, MII_ANAR);
+        OurCap = mdio_read(dev, phy_id, MII_ANADV);
 	if (sis900_debug > 1) {
 		printk(KERN_INFO "Link Part Status %4X\n", status);
 		printk(KERN_INFO "Our Status %4X\n", OurCap);
@@ -1677,8 +1115,8 @@
 		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) ;
+                while (( status = mdio_read(dev, phy_id, MII_STSOUT)) & 0x4000) ;
+                while (( status = mdio_read(dev, phy_id, MII_STSOUT)) & 0x0020) ;
                 if (status & 0x80)
                         *speed = HW_SPEED_100_MBPS;
                 if (status & 0x40)
@@ -1727,17 +1165,17 @@
         retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO,
 				FALSE,&status);
 	if (!retnVal) {
-		printk(KERN_INFO "Not wait for Reset Complete\n");
+		printk(KERN_INFO "%s: Not wait for Reset Complete\n", dev->name);
 	}
         retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE,
 				TRUE, &status);
 	if (!retnVal) {
-		printk(KERN_INFO "Not wait for AutoNego Complete\n");
+		printk(KERN_INFO "%s: Not wait for AutoNego Complete\n", dev->name);
 	}
         retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK,
 				TRUE, &status);
 	if (!retnVal) {
-		printk(KERN_INFO "Not wait for Link Complete\n");
+		printk(KERN_INFO "%s: Not wait for Link Complete\n", dev->name);
 	}
         if (status & MIISTAT_LINK) {
                 elPMDreadMode(dev, phy_id, speed, duplex);
@@ -1766,7 +1204,7 @@
 		}
 	}
 
-        mdio_write(dev, phy_id, MII_ANAR, cap);
+        mdio_write(dev, phy_id, MII_ANADV, cap);
 }
 
 static void elSetMediaType(struct device *dev, int speed, int duplex)
@@ -1814,9 +1252,8 @@
                 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;
+				   ||  (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;
         } else {
@@ -1835,9 +1272,7 @@
                 outl((u32)(0x00000004+i) << 16, 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);
 
@@ -1866,8 +1301,8 @@
         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));
+                printk(KERN_INFO "Receive Filter Register:%8.8x\n", 
+		       inl(ioaddr + rfcr));
         }
         return;
 }
@@ -1875,49 +1310,46 @@
 static void sis900_reset(struct device *dev)
 {
         long ioaddr = 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 *tp =
+			(struct sis900_private *)root_sis900_dev->priv;
+		struct device *next_dev = tp->next_module;
+		
+		unregister_netdev(root_sis900_dev);
+		release_region(root_sis900_dev->base_addr,
+			       tp->mac->io_size);
+		kfree(tp);
+		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.14p3/drivers/net/sis900.h
--- linux.vanilla/drivers/net/sis900.h	Thu Jan  1 01:00:00 1970
+++ linux.14p3/drivers/net/sis900.h	Fri Oct 29 23:30:41 1999
@@ -0,0 +1,243 @@
+/* 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
+#define TxFILLT_shift   	8
+#define TxDRNT_shift    	0
+#define TxDRNT_100      (1536>>5)
+#define TxDRNT_10		16 
+
+enum sis900_transmit_config_register_bits {
+	TxCSI = 0x80000000, TxHBI = 0x40000000, TxMLB = 0x20000000,
+	TxATP = 0x10000000, TxIFG = 0x0C000000, TxFILLT = 0x00003F00,
+	TxDRNT = 0x0000003F
+};
+
+/* recevie FFIFO thresholds */
+#define RxDRNT_shift     1
+#define RxDRNT_100	8
+#define RxDRNT_10		8 
+
+enum sis900_reveive_config_register_bits {
+	RxAEP  = 0x80000000, RxARP = 0x40000000, RxATP   = 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_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012,
+	MII_MASK    = 0x0013
+};
+
+/* 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 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/tulip.c linux.14p3/drivers/net/tulip.c
--- linux.vanilla/drivers/net/tulip.c	Sat Aug 14 02:27:30 1999
+++ linux.14p3/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/pci/oldproc.c linux.14p3/drivers/pci/oldproc.c
--- linux.vanilla/drivers/pci/oldproc.c	Wed Oct 20 01:12:39 1999
+++ linux.14p3/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/scsi/fdomain.c linux.14p3/drivers/scsi/fdomain.c
--- linux.vanilla/drivers/scsi/fdomain.c	Tue Dec 29 19:44:53 1998
+++ linux.14p3/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.14p3/drivers/scsi/gdth.h
--- linux.vanilla/drivers/scsi/gdth.h	Sat Aug 14 02:25:41 1999
+++ linux.14p3/drivers/scsi/gdth.h	Sat Oct 30 00:03:21 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.14p3/drivers/scsi/ide-scsi.c
--- linux.vanilla/drivers/scsi/ide-scsi.c	Sat Aug 14 02:27:31 1999
+++ linux.14p3/drivers/scsi/ide-scsi.c	Wed Oct 27 18:56:41 1999
@@ -308,7 +308,7 @@
 /*
  *	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.14p3/drivers/scsi/ips.h
--- linux.vanilla/drivers/scsi/ips.h	Wed Oct 20 01:12:41 1999
+++ linux.14p3/drivers/scsi/ips.h	Sat Oct 30 00:03:21 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.14p3/drivers/scsi/megaraid.h
--- linux.vanilla/drivers/scsi/megaraid.h	Sat Aug 28 20:00:57 1999
+++ linux.14p3/drivers/scsi/megaraid.h	Sat Oct 30 00:03:21 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.14p3/drivers/scsi/psi240i.h
--- linux.vanilla/drivers/scsi/psi240i.h	Mon Dec 28 06:19:22 1998
+++ linux.14p3/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.14p3/drivers/scsi/qlogicfc.c
--- linux.vanilla/drivers/scsi/qlogicfc.c	Wed Oct 20 01:12:41 1999
+++ linux.14p3/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.14p3/drivers/scsi/qlogicisp.c
--- linux.vanilla/drivers/scsi/qlogicisp.c	Wed Oct 20 01:12:42 1999
+++ linux.14p3/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/sym53c416.h linux.14p3/drivers/scsi/sym53c416.h
--- linux.vanilla/drivers/scsi/sym53c416.h	Sun Mar 28 19:04:11 1999
+++ linux.14p3/drivers/scsi/sym53c416.h	Sat Oct 30 00:03:21 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.14p3/drivers/scsi/sym53c8xx_defs.h
--- linux.vanilla/drivers/scsi/sym53c8xx_defs.h	Wed Oct 20 01:12:47 1999
+++ linux.14p3/drivers/scsi/sym53c8xx_defs.h	Sat Oct 30 00:03:21 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.h linux.14p3/drivers/scsi/u14-34f.h
--- linux.vanilla/drivers/scsi/u14-34f.h	Sun Mar 28 19:04:08 1999
+++ linux.14p3/drivers/scsi/u14-34f.h	Sat Oct 30 00:03:21 1999
@@ -18,7 +18,9 @@
 
 #define U14_34F_VERSION "4.33.00"
 
+#ifndef LinuxVersionCode
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#endif
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/Config.in linux.14p3/drivers/sound/Config.in
--- linux.vanilla/drivers/sound/Config.in	Wed Oct 20 01:12:47 1999
+++ linux.14p3/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.14p3/drivers/sound/Makefile
--- linux.vanilla/drivers/sound/Makefile	Wed Oct 20 01:12:47 1999
+++ linux.14p3/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.14p3/drivers/sound/ac97.h
--- linux.vanilla/drivers/sound/ac97.h	Wed Oct 20 01:12:47 1999
+++ linux.14p3/drivers/sound/ac97.h	Sat Oct 30 00:23:12 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.14p3/drivers/sound/adlib_card.c
--- linux.vanilla/drivers/sound/adlib_card.c	Thu May 14 18:33:17 1998
+++ linux.14p3/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.14p3/drivers/sound/es1370.c
--- linux.vanilla/drivers/sound/es1370.c	Sat Aug 14 02:27:34 1999
+++ linux.14p3/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.14p3/drivers/sound/es1371.c
--- linux.vanilla/drivers/sound/es1371.c	Sat Aug 14 02:27:34 1999
+++ linux.14p3/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.14p3/drivers/sound/esssolo1.c
--- linux.vanilla/drivers/sound/esssolo1.c	Wed Oct 20 01:12:48 1999
+++ linux.14p3/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.14p3/drivers/sound/lowlevel/miroaci.h
--- linux.vanilla/drivers/sound/lowlevel/miroaci.h	Sun Aug 23 21:32:25 1998
+++ linux.14p3/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.14p3/drivers/sound/maestro.c
--- linux.vanilla/drivers/sound/maestro.c	Thu Jan  1 01:00:00 1970
+++ linux.14p3/drivers/sound/maestro.c	Thu Oct 28 21:38:46 1999
@@ -0,0 +1,3582 @@
+/*****************************************************************************
+ *
+ *      ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.2.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-7    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 ASP, 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 ASP 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 ASP, 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.
+ *
+ *	For output we maintain a ring buffer of data that we are DMAing
+ *	to the card.  In mono operation this is nice and easy.  When
+ *	we receive data we tack it onto the ring buffer and make sure
+ *	the APU assigned to it is playing over the data.  When we fill
+ *	the ring buffer we put the client to sleep until there is
+ *	room again.  Easy.
+ *
+ *	However, this starts to stink when we use stereo.  The APUs
+ *	supposedly can decode LRLR packed stereo data, but it
+ *	doesn't work.  So we're forced to use dual mono APUs walking over
+ *	mono encoded data.  This requires us to split the input from
+ *	the client and complicates the buffer maths tremendously.  Ick.
+ *
+ *	This also pollutes the recording paths as well.  We have to use
+ *	2 L/R incoming APUs that are fixed at 16bit/48khz.  We then pipe
+ * 	these through 2 rate converion apus that mix them down to the
+ * 	requested frequency and write them to memory through the wavecache.
+ *	We also apparently need a 512byte region thats used as temp space 
+ *	between the incoming APUs and the rate converters.
+ *
+ *	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.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..
+ *	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?
+ *	anyone have a pt101 codec?
+ *	mmap(), but beware stereo encoding nastiness.
+ *	actually post pci writes
+ *	fix bob frequency
+ *	do smart things with ac97 2.0 bits.
+ *	ugh.. non aligned writes in the middle of a data stream.. ugh
+ *	sort out 0x34->0x36 crap in init
+ *	docking and dual codecs and 978?
+ *	pcm_sync?
+ *	actually use LRLR
+ *
+ *	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.10"
+
+#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	3
+#define MAX_DSPS	(1<<3)
+#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"
+};
+
+
+/* --------------------------------------------------------------------- */
+
+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] =     0x3232,
+	[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 */
+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 {
+			right = ((100 - right) * mh->scale) / 100;
+			left = ((100 - left) * mh->scale) / 100;
+			if((left == mh->scale) && (right == mh->scale))
+				val |= 0x8000;
+		}
+
+		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;
+}
+
+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;
+}
+
+/* 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)
+{
+	if(mode&ESS_FMT_16BIT) { 
+		s->apu_mode[apu] = 0x10;
+		s->apu_mode[apu+1] = 0x10;
+	} else {
+		s->apu_mode[apu] = 0x30;
+		s->apu_mode[apu+1] = 0x30;
+	}
+}
+
+/* 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);
+}
+
+static u16 compute_rate(u32 freq)
+{
+	if(freq==48000)
+		return 0xFFFF;
+	freq<<=16;
+	freq/=48000;
+	return freq;
+}
+
+static void set_dac_rate(struct ess_state *s, unsigned rate)
+{
+	u32 freq;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+
+	s->ratedac = rate;
+
+	if(!((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT))
+		rate >>= 1; /* who knows */
+
+/*	M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/
+
+	freq = compute_rate(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;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+
+	s->rateadc = rate;
+
+	freq = compute_rate(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;
+
+	/* we're given the full size of the buffer, but
+	in stereo each channel will only play its half */
+	if(mode&ESS_FMT_STEREO) {
+		size >>=1; 
+		high_apu++;
+	}
+	
+	for(channel=0; channel <= high_apu; channel++)
+	{
+		int i;
+		
+		if(!channel) 
+			pa = virt_to_bus(buffer);
+		else
+		/* right channel plays its split half.
+			*2 accomodates for rampant shifting earlier */
+			pa = virt_to_bus(buffer + size*2);
+
+		/* set the wavecache control reg */
+		tmpval = (pa - 0x10) & 0xFFF8;
+		if(!(mode & 2)) tmpval |= 4; /* 8bit */ 
+		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) ess->dma_dac.base = pa&0xFFFF;
+		
+		pa|=0x00400000;			/* System RAM */
+		
+		/* Begin loading the APU */		
+		for(i=0;i<15;i++)		/* clear all PBRs */
+			apu_set_register(ess, channel, i, 0x0000);
+			
+/* 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_STEREO)
+			/* set panning: left or right */
+			apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0));
+		else
+			apu_set_register(ess, channel, 10, 0x8F08);
+
+		if(mode&ESS_FMT_16BIT)
+			ess->apu_mode[channel]=0x10;
+		else
+			ess->apu_mode[channel]=0x30;
+	}
+	
+	/* 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;
+	/* swptr is always in bytes as read from an apu.. */
+	unsigned bptr = s->dma_dac.swptr;
+	unsigned len = s->dma_dac.fragsize;
+	int i=1;
+
+	if((s->fmt >> ESS_DAC_SHIFT)  & ESS_FMT_STEREO) {
+		i++;
+		bsize >>=1;
+	}
+		
+	for ( ;i; i-- , buf += bsize) {
+
+		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) {
+		/* this is so gross.  */
+		hwptr = (/*s->dma_dac.dmasize -*/ get_dmaa(s)) % s->dma_dac.dmasize; 
+		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;
+}
+
+/* god this is gross..*/
+/* again, the mode passed is shifted/masked */
+static int 
+split_stereo(unsigned char *real_buffer,unsigned char  *tmp_buffer, int offset, 
+	int count, int bufsize, int mode)
+{  
+	/* oh, bother.	stereo decoding APU's don't work in 16bit so we
+	use dual linear decoders.  which means we have to hack up stereo
+	buffer's we're given.  yuck.  */
+
+	unsigned char *so,*left,*right;
+	int i;
+
+	so = tmp_buffer;
+	left = real_buffer + offset;
+	right = real_buffer + bufsize/2 + offset;
+
+	if(mode & ESS_FMT_16BIT) {
+		for(i=count/4; i ; i--) {
+			*(right++) = (*(so+2));
+			*(right++) = (*(so+3));
+			*(left++) = (*so);
+			*(left++) = (*(so+1));
+			so+=4;
+		}
+	} else {
+		for(i=count/2; i ; i--) {
+			*(right++) = (*(so+1));
+			*(left++) = (*so);
+			so+=2;
+		}
+	}
+
+	return 0;
+}
+
+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;
+	unsigned char *splitbuf = NULL;
+	int cnt;
+	int mode = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
+	
+	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;
+	/* XXX be more clever than this.. */
+	if (!(splitbuf = kmalloc(count,GFP_KERNEL)))
+		return -ENOMEM; 
+	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;
+
+		if(mode & ESS_FMT_STEREO) {
+			/* in stereo we have the 'dual' buffers.. */
+			cnt = ((s->dma_dac.dmasize/2)-swptr)*2;
+		} else {
+			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;
+
+		/* our goofball stereo splitter can only deal in mults of 4 */
+		if (cnt > 0) 
+			cnt &= ~3;
+
+		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(mode & ESS_FMT_STEREO) {
+			if (copy_from_user(splitbuf, buffer, cnt)) {
+				if (!ret) ret = -EFAULT;
+				goto return_free;
+			}
+			split_stereo(s->dma_dac.rawbuf,splitbuf,swptr,cnt,s->dma_dac.dmasize,
+				mode);
+		} else {
+			if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+				if (!ret) ret = -EFAULT;
+				goto return_free;
+			}
+		}
+
+		if(mode & ESS_FMT_STEREO) {
+			/* again with the weird pointer magic */
+			swptr = (swptr + (cnt/2)) % (s->dma_dac.dmasize/2);
+		} else {
+			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:
+	if (splitbuf) kfree(splitbuf);
+	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;
+}
+
+/* this needs to be fixed to deal with the dual apus/buffers */
+#if 0
+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 (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(s, 0)) != 0)
+			return ret;
+		db = &s->dma_adc;
+	} else 
+		return -EINVAL;
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		return -EAGAIN;
+	db->mapped = 1;
+	return 0;
+}
+#endif
+
+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_S8|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_S8, 
+			(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,
+	NULL,	/* XXX &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 in 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) {
+		maestro_pt101_init(card,iobase);
+	} 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_apm_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[i]][5]=apu_get_register(s,i,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_apm_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_apm_suspend();break;
+	case APM_NORMAL_RESUME: 
+	case APM_CRITICAL_RESUME: 
+	case APM_STANDBY_RESUME: 
+		maestro_apm_resume();break;
+	default: break;
+	}
+
+	return 0;
+}
+#endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/maestro.h linux.14p3/drivers/sound/maestro.h
--- linux.vanilla/drivers/sound/maestro.h	Thu Jan  1 01:00:00 1970
+++ linux.14p3/drivers/sound/maestro.h	Thu Oct 28 21:38:46 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.14p3/drivers/sound/msnd_pinnacle.c
--- linux.vanilla/drivers/sound/msnd_pinnacle.c	Wed Oct 20 01:12:48 1999
+++ linux.14p3/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.14p3/drivers/sound/nm256_audio.c
--- linux.vanilla/drivers/sound/nm256_audio.c	Wed Oct 20 01:12:48 1999
+++ linux.14p3/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.14p3/drivers/sound/opl3.c
--- linux.vanilla/drivers/sound/opl3.c	Wed Aug 19 22:48:30 1998
+++ linux.14p3/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.14p3/drivers/sound/sb_ess.c
--- linux.vanilla/drivers/sound/sb_ess.c	Sat Aug 28 20:00:58 1999
+++ linux.14p3/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.14p3/drivers/sound/sonicvibes.c
--- linux.vanilla/drivers/sound/sonicvibes.c	Wed Oct 20 01:12:48 1999
+++ linux.14p3/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.14p3/drivers/sound/sound_core.c
--- linux.vanilla/drivers/sound/sound_core.c	Sat Aug 28 20:00:58 1999
+++ linux.14p3/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/matroxfb.c linux.14p3/drivers/video/matroxfb.c
--- linux.vanilla/drivers/video/matroxfb.c	Sat Aug 14 02:26:17 1999
+++ linux.14p3/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/fs/binfmt_aout.c linux.14p3/fs/binfmt_aout.c
--- linux.vanilla/fs/binfmt_aout.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/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.14p3/fs/buffer.c
--- linux.vanilla/fs/buffer.c	Sat Aug 14 02:27:35 1999
+++ linux.14p3/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.14p3/fs/dcache.c
--- linux.vanilla/fs/dcache.c	Sat Aug 14 02:25:58 1999
+++ linux.14p3/fs/dcache.c	Fri Oct 22 21:27:00 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 = 0;
+static unsigned int d_hash_shift = 0;
+static struct list_head *dentry_hashtable = NULL;
 static LIST_HEAD(dentry_unused);
 
 struct {
@@ -563,7 +565,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 +904,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 +925,33 @@
 	if (!dentry_cache)
 		panic("Cannot create dentry cache");
 
-	i = D_HASHSIZE;
+	memory_size = num_physpages << PAGE_SHIFT;
+	for (order = 5; (1UL << order) < 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.14p3/fs/dquot.c
--- linux.vanilla/fs/dquot.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/fs/dquot.c	Wed Oct 27 18:58:23 1999
@@ -1341,7 +1341,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 +1350,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;
 }
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/fs/ext2/ialloc.c linux.14p3/fs/ext2/ialloc.c
--- linux.vanilla/fs/ext2/ialloc.c	Tue Oct 20 22:08:14 1998
+++ linux.14p3/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.14p3/fs/ext2/inode.c
--- linux.vanilla/fs/ext2/inode.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/fs/ext2/inode.c	Fri Oct 22 21:27:20 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;
@@ -538,6 +542,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.14p3/fs/ext2/ioctl.c
--- linux.vanilla/fs/ext2/ioctl.c	Sat Aug 14 02:27:37 1999
+++ linux.14p3/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.14p3/fs/filesystems.c
--- linux.vanilla/fs/filesystems.c	Sat Aug 14 02:27:37 1999
+++ linux.14p3/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.14p3/fs/inode.c
--- linux.vanilla/fs/inode.c	Sat Aug 14 02:27:37 1999
+++ linux.14p3/fs/inode.c	Fri Oct 29 23:53:08 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.
  *
@@ -435,6 +438,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);
@@ -809,6 +816,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 +855,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/lockd/lockd_syms.c linux.14p3/fs/lockd/lockd_syms.c
--- linux.vanilla/fs/lockd/lockd_syms.c	Mon Apr  7 19:35:30 1997
+++ linux.14p3/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/svc.c linux.14p3/fs/lockd/svc.c
--- linux.vanilla/fs/lockd/svc.c	Sat Feb  6 20:46:21 1999
+++ linux.14p3/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.14p3/fs/locks.c
--- linux.vanilla/fs/locks.c	Sat Aug 14 02:27:37 1999
+++ linux.14p3/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/inode.c linux.14p3/fs/nfs/inode.c
--- linux.vanilla/fs/nfs/inode.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/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/nfsd/nfsctl.c linux.14p3/fs/nfsd/nfsctl.c
--- linux.vanilla/fs/nfsd/nfsctl.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/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.14p3/fs/nfsd/nfsfh.c
--- linux.vanilla/fs/nfsd/nfsfh.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/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.14p3/fs/nfsd/nfsproc.c
--- linux.vanilla/fs/nfsd/nfsproc.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/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.14p3/fs/nfsd/nfssvc.c
--- linux.vanilla/fs/nfsd/nfssvc.c	Sat Mar 20 20:28:14 1999
+++ linux.14p3/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.14p3/fs/nfsd/stats.c
--- linux.vanilla/fs/nfsd/stats.c	Thu Jan  7 17:28:47 1999
+++ linux.14p3/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.14p3/fs/nfsd/vfs.c
--- linux.vanilla/fs/nfsd/vfs.c	Wed Oct 20 01:12:49 1999
+++ linux.14p3/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/include/asm-alpha/delay.h linux.14p3/include/asm-alpha/delay.h
--- linux.vanilla/include/asm-alpha/delay.h	Tue Dec 29 21:56:15 1998
+++ linux.14p3/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.14p3/include/asm-alpha/io.h
--- linux.vanilla/include/asm-alpha/io.h	Sat Aug 14 02:27:37 1999
+++ linux.14p3/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.14p3/include/asm-i386/fixmap.h
--- linux.vanilla/include/asm-i386/fixmap.h	Sun Mar 28 19:03:26 1999
+++ linux.14p3/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.14p3/include/asm-i386/sgi-cobalt.h
--- linux.vanilla/include/asm-i386/sgi-cobalt.h	Thu Jan  1 01:00:00 1970
+++ linux.14p3/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/linux/apm_bios.h linux.14p3/include/linux/apm_bios.h
--- linux.vanilla/include/linux/apm_bios.h	Wed Oct 20 01:12:50 1999
+++ linux.14p3/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/fb.h linux.14p3/include/linux/fb.h
--- linux.vanilla/include/linux/fb.h	Sat Aug 14 02:26:01 1999
+++ linux.14p3/include/linux/fb.h	Wed Oct 27 18:41:36 1999
@@ -72,6 +72,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.14p3/include/linux/fs.h
--- linux.vanilla/include/linux/fs.h	Wed Oct 20 01:12:50 1999
+++ linux.14p3/include/linux/fs.h	Fri Oct 22 23:25:13 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.14p3/include/linux/genhd.h
--- linux.vanilla/include/linux/genhd.h	Sat Aug 14 02:27:39 1999
+++ linux.14p3/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.14p3/include/linux/i2c.h
--- linux.vanilla/include/linux/i2c.h	Wed Oct 20 01:12:50 1999
+++ linux.14p3/include/linux/i2c.h	Sat Oct 30 00:02:52 1999
@@ -38,6 +38,7 @@
 
 #define I2C_BUSID_BT848		1	/* I2C bus on a BT848 */
 	/* 2 is used in 2.3.x */
+#define I2C_BUSID_SGIVWFB	1	/* From the SGI official patch */
 #define I2C_BUSID_BUZ		3	/* I2C bus on a BUZ */
 #define I2C_BUSID_ZORAN		4	/* I2C bus on a Zoran */
 
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/include/linux/lockd/syscall.h linux.14p3/include/linux/lockd/syscall.h
--- linux.vanilla/include/linux/lockd/syscall.h	Thu Jan  1 01:00:00 1970
+++ linux.14p3/include/linux/lockd/syscall.h	Fri Oct 22 23:27:44 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.14p3/include/linux/mm.h
--- linux.vanilla/include/linux/mm.h	Sat Aug 28 20:00:59 1999
+++ linux.14p3/include/linux/mm.h	Sat Oct 30 00:01:54 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/netdevice.h linux.14p3/include/linux/netdevice.h
--- linux.vanilla/include/linux/netdevice.h	Sat Aug 28 20:00:59 1999
+++ linux.14p3/include/linux/netdevice.h	Sat Oct 30 00:03:03 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.14p3/include/linux/nfsd/nfsfh.h
--- linux.vanilla/include/linux/nfsd/nfsfh.h	Wed Oct 20 01:12:51 1999
+++ linux.14p3/include/linux/nfsd/nfsfh.h	Fri Oct 22 23:27:44 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.14p3/include/linux/nfsd/stats.h
--- linux.vanilla/include/linux/nfsd/stats.h	Wed Nov 26 21:08:38 1997
+++ linux.14p3/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.14p3/include/linux/nfsd/syscall.h
--- linux.vanilla/include/linux/nfsd/syscall.h	Mon Dec 28 22:10:01 1998
+++ linux.14p3/include/linux/nfsd/syscall.h	Fri Oct 22 23:27:44 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.14p3/include/linux/pagemap.h
--- linux.vanilla/include/linux/pagemap.h	Wed Oct 20 01:12:51 1999
+++ linux.14p3/include/linux/pagemap.h	Sat Oct 30 00:01:54 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.14p3/include/linux/parport.h
--- linux.vanilla/include/linux/parport.h	Sun Mar 28 19:04:25 1999
+++ linux.14p3/include/linux/parport.h	Sat Oct 30 00:02:52 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.14p3/include/linux/parport_pc.h
--- linux.vanilla/include/linux/parport_pc.h	Sat Aug 14 02:26:21 1999
+++ linux.14p3/include/linux/parport_pc.h	Sat Oct 30 00:02:52 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.14p3/include/linux/pci.h
--- linux.vanilla/include/linux/pci.h	Wed Oct 20 01:12:51 1999
+++ linux.14p3/include/linux/pci.h	Wed Oct 27 19:05:02 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
 
@@ -1067,6 +1093,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 +1103,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 +1346,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.14p3/include/linux/proc_fs.h
--- linux.vanilla/include/linux/proc_fs.h	Wed Oct 20 01:12:51 1999
+++ linux.14p3/include/linux/proc_fs.h	Sat Oct 30 00:01:54 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.14p3/include/linux/sched.h
--- linux.vanilla/include/linux/sched.h	Wed Oct 20 01:12:51 1999
+++ linux.14p3/include/linux/sched.h	Fri Oct 29 23:48:36 1999
@@ -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.14p3/include/linux/sunrpc/svc.h
--- linux.vanilla/include/linux/sunrpc/svc.h	Sun Mar 28 19:03:50 1999
+++ linux.14p3/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.14p3/include/linux/sunrpc/svcsock.h
--- linux.vanilla/include/linux/sunrpc/svcsock.h	Sun Mar 28 19:03:53 1999
+++ linux.14p3/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/swap.h linux.14p3/include/linux/swap.h
--- linux.vanilla/include/linux/swap.h	Sun Mar 28 19:03:26 1999
+++ linux.14p3/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/net/br.h linux.14p3/include/net/br.h
--- linux.vanilla/include/net/br.h	Sat Aug 14 02:27:40 1999
+++ linux.14p3/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.14p3/include/net/tcp.h
--- linux.vanilla/include/net/tcp.h	Sat Aug 28 20:00:59 1999
+++ linux.14p3/include/net/tcp.h	Sat Oct 30 00:05:13 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/init/main.c linux.14p3/init/main.c
--- linux.vanilla/init/main.c	Wed Oct 20 01:12:51 1999
+++ linux.14p3/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/sem.c linux.14p3/ipc/sem.c
--- linux.vanilla/ipc/sem.c	Sun Jan 10 03:16:44 1999
+++ linux.14p3/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.14p3/ipc/shm.c
--- linux.vanilla/ipc/shm.c	Wed Oct 20 01:12:51 1999
+++ linux.14p3/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.14p3/kernel/exit.c
--- linux.vanilla/kernel/exit.c	Wed Oct 20 01:12:51 1999
+++ linux.14p3/kernel/exit.c	Fri Oct 29 23:47:45 1999
@@ -444,6 +444,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 +517,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.14p3/kernel/ksyms.c
--- linux.vanilla/kernel/ksyms.c	Wed Oct 20 01:12:51 1999
+++ linux.14p3/kernel/ksyms.c	Fri Oct 22 21:34:26 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);
@@ -192,6 +196,9 @@
 
 #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/sysctl.c linux.14p3/kernel/sysctl.c
--- linux.vanilla/kernel/sysctl.c	Wed Oct 20 01:12:51 1999
+++ linux.14p3/kernel/sysctl.c	Fri Oct 22 22:19:05 1999
@@ -169,8 +169,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},
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/mm/filemap.c linux.14p3/mm/filemap.c
--- linux.vanilla/mm/filemap.c	Wed Oct 20 01:12:51 1999
+++ linux.14p3/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.14p3/mm/memory.c
--- linux.vanilla/mm/memory.c	Sat Aug 14 02:27:40 1999
+++ linux.14p3/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.14p3/mm/page_alloc.c
--- linux.vanilla/mm/page_alloc.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/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.14p3/mm/swapfile.c
--- linux.vanilla/mm/swapfile.c	Sat Aug 14 02:27:41 1999
+++ linux.14p3/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.14p3/mm/vmscan.c
--- linux.vanilla/mm/vmscan.c	Sat Aug 14 02:26:01 1999
+++ linux.14p3/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.14p3/net/802/fc.c
--- linux.vanilla/net/802/fc.c	Sat Aug 28 20:00:59 1999
+++ linux.14p3/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.14p3/net/Config.in
--- linux.vanilla/net/Config.in	Thu Feb 25 18:46:47 1999
+++ linux.14p3/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.14p3/net/bridge/br.c
--- linux.vanilla/net/bridge/br.c	Sat Aug 14 02:27:41 1999
+++ linux.14p3/net/bridge/br.c	Fri Oct 22 22:39: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);
@@ -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
@@ -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 */
@@ -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);
@@ -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("%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("%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.14p3/net/bridge/br_tree.c
--- linux.vanilla/net/bridge/br_tree.c	Sun Mar  7 23:25:23 1999
+++ linux.14p3/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.14p3/net/core/dev.c
--- linux.vanilla/net/core/dev.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/net/core/dev.c	Sat Oct 30 00:39:41 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
@@ -2017,6 +2020,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/ipv4/ip_forward.c linux.14p3/net/ipv4/ip_forward.c
--- linux.vanilla/net/ipv4/ip_forward.c	Sun Mar 21 15:22:00 1999
+++ linux.14p3/net/ipv4/ip_forward.c	Thu Oct 28 11:31:09 1999
@@ -10,6 +10,7 @@
  * 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,7 +176,7 @@
 			    (icmph->type==ICMP_TIME_EXCEEDED))
 			{
 #endif
-				maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE);
+				u32 maddr = rt->rt_src;
 				fw_res = ip_fw_masq_icmp(&skb, maddr);
 			        if (fw_res < 0) {
 					kfree_skb(skb);
@@ -187,7 +187,7 @@
 					/* ICMP matched - skip firewall */
 					goto skip_call_fw_firewall;
 #ifdef CONFIG_IP_MASQUERADE_ICMP
-			       }
+			}
 #endif				
 		}
 		if (rt->rt_flags&RTCF_MASQ)
@@ -226,7 +226,7 @@
 
 		if (maddr == 0)
 #endif
-			maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE);
+			maddr = rt->rt_src;
 
 			if (ip_fw_masquerade(&skb, maddr) < 0) {
 				kfree_skb(skb);
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/ip_fw.c linux.14p3/net/ipv4/ip_fw.c
--- linux.vanilla/net/ipv4/ip_fw.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/net/ipv4/ip_fw.c	Fri Oct 22 22:22:20 1999
@@ -488,7 +488,10 @@
                 static int enabled = 0;
 
                 if(!enabled)
+                {
+                	enabled=1;
                         sysctl_ip_always_defrag++;
+                }
 		*answer = FW_REDIRECT;
 		return 1;
 #endif
diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/net/ipv4/ip_masq.c linux.14p3/net/ipv4/ip_masq.c
--- linux.vanilla/net/ipv4/ip_masq.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/net/ipv4/ip_masq.c	Wed Oct 27 19:00:44 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,22 @@
 		 *	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 */
+			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.14p3/net/ipv4/ip_masq_user.c
--- linux.vanilla/net/ipv4/ip_masq_user.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/net/ipv4/ip_masq_user.c	Wed Oct 27 19:00:45 1999
@@ -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/tcp.c linux.14p3/net/ipv4/tcp.c
--- linux.vanilla/net/ipv4/tcp.c	Sat Aug 14 02:27:41 1999
+++ linux.14p3/net/ipv4/tcp.c	Fri Oct 22 21:35:49 1999
@@ -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.14p3/net/ipv4/tcp_input.c
--- linux.vanilla/net/ipv4/tcp_input.c	Sat Aug 28 20:00:59 1999
+++ linux.14p3/net/ipv4/tcp_input.c	Fri Oct 22 21:36:08 1999
@@ -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.14p3/net/ipv4/tcp_ipv4.c
--- linux.vanilla/net/ipv4/tcp_ipv4.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/net/ipv4/tcp_ipv4.c	Thu Oct 28 21:34:15 1999
@@ -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) {
@@ -181,7 +183,7 @@
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if (child->num != sk->num) {
 		unsigned short snum = ntohs(child->num);
-		for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+		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.14p3/net/ipv4/tcp_timer.c
--- linux.vanilla/net/ipv4/tcp_timer.c	Sat Aug 14 02:27:41 1999
+++ linux.14p3/net/ipv4/tcp_timer.c	Fri Oct 22 21:36:08 1999
@@ -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/tcp_ipv6.c linux.14p3/net/ipv6/tcp_ipv6.c
--- linux.vanilla/net/ipv6/tcp_ipv6.c	Sat Aug 28 20:00:59 1999
+++ linux.14p3/net/ipv6/tcp_ipv6.c	Fri Oct 22 21:36:09 1999
@@ -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/irlan/irlan_eth.c linux.14p3/net/irda/irlan/irlan_eth.c
--- linux.vanilla/net/irda/irlan/irlan_eth.c	Sat Aug 14 02:26:52 1999
+++ linux.14p3/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.14p3/net/irda/irmod.c
--- linux.vanilla/net/irda/irmod.c	Sat Aug 14 02:26:54 1999
+++ linux.14p3/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.14p3/net/netsyms.c
--- linux.vanilla/net/netsyms.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/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.14p3/net/sunrpc/clnt.c
--- linux.vanilla/net/sunrpc/clnt.c	Tue Oct  6 17:39:44 1998
+++ linux.14p3/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.14p3/net/sunrpc/svc.c
--- linux.vanilla/net/sunrpc/svc.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/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.14p3/net/sunrpc/svcsock.c
--- linux.vanilla/net/sunrpc/svcsock.c	Wed Oct 20 01:12:52 1999
+++ linux.14p3/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; */
 	}
 }
