diff -u --recursive --new-file v2.2.0-pre1/linux/CREDITS linux/CREDITS
--- v2.2.0-pre1/linux/CREDITS	Mon Dec 28 15:00:51 1998
+++ linux/CREDITS	Tue Dec 29 17:31:48 1998
@@ -147,7 +147,8 @@
 E: kgb@manjak.knm.org.pl
 P: 1024/FA6F16D1 96 D1 1A CF 5F CA 69 EC  F9 4F 36 1F 6D 60 7B DA
 D: Maintainer of the System V file system.
-D: SystemV fs update for 2.1.x dcache.
+D: System V fs update for 2.1.x dcache.
+D: Forward ported a couple of SCSI drivers.
 D: Various bugfixes.
 S: ul. Koscielna 12a
 S: 62-300 Wrzesnia
diff -u --recursive --new-file v2.2.0-pre1/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.2.0-pre1/linux/Documentation/Configure.help	Mon Dec 28 15:00:51 1998
+++ linux/Documentation/Configure.help	Wed Dec 30 14:50:00 1998
@@ -91,6 +91,9 @@
   single-CPU machines.  On a single-CPU machine, a non-SMP kernel
   will run faster than an SMP kernel.
 
+  i486 based SMP boards don't boot CONFIG_M586/M686 kernels. CONFIG_M686
+  SMP kernels might not work on all Pentium based boards.
+
   People using multiprocessor machines should also say Y to "Enhanced
   Real Time Clock Support", below.  The "Advanced Power Management"
   code will be disabled in an SMP kernel.
@@ -1557,23 +1560,23 @@
   This is the processor type of your CPU. This information is used for
   optimizing purposes. In order to compile a kernel that can run on
   all x86 CPU types (albeit not optimally fast), you can specify
-  "386" here. If you specify one of "486" or "Pentium" or "PPro",
-  then the kernel will run on all x86 architectures except on 386.
+  "386" here.
+
+  If you specify one of "486" or "Pentium" or "PPro", then the kernel
+  will not necessarily run on earlier architectures (ie a Pentium
+  optimized kernel will run on a PPro, but not necessarily on a i486).
 
   Here are the settings recommended for greatest speed:
    - "386" for the AMD/Cyrix/Intel 386DX/DXL/SL/SLC/SX and
      Cyrix/TI 486DLC/DLC2. Only "386" kernels will run on a 386 class
      machine.
    - "486" for the AMD/Cyrix/IBM/Intel DX4 or 486DX/DX2/SL/SX/SX2,
-     AMD/Cyrix 5x86, NexGen Nx586 and UMC U5D or U5S
-   - "Pentium" for the AMD K5, K6 and K6-3D, Cyrix MediaGX,
-     Cyrix/IBM/National Semiconductor 6x86 and GXm, IDT Centaur
-     WinChip C6, and Intel Pentium/Pentium MMX
+     AMD/Cyrix 5x86, NexGen Nx586 and UMC U5D or U5S.
+   - "586" for generic Pentium CPU's, possibly lacking the TSC register.
+   - "Pentium" for the Intel Pentium/Pentium MMX, AMD K5, K6 and K6-3D.
+     This option will assume that you have a time stamp counter.
    - "PPro" for the Cyrix/IBM/National Semiconductor 6x86MX, MII and
-     Intel Pentium II/Pentium Pro
-
-  In rare cases, it can make sense to specify "Pentium" even if
-  running on a 486: the kernel will be smaller but slower.
+     Intel Pentium II/Pentium Pro.
 
   If you don't know what to do, choose "386".
 
@@ -3776,14 +3779,19 @@
   given SCSI device. Go with the default unless you know what you're
   doing. Minimum is 2 and maximum is 8.
 
-Future Domain 16xx SCSI/AHA 2920 support
+Future Domain 16xx SCSI/AHA-2920A support
 CONFIG_SCSI_FUTURE_DOMAIN
   This is support for Future Domain's 16-bit SCSI host adapters
   (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and
   other adapters based on the Future Domain chipsets (Quantum
-  ISA-200S, ISA-250MG; Adaptec AHA-2920; and at least one IBM board).
+  ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board).
   It is explained in section 3.7 of the SCSI-HOWTO, available via FTP
   (user: anonymous) at ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. 
+
+  NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip and
+  should use the aic7xxx driver (CONFIG_SCSI_AIC7XXX).  The Future Domain
+  driver works with the older Adaptec AHA-2920A boards with a Future Domain
+  chip on them.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
diff -u --recursive --new-file v2.2.0-pre1/linux/Documentation/kernel-docs.txt linux/Documentation/kernel-docs.txt
--- v2.2.0-pre1/linux/Documentation/kernel-docs.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/kernel-docs.txt	Tue Dec 29 15:47:32 1998
@@ -0,0 +1,331 @@
+
+   
+ 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
+   to information, appeared again and again.
+   
+   Fortunately, as more and more people get to GNU/Linux, more and more
+   get interested in the Kernel. But reading the sources is not always
+   enough. It is easy to understand the code, but miss the concepts, the
+   philosophy and design decissions behind this code.
+   
+   Unfortunately, not many documents are available for beginners to
+   start. And, even if they exist, there was no "well-known" place which
+   kept track of them. These lines try to cover this lack. All documents
+   available on line known by the author are listed, while some reference
+   books are also mentioned.
+   
+   PLEASE, if you know any paper not listed here or write a new document,
+   send me an e-mail, and I'll include a reference to it here. Any
+   corrections, ideas or comments are also wellcomed.
+   
+   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"
+   helpfull 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 lines paragrahp
+            description. sk_buffs explained, too, with some beatiful
+            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,
+            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.
+       
+       
+       
+     * 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:
+       
+          + 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.
+   
+          + 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.
diff -u --recursive --new-file v2.2.0-pre1/linux/MAINTAINERS linux/MAINTAINERS
--- v2.2.0-pre1/linux/MAINTAINERS	Mon Dec 28 15:00:52 1998
+++ linux/MAINTAINERS	Wed Dec 30 15:21:47 1998
@@ -181,12 +181,6 @@
 W:	http://www.fi.muni.cz/~kas/cosa/
 S:	Maintained
 
-COSA/SRP SYNC SERIAL DRIVER
-P:	Jan "Yenya" Kasprzak
-M:	kas@fi.muni.cz
-W:	http://www.fi.muni.cz/~kas/cosa/
-S:	Maintained
-
 CREDITS FILE
 P:	John A. Martin
 M:	jam@acm.org
@@ -350,10 +344,10 @@
 S:	Maintained
 
 IDE DRIVER [GENERAL]
-P:	Mark Lord
-M:	mlord@pobox.com
+P:	Andre Hedrick
+M:	hedrick@astro.dyer.vanderbilt.edu
 L:	linux-kernel@vger.rutgers.edu
-S:	Odd Fixes
+S:	Maintained
 
 IDE/ATAPI CDROM DRIVER 
 P:	Jens Axboe
@@ -405,6 +399,13 @@
 P:	H. Peter Anvin
 M:	hpa@zytor.com
 L:	autofs@linux.kernel.org
+S:	Maintained
+
+KERNEL NFSD
+P:	G. Allen Morris III
+M:	gam3@acm.org
+L:	nfs-devel@linux.kernel.org (Linux NFS)
+W:	http://csua.berkeley.edu/~gam3/knfsd
 S:	Maintained
 
 LAPB module
diff -u --recursive --new-file v2.2.0-pre1/linux/Makefile linux/Makefile
--- v2.2.0-pre1/linux/Makefile	Mon Dec 28 15:00:52 1998
+++ linux/Makefile	Mon Dec 28 15:01:07 1998
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 0
-EXTRAVERSION =-pre1
+EXTRAVERSION =-pre2
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.2.0-pre1/linux/arch/alpha/config.in	Tue Dec 22 14:16:53 1998
+++ linux/arch/alpha/config.in	Wed Dec 30 21:22:00 1998
@@ -13,7 +13,6 @@
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
-  MODULES=y
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
   bool 'Kernel module loader' CONFIG_KMOD
 fi
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- v2.2.0-pre1/linux/arch/alpha/kernel/alpha_ksyms.c	Tue Dec 22 14:16:53 1998
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Tue Dec 29 13:56:15 1998
@@ -86,6 +86,7 @@
 EXPORT_SYMBOL(strncat);
 EXPORT_SYMBOL(strstr);
 EXPORT_SYMBOL(strtok);
+EXPORT_SYMBOL(strpbrk);
 EXPORT_SYMBOL(strchr);
 EXPORT_SYMBOL(strrchr);
 EXPORT_SYMBOL(memcmp);
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.2.0-pre1/linux/arch/alpha/kernel/entry.S	Fri Oct 23 22:01:19 1998
+++ linux/arch/alpha/kernel/entry.S	Tue Dec 29 13:56:15 1998
@@ -689,7 +689,8 @@
 	.set at
 	mb	/* Make the changed data visible before the freed lock. */
 	stq	$31,scheduler_lock	
-	br	ret_from_sys_call
+	lda	$26,ret_from_sys_call
+	jsr	$31,schedule_tail
 	.set noat
 .end ret_from_smpfork
 #endif /* __SMP__ */
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.2.0-pre1/linux/arch/alpha/kernel/irq.c	Mon Dec 28 15:00:52 1998
+++ linux/arch/alpha/kernel/irq.c	Tue Dec 29 13:56:15 1998
@@ -11,6 +11,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/kernel.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/alpha/kernel/irq.h linux/arch/alpha/kernel/irq.h
--- v2.2.0-pre1/linux/arch/alpha/kernel/irq.h	Mon Dec 28 15:00:52 1998
+++ linux/arch/alpha/kernel/irq.h	Wed Dec 30 15:06:22 1998
@@ -8,6 +8,8 @@
  * with the IRQ handling routines in irq.c.
  */
 
+#include <linux/config.h>
+
 #define STANDARD_INIT_IRQ_PROLOG	\
 	outb(0, DMA1_RESET_REG);	\
 	outb(0, DMA2_RESET_REG);	\
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
--- v2.2.0-pre1/linux/arch/alpha/kernel/ptrace.c	Fri Oct 23 22:01:19 1998
+++ linux/arch/alpha/kernel/ptrace.c	Tue Dec 29 16:17:00 1998
@@ -541,7 +541,9 @@
 	/* When I and D space are separate, these will need to be fixed.  */
 	case PTRACE_PEEKTEXT: /* read word at location addr. */
 	case PTRACE_PEEKDATA:
+		down(&child->mm->mmap_sem);
 		ret = read_long(child, addr, &tmp);
+		up(&child->mm->mmap_sem);
 		DBG(DBG_MEM, ("peek %#lx->%#lx\n", addr, tmp));
 		if (ret < 0)
 			goto out;
@@ -560,7 +562,9 @@
 	case PTRACE_POKETEXT: /* write the word at location addr. */
 	case PTRACE_POKEDATA:
 		DBG(DBG_MEM, ("poke %#lx<-%#lx\n", addr, data));
+		down(&child->mm->mmap_sem);
 		ret = write_long(child, addr, data);
+		up(&child->mm->mmap_sem);
 		goto out;
 
 	case PTRACE_POKEUSR: /* write the specified register */
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
--- v2.2.0-pre1/linux/arch/alpha/kernel/smp.c	Fri Oct 23 22:01:19 1998
+++ linux/arch/alpha/kernel/smp.c	Tue Dec 29 13:56:15 1998
@@ -30,7 +30,14 @@
 
 #include "proto.h"
 
-struct ipi_msg_flush_tb_struct ipi_msg_flush_tb;
+#define DEBUG_SMP 0
+#if DEBUG_SMP
+#define DBGS(args)	printk args
+#else
+#define DBGS(args)
+#endif
+
+struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned;
 
 struct cpuinfo_alpha cpu_data[NR_CPUS];
 
@@ -39,7 +46,6 @@
 
 unsigned int boot_cpu_id = 0;
 static int smp_activated = 0;
-static unsigned long ipicnt[NR_CPUS] = {0,}; /* IPI counts */
 
 int smp_found_config = 0; /* Have we found an SMP box */
 static int max_cpus = -1;
@@ -53,10 +59,12 @@
 volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
 volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
 
+cycles_t cacheflush_time;
+
 unsigned int prof_multiplier[NR_CPUS];
 unsigned int prof_counter[NR_CPUS];
 
-volatile int ipi_bits[NR_CPUS];
+volatile int ipi_bits[NR_CPUS] __cacheline_aligned;
 
 unsigned long boot_cpu_palrev;
 
@@ -73,87 +81,80 @@
 static void secondary_cpu_start(int, struct task_struct *);
 static void send_cpu_msg(char *, int);
 
-/* process bootcommand SMP options, like "nosmp" and "maxcpus=" */
-__initfunc(void smp_setup(char *str, int *ints))
+/* Process bootcommand SMP options, like "nosmp" and "maxcpus=" */
+void __init
+smp_setup(char *str, int *ints)
 {
 	if (ints && ints[0] > 0)
 		max_cpus = ints[1];
-        else
+	else
 		max_cpus = 0;
 }
 
-void smp_store_cpu_info(int id)
+static void __init
+smp_store_cpu_info(int id)
 {
 	/* This is it on Alpha, so far. */
-        cpu_data[id].loops_per_sec = loops_per_sec;
+	cpu_data[id].loops_per_sec = loops_per_sec;
 }
 
-void smp_commence(void)
+void __init
+smp_commence(void)
 {
 	/* Lets the callin's below out of their loop. */
 	mb();
 	smp_commenced = 1;
 }
 
-void smp_callin(void)
+void __init
+smp_callin(void)
 {
-        int cpuid = hard_smp_processor_id();
+	int cpuid = hard_smp_processor_id();
 
-#if 0
-	printk("CALLIN %d state 0x%lx\n", cpuid, current->state);
-#endif
+	DBGS(("CALLIN %d state 0x%lx\n", cpuid, current->state));
 #ifdef HUH
-        local_flush_cache_all();
-        local_flush_tlb_all();
+	local_flush_cache_all();
+	local_flush_tlb_all();
 #endif
 #if 0
-        set_irq_udt(mid_xlate[boot_cpu_id]);
+	set_irq_udt(mid_xlate[boot_cpu_id]);
 #endif
 
-        /* Get our local ticker going. */
-        smp_setup_percpu_timer();
+	/* Get our local ticker going. */
+	smp_setup_percpu_timer();
 
 #if 0
-        calibrate_delay();
+	calibrate_delay();
 #endif
-        smp_store_cpu_info(cpuid);
+	smp_store_cpu_info(cpuid);
 #ifdef HUH
-        local_flush_cache_all();
-        local_flush_tlb_all();
+	local_flush_cache_all();
+	local_flush_tlb_all();
 #endif
 
-        /* Allow master to continue. */
-        set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]);
+	/* Allow master to continue. */
+	set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]);
 #ifdef HUH
-        local_flush_cache_all();
-        local_flush_tlb_all();
+	local_flush_cache_all();
+	local_flush_tlb_all();
 #endif
 
 #ifdef NOT_YET
-        while(!task[cpuid] || current_set[cpuid] != task[cpuid])
-                barrier();
-#endif /* NOT_YET */
-
-#if 0
-        /* Fix idle thread fields. */
-        __asm__ __volatile__("ld [%0], %%g6\n\t"
-                             : : "r" (&current_set[cpuid])
-                             : "memory" /* paranoid */);
-        current->mm->mmap->vm_page_prot = PAGE_SHARED;
-        current->mm->mmap->vm_start = PAGE_OFFSET;
-        current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+	while(!task[cpuid] || current_set[cpuid] != task[cpuid])
+	        barrier();
 #endif
-        
+
 #ifdef HUH
-        local_flush_cache_all();
-        local_flush_tlb_all();
+	local_flush_cache_all();
+	local_flush_tlb_all();
 #endif
 #if 0
-        __sti();
+	__sti();
 #endif
 }
 
-asmlinkage int start_secondary(void *unused)
+asmlinkage int __init
+start_secondary(void *unused)
 {
 	extern asmlinkage void entInt(void);
 	extern void paging_init_secondary(void);
@@ -163,35 +164,83 @@
 	trap_init();
 	wrent(entInt, 0);
 
-        smp_callin();
-        while (!smp_commenced)
+	smp_callin();
+	while (!smp_commenced)
 		barrier();
 #if 1
-printk("start_secondary: commencing CPU %d current %p\n",
-       hard_smp_processor_id(), current);
+	printk("start_secondary: commencing CPU %d current %p\n",
+	       hard_smp_processor_id(), current);
 #endif
-        cpu_idle(NULL);
+	cpu_idle(NULL);
 }
 
+static void __init
+smp_tune_scheduling (void)
+{
+	/*
+	 * Rough estimation for SMP scheduling, this is the number of
+	 * cycles it takes for a fully memory-limited process to flush
+	 * the SMP-local cache.
+	 *
+	 * We are not told how much cache there is, so we have to guess.
+	 */
+
+	struct percpu_struct *cpu;
+	unsigned long on_chip_cache;
+	unsigned long freq;
+
+	cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset);
+	switch (cpu->type)
+	{
+	case EV45_CPU:
+		on_chip_cache = 16 + 16;
+		break;
+
+	case EV5_CPU:
+	case EV56_CPU:
+		on_chip_cache = 8 + 8 + 96;
+		break;
+
+	case PCA56_CPU:
+		on_chip_cache = 16 + 8;
+		break;
+
+	case EV6_CPU:
+		on_chip_cache = 64 + 64;
+		break;
+
+	default:
+		on_chip_cache = 8 + 8;
+		break;
+	}
+
+	freq = hwrpb->cycle_freq ? : est_cycle_freq;
+
+	/* Magic estimation stolen from x86 port.  */
+	cacheflush_time = freq / 1024 * on_chip_cache / 5000;
+}
+
+
 /*
  *      Cycle through the processors sending START msgs to boot each.
  */
-void smp_boot_cpus(void)
+void __init
+smp_boot_cpus(void)
 {
-        int cpucount = 0;
-        int i, first, prev;
+	int cpucount = 0;
+	int i, first, prev;
 
-        printk("smp_boot_cpus: Entering SMP Mode...\n");
+	printk("Entering SMP Mode.\n");
 
 #if 0
-        __sti();
+	__sti();
 #endif
 
-        for(i=0; i < NR_CPUS; i++) {
+	for(i=0; i < NR_CPUS; i++) {
 		cpu_number_map[i] = -1;
 		cpu_logical_map[i] = -1;
-                prof_counter[i] = 1;
-                prof_multiplier[i] = 1;
+	        prof_counter[i] = 1;
+	        prof_multiplier[i] = 1;
 		ipi_bits[i] = 0;
 	}
 
@@ -199,159 +248,155 @@
 	cpu_logical_map[0] = boot_cpu_id;
 	current->processor = boot_cpu_id; /* ??? */
 
-        smp_store_cpu_info(boot_cpu_id);
+	smp_store_cpu_info(boot_cpu_id);
+	smp_tune_scheduling();
 #ifdef NOT_YET
-        printk("CPU%d: ", boot_cpu_id);
-        print_cpu_info(&cpu_data[boot_cpu_id]);
-        set_irq_udt(mid_xlate[boot_cpu_id]);
-#endif /* NOT_YET */
-        smp_setup_percpu_timer();
+	printk("CPU%d: ", boot_cpu_id);
+	print_cpu_info(&cpu_data[boot_cpu_id]);
+	set_irq_udt(mid_xlate[boot_cpu_id]);
+#endif
+	smp_setup_percpu_timer();
 #ifdef HUH
-        local_flush_cache_all();
+	local_flush_cache_all();
 #endif
-        if (smp_num_probed == 1)
+	if (smp_num_probed == 1)
 		return;  /* Not an MP box. */
 
 #if NOT_YET
-        /*
-         * If SMP should be disabled, then really disable it!
-         */
-        if (!max_cpus)
+	/*
+	 * If SMP should be disabled, then really disable it!
+	 */
+	if (!max_cpus)
 	{
 		smp_found_config = 0;
-                printk(KERN_INFO "SMP mode deactivated.\n");
-        }
-#endif /* NOT_YET */
+	        printk(KERN_INFO "SMP mode deactivated.\n");
+	}
+#endif
 
-        for (i = 0; i < NR_CPUS; i++) {
+	for (i = 0; i < NR_CPUS; i++) {
 
 		if (i == boot_cpu_id)
 			continue;
 
-                if (cpu_present_map & (1 << i)) {
-                        struct task_struct *idle;
-                        int timeout;
-
-                        /* Cook up an idler for this guy. */
-                        kernel_thread(start_secondary, NULL, CLONE_PID);
-                        idle = task[++cpucount];
+	        if (cpu_present_map & (1 << i)) {
+	                struct task_struct *idle;
+	                int timeout;
+
+	                /* Cook up an idler for this guy. */
+	                kernel_thread(start_secondary, NULL, CLONE_PID);
+	                idle = task[++cpucount];
 			if (!idle)
 				panic("No idle process for CPU %d", i);
-                        idle->processor = i;
+	                idle->processor = i;
 
-#if 0
-printk("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n",
-       i, idle->state, idle->flags);
-#endif
+			DBGS(("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n",
+			      i, idle->state, idle->flags));
 
-                        /* whirrr, whirrr, whirrrrrrrrr... */
+	                /* whirrr, whirrr, whirrrrrrrrr... */
 #ifdef HUH
-                        local_flush_cache_all();
+	                local_flush_cache_all();
 #endif
-                        secondary_cpu_start(i, idle);
+	                secondary_cpu_start(i, idle);
 
-                        /* wheee... it's going... wait for 5 secs...*/
-                        for (timeout = 0; timeout < 50000; timeout++) {
+	                /* wheee... it's going... wait for 5 secs...*/
+	                for (timeout = 0; timeout < 50000; timeout++) {
 				if (cpu_callin_map[i])
 					break;
-                                udelay(100);
-                        }
-                        if (cpu_callin_map[i]) {
+	                        udelay(100);
+	                }
+	                if (cpu_callin_map[i]) {
 				/* Another "Red Snapper". */
 				cpu_number_map[i] = cpucount;
-                                cpu_logical_map[cpucount] = i;
-                        } else {
+	                        cpu_logical_map[cpucount] = i;
+	                } else {
 				cpucount--;
-                                printk("smp_boot_cpus: Processor %d"
+	                        printk("smp_boot_cpus: Processor %d"
 				       " is stuck 0x%lx.\n", i, idle->flags);
-                        }
-                }
-                if (!(cpu_callin_map[i])) {
+	                }
+	        }
+	        if (!(cpu_callin_map[i])) {
 			cpu_present_map &= ~(1 << i);
-                        cpu_number_map[i] = -1;
-                }
-        }
+	                cpu_number_map[i] = -1;
+	        }
+	}
 #ifdef HUH
-        local_flush_cache_all();
+	local_flush_cache_all();
 #endif
-        if (cpucount == 0) {
+	if (cpucount == 0) {
 		printk("smp_boot_cpus: ERROR - only one Processor found.\n");
-                cpu_present_map = (1 << smp_processor_id());
-        } else {
+	        cpu_present_map = (1 << smp_processor_id());
+	} else {
 		unsigned long bogosum = 0;
-                for (i = 0; i < NR_CPUS; i++) {
+	        for (i = 0; i < NR_CPUS; i++) {
 			if (cpu_present_map & (1 << i))
 				bogosum += cpu_data[i].loops_per_sec;
-                }
-                printk("smp_boot_cpus: Total of %d Processors activated"
+	        }
+	        printk("smp_boot_cpus: Total of %d Processors activated"
 		       " (%lu.%02lu BogoMIPS).\n",
-                       cpucount + 1,
-                       (bogosum + 2500)/500000,
-                       ((bogosum + 2500)/5000)%100);
-                smp_activated = 1;
-                smp_num_cpus = cpucount + 1;
-        }
-
-        /* Setup CPU list for IRQ distribution scheme. */
-        first = prev = -1;
-        for (i = 0; i < NR_CPUS; i++) {
+	               cpucount + 1,
+	               (bogosum + 2500)/500000,
+	               ((bogosum + 2500)/5000)%100);
+	        smp_activated = 1;
+	        smp_num_cpus = cpucount + 1;
+	}
+
+	/* Setup CPU list for IRQ distribution scheme. */
+	first = prev = -1;
+	for (i = 0; i < NR_CPUS; i++) {
 		if (cpu_present_map & (1 << i)) {
 			if (first == -1)
 				first = i;
 			if (prev != -1)
 				cpu_data[i].next = i;
-                        prev = i;
-                }
-        }
-        cpu_data[prev].next = first;
+	                prev = i;
+	        }
+	}
+	cpu_data[prev].next = first;
 
-        /* Ok, they are spinning and ready to go. */
-        smp_processors_ready = 1;
+	/* Ok, they are spinning and ready to go. */
+	smp_processors_ready = 1;
 }
 
-__initfunc(void ioapic_pirq_setup(char *str, int *ints))
+static void __init
+smp_setup_percpu_timer(void)
 {
-  /* this is prolly INTEL-specific */
-}
-
-static void smp_setup_percpu_timer(void)
-{
-        int cpu = smp_processor_id();
+	int cpu = smp_processor_id();
 
-        prof_counter[cpu] = prof_multiplier[cpu] = 1;
+	prof_counter[cpu] = prof_multiplier[cpu] = 1;
 #ifdef NOT_YET
-        load_profile_irq(mid_xlate[cpu], lvl14_resolution);
-        if (cpu == boot_cpu_id)
+	load_profile_irq(mid_xlate[cpu], lvl14_resolution);
+	if (cpu == boot_cpu_id)
 		enable_pil_irq(14);
 #endif
 }
 
 extern void update_one_process(struct task_struct *p, unsigned long ticks,
-                               unsigned long user, unsigned long system,
+	                       unsigned long user, unsigned long system,
 			       int cpu);
 
-void smp_percpu_timer_interrupt(struct pt_regs *regs)
+void
+smp_percpu_timer_interrupt(struct pt_regs *regs)
 {
 	int cpu = smp_processor_id();
 
 #ifdef NOT_YET
-        clear_profile_irq(mid_xlate[cpu]);
-        if(!user_mode(regs))
+	clear_profile_irq(mid_xlate[cpu]);
+	if(!user_mode(regs))
 		alpha_do_profile(regs->pc);
 #endif
 
-        if (!--prof_counter[cpu]) {
+	if (!--prof_counter[cpu]) {
 		int user = user_mode(regs);
-                if (current->pid) {
+	        if (current->pid) {
 			update_one_process(current, 1, user, !user, cpu);
 
-                        if (--current->counter < 0) {
+	                if (--current->counter < 0) {
 				current->counter = 0;
-                                current->need_resched = 1;
-                        }
+	                        current->need_resched = 1;
+	                }
 
-                        spin_lock(&ticker_lock);
-                        if (user) {
+	                spin_lock(&ticker_lock);
+	                if (user) {
 				if (current->priority < DEF_PRIORITY) {
 					kstat.cpu_nice++;
 					kstat.per_cpu_nice[cpu]++;
@@ -359,93 +404,86 @@
 					kstat.cpu_user++;
 					kstat.per_cpu_user[cpu]++;
 				}
-                        } else {
+	                } else {
 				kstat.cpu_system++;
 				kstat.per_cpu_system[cpu]++;
-                        }
-                        spin_unlock(&ticker_lock);
-                }
-                prof_counter[cpu] = prof_multiplier[cpu];
-        }
+	                }
+	                spin_unlock(&ticker_lock);
+	        }
+	        prof_counter[cpu] = prof_multiplier[cpu];
+	}
 }
 
-int setup_profiling_timer(unsigned int multiplier)
+int __init
+setup_profiling_timer(unsigned int multiplier)
 {
 #ifdef NOT_YET
-        int i;
-        unsigned long flags;
+	int i;
+	unsigned long flags;
 
-        /* Prevent level14 ticker IRQ flooding. */
-        if((!multiplier) || (lvl14_resolution / multiplier) < 500)
-                return -EINVAL;
-
-        save_and_cli(flags);
-        for(i = 0; i < NR_CPUS; i++) {
-                if(cpu_present_map & (1 << i)) {
-                        load_profile_irq(mid_xlate[i], lvl14_resolution / multip
+	/* Prevent level14 ticker IRQ flooding. */
+	if((!multiplier) || (lvl14_resolution / multiplier) < 500)
+	        return -EINVAL;
+
+	save_and_cli(flags);
+	for(i = 0; i < NR_CPUS; i++) {
+	        if(cpu_present_map & (1 << i)) {
+	                load_profile_irq(mid_xlate[i], lvl14_resolution / multip
 lier);
-                        prof_multiplier[i] = multiplier;
-                }
-        }
-        restore_flags(flags);
+	                prof_multiplier[i] = multiplier;
+	        }
+	}
+	restore_flags(flags);
 
-        return 0;
+	return 0;
 
 #endif
   return -EINVAL;
 }
 
-/* Only broken Intel needs this, thus it should not even be referenced globally.
-*/
-__initfunc(void initialize_secondary(void))
+/* Only broken Intel needs this, thus it should not even be
+   referenced globally.  */
+
+void __init
+initialize_secondary(void)
 {
-	printk("initialize_secondary: entry\n");
 }
 
-static void
+static void __init
 secondary_cpu_start(int cpuid, struct task_struct *idle)
 {
 	struct percpu_struct *cpu;
-        int timeout;
+	int timeout;
 	  
 	cpu = (struct percpu_struct *)
 		((char*)hwrpb
-			+ hwrpb->processor_offset
-			+ cpuid * hwrpb->processor_size);
+		 + hwrpb->processor_offset
+		 + cpuid * hwrpb->processor_size);
 
-	/* set context to idle thread this CPU will use when running */
-	/* assumption is that the idle thread is all set to go... ??? */
+	/* Set context to idle thread this CPU will use when running
+	   assumption is that the idle thread is all set to go... ??? */
 	memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct));
 	cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */
-#if 0
-printk("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n",
-       cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb);
-printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n",
-       cpuid, idle->state, idle->tss.pal_flags);
-#endif
-
-	/* setup HWRPB fields that SRM uses to activate secondary CPU */
-	 hwrpb->CPU_restart = __start_cpu;
-	 hwrpb->CPU_restart_data = (unsigned long) idle;
-
-	 /* recalculate and update the HWRPB checksum */
-	 {
-	   unsigned long sum, *lp1, *lp2;
-	   sum = 0;
-	   lp1 = (unsigned long *)hwrpb;
-	   lp2 = &hwrpb->chksum;
-	   while (lp1 < lp2)
-	     sum += *lp1++;
-	   *lp2 = sum;
-	 }
+
+	DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n",
+	      cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb));
+	DBGS(("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n",
+	      cpuid, idle->state, idle->tss.pal_flags));
+
+	/* Setup HWRPB fields that SRM uses to activate secondary CPU */
+	hwrpb->CPU_restart = __start_cpu;
+	hwrpb->CPU_restart_data = (unsigned long) idle;
+
+	/* Recalculate and update the HWRPB checksum */
+	hwrpb_update_checksum(hwrpb);
 
 	/*
 	 * Send a "start" command to the specified processor.
 	 */
 
 	/* SRM III 3.4.1.3 */
-	cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */
-	cpu->flags &= ~1;/* turn off Bootstrap In Progress */
+	cpu->flags |= 0x22;	/* turn on Context Valid and Restart Capable */
+	cpu->flags &= ~1;	/* turn off Bootstrap In Progress */
 	mb();
 
 	send_cpu_msg("START\r\n", cpuid);
@@ -454,7 +492,7 @@
 	for (timeout = 10000; !(cpu->flags & 1); timeout--) {
 		if (timeout <= 0) {
 			printk("Processor %d failed to start\n", cpuid);
-                                /* needed for pset_info to work */
+	                        /* needed for pset_info to work */
 #if 0
 			ipc_processor_enable(cpu_to_processor(cpunum));
 #endif
@@ -462,49 +500,61 @@
 		}
 		mdelay(1);
 	}
-#if 0
-	printk("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid);
-#endif
+	DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid));
 }
 
 static void
 send_cpu_msg(char *str, int cpuid)
 {
 	struct percpu_struct *cpu;
-        register char *cp1, *cp2;
-        unsigned long cpumask;
-        int timeout;
+	register char *cp1, *cp2;
+	unsigned long cpumask;
+	size_t len;
+	int timeout;
 
-	  
 	cpu = (struct percpu_struct *)
 		((char*)hwrpb
-			+ hwrpb->processor_offset
-			+ cpuid * hwrpb->processor_size);
+		 + hwrpb->processor_offset
+		 + cpuid * hwrpb->processor_size);
+
+	cpumask = (1L << cpuid);
+	if (hwrpb->txrdy & cpumask)
+		goto delay1;
+	ready1:
+
+	cp2 = str;
+	len = strlen(cp2);
+	*(unsigned int *)&cpu->ipc_buffer[0] = len;
+	cp1 = (char *) &cpu->ipc_buffer[1];
+	memcpy(cp1, cp2, len);
+
+	/* atomic test and set */
+	set_bit(cpuid, &hwrpb->rxrdy);
+
+	if (hwrpb->txrdy & cpumask)
+		goto delay2;
+	ready2:
+	return;
 
-        cpumask = (1L << cpuid);
-        for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
-                if (timeout <= 0) {
-                        printk("Processor %x not ready\n", cpuid);
-                        return;
-                }
-                mdelay(1);
-        }
-
-        cp1 = (char *) &cpu->ipc_buffer[1];
-        cp2 = str;
-        while (*cp2) *cp1++ = *cp2++;
-        *(unsigned int *)&cpu->ipc_buffer[0] = cp2 - str; /* hack */
-
-        /* atomic test and set */
-        set_bit(cpuid, &hwrpb->rxrdy);
-
-        for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
-                if (timeout <= 0) {
-                        printk("Processor %x not ready\n", cpuid);
-                        return;
-                }
-                mdelay(1);
-        }
+delay1:
+	for (timeout = 10000; timeout > 0; --timeout) {
+		if (!(hwrpb->txrdy & cpumask))
+			goto ready1;
+		udelay(100);
+	}
+	goto timeout;
+
+delay2:
+	for (timeout = 10000; timeout > 0; --timeout) {
+		if (!(hwrpb->txrdy & cpumask))
+			goto ready2;
+		udelay(100);
+	}
+	goto timeout;
+
+timeout:
+	printk("Processor %x not ready\n", cpuid);
+	return;
 }
 
 /*
@@ -512,7 +562,8 @@
  *
  * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined
  */
-__initfunc(void setup_smp(void))
+void __init
+setup_smp(void)
 {
 	struct percpu_struct *cpubase, *cpu;
 	int i;
@@ -523,10 +574,10 @@
 	}
 
 	if (hwrpb->nr_processors > 1) {
-#if 0
-printk("setup_smp: nr_processors 0x%lx\n",
-       hwrpb->nr_processors);
-#endif
+
+		DBGS(("setup_smp: nr_processors %ld\n",
+		      hwrpb->nr_processors));
+
 		cpubase = (struct percpu_struct *)
 			((char*)hwrpb + hwrpb->processor_offset);
 		boot_cpu_palrev = cpubase->pal_revision;
@@ -541,12 +592,11 @@
 				if (i != boot_cpu_id)
 				  cpu->pal_revision = boot_cpu_palrev;
 			}
-#if 0
-printk("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n",
-       i, cpu->flags, cpu->type);
- printk("setup_smp: CPU %d: PAL rev 0x%lx\n",
-	i, cpu->pal_revision);
-#endif
+
+			DBGS(("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n",
+			      i, cpu->flags, cpu->type));
+			DBGS(("setup_smp: CPU %d: PAL rev 0x%lx\n",
+			      i, cpu->pal_revision));
 		}
 	} else {
 		smp_num_probed = 1;
@@ -560,132 +610,59 @@
 static void
 secondary_console_message(void)
 {
-        int mycpu, i, cnt;
+	int mycpu, i, cnt;
 	unsigned long txrdy = hwrpb->txrdy;
 	char *cp1, *cp2, buf[80];
-        struct percpu_struct *cpu;
-
-        mycpu = hard_smp_processor_id();
-
-#if 0
-printk("secondary_console_message: TXRDY 0x%lx.\n", txrdy);
-#endif
-	 for (i = 0; i < NR_CPUS; i++) {
-	   if (txrdy & (1L << i)) {
-#if 0
-printk("secondary_console_message: TXRDY contains CPU %d.\n", i);
-#endif
-	     cpu = (struct percpu_struct *)
-	       ((char*)hwrpb
-		+ hwrpb->processor_offset
-		+ i * hwrpb->processor_size);
-#if 1
-	     printk("secondary_console_message: on %d from %d"
-		    " HALT_REASON 0x%lx FLAGS 0x%lx\n",
-		    mycpu, i, cpu->halt_reason, cpu->flags);
-#endif
-	     cnt = cpu->ipc_buffer[0] >> 32;
-	     if (cnt <= 0 || cnt >= 80)
-	       strcpy(buf,"<<< BOGUS MSG >>>");
-	     else {
-	       cp1 = (char *) &cpu->ipc_buffer[11];
-	       cp2 = buf;
-	       while (cnt--) {
-		 if (*cp1 == '\r' || *cp1 == '\n') {
-		   *cp2++ = ' '; cp1++;
-		 } else
-		   *cp2++ = *cp1++;
-	       }
-	       *cp2 = 0;
-	     }
-#if 1
-	     printk("secondary_console_message: on %d message is '%s'\n",
-		    mycpu, buf);
-#endif
-	   }
-		}
-	 hwrpb->txrdy = 0;
-	 return;
-}
-
-static int
-halt_on_panic(unsigned int this_cpu)
-{
-	halt();
-	return 0;
-}
+	struct percpu_struct *cpu;
 
-static int
-local_flush_tlb_all(unsigned int this_cpu)
-{
-	tbia();
-	clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
-	return 0;
-}
+	DBGS(("secondary_console_message: TXRDY 0x%lx.\n", txrdy));
 
-static int
-local_flush_tlb_mm(unsigned int this_cpu)
-{
-	struct mm_struct * mm = ipi_msg_flush_tb.p.flush_mm;
-	if (mm == current->mm)
-		flush_tlb_current(mm);
-	clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
-	return 0;
-}
+	mycpu = hard_smp_processor_id();
 
-static int
-local_flush_tlb_page(unsigned int this_cpu)
-{
-	struct vm_area_struct * vma = ipi_msg_flush_tb.p.flush_vma;
-	struct mm_struct * mm = vma->vm_mm;
+	for (i = 0; i < NR_CPUS; i++) {
+		if (!(txrdy & (1L << i)))
+			continue;
 
-	if (mm == current->mm)
-		flush_tlb_current_page(mm, vma, ipi_msg_flush_tb.flush_addr);
-	clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
-	return 0;
-}
+		DBGS(("secondary_console_message: "
+		      "TXRDY contains CPU %d.\n", i));
 
-static int
-wrapper_local_flush_tlb_page(unsigned int this_cpu)
-{
-#if 0
-	int cpu = smp_processor_id();
+		cpu = (struct percpu_struct *)
+		  ((char*)hwrpb
+		   + hwrpb->processor_offset
+		   + i * hwrpb->processor_size);
+
+ 		printk("secondary_console_message: on %d from %d"
+		       " HALT_REASON 0x%lx FLAGS 0x%lx\n",
+		       mycpu, i, cpu->halt_reason, cpu->flags);
+
+		cnt = cpu->ipc_buffer[0] >> 32;
+		if (cnt <= 0 || cnt >= 80)
+			strcpy(buf, "<<< BOGUS MSG >>>");
+		else {
+			cp1 = (char *) &cpu->ipc_buffer[11];
+			cp2 = buf;
+			strcpy(cp2, cp1);
+			
+			while ((cp2 = strchr(cp2, '\r')) != 0) {
+				*cp2 = ' ';
+				if (cp2[1] == '\n')
+					cp2[1] = ' ';
+			}
+		}
 
-	if (cpu) {
-	  printk("wrapper: ipi_msg_flush_tb.flush_addr 0x%lx [%d]\n",
-		 ipi_msg_flush_tb.flush_addr, atomic_read(&global_irq_count));
+		printk("secondary_console_message: on %d message is '%s'\n",
+		       mycpu, buf);
 	}
-#endif
-	local_flush_tlb_page(this_cpu);
-	return 0;
-}
 
-static int
-unknown_ipi(unsigned int this_cpu)
-{
-	printk("unknown_ipi() on CPU %d:  ", this_cpu);
-	return 1;
+	hwrpb->txrdy = 0;
 }
 
 enum ipi_message_type {
-  CPU_STOP,
-  TLB_ALL,
-  TLB_MM,
-  TLB_PAGE,
-  TLB_RANGE
-};
-
-static int (* ipi_func[32])(unsigned int) = {
-  halt_on_panic,
-  local_flush_tlb_all,
-  local_flush_tlb_mm,
-  wrapper_local_flush_tlb_page,
-  local_flush_tlb_mm,		/* a.k.a. local_flush_tlb_range */
-  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
-  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
-  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
-  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
-  unknown_ipi, unknown_ipi, unknown_ipi
+	IPI_TLB_ALL,
+	IPI_TLB_MM,
+	IPI_TLB_PAGE,
+	IPI_RESCHEDULE,
+	IPI_CPU_STOP
 };
 
 void
@@ -693,122 +670,165 @@
 {
 	int this_cpu = smp_processor_id();
 	volatile int * pending_ipis = &ipi_bits[this_cpu];
-	int ops;
+	unsigned long ops, which;
 
-	mb();		/* Order bit setting and interrupt. */
-#if 0
-	printk("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n",
-	       this_cpu, *pending_ipis, regs->pc);
-#endif
-	while ((ops = *pending_ipis)) {
-		int first;
-		for (first = 0; (ops & 1) == 0; ++first, ops >>= 1)
-			; /* look for the first thing to do */
-		clear_bit(first, pending_ipis);
-		mb();	/* Order bit clearing and data access. */
-		if ((*ipi_func[first])(this_cpu))
-			printk("%d\n", first);
-		mb();	/* Order data access and bit clearing. */
+	DBGS(("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n",
+	      this_cpu, *pending_ipis, regs->pc));
+
+	mb();	/* Order interrupt and bit testing. */
+	while ((ops = xchg(pending_ipis, 0)) != 0) {
+	  mb();	/* Order bit clearing and data access. */
+	  do {
+		unsigned long which;
+
+		which = ops & -ops;
+		ops &= ~which;
+		which = ffz(~which);
+
+		if (which < IPI_RESCHEDULE) {
+			if (which == IPI_TLB_ALL)
+				tbia();
+			else if (which == IPI_TLB_MM) {
+				struct mm_struct * mm;
+				mm = ipi_msg_flush_tb.p.flush_mm;
+				if (mm == current->mm)
+					flush_tlb_current(mm);
+			}
+			else /* IPI_TLB_PAGE */ {
+				struct vm_area_struct * vma;
+				struct mm_struct * mm;
+				unsigned long addr;
+
+				vma = ipi_msg_flush_tb.p.flush_vma;
+				mm = vma->vm_mm;
+				addr = ipi_msg_flush_tb.flush_addr;
+
+				if (mm == current->mm)
+					flush_tlb_current_page(mm, vma, addr);
+			}
+			clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+		}
+		else if (which == IPI_RESCHEDULE) {
+			/* Reschedule callback.  Everything to be done
+			   is done by the interrupt return path.  */
+		}
+		else if (which == IPI_CPU_STOP) {
+			halt();
+		}
+		else {
+			printk(KERN_CRIT "unknown_ipi() on CPU %ld: %d\n",
+			       this_cpu, which);
+		}
+	  } while (ops);
+	  mb();	/* Order data access and bit testing. */
 	}
+
+	cpu_data[this_cpu].ipi_count++;
+
 	if (hwrpb->txrdy)
-	  secondary_console_message();
+		secondary_console_message();
 }
 
-void
-send_ipi_message(long to_whom, enum ipi_message_type operation)
+static void
+send_ipi_message(unsigned long to_whom, enum ipi_message_type operation)
 {
-	int i;
-	unsigned int j;
+	long i, j;
 
-	mb();			/* Order out-of-band data and bit setting. */
-	for (i = 0, j = 1; i < NR_CPUS; ++i, j += j) {
-		if ((to_whom & j) == 0)
-			continue;
-		set_bit(operation, &ipi_bits[i]);
-		mb();		/* Order bit setting and interrupt. */
-		wripir(i);
+	/* Reduce the number of memory barriers by doing two loops,
+	   one to set the bits, one to invoke the interrupts.  */
+
+	mb();	/* Order out-of-band data and bit setting. */
+
+	for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) {
+		if (to_whom & j)
+			set_bit(operation, &ipi_bits[i]);
+	}
+
+	mb();	/* Order bit setting and interrupt. */
+
+	for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) {
+		if (to_whom & j)
+			wripir(i);
 	}
 }
 
 int
 smp_info(char *buffer)
 {
-	int i;
+	long i;
 	unsigned long sum = 0;
 	for (i = 0; i < NR_CPUS; i++)
-		sum += ipicnt[i];
+		sum += cpu_data[i].ipi_count;
 
-        return sprintf(buffer, "CPUs probed %d active %d map 0x%x IPIs %ld\n",
+	return sprintf(buffer, "CPUs probed %d active %d map 0x%x IPIs %ld\n",
 		       smp_num_probed, smp_num_cpus, cpu_present_map, sum);
 }
 
-/* wrapper for call from panic() */
 void
-smp_message_pass(int target, int msg, unsigned long data, int wait)
+smp_send_reschedule(int cpu)
 {
-	int me = smp_processor_id();
-
-	if (msg != MSG_STOP_CPU)
-		goto barf;
+	send_ipi_message(1 << cpu, IPI_RESCHEDULE);
+}
 
-	send_ipi_message(CPU_STOP, cpu_present_map ^ (1 << me));
-	return;
-barf:
-	printk("Yeeee, trying to send SMP msg(%d) on CPU %d\n", msg, me);
-	panic("Bogon SMP message pass.");
+void
+smp_send_stop(void)
+{
+	unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id());
+	send_ipi_message(to_whom, IPI_CPU_STOP);
 }
 
 void
 flush_tlb_all(void)
 {
-	unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
-	int timeout = 10000;
+	unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id());
+	long timeout = 1000000;
 
 	spin_lock_own(&kernel_flag, "flush_tlb_all");
 
 	ipi_msg_flush_tb.flush_tb_mask = to_whom;
-	send_ipi_message(to_whom, TLB_ALL);
+	send_ipi_message(to_whom, IPI_TLB_ALL);
 	tbia();
 
-	while (ipi_msg_flush_tb.flush_tb_mask) {
-		if (--timeout < 0) {
-			printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n",
-			       smp_processor_id(),
-			       ipi_msg_flush_tb.flush_tb_mask);
-			ipi_msg_flush_tb.flush_tb_mask = 0;
-			break;
-		}
-		/* Wait for all clear from other CPUs. */
-		udelay(100);
+	while (ipi_msg_flush_tb.flush_tb_mask && --timeout) {
+		udelay(1);
+		barrier();
+	}
+
+	if (timeout == 0) {
+		printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n",
+		       smp_processor_id(),
+		       ipi_msg_flush_tb.flush_tb_mask);
+		ipi_msg_flush_tb.flush_tb_mask = 0;
 	}
 }
 
 void
 flush_tlb_mm(struct mm_struct *mm)
 {
-	unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
-	int timeout = 10000;
+	unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id());
+	long timeout = 1000000;
 
 	spin_lock_own(&kernel_flag, "flush_tlb_mm");
 
-	ipi_msg_flush_tb.p.flush_mm = mm;
 	ipi_msg_flush_tb.flush_tb_mask = to_whom;
-	send_ipi_message(to_whom, TLB_MM);
+	ipi_msg_flush_tb.p.flush_mm = mm;
+	send_ipi_message(to_whom, IPI_TLB_MM);
 
 	if (mm != current->mm)
 		flush_tlb_other(mm);
 	else
 		flush_tlb_current(mm);
 
-	while (ipi_msg_flush_tb.flush_tb_mask) {
-	  if (--timeout < 0) {
-	    printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n",
-		   smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
-	    ipi_msg_flush_tb.flush_tb_mask = 0;
-	    break;
-	  }
-	  udelay(100);
-		; /* Wait for all clear from other CPUs. */
+	while (ipi_msg_flush_tb.flush_tb_mask && --timeout) {
+		udelay(1);
+		barrier();
+	}
+
+	if (timeout == 0) {
+		printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n",
+		       smp_processor_id(),
+		       ipi_msg_flush_tb.flush_tb_mask);
+		ipi_msg_flush_tb.flush_tb_mask = 0;
 	}
 }
 
@@ -816,68 +836,40 @@
 flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 {
 	int cpu = smp_processor_id();
-	unsigned int to_whom = cpu_present_map ^ (1 << cpu);
+	unsigned long to_whom = cpu_present_map ^ (1 << cpu);
 	struct mm_struct * mm = vma->vm_mm;
-	int timeout = 10000;
+	int timeout = 1000000;
 
 	spin_lock_own(&kernel_flag, "flush_tlb_page");
 
+	ipi_msg_flush_tb.flush_tb_mask = to_whom;
 	ipi_msg_flush_tb.p.flush_vma = vma;
 	ipi_msg_flush_tb.flush_addr = addr;
-	ipi_msg_flush_tb.flush_tb_mask = to_whom;
-	send_ipi_message(to_whom, TLB_PAGE);
+	send_ipi_message(to_whom, IPI_TLB_PAGE);
 
 	if (mm != current->mm)
 		flush_tlb_other(mm);
 	else
 		flush_tlb_current_page(mm, vma, addr);
 
-	while (ipi_msg_flush_tb.flush_tb_mask) {
-	  if (--timeout < 0) {
-	    printk("flush_tlb_page: STUCK on CPU %d [0x%x,0x%lx,%d]\n",
-		   cpu, ipi_msg_flush_tb.flush_tb_mask, addr,
-		   global_irq_holder);
-	    ipi_msg_flush_tb.flush_tb_mask = 0;
-	    break;
-	  }
-	  udelay(100);
-		; /* Wait for all clear from other CPUs. */
+	while (ipi_msg_flush_tb.flush_tb_mask && --timeout) {
+		udelay(1);
+		barrier();
+	}
+
+	if (timeout == 0) {
+		printk("flush_tlb_page: STUCK on CPU %d mask 0x%x\n",
+		       smp_processor_id(),
+		       ipi_msg_flush_tb.flush_tb_mask);
+		ipi_msg_flush_tb.flush_tb_mask = 0;
 	}
 }
 
 void
 flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-#if 0
+	/* On the Alpha we always flush the whole user tlb.  */
 	flush_tlb_mm(mm);
-#else
-	unsigned int to_whom;
-	int timeout;
-
-	timeout = 10000;
-	to_whom = cpu_present_map ^ (1 << smp_processor_id());
-
-	spin_lock_own(&kernel_flag, "flush_tlb_range");
-
-	ipi_msg_flush_tb.p.flush_mm = mm;
-	ipi_msg_flush_tb.flush_tb_mask = to_whom;
-	send_ipi_message(to_whom, TLB_MM);
-
-	if (mm != current->mm)
-		flush_tlb_other(mm);
-	else
-		flush_tlb_current(mm);
-
-	while (ipi_msg_flush_tb.flush_tb_mask) {
-	  if (--timeout < 0) {
-	    printk("flush_tlb_range: STUCK on CPU %d mask 0x%x\n",
-		   smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
-	    ipi_msg_flush_tb.flush_tb_mask = 0;
-	    break;
-	  }
-	  udelay(100); /* Wait for all clear from other CPUs. */
-	}
-#endif
 }
 
 #if DEBUG_SPINLOCK
@@ -902,8 +894,8 @@
 
 #else
 
-#define spinlock_raise_ipl(LOCK)	0
-#define spinlock_restore_ipl(PREV)	((void)0)
+#define spinlock_raise_ipl(LOCK)	((LOCK), 0)
+#define spinlock_restore_ipl(PREV)	((void)(PREV))
 
 #endif /* MANAGE_SPINLOCK_IPL */
 
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v2.2.0-pre1/linux/arch/i386/Makefile	Tue Jul 21 00:15:30 1998
+++ linux/arch/i386/Makefile	Wed Dec 30 14:17:25 1998
@@ -35,6 +35,10 @@
 CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586
 endif
 
+ifdef CONFIG_M586TSC
+CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586
+endif
+
 ifdef CONFIG_M686
 CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686
 endif
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.2.0-pre1/linux/arch/i386/config.in	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/config.in	Wed Dec 30 14:50:18 1998
@@ -14,8 +14,24 @@
 choice 'Processor family' \
 	"386			CONFIG_M386	\
 	 486/Cx486		CONFIG_M486	\
-	 Pentium/K5/5x86/6x86	CONFIG_M586	\
-	 PPro/K6/6x86MX		CONFIG_M686" Pentium
+	 586/K5/5x86/6x86	CONFIG_M586	\
+	 Pentium/TSC		CONFIG_M586TSC	\
+	 PPro/K6/6x86MX		CONFIG_M686" PPro
+#
+# Define implied options from the CPU selection here
+#
+if [ "$CONFIG_M386" != "n" ]; then
+  define_bool CONFIG_WP_WORKS_OK y
+  define_bool CONFIG_INVLPG y
+  define_bool CONFIG_BSWAP y
+fi
+if [ "$CONFIG_M686" = "y" -o "$CONFIG_M586TSC" = "y" ]; then
+  define_bool CONFIG_TSC y
+fi
+if [ "$CONFIG_M686" = "y" ]; then
+  define_bool CONFIG_GOOD_APIC y
+fi
+
 bool 'Math emulation' CONFIG_MATH_EMULATION
 bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
 bool 'Symmetric multi-processing support' CONFIG_SMP
@@ -36,15 +52,14 @@
 bool 'Networking support' CONFIG_NET
 bool 'PCI support' CONFIG_PCI
 if [ "$CONFIG_PCI" = "y" ]; then
-  unset CONFIG_PCI_BIOS CONFIG_PCI_DIRECT
   choice 'PCI access mode' \
 	"BIOS		CONFIG_PCI_GOBIOS	\
 	 Direct		CONFIG_PCI_GODIRECT	\
 	 Any		CONFIG_PCI_GOANY"	Any
-  if [ -n "$CONFIG_PCI_GOBIOS" -o -n "$CONFIG_PCI_GOANY" ]; then
+  if [ "$CONFIG_PCI_GOBIOS" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then
     define_bool CONFIG_PCI_BIOS y
   fi
-  if [ -n "$CONFIG_PCI_GODIRECT" -o -n "$CONFIG_PCI_GOANY" ]; then
+  if [ "$CONFIG_PCI_GODIRECT" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then
     define_bool CONFIG_PCI_DIRECT y
   fi
   bool '   PCI quirks' CONFIG_PCI_QUIRKS
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.2.0-pre1/linux/arch/i386/defconfig	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/defconfig	Wed Dec 30 14:44:48 1998
@@ -12,8 +12,11 @@
 #
 # CONFIG_M386 is not set
 # CONFIG_M486 is not set
-CONFIG_M586=y
-# CONFIG_M686 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+CONFIG_M686=y
+CONFIG_TSC=y
+CONFIG_GOOD_APIC=y
 # CONFIG_MATH_EMULATION is not set
 # CONFIG_MTRR is not set
 CONFIG_SMP=y
@@ -176,7 +179,6 @@
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
-# CONFIG_SCSI_QLOGIC_FC is not set
 # CONFIG_SCSI_SEAGATE is not set
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
@@ -305,7 +307,6 @@
 #
 # CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
-CONFIG_NFSD=y
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.2.0-pre1/linux/arch/i386/kernel/i386_ksyms.c	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/kernel/i386_ksyms.c	Tue Dec 29 11:18:18 1998
@@ -58,6 +58,9 @@
 EXPORT_SYMBOL_NOVERS(__put_user_2);
 EXPORT_SYMBOL_NOVERS(__put_user_4);
 
+EXPORT_SYMBOL(strtok);
+EXPORT_SYMBOL(strpbrk);
+
 EXPORT_SYMBOL(strncpy_from_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 EXPORT_SYMBOL(clear_user);
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.2.0-pre1/linux/arch/i386/kernel/irq.c	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/kernel/irq.c	Tue Dec 29 11:21:49 1998
@@ -900,7 +900,7 @@
 	/*
 	 * Wait for spurious interrupts to trigger
 	 */
-	for (delay = jiffies + HZ/10; delay > jiffies; )
+	for (delay = jiffies + HZ/10; time_after(delay, jiffies); )
 		/* about 100ms delay */ synchronize_irq();
 
 	/*
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.2.0-pre1/linux/arch/i386/kernel/ptrace.c	Fri Nov 27 13:09:22 1998
+++ linux/arch/i386/kernel/ptrace.c	Tue Dec 29 15:59:32 1998
@@ -420,7 +420,9 @@
 		case PTRACE_PEEKDATA: {
 			unsigned long tmp;
 
+			down(&child->mm->mmap_sem);
 			ret = read_long(child, addr, &tmp);
+			up(&child->mm->mmap_sem);
 			if (ret >= 0)
 				ret = put_user(tmp,(unsigned long *) data);
 			goto out;
@@ -451,7 +453,9 @@
       /* when I and D space are separate, this will have to be fixed. */
 		case PTRACE_POKETEXT: /* write the word at location addr. */
 		case PTRACE_POKEDATA:
+			down(&child->mm->mmap_sem);
 			ret = write_long(child,addr,data);
+			up(&child->mm->mmap_sem);
 			goto out;
 
 		case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.2.0-pre1/linux/arch/i386/kernel/setup.c	Sun Nov  8 14:02:42 1998
+++ linux/arch/i386/kernel/setup.c	Tue Dec 29 14:20:42 1998
@@ -597,7 +597,7 @@
 
 	for(n=0; n<NR_CPUS; n++, c++) {
 #ifdef __SMP__
-		if (!(cpu_present_map & (1<<n)))
+		if (!(cpu_online_map & (1<<n)))
 			continue;
 #endif
 		p += sprintf(p,"processor\t: %d\n"
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.2.0-pre1/linux/arch/i386/kernel/smp.c	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/kernel/smp.c	Wed Dec 30 14:47:10 1998
@@ -119,19 +119,21 @@
  * function prototypes:
  */
 static void cache_APIC_registers (void);
-
+static void stop_this_cpu (void);
 
 static int smp_b_stepping = 0;				/* Set if we find a B stepping CPU			*/
 
 static int max_cpus = -1;				/* Setup configured maximum number of CPUs to activate	*/
 int smp_found_config=0;					/* Have we found an SMP box 				*/
 
-unsigned long cpu_present_map = 0;			/* Bitmask of existing CPUs 				*/
+unsigned long cpu_present_map = 0;			/* Bitmask of physically existing CPUs 				*/
+unsigned long cpu_online_map = 0;			/* Bitmask of currently online CPUs 				*/
 int smp_num_cpus = 1;					/* Total count of live CPUs 				*/
 int smp_threads_ready=0;				/* Set when the idlers are all forked 			*/
 volatile int cpu_number_map[NR_CPUS];			/* which CPU maps to which logical number		*/
 volatile int __cpu_logical_map[NR_CPUS];			/* which logical number maps to which CPU		*/
-volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};	/* We always use 0 the rest is ready for parallel delivery */
+static volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};	/* We always use 0 the rest is ready for parallel delivery */
+static volatile unsigned long cpu_callout_map[NR_CPUS] = {0,};	/* We always use 0 the rest is ready for parallel delivery */
 volatile unsigned long smp_invalidate_needed;		/* Used for the invalidate map that's also checked in the spinlock */
 volatile unsigned long kstack_ptr;			/* Stack vector for booting CPUs			*/
 struct cpuinfo_x86 cpu_data[NR_CPUS];			/* Per CPU bogomips and other parameters 		*/
@@ -694,16 +696,55 @@
 	return memory_start;
 }
 
+extern void calibrate_delay(void);
+
 void __init smp_callin(void)
 {
-	extern void calibrate_delay(void);
-	int cpuid=GET_APIC_ID(apic_read(APIC_ID));
+	int cpuid;
+	unsigned long timeout;
+
+	/*
+	 * (This works even if the APIC is not enabled.)
+	 */
+	cpuid = GET_APIC_ID(apic_read(APIC_ID));
+
+	SMP_PRINTK(("CPU#%d waiting for CALLOUT\n", cpuid));
+
+	/*
+	 * STARTUP IPIs are fragile beasts as they might sometimes
+	 * trigger some glue motherboard logic. Complete APIC bus
+	 * silence for 1 second, this overestimates the time the
+	 * boot CPU is spending to send the up to 2 STARTUP IPIs
+	 * by a factor of two. This should be enough.
+	 */
+
+	/*
+	 * Waiting 2s total for startup (udelay is not yet working)
+	 */
+	timeout = jiffies + 2*HZ;
+	while (time_before(jiffies,timeout))
+	{
+		/*
+		 * Has the boot CPU finished it's STARTUP sequence?
+		 */
+		if (test_bit(cpuid, (unsigned long *)&cpu_callout_map[0]))
+			break;
+	}
+
+	while (!time_before(jiffies,timeout)) {
+		printk("BUG: CPU%d started up but did not get a callout!\n",
+			cpuid);
+		stop_this_cpu();
+	}
 
 	/*
-	 *	Activate our APIC
+	 * the boot CPU has finished the init stage and is spinning
+	 * on callin_map until we finish. We are free to set up this
+	 * CPU, first the APIC. (this is probably redundant on most
+	 * boards)
 	 */
 	
-	SMP_PRINTK(("CALLIN %d %d\n",hard_smp_processor_id(), smp_processor_id()));
+	SMP_PRINTK(("CALLIN, before enable_local_APIC().\n"));
 	enable_local_APIC();
 
 	/*
@@ -711,7 +752,12 @@
 	 */
 	setup_APIC_clock();
 
- 	sti();
+ 	__sti();
+
+#ifdef CONFIG_MTRR
+	/*  Must be done before calibration delay is computed  */
+	mtrr_init_secondary_cpu ();
+#endif
 	/*
 	 *	Get our bogomips.
 	 */
@@ -738,10 +784,11 @@
  */
 int __init start_secondary(void *unused)
 {
-#ifdef CONFIG_MTRR
-	/*  Must be done before calibration delay is computed  */
-	mtrr_init_secondary_cpu ();
-#endif
+	/*
+	 * Dont put anything before smp_callin(), SMP
+	 * booting is too fragile that we want to limit the
+	 * things done here to the most necessary things.
+	 */
 	smp_callin();
 	while (!atomic_read(&smp_commenced))
 		/* nothing */ ;
@@ -917,12 +964,18 @@
 		apic_write(APIC_ICR, cfg);						/* Kick the second 	*/
 
 		SMP_PRINTK(("Startup point 1.\n"));
+
 		timeout = 0;
+		SMP_PRINTK(("Waiting for send to finish...\n"));
 		do {
-			SMP_PRINTK(("Sleeping.\n")); mdelay(1000);
-			udelay(10);
-		} while ( (send_status = (apic_read(APIC_ICR) & 0x1000))
-			  && (timeout++ < 1000));
+			SMP_PRINTK(("+"));
+			udelay(100);
+			send_status = apic_read(APIC_ICR) & 0x1000;
+		} while (send_status && (timeout++ < 1000));
+
+		/*
+		 * Give the other CPU some time to accept the IPI.
+		 */
 		udelay(200);
 		accept_status = (apic_read(APIC_ESR) & 0xEF);
 	}
@@ -935,6 +988,13 @@
 
 	if ( !(send_status || accept_status) )
 	{
+		/*
+		 * allow APs to start initializing.
+		 */
+		SMP_PRINTK(("Before Callout %d.\n", i));
+		set_bit(i, (unsigned long *)&cpu_callout_map[0]);
+		SMP_PRINTK(("After Callout %d.\n", i));
+
 		for(timeout=0;timeout<50000;timeout++)
 		{
 			if (cpu_callin_map[0]&(1<<i))
@@ -980,6 +1040,7 @@
 
 static void smp_tune_scheduling (void)
 {
+	unsigned long cachesize;
 	/*
 	 * Rough estimation for SMP scheduling, this is the number of
 	 * cycles it takes for a fully memory-limited process to flush
@@ -990,7 +1051,21 @@
 	 *  L1 cache), on PIIs it's around 50-100 usecs, depending on
 	 *  the cache size)
 	 */
-	cacheflush_time = cpu_hz/1024*boot_cpu_data.x86_cache_size/5000;
+
+	if (boot_cpu_data.x86 <= 4) {
+		/*
+		 * this basically disables processor-affinity
+		 * scheduling on <=i486 based SMP boards.
+		 */
+		cacheflush_time = 0;
+	} else {
+		cachesize = boot_cpu_data.x86_cache_size;
+		if (cachesize == -1)
+			cachesize = 8; /* Pentiums */
+
+		cacheflush_time = cpu_hz/1024*cachesize/5000;
+	}
+
 	printk("per-CPU timeslice cutoff: %ld.%ld usecs.\n",
 		(long)cacheflush_time/(cpu_hz/1000000),
 		((long)cacheflush_time*100/(cpu_hz/1000000)) % 100);
@@ -1032,7 +1107,13 @@
 	printk("CPU%d: ", boot_cpu_id);
 	print_cpu_info(&cpu_data[boot_cpu_id]);
 
+	/*
+	 * not necessary because the MP table should list the boot
+	 * CPU too, but we do it for the sake of robustness anyway.
+	 * (and for the case when a non-SMP board boots an SMP kernel)
+	 */
 	cpu_present_map |= (1 << hard_smp_processor_id());
+
 	cpu_number_map[boot_cpu_id] = 0;
 
 	/*
@@ -1106,6 +1187,14 @@
 	 *	Now scan the CPU present map and fire up the other CPUs.
 	 */
 
+	/*
+	 * Add all detected CPUs. (later on we can down individual
+	 * CPUs which will change cpu_online_map but not necessarily
+	 * cpu_present_map. We are pretty much ready for hot-swap CPUs.)
+	 */
+	cpu_online_map = cpu_present_map;
+	mb();
+
 	SMP_PRINTK(("CPU map: %lx\n", cpu_present_map));
 
 	for(i=0;i<NR_CPUS;i++)
@@ -1116,7 +1205,7 @@
 		if (i == boot_cpu_id)
 			continue;
 
-		if ((cpu_present_map & (1 << i))
+		if ((cpu_online_map & (1 << i))
 		    && (max_cpus < 0 || max_cpus > cpucount+1))
 		{
 			do_boot_cpu(i);
@@ -1126,9 +1215,9 @@
 		 *	Make sure we unmap all failed CPUs
 		 */
 		
-		if (cpu_number_map[i] == -1 && (cpu_present_map & (1 << i))) {
-			printk("CPU #%d not responding. Removing from cpu_present_map.\n",i);
-			cpu_present_map &= ~(1 << i);
+		if (cpu_number_map[i] == -1 && (cpu_online_map & (1 << i))) {
+			printk("CPU #%d not responding. Removing from cpu_online_map.\n",i);
+			cpu_online_map &= ~(1 << i);
                 }
         }
 
@@ -1168,14 +1257,14 @@
 	if (cpucount==0)
 	{
 		printk(KERN_ERR "Error: only one processor found.\n");
-		cpu_present_map=(1<<hard_smp_processor_id());
+		cpu_online_map = (1<<hard_smp_processor_id());
 	}
 	else
 	{
 		unsigned long bogosum=0;
 		for(i=0;i<32;i++)
 		{
-			if (cpu_present_map&(1<<i))
+			if (cpu_online_map&(1<<i))
 				bogosum+=cpu_data[i].loops_per_sec;
 		}
 		printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
@@ -1213,7 +1302,7 @@
  * Silly serialization to work around CPU bug in P5s.
  * We can safely turn it off on a 686.
  */
-#if defined(CONFIG_M686) & !defined(SMP_DEBUG)
+#ifdef CONFIG_GOOD_APIC
 # define FORCE_APIC_SERIALIZATION 0
 #else
 # define FORCE_APIC_SERIALIZATION 1
@@ -1386,44 +1475,51 @@
 	unsigned long flags;
 
 	/*
-	 * The assignment is safe because it's volatile so the
-	 * compiler cannot reorder it, because the i586 has
-	 * strict memory ordering and because only the kernel
-	 * lock holder may issue a tlb flush. If you break any
-	 * one of those three change this to an atomic bus
-	 * locked or.
+	 * it's important that we do not generate any APIC traffic
+	 * until the AP CPUs have booted up!
 	 */
+	if (cpu_online_map) {
+		/*
+		 * The assignment is safe because it's volatile so the
+		 * compiler cannot reorder it, because the i586 has
+		 * strict memory ordering and because only the kernel
+		 * lock holder may issue a tlb flush. If you break any
+		 * one of those three change this to an atomic bus
+		 * locked or.
+		 */
 
-	smp_invalidate_needed = cpu_present_map;
+		smp_invalidate_needed = cpu_online_map;
 
-	/*
-	 * Processors spinning on some lock with IRQs disabled
-	 * will see this IRQ late. The smp_invalidate_needed
-	 * map will ensure they don't do a spurious flush tlb
-	 * or miss one.
-	 */
+		/*
+		 * Processors spinning on some lock with IRQs disabled
+		 * will see this IRQ late. The smp_invalidate_needed
+		 * map will ensure they don't do a spurious flush tlb
+		 * or miss one.
+		 */
 	
-	__save_flags(flags);
-	__cli();
+		__save_flags(flags);
+		__cli();
 
-	send_IPI_allbutself(INVALIDATE_TLB_VECTOR);
+		send_IPI_allbutself(INVALIDATE_TLB_VECTOR);
 
-	/*
-	 * Spin waiting for completion
-	 */
-
-	stuck = 50000000;
-	while (smp_invalidate_needed) {
 		/*
-		 * Take care of "crossing" invalidates
+		 * Spin waiting for completion
 		 */
-		if (test_bit(cpu, &smp_invalidate_needed))
+
+		stuck = 50000000;
+		while (smp_invalidate_needed) {
+			/*
+			 * Take care of "crossing" invalidates
+			 */
+			if (test_bit(cpu, &smp_invalidate_needed))
 			clear_bit(cpu, &smp_invalidate_needed);
-		--stuck;
-		if (!stuck) {
-			printk("stuck on TLB IPI wait (CPU#%d)\n",cpu);
-			break;
+			--stuck;
+			if (!stuck) {
+				printk("stuck on TLB IPI wait (CPU#%d)\n",cpu);
+				break;
+			}
 		}
+		__restore_flags(flags);
 	}
 
 	/*
@@ -1431,7 +1527,6 @@
 	 */
 	local_flush_tlb();
 
-	__restore_flags(flags);
 }
 
 
@@ -1584,14 +1679,24 @@
 	ack_APIC_irq();
 }
 
+static void stop_this_cpu (void)
+{
+	/*
+	 * Remove this CPU:
+	 */
+	clear_bit(smp_processor_id(), &cpu_online_map);
+
+	if (cpu_data[smp_processor_id()].hlt_works_ok)
+		for(;;) __asm__("hlt");
+	for (;;);
+}
+
 /*
  *	CPU halt call-back
  */
 asmlinkage void smp_stop_cpu_interrupt(void)
 {
-	if (cpu_data[smp_processor_id()].hlt_works_ok)
-		for(;;) __asm__("hlt");
-	for (;;) ;
+	stop_this_cpu();
 }
 
 void (*mtrr_hook) (void) = NULL;
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.2.0-pre1/linux/arch/i386/kernel/time.c	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/kernel/time.c	Wed Dec 30 14:19:37 1998
@@ -118,10 +118,7 @@
 
 #define TICK_SIZE tick
 
-/*
- * Older CPU's don't have the rdtsc instruction..
- */
-#if CPU < 586
+#ifndef CONFIG_TSC
 
 /* This function must be called with interrupts disabled 
  * It was inspired by Steve McCanne's microtime-i386 for BSD.  -- jrs
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.2.0-pre1/linux/arch/i386/mm/init.c	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/mm/init.c	Wed Dec 30 14:46:33 1998
@@ -408,7 +408,7 @@
 	if (boot_cpu_data.wp_works_ok < 0) {
 		boot_cpu_data.wp_works_ok = 0;
 		printk("No.\n");
-#ifndef CONFIG_M386
+#ifdef CONFIG_WP_WORKS_OK
 		panic("This kernel doesn't support CPU's with broken WP. Recompile it for a 386!");
 #endif
 	} else
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.2.0-pre1/linux/arch/ppc/defconfig	Mon Dec 28 15:00:52 1998
+++ linux/arch/ppc/defconfig	Wed Dec 30 10:55:07 1998
@@ -14,8 +14,8 @@
 # CONFIG_ALL_PPC is not set
 # CONFIG_APUS is not set
 # CONFIG_MBX is not set
-# CONFIG_SMP is not set
 CONFIG_MACH_SPECIFIC=y
+# CONFIG_SMP is not set
 
 #
 # General setup
@@ -165,6 +165,7 @@
 # SCSI low-level drivers
 #
 # CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
@@ -175,14 +176,16 @@
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_BUSLOGIC is not set
 # CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_EATA_DMA is not set
 # CONFIG_SCSI_EATA_PIO is not set
-# CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
 # CONFIG_SCSI_GDTH is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 # CONFIG_SCSI_NCR53C8XX is not set
@@ -192,6 +195,7 @@
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
 # CONFIG_SCSI_SEAGATE is not set
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
@@ -219,6 +223,7 @@
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_RTL8139 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 is not set
@@ -226,7 +231,7 @@
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
 CONFIG_DE4X5=y
-CONFIG_DEC_ELCP=m
+# CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
 # CONFIG_EEXPRESS_PRO100 is not set
 # CONFIG_LNE390 is not set
@@ -254,6 +259,7 @@
 # CONFIG_TR is not set
 # CONFIG_SHAPER is not set
 # CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
 
 #
 # Amateur Radio support
@@ -283,6 +289,7 @@
 CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
 # CONFIG_FB_MATROX is not set
+CONFIG_FB_ATY=y
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
@@ -312,9 +319,17 @@
 # CONFIG_MOUSE is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
+CONFIG_NVRAM=y
 # CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
 # CONFIG_VIDEO_DEV is not set
-CONFIG_NVRAM=y
+
+#
+# Joystick support
+#
 # CONFIG_JOYSTICK is not set
 
 #
@@ -326,37 +341,47 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
+CONFIG_AUTOFS_FS=y
+# 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_UMSDOS_FS is not set
 CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
 CONFIG_NFSD=y
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
-CONFIG_HFS_FS=y
-# CONFIG_ROMFS_FS is not set
-CONFIG_AUTOFS_FS=y
-# CONFIG_UFS_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
 # CONFIG_BSD_DISKLABEL is not set
+CONFIG_MAC_PARTITION=y
 # CONFIG_SMD_DISKLABEL is not set
 # CONFIG_SOLARIS_X86_PARTITION is not set
-CONFIG_DEVPTS_FS=y
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-CONFIG_MAC_PARTITION=y
+# CONFIG_UNIXWARE_DISKLABEL is not set
 CONFIG_NLS=y
 
 #
@@ -387,6 +412,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_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
 #
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v2.2.0-pre1/linux/arch/ppc/kernel/head.S	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/head.S	Wed Dec 30 10:55:07 1998
@@ -1,7 +1,7 @@
 /*
  *  arch/ppc/kernel/head.S
  *
- *  $Id: head.S,v 1.113 1998/12/02 18:41:00 cort Exp $
+ *  $Id: head.S,v 1.114 1998/12/28 10:28:45 paulus Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -532,9 +532,10 @@
 
 	stw	r3,(_CCR+4)(r21);
 #endif
-	addi	r3,r1,STACK_FRAME_OVERHEAD;
-	li	r20,MSR_KERNEL;
-	bl	transfer_to_handler;
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	li	r20,MSR_KERNEL
+	li	r4,0
+	bl	transfer_to_handler
 	.long	do_IRQ;
 	.long	int_return
 	
@@ -1199,12 +1200,6 @@
  * physical address of the hash table are known.  These definitions
  * of Hash_base and Hash_bits below are just an example.
  */
-/*
- * Note that the 603s won't come here, since the 603
- * loads tlb directly into the tlb from the linux tables, while
- * others (601, 604, etc.) call hash_page() to load entries from
- * the linux tables into the hash table.  -- Cort
- */	
 Hash_base = 0x180000
 Hash_bits = 12				/* e.g. 256kB hash table */
 Hash_msk = (((1 << Hash_bits) - 1) * 64)
@@ -1219,11 +1214,19 @@
 	lis	r2,hash_table_lock@h
 	ori	r2,r2,hash_table_lock@l
 	tophys(r2,r2,r6)
+	lis	r6,100000000@h
+	mtctr	r6
+	lwz	r0,PROCESSOR-TSS(r5)
+	or	r0,r0,r6
 10:	lwarx	r6,0,r2
-	stwcx.	r2,0,r2
-	bne-	10b
-	cmpi	0,0,r6,0
-	bne	10b
+	cmpi	0,r6,0
+	bne-	12f
+	stwcx.	r0,0,r2
+	beq+	11f
+12:	cmpw	r6,r0
+	bdnzf	2,10b
+	tw	31,31,31
+11:
 #endif
 	/* Get PTE (linux-style) and check access */
 	lwz	r5,PG_TABLES(r5)		
@@ -1271,6 +1274,9 @@
 	li	r2,8			/* PTEs/group */
 	bne	10f			/* no PTE: go look for an empty slot */
 	tlbie	r3			/* invalidate TLB entry */
+#ifdef __SMP__
+	tlbsync
+#endif
 
 	/* Search the primary PTEG for a PTE whose 1st word matches r5 */
 	mtctr	r2
@@ -1315,7 +1321,6 @@
 	bdnzf	2,2b
 	beq+	found_empty
 
-#if 1
 	/*
 	 * Choose an arbitrary slot in the primary PTEG to overwrite.
 	 * Since both the primary and secondary PTEGs are full, and we
@@ -1331,26 +1336,6 @@
 	andi.	r2,r2,0x38
 	stw	r2,next_slot@l(0)
 	add	r3,r4,r2
-#else
-	/* now, allow 2nd hash as well as 1st */
-	lwz	r2,next_slot@l(0)
-	addi	r2,r2,8
-	andi.	r2,r2,0x78
-	stw	r2,next_slot@l(0)
-	cmpi	0,0,r2,0x38             /* if it's the 2nd hash */
-	bgt	second_evict
-first_evict:
-	xori	r5,r5,0x40		/* clear H bit again */
-	add	r3,r4,r2
-	b	11f
-second_evict:
-	.globl	hash_page_patch_D
-hash_page_patch_D:	
-	xoris	r3,r4,Hash_msk>>16	/* compute secondary hash */
-	xori	r3,r3,0xffc0
-	subi	r2,r2,0x40
-	addi	r3,r3,r2
-#endif
 11:		
 	/* update counter of evicted pages */
 	lis	r2,htab_evicts@h
@@ -1390,6 +1375,13 @@
 	addi	r3,r3,1
 	stw	r3,0(r2)
 
+#ifdef __SMP__
+	lis	r2,hash_table_lock@ha
+	tophys(r2,r2,r6)
+	li	r0,0
+	stw	r0,hash_table_lock@l(r2)
+#endif
+
 	/* Return from the exception */
 	lwz	r3,_CCR(r21)
 	lwz	r4,_LINK(r21)
@@ -1397,13 +1389,6 @@
 	mtcrf	0xff,r3
 	mtlr	r4
 	mtctr	r5
-#ifdef __SMP__
-	lis	r2,hash_table_lock@h
-	ori	r2,r2,hash_table_lock@l
-	tophys(r2,r2,r6)
-	li	r6,0
-	stw	r6,0(r2)
-#endif		
 	REST_GPR(0, r21)
 	REST_2GPRS(1, r21)
 	REST_4GPRS(3, r21)
@@ -1418,11 +1403,10 @@
 	
 hash_page_out:
 #ifdef __SMP__
-	lis	r2,hash_table_lock@h
-	ori	r2,r2,hash_table_lock@l
+	lis	r2,hash_table_lock@ha
 	tophys(r2,r2,r6)
-	li	r6,0
-	stw	r6,0(r2)
+	li	r0,0
+	stw	r0,hash_table_lock@l(r2)
 #endif		
 	blr
 next_slot:
@@ -1741,6 +1725,9 @@
 2:
 	SYNC			/* Force all PTE updates to finish */
 	tlbia			/* Clear all TLB entries */
+#ifdef __SMP__
+	tlbsync
+#endif
 #ifndef CONFIG_8xx
 	mtspr	SDR1,r6
 	li	r0,16		/* load up segment register values */
@@ -2012,16 +1999,13 @@
 
 /* FALL THROUGH into int_return */
 #ifdef __SMP__
-	/* drop scheduler_lock since we weren't called by schedule() */
+	/* call schedule_tail if this is the first time for a child process */
 	lwz	r5,TSS_SMP_FORK_RET(r4)
 	cmpi	0,r5,0
 	beq+	int_return
 	li	r3,0
-	lis	r5,scheduler_lock@ha
 	stw	r3,TSS_SMP_FORK_RET(r4)
-	stw	r3,scheduler_lock@l+4(r5)	/* owner_pc */
-	stw	r3,scheduler_lock@l+8(r5)	/* owner_cpu */
-	stw	r3,scheduler_lock@l(r5)		/* lock */
+	bl	schedule_tail
 #endif /* __SMP__ */
 
 /*
@@ -2121,6 +2105,7 @@
 	li	r0,0x0fac
 	stw	r0,TRAP(r1)
 	addi	r3,r1,STACK_FRAME_OVERHEAD
+	li	r4,1
 	bl	do_IRQ
 	addi	r1,r1,INT_FRAME_SIZE+STACK_UNDERHEAD
 	lwz	r0,4(r1)
@@ -2227,15 +2212,6 @@
  */
 #ifndef CONFIG_8xx	
 _GLOBAL(flush_hash_segments)
-#ifdef __SMP__
-	lis	r9,hash_table_lock@h
-	ori	r9,r9,hash_table_lock@l
-10:	lwarx	r6,0,r9
-	stwcx.	r9,0,r9
-	bne-	10b
-	cmpi	0,0,r6,0
-	bne	10b
-#endif
 #ifdef NO_RELOAD_HTAB
 /*
  * Bitmask of PVR numbers of 603-like chips,
@@ -2247,8 +2223,31 @@
 	rlwinm	r0,r0,16,27,31
 	lis	r9,PVR_603_LIKE@h
 	rlwnm.	r0,r9,r0,0,0
-	bne	99f
+	beq+	99f
+	tlbia
+	isync
+	blr
+99:
 #endif /* NO_RELOAD_HTAB */
+#ifdef __SMP__
+	/* Note - we had better not do anything which could generate
+	   a hash table miss while we have the hash table locked,
+	   or we'll get a deadlock.  -paulus */
+	mfmsr	r10
+	sync
+	rlwinm	r0,r10,0,17,15	/* clear bit 16 (MSR_EE) */
+	mtmsr	r0
+	SYNC
+	lis	r9,hash_table_lock@h
+	ori	r9,r9,hash_table_lock@l
+	lwz	r8,PROCESSOR(r2)
+	oris	r8,r8,8
+10:	lwarx	r6,0,r9
+	cmpi	0,r6,0
+	bne-	10b
+	stwcx.	r8,0,r9
+	bne-	10b
+#endif
 	rlwinm	r3,r3,7,1,24		/* put VSID lower limit in position */
 	oris	r3,r3,0x8000		/* set V bit */
 	rlwinm	r4,r4,7,1,24		/* put VSID upper limit in position */
@@ -2270,14 +2269,16 @@
 	stw	r0,0(r5)		/* invalidate entry */
 2:	bdnz	1b			/* continue with loop */
 	sync
-99:	tlbia
+	tlbia
 	isync
 #ifdef __SMP__
-	lis	r3,hash_table_lock@h
-	ori	r3,r3,hash_table_lock@l
-	li	r6,0
-	stw	r6,0(r3)
-#endif		
+	tlbsync
+	lis	r3,hash_table_lock@ha
+	li	r0,0
+	stw	r0,hash_table_lock@l(r3)
+	mtmsr	r10
+	SYNC
+#endif
 	blr
 
 /*
@@ -2286,22 +2287,36 @@
  * flush_hash_page(unsigned context, unsigned long va)
  */
 _GLOBAL(flush_hash_page)
-#ifdef __SMP__
-	lis	r9,hash_table_lock@h
-	ori	r9,r9,hash_table_lock@l
-10:	lwarx	r6,0,r9
-	stwcx.	r9,0,r9
-	bne-	10b
-	cmpi	0,0,r6,0
-	bne	10b
-#endif
 #ifdef NO_RELOAD_HTAB
 	mfspr	r0,PVR
 	rlwinm	r0,r0,16,27,31
 	lis	r9,PVR_603_LIKE@h
 	rlwnm.	r0,r9,r0,0,0
-	bne	99f
+	beq+	99f
+	tlbie	r4			/* in hw tlb too */
+	isync
+	blr
+99:
 #endif /* NO_RELOAD_HTAB */		
+#ifdef __SMP__
+	/* Note - we had better not do anything which could generate
+	   a hash table miss while we have the hash table locked,
+	   or we'll get a deadlock.  -paulus */
+	mfmsr	r10
+	sync
+	rlwinm	r0,r10,0,17,15		/* clear bit 16 (MSR_EE) */
+	mtmsr	r0
+	SYNC
+	lis	r9,hash_table_lock@h
+	ori	r9,r9,hash_table_lock@l
+	lwz	r8,PROCESSOR(r2)
+	oris	r8,r8,9
+10:	lwarx	r6,0,r9
+	cmpi	0,r6,0
+	bne-	10b
+	stwcx.	r8,0,r9
+	bne-	10b
+#endif
 	rlwinm	r3,r3,11,1,20		/* put context into vsid */
 	rlwimi	r3,r4,11,21,24		/* put top 4 bits of va into vsid */
 	oris	r3,r3,0x8000		/* set V (valid) bit */
@@ -2334,16 +2349,19 @@
 3:	li	r0,0
 	stw	r0,0(r7)		/* invalidate entry */
 4:	sync
-99:	tlbie	r4			/* in hw tlb too */
+	tlbie	r4			/* in hw tlb too */
 	isync
 #ifdef __SMP__
+	tlbsync
 	lis	r3,hash_table_lock@h
-	ori	r3,r3,hash_table_lock@l
-	li	r6,0
-	stw	r6,0(r3)
-#endif		
+	li	r0,0
+	stw	r0,hash_table_lock@l(r3)
+	mtmsr	r10
+	SYNC
+#endif
 	blr
 #endif /* CONFIG_8xx */
+
 /*
  * This routine is just here to keep GCC happy - sigh...
  */	
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- v2.2.0-pre1/linux/arch/ppc/kernel/idle.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/kernel/idle.c	Wed Dec 30 10:55:07 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: idle.c,v 1.56 1998/10/13 19:14:36 paulus Exp $
+ * $Id: idle.c,v 1.57 1998/12/28 10:28:46 paulus Exp $
  *
  * Idle daemon for PowerPC.  Idle daemon will handle any action
  * that needs to be taken when the system becomes idle.
@@ -41,26 +41,25 @@
 
 int idled(void *unused)
 {
-	int ret = -EPERM;
-
+	/* endless loop with no priority at all */
+	current->priority = 0;
+	current->counter = -100;
 	for (;;)
 	{
 		__sti();
 		
-		/* endless loop with no priority at all */
-		current->priority = 0;
-		current->counter = 0;
-
 		check_pgt_cache();
 
 		if ( !current->need_resched && zero_paged_on ) zero_paged();
 		if ( !current->need_resched && htab_reclaim_on ) htab_reclaim();
 		if ( !current->need_resched ) power_save();
-		run_task_queue(&tq_scheduler);
-		schedule();
+
+#ifdef __SMP__
+		if (current->need_resched)
+#endif
+			schedule();
 	}
-	ret = 0;
-	return ret;
+	return 0;
 }
 
 #ifdef __SMP__
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- v2.2.0-pre1/linux/arch/ppc/kernel/irq.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/irq.c	Wed Dec 30 10:55:07 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: irq.c,v 1.90 1998/12/10 02:39:46 cort Exp $
+ * $Id: irq.c,v 1.91 1998/12/28 10:28:47 paulus Exp $
  *
  *  arch/ppc/kernel/irq.c
  *
@@ -457,7 +457,6 @@
 }
 
 
-#define MAXCOUNT 100000000
 static inline void wait_on_irq(int cpu)
 {
 	int count = MAXCOUNT;
@@ -510,7 +509,7 @@
 void synchronize_bh(void)
 {
 	if (atomic_read(&global_bh_count) && !in_interrupt())
-			wait_on_bh();
+		wait_on_bh();
 }
 
 
@@ -532,6 +531,8 @@
 
 static inline void get_irqlock(int cpu)
 {
+	unsigned int loops = MAXCOUNT;
+
 	if (test_and_set_bit(0,&global_irq_lock)) {
 		/* do we already hold the lock? */
 		if ((unsigned char) cpu == global_irq_holder)
@@ -539,12 +540,17 @@
 		/* Uhhuh.. Somebody else got it. Wait.. */
 		do {
 			do {
-				
+				if (loops-- == 0) {
+					printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder);
+#ifdef CONFIG_XMON
+					xmon(0);
+#endif
+				}
 			} while (test_bit(0,&global_irq_lock));
 		} while (test_and_set_bit(0,&global_irq_lock));		
 	}
 	/* 
-	 * We also to make sure that nobody else is running
+	 * We also need to make sure that nobody else is running
 	 * in an interrupt context. 
 	 */
 	wait_on_irq(cpu);
@@ -640,7 +646,7 @@
 
 #endif /* __SMP__ */
 
-asmlinkage void do_IRQ(struct pt_regs *regs)
+asmlinkage void do_IRQ(struct pt_regs *regs, int isfake)
 {
 	int irq;
 	unsigned long bits;
@@ -659,15 +665,39 @@
 #ifdef __SMP__
 	if ( cpu != 0 )
 	{
-		if (!atomic_read(&n_lost_interrupts))
+		if (!isfake)
 		{
 			extern void smp_message_recv(void);
+#ifdef CONFIG_XMON
+			static int xmon_2nd;
+			if (xmon_2nd)
+				xmon(regs);
+#endif
 			smp_message_recv();
 			goto out;
 		}
 		/* could be here due to a do_fake_interrupt call but we don't
 		   mess with the controller from the second cpu -- Cort */
 		goto out;
+	}
+
+	{
+		unsigned int loops = MAXCOUNT;
+		while (test_bit(0, &global_irq_lock)) {
+			if (smp_processor_id() == global_irq_holder) {
+				printk("uh oh, interrupt while we hold global irq lock!\n");
+#ifdef CONFIG_XMON
+				xmon(0);
+#endif
+				break;
+			}
+			if (loops-- == 0) {
+				printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder);
+#ifdef CONFIG_XMON
+				xmon(0);
+#endif
+			}
+		}
 	}
 #endif /* __SMP__ */			
 
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.2.0-pre1/linux/arch/ppc/kernel/misc.S	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/misc.S	Wed Dec 30 10:55:07 1998
@@ -610,16 +610,6 @@
 	bnelr			/* return if parent */
 	mtlr	r4              /* fn addr in lr */
 	mr	r3,r5	        /* load arg and call fn */
-#if 0/*def __SMP__*/
-	/* drop scheduler_lock since schedule() called us */
-	lis	r4,scheduler_lock@ha
-	li	r5,0
-	stw	r5,scheduler_lock@l+4(r4)	/* owner_pc */
-	stw	r5,scheduler_lock@l+8(r4)	/* owner_cpu */
-	stw	r5,scheduler_lock@l(r4)
-	sync
-	isync
-#endif /* __SMP__ */
 	blrl
 	li	r0,__NR_exit	/* exit after child exits */
         li	r3,0
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- v2.2.0-pre1/linux/arch/ppc/kernel/ppc_ksyms.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Wed Dec 30 10:55:07 1998
@@ -25,6 +25,7 @@
 #include <asm/pci-bridge.h>
 #include <asm/irq.h>
 #include <asm/feature.h>
+#include <asm/spinlock.h>
 
 #define __KERNEL_SYSCALLS__
 #include <linux/unistd.h>
@@ -32,7 +33,7 @@
 extern void transfer_to_handler(void);
 extern void int_return(void);
 extern void syscall_trace(void);
-extern void do_IRQ(struct pt_regs *regs);
+extern void do_IRQ(struct pt_regs *regs, int isfake);
 extern void MachineCheckException(struct pt_regs *regs);
 extern void AlignmentException(struct pt_regs *regs);
 extern void ProgramCheckException(struct pt_regs *regs);
@@ -156,6 +157,19 @@
 EXPORT_SYMBOL(giveup_fpu);
 EXPORT_SYMBOL(flush_icache_range);
 EXPORT_SYMBOL(xchg_u32);
+#ifdef __SMP__
+EXPORT_SYMBOL(__global_cli);
+EXPORT_SYMBOL(__global_sti);
+EXPORT_SYMBOL(__global_save_flags);
+EXPORT_SYMBOL(__global_restore_flags);
+EXPORT_SYMBOL(_spin_lock);
+EXPORT_SYMBOL(_spin_unlock);
+EXPORT_SYMBOL(spin_trylock);
+EXPORT_SYMBOL(_read_lock);
+EXPORT_SYMBOL(_read_unlock);
+EXPORT_SYMBOL(_write_lock);
+EXPORT_SYMBOL(_write_unlock);
+#endif
 
 #ifndef CONFIG_MACH_SPECIFIC
 EXPORT_SYMBOL(_machine);
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.2.0-pre1/linux/arch/ppc/kernel/process.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/process.c	Wed Dec 30 10:55:07 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: process.c,v 1.68 1998/11/15 19:59:02 cort Exp $
+ * $Id: process.c,v 1.69 1998/12/28 10:28:49 paulus Exp $
  *
  *  linux/arch/ppc/kernel/process.c
  *
@@ -182,13 +182,6 @@
 	if ( prev->tss.regs->msr & MSR_FP )
 		smp_giveup_fpu(prev);
 
-	/* be noisy about processor changes for debugging -- Cort */
-	if ( (new->last_processor != NO_PROC_ID) &&
-	     (new->last_processor != new->processor) )
-		printk("switch_to(): changing cpu's %d -> %d %s/%d\n",
-		       new->last_processor,new->processor,
-		       new->comm,new->pid);
-	
 	prev->last_processor = prev->processor;
 	current_set[smp_processor_id()] = new;
 #endif /* __SMP__ */
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/ptrace.c linux/arch/ppc/kernel/ptrace.c
--- v2.2.0-pre1/linux/arch/ppc/kernel/ptrace.c	Thu Aug  6 14:06:29 1998
+++ linux/arch/ppc/kernel/ptrace.c	Tue Dec 29 16:32:36 1998
@@ -362,7 +362,9 @@
 		case PTRACE_PEEKDATA: {
 			unsigned long tmp;
 
+			down(&child->mm->mmap_sem);
 			ret = read_long(child, addr, &tmp);
+			up(&child->mm->mmap_sem);
 			if (ret < 0)
 				goto out;
 			ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
@@ -410,7 +412,9 @@
       /* If I and D space are separate, this will have to be fixed. */
 		case PTRACE_POKETEXT: /* write the word at location addr. */
 		case PTRACE_POKEDATA:
+			down(&child->mm->mmap_sem);
 			ret = write_long(child,addr,data);
+			up(&child->mm->mmap_sem);
 			goto out;
 
 		case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- v2.2.0-pre1/linux/arch/ppc/kernel/smp.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/smp.c	Wed Dec 30 10:55:07 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: smp.c,v 1.38 1998/12/02 21:23:49 cort Exp $
+ * $Id: smp.c,v 1.39 1998/12/28 10:28:51 paulus Exp $
  *
  * Smp support for ppc.
  *
@@ -43,6 +43,7 @@
 unsigned int prof_multiplier[NR_CPUS];
 unsigned int prof_counter[NR_CPUS];
 int first_cpu_booted = 0;
+cycles_t cacheflush_time;
 
 /* all cpu mappings are 1-1 -- Cort */
 int cpu_number_map[NR_CPUS] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,};
@@ -144,7 +145,14 @@
 
 void smp_send_reschedule(int cpu)
 {
-	smp_message_pass(cpu, MSG_RESCHEDULE, 0, 0);
+	/* This is only used if `cpu' is running an idle task,
+	   so it will reschedule itself anyway... */
+	/*smp_message_pass(cpu, MSG_RESCHEDULE, 0, 0);*/
+}
+
+void smp_send_stop(void)
+{
+	smp_message_pass(MSG_ALL_BUT_SELF, MSG_STOP_CPU, 0, 0);
 }
 
 spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED;
@@ -185,11 +193,12 @@
 	extern void __secondary_start(void);
 	int i;
 	struct task_struct *p;
-	
+	unsigned long a;
+
         printk("Entering SMP Mode...\n");
 	
 	first_cpu_booted = 1;
-	dcbf(&first_cpu_booted);
+	/*dcbf(&first_cpu_booted);*/
 
 	for (i = 0; i < NR_CPUS; i++) {
 		prof_counter[i] = 1;
@@ -200,7 +209,13 @@
         smp_store_cpu_info(0);
         active_kernel_processor = 0;
 	current->processor = 0;
-	
+
+	/*
+	 * XXX very rough, assumes 20 bus cycles to read a cache line,
+	 * timebase increments every 4 bus cycles, 32kB L1 data cache.
+	 */
+	cacheflush_time = 5 * 1024;
+
 	if ( _machine != _MACH_Pmac )
 	{
 		printk("SMP not supported on this machine.\n");
@@ -213,10 +228,16 @@
 	if ( !p )
 		panic("No idle task for secondary processor\n");
 	p->processor = 1;
+	p->has_cpu = 1;
 	current_set[1] = p;
 
 	/* need to flush here since secondary bat's aren't setup */
-	dcbf((void *)&current_set[1]);
+	/* XXX ??? */
+	for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32)
+		asm volatile("dcbf 0,%0" : : "r" (a) : "memory");
+	asm volatile("sync");
+
+	/*dcbf((void *)&current_set[1]);*/
 	/* setup entry point of secondary processor */
 	*(volatile unsigned long *)(0xf2800000) =
 		(unsigned long)__secondary_start-KERNELBASE;
@@ -238,7 +259,7 @@
 	if(cpu_callin_map[1]) {
 		printk("Processor %d found.\n", smp_num_cpus);
 		smp_num_cpus++;
-#if 0 /* this sync's the decr's, but we don't want this now -- Cort */
+#if 1 /* this sync's the decr's, but we don't want this now -- Cort */
 		set_dec(decrementer_count);
 #endif
 	} else {
@@ -287,6 +308,7 @@
 	while(!smp_commenced)
 		barrier();
 	__sti();
+	printk("SMP %d: smp_callin done\n", current->processor);
 }
 
 void __init smp_setup(char *str, int *ints)
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.2.0-pre1/linux/arch/ppc/kernel/time.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/time.c	Wed Dec 30 10:55:07 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: time.c,v 1.38 1998/11/16 15:56:15 cort Exp $
+ * $Id: time.c,v 1.39 1998/12/28 10:28:51 paulus Exp $
  * Common time routines among all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -78,6 +78,26 @@
 	unsigned dcache_locked = unlock_dcache();
 	
 	hardirq_enter(cpu);
+#ifdef __SMP__
+	{
+		unsigned int loops = 100000000;
+		while (test_bit(0, &global_irq_lock)) {
+			if (smp_processor_id() == global_irq_holder) {
+				printk("uh oh, interrupt while we hold global irq lock!\n");
+#ifdef CONFIG_XMON
+				xmon(0);
+#endif
+				break;
+			}
+			if (loops-- == 0) {
+				printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder);
+#ifdef CONFIG_XMON
+				xmon(0);
+#endif
+			}
+		}
+	}
+#endif /* __SMP__ */			
 	
 	while ((dval = get_dec()) < 0) {
 		/*
@@ -150,12 +170,15 @@
 	save_flags(flags);
 	cli();
 	*tv = xtime;
+	/* XXX we don't seem to have the decrementers synced properly yet */
+#ifndef __SMP__
 	tv->tv_usec += (decrementer_count - get_dec())
 	    * count_period_num / count_period_den;
 	if (tv->tv_usec >= 1000000) {
 		tv->tv_usec -= 1000000;
 		tv->tv_sec++;
 	}
+#endif
 	restore_flags(flags);
 }
 
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/lib/locks.c linux/arch/ppc/lib/locks.c
--- v2.2.0-pre1/linux/arch/ppc/lib/locks.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/lib/locks.c	Wed Dec 30 10:55:07 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: locks.c,v 1.20 1998/10/08 01:17:32 cort Exp $
+ * $Id: locks.c,v 1.21 1998/12/28 10:28:53 paulus Exp $
  *
  * Locks for smp ppc 
  * 
@@ -18,7 +18,7 @@
 #define DEBUG_LOCKS 1
 
 #undef INIT_STUCK
-#define INIT_STUCK 0xffffffff
+#define INIT_STUCK 200000000 /*0xffffffff*/
 
 void _spin_lock(spinlock_t *lock)
 {
@@ -76,9 +76,9 @@
 		      lp->owner_pc,lp->lock);
 #endif /* DEBUG_LOCKS */
 	lp->owner_pc = lp->owner_cpu = 0;
-	eieio();
-	lp->lock = 0;
-	eieio();
+	eieio();	/* actually I believe eieio only orders */
+	lp->lock = 0;	/* non-cacheable accesses (on 604 at least) */
+	eieio();	/*  - paulus. */
 }
 		
 /*
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig
--- v2.2.0-pre1/linux/arch/ppc/pmac_defconfig	Mon Dec 28 15:00:52 1998
+++ linux/arch/ppc/pmac_defconfig	Wed Dec 30 10:55:07 1998
@@ -14,8 +14,8 @@
 # CONFIG_ALL_PPC is not set
 # CONFIG_APUS is not set
 # CONFIG_MBX is not set
-# CONFIG_SMP is not set
 CONFIG_MACH_SPECIFIC=y
+# CONFIG_SMP is not set
 
 #
 # General setup
@@ -165,6 +165,7 @@
 # SCSI low-level drivers
 #
 # CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
@@ -175,14 +176,16 @@
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_BUSLOGIC is not set
 # CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_EATA_DMA is not set
 # CONFIG_SCSI_EATA_PIO is not set
-# CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
 # CONFIG_SCSI_GDTH is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 # CONFIG_SCSI_NCR53C8XX is not set
@@ -192,6 +195,7 @@
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
 # CONFIG_SCSI_SEAGATE is not set
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
@@ -219,6 +223,7 @@
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_RTL8139 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 is not set
@@ -226,7 +231,7 @@
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
 CONFIG_DE4X5=y
-CONFIG_DEC_ELCP=m
+# CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
 # CONFIG_EEXPRESS_PRO100 is not set
 # CONFIG_LNE390 is not set
@@ -254,6 +259,7 @@
 # CONFIG_TR is not set
 # CONFIG_SHAPER is not set
 # CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
 
 #
 # Amateur Radio support
@@ -283,6 +289,7 @@
 CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
 # CONFIG_FB_MATROX is not set
+CONFIG_FB_ATY=y
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
@@ -312,9 +319,17 @@
 # CONFIG_MOUSE is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
+CONFIG_NVRAM=y
 # CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
 # CONFIG_VIDEO_DEV is not set
-CONFIG_NVRAM=y
+
+#
+# Joystick support
+#
 # CONFIG_JOYSTICK is not set
 
 #
@@ -326,37 +341,47 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
+CONFIG_AUTOFS_FS=y
+# 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_UMSDOS_FS is not set
 CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
 CONFIG_NFSD=y
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
-CONFIG_HFS_FS=y
-# CONFIG_ROMFS_FS is not set
-CONFIG_AUTOFS_FS=y
-# CONFIG_UFS_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
 # CONFIG_BSD_DISKLABEL is not set
+CONFIG_MAC_PARTITION=y
 # CONFIG_SMD_DISKLABEL is not set
 # CONFIG_SOLARIS_X86_PARTITION is not set
-CONFIG_DEVPTS_FS=y
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-CONFIG_MAC_PARTITION=y
+# CONFIG_UNIXWARE_DISKLABEL is not set
 CONFIG_NLS=y
 
 #
@@ -387,6 +412,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_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
 #
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/ppc/vmlinux.lds linux/arch/ppc/vmlinux.lds
--- v2.2.0-pre1/linux/arch/ppc/vmlinux.lds	Fri May  8 23:14:45 1998
+++ linux/arch/ppc/vmlinux.lds	Wed Dec 30 10:55:07 1998
@@ -64,6 +64,9 @@
   __ex_table : { *(__ex_table) }
   __stop___ex_table = .;
 
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
   . = ALIGN(4096);
   __init_begin = .;
   .text.init : { *(.text.init) }
diff -u --recursive --new-file v2.2.0-pre1/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.2.0-pre1/linux/arch/sparc/kernel/ptrace.c	Thu May  7 22:51:47 1998
+++ linux/arch/sparc/kernel/ptrace.c	Tue Dec 29 16:35:08 1998
@@ -592,7 +592,9 @@
 			pt_error_return(regs, EINVAL);
 			goto out;
 		}
+		down(&child->mm->mmap_sem);
 		res = read_long(child, addr, &tmp);
+		up(&child->mm->mmap_sem);
 		if (res < 0) {
 			pt_error_return(regs, -res);
 			goto out;
@@ -619,8 +621,10 @@
 			pt_error_return(regs, EINVAL);
 			goto out;
 		}
+		down(&child->mm->mmap_sem);
 		vma = find_extend_vma(child, addr);
 		res = write_long(child, addr, data);
+		up(&child->mm->mmap_sem);
 		if(res < 0)
 			pt_error_return(regs, -res);
 		else
@@ -761,7 +765,9 @@
 			goto out;
 		}
 		while(len) {
+			down(&child->mm->mmap_sem);
 			res = read_byte(child, src, &tmp);
+			up(&child->mm->mmap_sem);
 			if(res < 0) {
 				pt_error_return(regs, -res);
 				goto out;
@@ -788,7 +794,9 @@
 			unsigned long tmp;
 
 			__get_user(tmp, src);
+			down(&child->mm->mmap_sem);
 			res = write_byte(child, dest, tmp);
+			up(&child->mm->mmap_sem);
 			if(res < 0) {
 				pt_error_return(regs, -res);
 				goto out;
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.2.0-pre1/linux/drivers/block/Config.in	Thu Nov 19 09:56:28 1998
+++ linux/drivers/block/Config.in	Tue Dec 29 11:21:49 1998
@@ -69,7 +69,7 @@
       bool '     QDI QD6580 support' CONFIG_BLK_DEV_QD6580
       bool '     UMC-8672 support' CONFIG_BLK_DEV_UMC8672
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-        if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+        if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" ]; then
         bool '     PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
 	 fi
       fi
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/acsi.c linux/drivers/block/acsi.c
--- v2.2.0-pre1/linux/drivers/block/acsi.c	Thu Nov 12 16:21:18 1998
+++ linux/drivers/block/acsi.c	Tue Dec 29 11:23:59 1998
@@ -425,7 +425,7 @@
 {
 	if (INT_LEVEL < 6) {
 		unsigned long maxjif;
-		for( maxjif = jiffies + timeout; jiffies < maxjif; )
+		for( maxjif = jiffies + timeout; time_before(jiffies, maxjif); )
 			if (!(mfp.par_dt_reg & 0x20)) return( 1 );
 	}
 	else {
@@ -442,7 +442,7 @@
 {
 	if (INT_LEVEL < 6) {
 		unsigned long maxjif;
-		for( maxjif = jiffies + timeout; jiffies < maxjif; )
+		for( maxjif = jiffies + timeout; time_before(jiffies, maxjif); )
 			if (mfp.par_dt_reg & 0x20) return( 1 );
 	}
 	else {
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/acsi_slm.c linux/drivers/block/acsi_slm.c
--- v2.2.0-pre1/linux/drivers/block/acsi_slm.c	Wed Aug 26 11:37:34 1998
+++ linux/drivers/block/acsi_slm.c	Tue Dec 29 11:23:59 1998
@@ -101,17 +101,8 @@
 		cmd[1] = (cmd[1] & ~0xe0) | (lun)<<5;	\
 	} while(0)
 
-#define	START_TIMER(to)							\
-    do {										\
-        del_timer( &slm_timer );				\
-        slm_timer.expires = jiffies + (to);			\
-        add_timer( &slm_timer );				\
-	} while(0)
-
-#define	STOP_TIMER()							\
-    do {										\
-        del_timer( &slm_timer );				\
-	} while(0)
+#define	START_TIMER(to)	mod_timer(&slm_timer, jiffies + (to))
+#define	STOP_TIMER()	del_timer(&slm_timer)
 
 
 static char slmreqsense_cmd[6] = { 0x03, 0, 0, 0, 0, 0 };
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/ataflop.c linux/drivers/block/ataflop.c
--- v2.2.0-pre1/linux/drivers/block/ataflop.c	Wed Aug 26 11:37:35 1998
+++ linux/drivers/block/ataflop.c	Tue Dec 29 11:23:59 1998
@@ -1838,7 +1838,7 @@
 	FDC_WRITE (FDCREG_TRACK, 0xff00);
 	FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | FDCCMDADD_H | FDCSTEP_6 );
 
-	for( ok = 0, timeout = jiffies + 2*HZ+HZ/2; jiffies < timeout; ) {
+	for( ok = 0, timeout = jiffies + 2*HZ+HZ/2; time_before(jiffies, timeout); ) {
 		if (!(mfp.par_dt_reg & 0x20))
 			break;
 	}
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.2.0-pre1/linux/drivers/block/ide-disk.c	Thu Sep 17 17:53:35 1998
+++ linux/drivers/block/ide-disk.c	Wed Dec 30 13:36:02 1998
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-disk.c	Version 1.04  Jan   7, 1998
+ *  linux/drivers/block/ide-disk.c	Version 1.08  Dec   10, 1998
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -7,6 +7,7 @@
 /*
  *  Maintained by Mark Lord  <mlord@pobox.com>
  *            and Gadi Oxman <gadio@netvision.net.il>
+ *            and Andre Hedrick <hedrick@astro.dyer.vanderbilt.edu>
  *
  * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c.
  *
@@ -19,10 +20,13 @@
  * Version 1.05		add capacity support for ATA3 >= 8GB
  * Version 1.06		get boot-up messages to show full cyl count
  * Version 1.07		disable door-locking if it fails
- * Version 1.07a	fixed mult_count enables
+ * Version 1.08		fixed CHS/LBA translations for ATA4 > 8GB,
+ *			process of adding new ATA4 compliance.
+ *			fixed problems in allowing fdisk to see
+ *			the entire disk.
  */
 
-#define IDEDISK_VERSION	"1.07"
+#define IDEDISK_VERSION	"1.08"
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
@@ -87,15 +91,28 @@
 	unsigned long chs_sects   = id->cyls * id->heads * id->sectors;
 	unsigned long _10_percent = chs_sects / 10;
 
-	/* very large drives (8GB+) may lie about the number of cylinders */
-	if (id->cyls == 16383 && id->heads == 16 && id->sectors == 63 && lba_sects > chs_sects) {
+	/*
+	 * very large drives (8GB+) may lie about the number of cylinders
+	 * This is a split test for drives 8 Gig and Bigger only.
+	 */
+	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 */
+	}
+	/*
+	 * very large drives (8GB+) may lie about the number of cylinders
+	 * This is a split test for drives less than 8 Gig only.
+	 */
+	if ((id->lba_capacity < 16514064) && (lba_sects > chs_sects) &&
+	    (id->heads == 16) && (id->sectors == 63)) {
 		id->cyls = lba_sects / (16 * 63); /* correct cyls */
 		return 1;	/* lba_capacity is our only option */
 	}
 	/* perform a rough sanity check on lba_sects:  within 10% is "okay" */
-	if ((lba_sects - chs_sects) < _10_percent)
+	if ((lba_sects - chs_sects) < _10_percent) {
 		return 1;	/* lba_capacity is good */
-
+	}
 	/* some drives have the word order reversed */
 	lba_sects = (lba_sects << 16) | (lba_sects >> 16);
 	if ((lba_sects - chs_sects) < _10_percent) {
@@ -487,13 +504,13 @@
 		drive->special.b.set_multmode = 1;
 }
 
+#ifdef CONFIG_PROC_FS
+
 static int smart_enable(ide_drive_t *drive)
 {
 	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL);
 }
 
-#ifdef CONFIG_PROC_FS
-
 static int get_smart_values(ide_drive_t *drive, byte *buf)
 {
 	(void) smart_enable(drive);
@@ -603,7 +620,7 @@
 	int major = HWIF(drive)->major;
 	int minor = drive->select.b.unit << PARTN_BITS;
 
-	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_SHORT,	0,	1023,				1,	1,	&drive->bios_cyl,		NULL);
+	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_SHORT,	0,	65535,				1,	1,	&drive->bios_cyl,		NULL);
 	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
 	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
 	ide_add_setting(drive,	"bswap",		SETTING_READ,					-1,			-1,			TYPE_BYTE,	0,	1,				1,	1,	&drive->bswap,			NULL);
@@ -676,9 +693,8 @@
 		drive->sect    = drive->bios_sect = id->sectors;
 	}
 	/* Handle logical geometry translation by the drive */
-	if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
-	 && (id->cur_heads <= 16) && id->cur_sectors)
-	{
+	if ((id->field_valid & 1) && id->cur_cyls &&
+	    id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
 		/*
 		 * Extract the physical drive geometry for our use.
 		 * Note that we purposely do *not* update the bios info.
@@ -703,27 +719,49 @@
 		}
 	}
 	/* Use physical geometry if what we have still makes no sense */
-	if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
-		drive->cyl  = id->cyls;
-		drive->head = id->heads;
-		drive->sect = id->sectors;
+	if ((!drive->head || drive->head > 16) &&
+	    id->heads && id->heads <= 16) {
+		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;
 	}
 
 	/* calculate drive capacity, and select LBA if possible */
-	(void) idedisk_capacity (drive);
+	capacity = idedisk_capacity (drive);
 
-	/* Correct the number of cyls if the bios value is too small */
-	if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
-		if (drive->cyl > drive->bios_cyl)
-			drive->bios_cyl = drive->cyl;
+	/*
+	 * if possible, give fdisk access to more of the drive,
+	 * by correcting bios_cyls:
+	 */
+	if ((capacity >= (id->cyls * id->heads * id->sectors)) &&
+	    (!drive->forced_geom)) {
+		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 0	/* done instead for entire identify block in arch/ide.h stuff */
 	/* fix byte-ordering of buffer size field */
 	id->buf_size = le16_to_cpu(id->buf_size);
 #endif
 	printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
-	 drive->name, id->model, idedisk_capacity(drive)/2048L, id->buf_size/2,
-	 drive->bios_cyl, drive->bios_head, drive->bios_sect);
+			drive->name, id->model,
+			capacity/2048L, id->buf_size/2,
+			drive->bios_cyl, drive->bios_head, drive->bios_sect);
+
 	if (drive->using_dma) {
 		if ((id->field_valid & 4) &&
 		    (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
@@ -735,12 +773,34 @@
 		}
 	}
 	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) {
+#if 1	/* original, pre IDE-NFG, per request of AC */
+		drive->mult_req = INITIAL_MULT_COUNT;
+		if (drive->mult_req > id->max_multsect)
+			drive->mult_req = id->max_multsect;
+		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
+			drive->special.b.set_multmode = 1;
+#else
 		id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
 		id->multsect_valid = id->multsect ? 1 : 0;
 		drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
 		drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
+#endif
 	}
 	drive->no_io_32bit = id->dword_io ? 1 : 0;
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
--- v2.2.0-pre1/linux/drivers/block/ide-dma.c	Fri Oct 23 22:01:20 1998
+++ linux/drivers/block/ide-dma.c	Tue Dec 29 11:14:57 1998
@@ -68,8 +68,9 @@
  *       SIIG's UltraIDE Pro CN-2449
  * TTI   HPT343 Chipset "Modified SCSI Class" but reports as an
  *       unknown storage device.
- * NEW	check_drive_lists(ide_drive_t *drive, int good_bad)
+ * NEW	 check_drive_lists(ide_drive_t *drive, int good_bad)
  */
+
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -91,9 +92,8 @@
  */
 const char *good_dma_drives[] = {"Micropolis 2112A",
 				 "CONNER CTMA 4000",
+				 "CONNER CTT8000-A",
 				 "ST34342A",	/* for Sun Ultra */
-				 "WDC AC2340F",	/* DMA mode1 */
-				 "WDC AC2340H",	/* DMA mode1 */
 				 NULL};
 
 /*
@@ -101,7 +101,10 @@
  * of drives which supposedly support (U)DMA but which are
  * known to corrupt data with this interface under Linux.
  */
-const char *bad_dma_drives[] = {"WDC AC22100H",
+const char *bad_dma_drives[] = {"WDC AC11000H",
+				"WDC AC22100H",
+				"WDC AC32500H",
+				"WDC AC33100H",
  				NULL};
 
 /*
@@ -331,8 +334,8 @@
 			return 0;
 		case ide_dma_end: /* returns 1 on error, 0 otherwise */
 			drive->waiting_for_dma = 0;
-			dma_stat = inb(dma_base+2);
-			outb(inb(dma_base)&~1, dma_base);		/* stop DMA */
+			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+			dma_stat = inb(dma_base+2);		/* get DMA status */
 			outb(dma_stat|6, dma_base+2);	/* clear the INTR & ERROR bits */
 			return (dma_stat & 7) != 4;	/* verify good DMA status */
 		case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.2.0-pre1/linux/drivers/block/ide-tape.c	Fri Oct  9 13:27:07 1998
+++ linux/drivers/block/ide-tape.c	Wed Dec 30 14:51:31 1998
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-tape.c	Version 1.13		Jan   2, 1998
+ * linux/drivers/block/ide-tape.c	Version 1.14		Dec  30, 1998
  *
  * Copyright (C) 1995 - 1998 Gadi Oxman <gadio@netvision.net.il>
  *
@@ -212,6 +212,8 @@
  *                        number of tape blocks.
  *                       Add support for INTERRUPT DRQ devices.
  * Ver 1.13  Jan  2 98   Add "speed == 0" work-around for HP COLORADO 5GB
+ * Ver 1.14  Dec 30 99   Partial fixes for the Sony/AIWA tape drives.
+ *                       Replace cli()/sti() with hwgroup spinlocks.
  *
  * Here are some words from the first releases of hd.c, which are quoted
  * in ide.c and apply here as well:
@@ -479,6 +481,11 @@
 #define IDETAPE_FIFO_THRESHOLD 		2
 
 /*
+ *	Some tape drives require a long irq timeout
+ */
+#define IDETAPE_WAIT_CMD		60
+
+/*
  *	DSC timings.
  */
 #define IDETAPE_DSC_RW_MIN		5*HZ/100	/* 50 msec */
@@ -1432,8 +1439,7 @@
 #if IDETAPE_DEBUG_LOG
 	printk (KERN_INFO "Reached idetape_add_stage_tail\n");
 #endif /* IDETAPE_DEBUG_LOG */
-	save_flags (flags);	/* all CPUs (overkill?) */
-	cli();			/* all CPUs (overkill?) */
+	spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
 	stage->next=NULL;
 	if (tape->last_stage != NULL)
 		tape->last_stage->next=stage;
@@ -1444,7 +1450,7 @@
 		tape->next_stage=tape->last_stage;
 	tape->nr_stages++;
 	tape->nr_pending_stages++;
-	restore_flags (flags);	/* all CPUs (overkill?) */
+	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
 }
 
 /*
@@ -1756,6 +1762,8 @@
 
 		ide__sti();	/* local CPU only */
 
+		if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD)
+			status.b.check = 0;
 		if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {	/* Error detected */
 #if IDETAPE_DEBUG_LOG
 			printk (KERN_INFO "ide-tape: %s: I/O error, ",tape->name);
@@ -1811,7 +1819,7 @@
 			if (temp > pc->buffer_size) {
 				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,WAIT_CMD);
+				ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD);
 				return;
 			}
 #if IDETAPE_DEBUG_LOG
@@ -1833,7 +1841,7 @@
 	pc->actually_transferred+=bcount.all;					/* Update the current position */
 	pc->current_position+=bcount.all;
 
-	ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);		/* And set the interrupt handler again */
+	ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD);		/* And set the interrupt handler again */
 }
 
 /*
@@ -1884,18 +1892,29 @@
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_pc_t *pc = tape->pc;
 	idetape_ireason_reg_t ireason;
+	int retries = 100;
 
 	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
 		printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
 		return;
 	}
 	ireason.all=IN_BYTE (IDE_IREASON_REG);
+	while (retries-- && (!ireason.b.cod || ireason.b.io)) {
+		printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, retrying\n");
+		udelay(100);
+		ireason.all = IN_BYTE(IDE_IREASON_REG);
+		if (retries == 0) {
+			printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, ignoring\n");
+			ireason.b.cod = 1;
+			ireason.b.io = 0;
+		}
+	}
 	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;
 	}
-	ide_set_handler(drive, &idetape_pc_intr, WAIT_CMD);	/* Set the interrupt routine */
+	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 */
 }
 
@@ -1961,7 +1980,7 @@
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 	if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
-		ide_set_handler(drive, &idetape_transfer_pc, WAIT_CMD);
+		ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD);
 		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
 	} else {
 		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
@@ -2313,7 +2332,7 @@
  *	The caller should ensure that the request will not be serviced
  *	before we install the semaphore (usually by disabling interrupts).
  */
-static void idetape_wait_for_request (struct request *rq)
+static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq)
 {
 	struct semaphore sem = MUTEX_LOCKED;
 
@@ -2324,7 +2343,9 @@
 	}
 #endif /* IDETAPE_DEBUG_BUGS */
 	rq->sem = &sem;
-	down (&sem);
+	spin_unlock(&HWGROUP(drive)->spinlock);
+	down(&sem);
+	spin_lock_irq(&HWGROUP(drive)->spinlock);
 }
 
 /*
@@ -2398,11 +2419,10 @@
 		 */
 		return (idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bh));
 	}
-	save_flags (flags);	/* all CPUs (overkill?) */
-	cli();			/* all CPUs (overkill?) */
+	spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
 	if (tape->active_stage == tape->first_stage)
-		idetape_wait_for_request (tape->active_data_request);
-	restore_flags (flags);	/* all CPUs (overkill?) */
+		idetape_wait_for_request(drive, tape->active_data_request);
+	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
 
 	rq_ptr = &tape->first_stage->rq;
 	bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors);
@@ -2451,13 +2471,12 @@
 	 *	Pay special attention to possible race conditions.
 	 */
 	while ((new_stage = idetape_kmalloc_stage (tape)) == NULL) {
-		save_flags (flags);	/* all CPUs (overkill?) */
-		cli();			/* all CPUs (overkill?) */
+		spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
 		if (idetape_pipeline_active (tape)) {
-			idetape_wait_for_request (tape->active_data_request);
-			restore_flags (flags);	/* all CPUs (overkill?) */
+			idetape_wait_for_request(drive, tape->active_data_request);
+			spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
 		} else {
-			restore_flags (flags);	/* all CPUs (overkill?) */
+			spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
 			idetape_insert_pipeline_into_queue (drive);
 			if (idetape_pipeline_active (tape))
 				continue;
@@ -2513,13 +2532,12 @@
 	
 	if (tape->first_stage == NULL)
 		return;
-		
-	save_flags (flags);	/* all CPUs (overkill?) */
-	cli();			/* all CPUs (overkill?) */
+
+	spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
 	tape->next_stage = NULL;
 	if (idetape_pipeline_active (tape))
-		idetape_wait_for_request (tape->active_data_request);
-	restore_flags (flags);	/* all CPUs (overkill?) */
+		idetape_wait_for_request(drive, tape->active_data_request);
+	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
 
 	while (tape->first_stage != NULL)
 		idetape_remove_stage_head (drive);
@@ -2539,8 +2557,7 @@
 	if (!idetape_pipeline_active (tape))
 		idetape_insert_pipeline_into_queue (drive);
 
-	save_flags (flags);	/* all CPUs (overkill?) */
-	cli();			/* all CPUs (overkill?) */
+	spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
 	if (!idetape_pipeline_active (tape))
 		goto abort;
 #if IDETAPE_DEBUG_BUGS
@@ -2548,9 +2565,9 @@
 		printk ("ide-tape: tape->last_stage == NULL\n");
 	else
 #endif /* IDETAPE_DEBUG_BUGS */
-	idetape_wait_for_request (&tape->last_stage->rq);
+	idetape_wait_for_request(drive, &tape->last_stage->rq);
 abort:
-	restore_flags (flags);	/* all CPUs (overkill?) */
+	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
 }
 
 static void idetape_pad_zeros (ide_drive_t *drive, int bcount)
@@ -2795,11 +2812,10 @@
 			 *	Wait until the first read-ahead request
 			 *	is serviced.
 			 */
-			save_flags (flags);	/* all CPUs (overkill?) */
-			cli();			/* all CPUs (overkill?) */
+			spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
 			if (tape->active_stage == tape->first_stage)
-				idetape_wait_for_request (tape->active_data_request);
-			restore_flags (flags);	/* all CPUs (overkill?) */
+				idetape_wait_for_request(drive, tape->active_data_request);
+			spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
 
 			if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK)
 				count++;
@@ -3457,7 +3473,7 @@
 		return;
 	}
 	header = (idetape_mode_parameter_header_t *) pc.buffer;
-	capabilities = (idetape_capabilities_page_t *) (header + 1);
+	capabilities = (idetape_capabilities_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl);
 
 	capabilities->max_speed = ntohs (capabilities->max_speed);
 	capabilities->ctl = ntohs (capabilities->ctl);
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.2.0-pre1/linux/drivers/block/ide.c	Fri Oct 23 22:01:20 1998
+++ linux/drivers/block/ide.c	Wed Dec 30 15:48:22 1998
@@ -2275,6 +2275,12 @@
  *				and quite likely to cause trouble with
  *				older/odd IDE drives.
  *
+ * "hdx=slow"		: insert a huge pause after each access to the data
+ *				port. Should be used only as a last resort.
+ *
+ * "hdx=swapdata"	: when the drive is a disk, byte swap all data
+ * "hdx=bswap"		: same as above..........
+ *
  * "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in MHz,
  *				where "xx" is between 20 and 66 inclusive,
  *				used when tuning chipset PIO modes.
@@ -2333,12 +2339,16 @@
 	if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
 		const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
 				"serialize", "autotune", "noautotune",
-				"slow", "swapdata", NULL};
+				"slow", "swapdata", "bswap", NULL};
 		unit = s[2] - 'a';
 		hw   = unit / MAX_DRIVES;
 		unit = unit % MAX_DRIVES;
 		hwif = &ide_hwifs[hw];
 		drive = &hwif->drives[unit];
+		if (strncmp(s + 4, "ide-", 4) == 0) {
+			strncpy(drive->driver_req, s + 4, 9);
+			goto done;
+		}
 		switch (match_parm(&s[3], hd_words, vals, 3)) {
 			case -1: /* "none" */
 				drive->nobios = 1;  /* drop into "noprobe" */
@@ -2366,7 +2376,8 @@
 			case -8: /* "slow" */
 				drive->slow = 1;
 				goto done;
-			case -9: /* swapdata */
+			case -9: /* swapdata or bswap */
+			case -10:
 				drive->bswap = 1;
 				goto done;
 			case 3: /* cyl,head,sect */
@@ -2567,16 +2578,44 @@
 	const byte *heads = head_vals;
 	unsigned long tracks;
 
-	if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
+	drive = get_info_ptr(i_rdev);
+	if (!drive)
+		return 0;
+
+	if (drive->forced_geom) {
+		/* bombs otherwise /axboe */
+		if (drive == NULL) 
+			return 0;
+		/*
+		 * Update the current 3D drive values.
+		 */
+		drive->id->cur_cyls	= drive->bios_cyl;
+		drive->id->cur_heads	= drive->bios_head;
+		drive->id->cur_sectors	= drive->bios_sect;
 		return 0;
+	}
 
-	if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
+	if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) {
+		/*
+		 * Update the current 3D drive values.
+		 */
+		drive->id->cur_cyls	= drive->bios_cyl;
+		drive->id->cur_heads	= drive->bios_head;
+		drive->id->cur_sectors	= drive->bios_sect;
 		return 0;		/* we already have a translation */
+	}
 
 	printk("%s ", msg);
 
-	if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63))
+	if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) {
+		/*
+		 * Update the current 3D drive values.
+		 */
+		drive->id->cur_cyls	= drive->bios_cyl;
+		drive->id->cur_heads	= drive->bios_head;
+		drive->id->cur_sectors	= drive->bios_sect;
 		return 0;		/* small disk: no translation needed */
+	}
 
 	if (drive->id) {
 		drive->cyl  = drive->id->cyls;
@@ -2614,6 +2653,12 @@
 	}
 	drive->part[0].nr_sects = current_capacity(drive);
 	printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+	/*
+	 * Update the current 3D drive values.
+	 */
+	drive->id->cur_cyls    = drive->bios_cyl;
+	drive->id->cur_heads   = drive->bios_head;
+	drive->id->cur_sectors = drive->bios_sect;
 	return 1;
 }
 
@@ -2703,7 +2748,11 @@
 	(void) idefloppy_init();
 #endif /* CONFIG_BLK_DEV_IDEFLOPPY */
 #ifdef CONFIG_BLK_DEV_IDESCSI
+ #ifdef CONFIG_SCSI
 	(void) idescsi_init();
+ #else
+    #warning ide scsi-emulation selected but no SCSI-subsystem in kernel
+ #endif
 #endif /* CONFIG_BLK_DEV_IDESCSI */
 }
 
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.2.0-pre1/linux/drivers/block/ide.h	Fri Oct 23 22:01:20 1998
+++ linux/drivers/block/ide.h	Wed Dec 30 22:34:26 1998
@@ -438,6 +438,8 @@
 	*start = page + off;		\
 	return len;			\
 }
+#else
+#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0;
 #endif
 
 /*
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/block/pdc4030.c linux/drivers/block/pdc4030.c
--- v2.2.0-pre1/linux/drivers/block/pdc4030.c	Thu May  7 22:51:48 1998
+++ linux/drivers/block/pdc4030.c	Tue Dec 29 11:24:57 1998
@@ -92,13 +92,13 @@
 	timeout = HZ * 10;
 	timeout += jiffies;
 	do {
-		if(jiffies > timeout) {
+		if(time_after(jiffies, timeout)) {
 			return 2; /* device timed out */
 		}
 		/* This is out of delay_10ms() */
 		/* Delays at least 10ms to give interface a chance */
 		timer = jiffies + (HZ + 99)/100 + 1;
-		while (timer > jiffies);
+		while (time_after(timer, jiffies));
 		status_val = IN_BYTE(IDE_SECTOR_REG);
 	} while (status_val != 0x50 && status_val != 0x70);
 
@@ -257,7 +257,7 @@
 	struct request *rq;
 
         if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
-            if (jiffies < hwgroup->poll_timeout) {
+            if (time_before(jiffies, hwgroup->poll_timeout)) {
                 ide_set_handler (drive, &promise_write_pollfunc, 1);
                 return; /* continue polling... */
             }
@@ -335,7 +335,7 @@
 		if(IN_BYTE(IDE_SELECT_REG) & 0x01)
 		    return;
 		udelay(1);
-	    } while (jiffies < timeout);
+	    } while (time_before(jiffies, timeout));
 	    printk("%s: reading: No DRQ and not waiting - Odd!\n",
 		   drive->name);
 	    return;
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.2.0-pre1/linux/drivers/char/console.c	Sun Nov  8 14:02:53 1998
+++ linux/drivers/char/console.c	Tue Dec 29 14:28:37 1998
@@ -1029,7 +1029,7 @@
 				  */
 				translate = set_translate(charset == 0
 						? G0_charset
-						: G1_charset);
+						: G1_charset,currcons);
 				disp_ctrl = 0;
 				toggle_meta = 0;
 				break;
@@ -1037,7 +1037,7 @@
 				  * Select first alternate font, lets
 				  * chars < 32 be displayed as ROM chars.
 				  */
-				translate = set_translate(IBMPC_MAP);
+				translate = set_translate(IBMPC_MAP,currcons);
 				disp_ctrl = 1;
 				toggle_meta = 0;
 				break;
@@ -1045,7 +1045,7 @@
 				  * Select second alternate font, toggle
 				  * high bit before displaying as ROM char.
 				  */
-				translate = set_translate(IBMPC_MAP);
+				translate = set_translate(IBMPC_MAP,currcons);
 				disp_ctrl = 1;
 				toggle_meta = 1;
 				break;
@@ -1328,7 +1328,7 @@
 	color		= s_color;
 	G0_charset	= saved_G0;
 	G1_charset	= saved_G1;
-	translate	= set_translate(charset ? G1_charset : G0_charset);
+	translate	= set_translate(charset ? G1_charset : G0_charset,currcons);
 	update_attr(currcons);
 	need_wrap = 0;
 }
@@ -1343,7 +1343,7 @@
 	bottom		= video_num_lines;
 	vc_state	= ESnormal;
 	ques		= 0;
-	translate	= set_translate(LAT1_MAP);
+	translate	= set_translate(LAT1_MAP,currcons);
 	G0_charset	= LAT1_MAP;
 	G1_charset	= GRAF_MAP;
 	charset		= 0;
@@ -1426,12 +1426,12 @@
 		return;
 	case 14:
 		charset = 1;
-		translate = set_translate(G1_charset);
+		translate = set_translate(G1_charset,currcons);
 		disp_ctrl = 1;
 		return;
 	case 15:
 		charset = 0;
-		translate = set_translate(G0_charset);
+		translate = set_translate(G0_charset,currcons);
 		disp_ctrl = 0;
 		return;
 	case 24: case 26:
@@ -1738,7 +1738,7 @@
 		else if (c == 'K')
 			G0_charset = USER_MAP;
 		if (charset == 0)
-			translate = set_translate(G0_charset);
+			translate = set_translate(G0_charset,currcons);
 		vc_state = ESnormal;
 		return;
 	case ESsetG1:
@@ -1751,7 +1751,7 @@
 		else if (c == 'K')
 			G1_charset = USER_MAP;
 		if (charset == 1)
-			translate = set_translate(G1_charset);
+			translate = set_translate(G1_charset,currcons);
 		vc_state = ESnormal;
 		return;
 	default:
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/consolemap.c linux/drivers/char/consolemap.c
--- v2.2.0-pre1/linux/drivers/char/consolemap.c	Mon Sep 28 10:51:33 1998
+++ linux/drivers/char/consolemap.c	Tue Dec 29 14:28:37 1998
@@ -7,6 +7,8 @@
  * aeb, 950210
  *
  * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998
+ *
+ * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998
  */
 
 #include <linux/kd.h>
@@ -167,7 +169,7 @@
 
 #define MAX_GLYPH 512		/* Max possible glyph value */
 
-static int inv_translate;
+static int inv_translate[MAX_NR_CONSOLES];
 
 struct uni_pagedir {
 	u16 		**uni_pgdir[32];
@@ -204,9 +206,9 @@
 	}
 }
 
-unsigned short *set_translate(int m)
+unsigned short *set_translate(int m,int currcons)
 {
-	inv_translate = m;
+	inv_translate[currcons] = m;
 	return translations[m];
 }
 
@@ -220,14 +222,13 @@
 unsigned char inverse_translate(struct vc_data *conp, int glyph)
 {
 	struct uni_pagedir *p;
-	
 	if (glyph < 0 || glyph >= MAX_GLYPH)
 		return 0;
 	else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc) ||
-		 !p->inverse_translations[inv_translate])
+		 !p->inverse_translations[inv_translate[conp->vc_num]])
 		return glyph;
 	else
-		return p->inverse_translations[inv_translate][glyph];
+		return p->inverse_translations[inv_translate[conp->vc_num]][glyph];
 }
 
 static void update_user_maps(void)
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.2.0-pre1/linux/drivers/char/cyclades.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/char/cyclades.c	Wed Dec 30 21:20:31 1998
@@ -1,7 +1,7 @@
 #define BLOCKMOVE
 #define	Z_WAKE
 static char rcsid[] =
-"$Revision: 2.2.1.8 $$Date: 1998/11/13 12:46:20 $";
+"$Revision: 2.2.1.9 $$Date: 1998/12/30 18:18:30 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -31,6 +31,10 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
+ * Revision 2.2.1.9  1998/12/30 18:18:30 ivan
+ * Changed access to PLX PCI bridge registers from I/O to MMIO, in 
+ * order to make PLX9050-based boards work with certain motherboards.
+ *
  * Revision 2.2.1.8  1998/11/13 12:46:20 ivan
  * cy_close function now resets (correctly) the tty->closing flag;
  * JIFFIES_DIFF macro fixed.
@@ -4545,7 +4549,6 @@
 
   struct pci_dev	*pdev = NULL;
   unsigned char		cyy_rev_id;
-  unsigned long         pci_intr_ctrl;
   unsigned char         cy_pci_irq = 0;
   uclong                cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
   unsigned short        i,j,cy_pci_nchan, plx_ver;
@@ -4587,7 +4590,7 @@
             printk("Cyclom-Y/PCI:found  winaddr=0x%lx ioaddr=0x%lx\n",
 		(ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
 #endif
-                cy_pci_addr1  &= PCI_BASE_ADDRESS_IO_MASK;
+                cy_pci_addr0  &= PCI_BASE_ADDRESS_MEM_MASK;
                 cy_pci_addr2  &= PCI_BASE_ADDRESS_MEM_MASK;
 
 #if defined(__alpha__)
@@ -4604,7 +4607,8 @@
 	            continue;
                 }
 #else
-                    cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
+		    cy_pci_addr0  = (ulong) ioremap(cy_pci_addr0, CyPCI_Yctl);
+		    cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
 #endif
 
 #ifdef CY_PCI_DEBUG
@@ -4652,7 +4656,7 @@
 
                 /* set cy_card */
                 cy_card[j].base_addr = (ulong)cy_pci_addr2;
-                cy_card[j].ctl_addr = 0;
+                cy_card[j].ctl_addr = (ulong)cy_pci_addr0;
                 cy_card[j].irq = (int) cy_pci_irq;
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
@@ -4664,20 +4668,16 @@
 		switch (plx_ver) {
 		    case PLX_9050:
 
-		    outw(inw(cy_pci_addr1+0x4c)|0x0040,cy_pci_addr1+0x4c);
-		    pci_intr_ctrl = (unsigned long)
-				(inw(cy_pci_addr1+0x4c)
-				| inw(cy_pci_addr1+0x4e)<<16);
+		    cy_writew(cy_pci_addr0+0x4c, 
+			cy_readw(cy_pci_addr0+0x4c)|0x0040);
 		    break;
 
 		    case PLX_9060:
 		    case PLX_9080:
 		    default: /* Old boards, use PLX_9060 */
 
-		    outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68);
-		    pci_intr_ctrl = (unsigned long)
-				(inw(cy_pci_addr1+0x68)
-				| inw(cy_pci_addr1+0x6a)<<16);
+		    cy_writew(cy_pci_addr0+0x68, 
+			cy_readw(cy_pci_addr0+0x68)|0x0900);
 		    break;
 		}
 
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/epca.c linux/drivers/char/epca.c
--- v2.2.0-pre1/linux/drivers/char/epca.c	Sun Nov  8 14:02:54 1998
+++ linux/drivers/char/epca.c	Tue Dec 29 11:24:57 1998
@@ -286,7 +286,8 @@
 
 #ifdef ENABLE_PCI
 static int init_PCI(int);
-static int get_PCI_configuration(char, char, unsigned int *, unsigned int *,
+static int get_PCI_configuration(unsigned char, unsigned char, 
+					     unsigned int *, unsigned int *,
                                              unsigned int *, unsigned int *,
                                              unsigned int *, unsigned int *);
 #endif /* ENABLE_PCI */
@@ -4037,7 +4038,7 @@
 #ifdef ENABLE_PCI
 /* --------------------- Begin get_PCI_configuration  ---------------------- */
 
-int get_PCI_configuration(char bus, char device_fn,
+int get_PCI_configuration(unsigned char bus, unsigned char device_fn,
                           unsigned int *base_addr0, unsigned int *base_addr1,
                           unsigned int *base_addr2, unsigned int *base_addr3,
                           unsigned int *base_addr4, unsigned int *base_addr5)
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/ftape/RELEASE-NOTES linux/drivers/char/ftape/RELEASE-NOTES
--- v2.2.0-pre1/linux/drivers/char/ftape/RELEASE-NOTES	Tue Nov 25 14:45:26 1997
+++ linux/drivers/char/ftape/RELEASE-NOTES	Tue Dec 29 11:27:38 1998
@@ -423,9 +423,7 @@
 
 This release is a simple bugfix version.
 
-- Linux/SMP: ftape *should* work, if you remember to add the symbol __SMP__
-  to the Makefile (you know what I'm talking about, if you're playing with
-  Linux/SMP :).
+- Linux/SMP: ftape *should* work.
 - FC-10/20: Only accepts IRQs 3-7, or 9.  If IRQ 9, properly tell the card
   to use IRQ 2.  Thanks to Greg Crider (gcrider@iclnet.org) for finding and
   locating this bug and testing the patch.
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/ftape/lowlevel/fdc-io.c linux/drivers/char/ftape/lowlevel/fdc-io.c
--- v2.2.0-pre1/linux/drivers/char/ftape/lowlevel/fdc-io.c	Thu Nov 12 16:21:19 1998
+++ linux/drivers/char/ftape/lowlevel/fdc-io.c	Tue Dec 29 11:27:38 1998
@@ -1348,21 +1348,12 @@
 	if (fdc.hook == &do_ftape) {
 		/*  Get fast interrupt handler.
 		 */
-#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
 		if (request_irq(fdc.irq, ftape_interrupt,
 				SA_INTERRUPT, "ft", ftape_id)) {
 			TRACE_ABORT(-EIO, ft_t_bug,
 				    "Unable to grab IRQ%d for ftape driver",
 				    fdc.irq);
 		}
-#else
-		if (request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT,
-				ftape_id)) {
-			TRACE_ABORT(-EIO, ft_t_bug,
-				    "Unable to grab IRQ%d for ftape driver",
-				    fdc.irq);
-		}
-#endif
 		if (request_dma(fdc.dma, ftape_id)) {
 #if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
 			free_irq(fdc.irq, ftape_id);
@@ -1373,7 +1364,6 @@
 			      "Unable to grab DMA%d for ftape driver",
 			      fdc.dma);
 		}
-		enable_irq(fdc.irq);
 	}
 	if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
 		/* Using same dma channel or irq as standard fdc, need
@@ -1395,12 +1385,7 @@
 	if (fdc.hook == &do_ftape) {
 		disable_dma(fdc.dma);	/* just in case... */
 		free_dma(fdc.dma);
-		disable_irq(fdc.irq);
-#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
 		free_irq(fdc.irq, ftape_id);
-#else
-                free_irq(fdc.irq);
-#endif
 	}
 	if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
 		/* Using same dma channel as standard fdc, need to
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/hfmodem/Config.in linux/drivers/char/hfmodem/Config.in
--- v2.2.0-pre1/linux/drivers/char/hfmodem/Config.in	Wed Feb  4 11:36:00 1998
+++ linux/drivers/char/hfmodem/Config.in	Tue Dec 29 11:28:49 1998
@@ -1,5 +1,5 @@
 comment 'Misc. hamradio protocols'
-tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
+dep_tristate 'Shortwave radio modem driver' CONFIG_HFMODEM $CONFIG_PARPORT
 if [ "$CONFIG_HFMODEM" != "n" ]; then
   bool '   HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
   bool '   HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v2.2.0-pre1/linux/drivers/char/istallion.c	Sun Nov  8 14:02:54 1998
+++ linux/drivers/char/istallion.c	Tue Dec 29 11:29:54 1998
@@ -2311,7 +2311,7 @@
 		if (signal_pending(current))
 			break;
 		stli_delay(2);
-		if (jiffies >= tend)
+		if (time_after_eq(jiffies, tend))
 			break;
 	}
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/riscom8.c linux/drivers/char/riscom8.c
--- v2.2.0-pre1/linux/drivers/char/riscom8.c	Sun Nov  8 14:02:55 1998
+++ linux/drivers/char/riscom8.c	Tue Dec 29 11:29:54 1998
@@ -228,7 +228,7 @@
 {
 	unsigned long i;
 	
-	for (i = jiffies + delay; i > jiffies; ) ;
+	for (i = jiffies + delay; time_after(i,jiffies); ) ;
 }
 
 /* Reset and setup CD180 chip */
@@ -1172,8 +1172,8 @@
 		timeout = jiffies+HZ;
 		while(port->IER & IER_TXEMPTY)  {
 			current->state = TASK_INTERRUPTIBLE;
-			schedule_timeout(port->timeout);
-			if (jiffies > timeout)
+ 			schedule_timeout(port->timeout);
+			if (time_after(jiffies, timeout))
 				break;
 		}
 	}
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/specialix.c linux/drivers/char/specialix.c
--- v2.2.0-pre1/linux/drivers/char/specialix.c	Sun Nov  8 14:02:56 1998
+++ linux/drivers/char/specialix.c	Tue Dec 29 11:29:54 1998
@@ -353,7 +353,7 @@
 {
 	unsigned long i;
 	
-	for (i = jiffies + delay; i > jiffies; ) ;
+	for (i = jiffies + delay; time_after(i, jiffies); ) ;
 }
 
 
@@ -1073,7 +1073,7 @@
 	/* Set baud rate for port */
 	tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
 		CD186x_TPC/2) / CD186x_TPC);
-	if ((tmp < 0x10) && (again < jiffies)) { 
+	if ((tmp < 0x10) && time_before(again, jiffies)) { 
 		again = jiffies + HZ * 60;
 		/* Page 48 of version 2.0 of the CL-CD1865 databook */
 		if (tmp >= 12) {
@@ -1551,8 +1551,8 @@
 		timeout = jiffies+HZ;
 		while(port->IER & IER_TXEMPTY) {
 			current->state = TASK_INTERRUPTIBLE;
-			schedule_timeout(port->timeout);
-			if (jiffies > timeout) {
+ 			schedule_timeout(port->timeout);
+			if (time_after(jiffies, timeout)) {
 				printk (KERN_INFO "Timeout waiting for close\n");
 				break;
 			}
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.2.0-pre1/linux/drivers/char/stallion.c	Sun Nov  8 14:02:56 1998
+++ linux/drivers/char/stallion.c	Tue Dec 29 11:29:54 1998
@@ -1697,7 +1697,7 @@
 		if (signal_pending(current))
 			break;
 		stl_delay(2);
-		if (jiffies >= tend)
+		if (time_after_eq(jiffies, tend))
 			break;
 	}
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.2.0-pre1/linux/drivers/isdn/Config.in	Wed Apr  1 20:11:49 1998
+++ linux/drivers/isdn/Config.in	Tue Dec 29 11:29:54 1998
@@ -38,7 +38,9 @@
     bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET
     bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
     if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+      if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
         bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930
+      fi
     fi
 fi
 if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.2.0-pre1/linux/drivers/net/Makefile	Tue Dec 22 14:16:55 1998
+++ linux/drivers/net/Makefile	Tue Dec 29 11:29:54 1998
@@ -1035,13 +1035,6 @@
 #
 # HIPPI adapters
 #
-ifeq ($(CONFIG_CERN_HIPPI),y)
-L_OBJS += cern_hippi.o
-else
-  ifeq ($(CONFIG_CERN_HIPPI),m)
-  M_OBJS += cern_hippi.o
-  endif
-endif
 
 ifeq ($(CONFIG_ROADRUNNER),y)
 L_OBJS += rrunner.o
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/bmac.c linux/drivers/net/bmac.c
--- v2.2.0-pre1/linux/drivers/net/bmac.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/net/bmac.c	Wed Dec 30 10:55:07 1998
@@ -5,6 +5,7 @@
  * Copyright (C) 1998 Randy Gobbel.
  */
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -59,7 +60,6 @@
 	int rx_fill;
 	int rx_empty;
 	struct sk_buff *tx_bufs[N_TX_RING];
-	char *tx_double[N_TX_RING]; /* yuck--double buffering */
 	int tx_fill;
 	int tx_empty;
 	unsigned char tx_fullup;
@@ -297,7 +297,7 @@
 	val = bmac_mif_readbits(dev, 17);
 	bmwrite(dev, MIFCSR, 4);
 	MIFDELAY;
-	printk(KERN_DEBUG "bmac_mif_read(%x) -> %x\n", addr, val);
+	/* printk(KERN_DEBUG "bmac_mif_read(%x) -> %x\n", addr, val); */
 	return val;
 }
 
@@ -438,9 +438,11 @@
 			bmac_mif_write(dev, 4, 0xa1);
 			bmac_mif_write(dev, 0, 0x1200);
 		}
+#if 0
 		/* XXX debugging */
 		bmac_mif_read(dev, 0);
 		bmac_mif_read(dev, 4);
+#endif
 	}
 	bmac_init_registers(dev);
 	return 1;
@@ -488,23 +490,15 @@
 }
 
 static void
-bmac_construct_xmt(struct sk_buff *skb, volatile struct dbdma_cmd *cp,
-		   char *doubleBuf)
+bmac_construct_xmt(struct sk_buff *skb, volatile struct dbdma_cmd *cp)
 {
-	void *vaddr, *page_break;
+	void *vaddr;
 	unsigned long baddr;
 	unsigned long len;
 
 	len = skb->len;
 	vaddr = skb->data;
 	baddr = virt_to_bus(vaddr);
-	page_break = round_page(vaddr);
-	if (trunc_page(vaddr) != trunc_page(vaddr+len) &&
-	    (unsigned long)round_page(baddr) != virt_to_bus(page_break))  {
-		baddr = virt_to_bus(doubleBuf);
-		XXDEBUG(("bmac: double buffering, double=%#08x, skb->data=%#08x, len=%d\n", doubleBuf, skb->data, len));
-	} else
-		flush_page_to_ram((unsigned long)vaddr);
 
 	dbdma_setcmd(cp, (OUTPUT_LAST | INTR_ALWAYS | WAIT_IFCLR), len, baddr, 0);
 }
@@ -530,17 +524,8 @@
 static int
 bmac_init_tx_ring(struct bmac_data *bp)
 {
-	int i;
 	volatile struct dbdma_regs *td = bp->tx_dma;
-	char *addr;
 
-	if (!bp->tx_allocated) {
-		/* zero out tx cmds, alloc space for double buffering */
-		addr = (char *)kmalloc(ETHERMTU * N_TX_RING, GFP_DMA);
-		if (addr == NULL) return 0;
-		for (i = 0; i < N_TX_RING; i++, addr += ETHERMTU) bp->tx_double[i] = addr;
-		bp->tx_allocated = 1;
-	}
 	memset((char *)bp->tx_cmds, 0, (N_TX_RING+1) * sizeof(struct dbdma_cmd));
 
 	bp->tx_empty = 0;
@@ -615,7 +600,7 @@
 
 	dbdma_setcmd(&bp->tx_cmds[i], DBDMA_STOP, 0, 0, 0);
 
-	bmac_construct_xmt(skb, &bp->tx_cmds[bp->tx_fill], bp->tx_double[bp->tx_fill]);
+	bmac_construct_xmt(skb, &bp->tx_cmds[bp->tx_fill]);
 
 	bp->tx_bufs[bp->tx_fill] = skb;
 	bp->tx_fill = i;
@@ -1177,6 +1162,8 @@
 {
 	struct bmac_data *bp = dev->priv;
 	unsigned long flags;
+	struct sk_buff *skb;
+	unsigned char *data;
 
 	save_flags(flags); cli();
 	bp->reset_and_enabled = 0;
@@ -1187,16 +1174,17 @@
 		bmac_start_chip(dev);
 		bmwrite(dev, INTDISABLE, EnableNormal);
 		bp->reset_and_enabled = 1;
-		/* 	{ */
-		/* 	    unsigned char random_packet[100]; */
-		/* 	    unsigned int i; */
-		/* 	    struct sk_buff *skb = dev_alloc_skb(RX_BUFLEN+2); */
-		/* 	    unsigned char *data = skb_put(skb, sizeof(random_packet)); */
-		/* XXDEBUG(("transmitting random packet\n")); */
-		/* 	    for (i = 0; i < sizeof(random_packet); i++) data[i] = i; */
-		/* 	    bmac_transmit_packet(skb, dev); */
-		/* XXDEBUG(("done transmitting random packet\n")); */
-		/* 	} */
+
+		/*
+		 * It seems that the bmac can't receive until it's transmitted
+		 * a packet.  So we give it a dummy packet to transmit.
+		 */
+		skb = dev_alloc_skb(ETHERMINPACKET);
+		data = skb_put(skb, ETHERMINPACKET);
+		memset(data, 0, ETHERMINPACKET);
+		memcpy(data, dev->dev_addr, 6);
+		memcpy(data+6, dev->dev_addr, 6);
+		bmac_transmit_packet(skb, dev);
 	}
 	restore_flags(flags);
 	return 1;
@@ -1241,8 +1229,13 @@
 		dev->priv = kmalloc(PRIV_BYTES, GFP_KERNEL);
 		if (dev->priv == 0) return -ENOMEM;
 	}
+
+#ifdef MODULE
+	bmac_devs = dev;
+#endif
     
-	dev->base_addr = bmacs->addrs[0].address;
+	dev->base_addr = (unsigned long)
+		ioremap(bmacs->addrs[0].address, bmacs->addrs[0].size);
 	dev->irq = bmacs->intrs[0].line;
 
 	bmwrite(dev, INTDISABLE, DisableAll);
@@ -1257,7 +1250,7 @@
 		}
 	}
     
-	printk(KERN_INFO "%s: BMAC at", dev->name);
+	printk(KERN_INFO "%s: BMAC%s at", dev->name, (is_bmac_plus? "+": ""));
 	rev = addr[0] == 0 && addr[1] == 0xA0;
 	for (j = 0; j < 6; ++j) {
 		dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
@@ -1280,9 +1273,11 @@
     
 	bp = (struct bmac_data *) dev->priv;
 	memset(bp, 0, sizeof(struct bmac_data));
-	bp->tx_dma = (volatile struct dbdma_regs *) bmacs->addrs[1].address;
+	bp->tx_dma = (volatile struct dbdma_regs *)
+		ioremap(bmacs->addrs[1].address, bmacs->addrs[1].size);
 	bp->tx_dma_intr = bmacs->intrs[1].line;
-	bp->rx_dma = (volatile struct dbdma_regs *) bmacs->addrs[2].address;
+	bp->rx_dma = (volatile struct dbdma_regs *)
+		ioremap(bmacs->addrs[2].address, bmacs->addrs[2].size);
 	bp->rx_dma_intr = bmacs->intrs[2].line;
     
 	bp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(bp + 1);
@@ -1370,8 +1365,6 @@
 		}
 	}
 	bp->rx_allocated = 0;
-	XXDEBUG(("bmac: free doubles\n"));/*MEMORY LEAK BELOW!!! FIX!!! */
-	if (bp->tx_double[0] != NULL) kfree(bp->tx_double[0]);
 	XXDEBUG(("bmac: free tx bufs\n"));
 	for (i = 0; i<N_TX_RING; i++) {
 		if (bp->tx_bufs[i] != NULL) {
@@ -1379,7 +1372,6 @@
 			bp->tx_bufs[i] = NULL;
 		}
 	}
-	bp->tx_allocated = 0;
 	bp->reset_and_enabled = 0;
 	XXDEBUG(("bmac: all bufs freed\n"));
 
@@ -1539,3 +1531,32 @@
   
 	return len;
 }
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Randy Gobbel/Paul Mackerras");
+MODULE_DESCRIPTION("PowerMac BMAC ethernet driver.");
+
+int init_module(void)
+{
+    int res;
+   
+    if(bmac_devs != NULL)
+        return -EBUSY;
+    res = bmac_probe(NULL);
+    return res;
+}
+void cleanup_module(void)
+{
+    struct bmac_data *bp = (struct bmac_data *) bmac_devs->priv;
+    unregister_netdev(bmac_devs);
+
+    free_irq(bmac_devs->irq, bmac_misc_intr);
+    free_irq(bp->tx_dma_intr, bmac_txdma_intr);
+    free_irq(bp->rx_dma_intr, bmac_rxdma_intr);
+
+    kfree(bmac_devs);
+    bmac_devs = NULL;
+}
+
+#endif
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.2.0-pre1/linux/drivers/net/de4x5.c	Sun Nov  8 14:03:00 1998
+++ linux/drivers/net/de4x5.c	Wed Dec 30 15:09:18 1998
@@ -415,11 +415,12 @@
 			   alignment for Alpha's and avoid their unaligned
 			   access traps. This flag is merely for log messages:
 			   should do something more definitive though...
+      0.543  30-Dec-98    Add SMP spin locking.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.542 1998/9/15 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.543 1998/12/30 davies@maniac.ultranet.com\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -443,6 +444,7 @@
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
 #include <asm/uaccess.h>
+#include <asm/spinlock.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -508,11 +510,11 @@
 ** recognised by this driver.
 */
 static struct phy_table phy_info[] = {
-    {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},       /* National TX */
-    {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},       /* Broadcom T4 */
-    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},       /* SEEQ T4     */
-    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}},       /* Cypress T4  */
-    {0, 0x7810     , 1, {0x05, 0x0380, 0x0380}}    /* Level One?  */
+    {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},       /* National TX      */
+    {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},       /* Broadcom T4      */
+    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},       /* SEEQ T4          */
+    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}},       /* Cypress T4       */
+    {0, 0x7810     , 1, {0x14, 0x0800, 0x0800}}    /* Level One LTX970 */
 };
 
 /*
@@ -759,7 +761,8 @@
     int tx_new, tx_old;                     /* TX descriptor ring pointers  */
     char setup_frame[SETUP_FRAME_LEN];      /* Holds MCA and PA info.       */
     char frame[64];                         /* Min sized packet for loopback*/
-    struct net_device_stats stats;           /* Public stats                 */
+    spinlock_t lock;                        /* Adapter specific spinlock    */
+    struct net_device_stats stats;          /* Public stats                 */
     struct {
 	u_int bins[DE4X5_PKT_STAT_SZ];      /* Private stats counters       */
 	u_int unicast;
@@ -1192,6 +1195,7 @@
 	lp->timeout = -1;
 	lp->useSROM = useSROM;
 	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
+	lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
 	de4x5_parse_params(dev);
 
 	/*
@@ -1345,7 +1349,7 @@
     ** Re-initialize the DE4X5... 
     */
     status = de4x5_init(dev);
-    
+    lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
     lp->state = OPEN;
     de4x5_dbg_open(dev);
     
@@ -1497,6 +1501,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     int status = 0;
+    u_long flags = 0;
 
     test_and_set_bit(0, (void*)&dev->tbusy);     /* Stop send re-tries */
     if (lp->tx_enable == NO) {                   /* Cannot send for now */
@@ -1508,9 +1513,9 @@
     ** interrupts are lost by delayed descriptor status updates relative to
     ** the irq assertion, especially with a busy PCI bus.
     */
-    cli();
+    spin_lock_irqsave(&lp->lock, flags);
     de4x5_tx(dev);
-    sti();
+    spin_unlock_irqrestore(&lp->lock, flags);
 
     /* Test if cache is already locked - requeue skb if so */
     if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) 
@@ -1534,7 +1539,7 @@
 	}
 
 	while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) {
-	    cli();
+	    spin_lock_irqsave(&lp->lock, flags);
 	    test_and_set_bit(0, (void*)&dev->tbusy);
 	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
  	    lp->stats.tx_bytes += skb->len;
@@ -1547,7 +1552,7 @@
 		dev->tbusy = 0;         /* Another pkt may be queued */
 	    }
 	    skb = de4x5_get_cache(dev);
-	    sti();
+	    spin_unlock_irqrestore(&lp->lock, flags);
 	}
 	if (skb) de4x5_putb_cache(dev, skb);
     }
@@ -1581,6 +1586,7 @@
 	return;
     }
     lp = (struct de4x5_private *)dev->priv;
+    spin_lock(&lp->lock);
     iobase = dev->base_addr;
 	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
@@ -1628,6 +1634,7 @@
 
     lp->interrupt = UNMASK_INTERRUPTS;
     ENABLE_IRQs;
+    spin_unlock(&lp->lock);
     
     return;
 }
@@ -2069,7 +2076,9 @@
 	    irq = inb(EISA_REG0);
 	    irq = de4x5_irq[(irq >> 1) & 0x03];
 
-	    if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	    if (is_DC2114x) {
+		device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143);
+	    }
 	    lp->chipset = device;
 
 	    /* Write the PCI Configuration Registers */
@@ -2165,7 +2174,9 @@
 	lp->bus_num = pb;
 	    
 	/* Set the chipset information */
-	if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	if (is_DC2114x) {
+	    device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143);
+	}
 	lp->chipset = device;
 
 	/* Get the board I/O address (64 bits on sparc64) */
@@ -2249,7 +2260,9 @@
 	lp->bus_num = pb;
 	    
 	/* Set the chipset information */
-	if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	if (is_DC2114x) {
+	    device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143);
+	}
 	lp->chipset = device;
 
 	/* Get the board I/O address (64 bits on sparc64) */
@@ -3223,18 +3236,19 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
+    u_long flags = 0;
 
     if (lp->media != lp->c_media) {
         de4x5_dbg_media(dev);
 	lp->c_media = lp->media;          /* Stop scrolling media messages */
     }
 
-    cli();
+    spin_lock_irqsave(&lp->lock, flags);
     de4x5_rst_desc_ring(dev);
     de4x5_setup_intr(dev);
     lp->tx_enable = YES;
     dev->tbusy = 0;
-    sti();
+    spin_unlock_irqrestore(&lp->lock, flags);
     outl(POLL_DEMAND, DE4X5_TPD);
     mark_bh(NET_BH);
 
@@ -5524,116 +5538,90 @@
 	u16 sval[72];
 	u32 lval[36];
     } tmp;
+    u_long flags = 0;
     
     switch(ioc->cmd) {
     case DE4X5_GET_HWADDR:           /* Get the hardware address */
 	ioc->len = ETH_ALEN;
-	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
-	if (status)
-	    break;
+	if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT;
 	for (i=0; i<ETH_ALEN; i++) {
 	    tmp.addr[i] = dev->dev_addr[i];
 	}
 	copy_to_user(ioc->data, tmp.addr, ioc->len);
-	
 	break;
+
     case DE4X5_SET_HWADDR:           /* Set the hardware address */
-	status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN);
-	if (status)
-	    break;
-	status = -EPERM;
-	if (!capable(CAP_NET_ADMIN))
-	    break;
-	status = 0;
+	if (!capable(CAP_NET_ADMIN)) return -EPERM;
+	if (verify_area(VERIFY_READ, ioc->data, ETH_ALEN)) return -EFAULT;
 	copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
 	for (i=0; i<ETH_ALEN; i++) {
 	    dev->dev_addr[i] = tmp.addr[i];
 	}
 	build_setup_frame(dev, PHYS_ADDR_ONLY);
 	/* Set up the descriptor and give ownership to the card */
-	while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);
+	while (test_and_set_bit(0, (void *)&dev->tbusy) != 0) barrier();
 	load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
 		                                       SETUP_FRAME_LEN, NULL);
 	lp->tx_new = (++lp->tx_new) % lp->txRingSize;
 	outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
 	dev->tbusy = 0;                              /* Unlock the TX ring */
-	
 	break;
+
     case DE4X5_SET_PROM:             /* Set Promiscuous Mode */
-	if (capable(CAP_NET_ADMIN)) {
-	    omr = inl(DE4X5_OMR);
-	    omr |= OMR_PR;
-	    outl(omr, DE4X5_OMR);
-	    dev->flags |= IFF_PROMISC;
-	} else {
-	    status = -EPERM;
-	}
-	
+	if (!capable(CAP_NET_ADMIN)) return -EPERM;
+	omr = inl(DE4X5_OMR);
+	omr |= OMR_PR;
+	outl(omr, DE4X5_OMR);
+	dev->flags |= IFF_PROMISC;
 	break;
+
     case DE4X5_CLR_PROM:             /* Clear Promiscuous Mode */
-	if (capable(CAP_NET_ADMIN)) {
-	    omr = inl(DE4X5_OMR);
-	    omr &= ~OMR_PR;
-	    outb(omr, DE4X5_OMR);
-	    dev->flags &= ~IFF_PROMISC;
-	} else {
-	    status = -EPERM;
-	}
-	
+	if (!capable(CAP_NET_ADMIN)) return -EPERM;
+	omr = inl(DE4X5_OMR);
+	omr &= ~OMR_PR;
+	outb(omr, DE4X5_OMR);
+	dev->flags &= ~IFF_PROMISC;
 	break;
+
     case DE4X5_SAY_BOO:              /* Say "Boo!" to the kernel log file */
 	printk("%s: Boo!\n", dev->name);
-	
 	break;
+
     case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
-	if (capable(CAP_NET_ADMIN)) {
-	    omr = inl(DE4X5_OMR);
-	    omr |= OMR_PM;
-	    outl(omr, DE4X5_OMR);
-	} else {
-	    status = -EPERM;
-	}
-	
+	if (!capable(CAP_NET_ADMIN)) return -EPERM;
+	omr = inl(DE4X5_OMR);
+	omr |= OMR_PM;
+	outl(omr, DE4X5_OMR);
 	break;
+
     case DE4X5_GET_STATS:            /* Get the driver statistics */
 	ioc->len = sizeof(lp->pktStats);
-	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
-	if (status)
-	    break;
-	
-	cli();
+	if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT;
+	spin_lock_irqsave(&lp->lock, flags);
 	copy_to_user(ioc->data, &lp->pktStats, ioc->len); 
-	sti();
-	
+	spin_unlock_irqrestore(&lp->lock, flags);
 	break;
+
     case DE4X5_CLR_STATS:            /* Zero out the driver statistics */
-	if (capable(CAP_NET_ADMIN)) {
-	    cli();
-	    memset(&lp->pktStats, 0, sizeof(lp->pktStats));
-	    sti();
-	} else {
-	    status = -EPERM;
-	}
-	
+	if (!capable(CAP_NET_ADMIN)) return -EPERM;
+	spin_lock_irqsave(&lp->lock, flags);
+	memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+	spin_unlock_irqrestore(&lp->lock, flags);
 	break;
+
     case DE4X5_GET_OMR:              /* Get the OMR Register contents */
 	tmp.addr[0] = inl(DE4X5_OMR);
-	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
-	    copy_to_user(ioc->data, tmp.addr, 1);
-	}
-	
+	if (verify_area(VERIFY_WRITE, ioc->data, 1)) return -EFAULT;
+	copy_to_user(ioc->data, tmp.addr, 1);
 	break;
+
     case DE4X5_SET_OMR:              /* Set the OMR Register contents */
-	if (capable(CAP_NET_ADMIN)) {
-	    if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
-		copy_from_user(tmp.addr, ioc->data, 1);
-		outl(tmp.addr[0], DE4X5_OMR);
-	    }
-	} else {
-	    status = -EPERM;
-	}
-	
+	if (!capable(CAP_NET_ADMIN)) return -EPERM;
+	if (verify_area(VERIFY_READ, ioc->data, 1)) return -EFAULT;
+	copy_from_user(tmp.addr, ioc->data, 1);
+	outl(tmp.addr[0], DE4X5_OMR);
 	break;
+
     case DE4X5_GET_REG:              /* Get the DE4X5 Registers */
 	j = 0;
 	tmp.lval[0] = inl(DE4X5_STS); j+=4;
@@ -5645,9 +5633,8 @@
 	tmp.lval[6] = inl(DE4X5_STRR); j+=4;
 	tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
 	ioc->len = j;
-	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    copy_to_user(ioc->data, tmp.addr, ioc->len);
-	}
+	if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT;
+	copy_to_user(ioc->data, tmp.addr, ioc->len);
 	break;
 	
 #define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */
@@ -5736,14 +5723,13 @@
 	tmp.addr[j++] = dev->tbusy;
 	
 	ioc->len = j;
-	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    copy_to_user(ioc->data, tmp.addr, ioc->len);
-	}
-	
+	if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT;
+	copy_to_user(ioc->data, tmp.addr, ioc->len);
 	break;
+
 */
     default:
-	status = -EOPNOTSUPP;
+	return -EOPNOTSUPP;
     }
     
     return status;
@@ -5775,6 +5761,12 @@
 	if (!mdev) mdev = p;
 
 	if (register_netdev(p) != 0) {
+	    struct de4x5_private *lp = (struct de4x5_private *)p->priv;
+	    if (lp) {
+		release_region(p->base_addr, (lp->bus == PCI ? 
+					      DE4X5_PCI_TOTAL_SIZE :
+					      DE4X5_EISA_TOTAL_SIZE));
+	    }
 	    kfree(p);
 	} else {
 	    status = 0;                 /* At least one adapter will work */
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/de4x5.h linux/drivers/net/de4x5.h
--- v2.2.0-pre1/linux/drivers/net/de4x5.h	Thu Jul 16 18:09:25 1998
+++ linux/drivers/net/de4x5.h	Wed Dec 30 15:09:18 1998
@@ -121,6 +121,7 @@
 #define DC2114x     DC2114x_DID
 #define DC21142     (DC2114x_DID | 0x0010)
 #define DC21143     (DC2114x_DID | 0x0030)
+#define DC2114x_BRK 0x0020           /* CFRV break between DC21142 & DC21143 */
 
 #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
 #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v2.2.0-pre1/linux/drivers/net/dgrs.c	Mon Apr  6 17:41:00 1998
+++ linux/drivers/net/dgrs.c	Tue Dec 29 11:32:06 1998
@@ -1098,7 +1098,7 @@
 	 */
 	proc_reset(dev0, 0);
 
-	for (i = jiffies + 8 * HZ; i > jiffies; )
+	for (i = jiffies + 8 * HZ; time_after(i, jiffies); )
 	{
 		if (priv0->bcomm->bc_status >= BC_RUN)
 			break;
@@ -1196,7 +1196,7 @@
 		return (rc);
 
 	priv->intrcnt = 0;
-	for (i = jiffies + 2*HZ + HZ/2; i > jiffies; )
+	for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); )
 		if (priv->intrcnt >= 2)
 			break;
 	if (priv->intrcnt < 2)
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/eepro.c linux/drivers/net/eepro.c
--- v2.2.0-pre1/linux/drivers/net/eepro.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/eepro.c	Tue Dec 29 11:30:56 1998
@@ -8,7 +8,7 @@
 	according to the terms of the GNU Public License,
 	incorporated herein by reference.
 
-	The author may be reached at bao.ha@srs.gov
+	The author may be reached at bao.ha@srs.gov 
 	or 418 Hastings Place, Martinez, GA 30907.
 
 	Things remaining to do:
@@ -23,6 +23,33 @@
 	This is a compatibility hardware problem.
 
 	Versions:
+	0.11b   Pascal Dupuis : works as a module under 2.1.xx
+	        debug messages are flagged as KERN_DEBUG to avoid console 
+		flooding 
+		added locking at critical parts
+	0.11a   Attempt to get 2.1.xx support up (RMC)
+	0.11	Brian Candler added support for multiple cards. Tested as
+		a module, no idea if it works when compiled into kernel.
+
+	0.10e	Rick Bressler notified me that ifconfig up;ifconfig down fails
+		because the irq is lost somewhere. Fixed that by moving 
+		request_irq and free_irq to eepro_open and eepro_close respectively.
+	0.10d	Ugh! Now Wakeup works. Was seriously broken in my first attempt.
+		I'll need to find a way to specify an ioport other than
+		the default one in the PnP case. PnP definitively sucks.
+		And, yes, this is not the only reason.
+	0.10c	PnP Wakeup Test for 595FX. uncomment #define PnPWakeup;
+		to use.
+	0.10b	Should work now with (some) Pro/10+. At least for 
+		me (and my two cards) it does. _No_ guarantee for 
+		function with non-Pro/10+ cards! (don't have any)
+		(RMC, 9/11/96)
+
+	0.10	Added support for the Etherexpress Pro/10+.  The
+		IRQ map was changed significantly from the old
+		pro/10.  The new interrupt map was provided by
+		Rainer M. Canavan (Canavan@Zeus.cs.bonn.edu).
+		(BCH, 9/3/96)
 
 	0.09	Fixed a race condition in the transmit algorithm,
 		which causes crashes under heavy load with fast
@@ -38,7 +65,7 @@
 	0.07a	Fix a stat report which counts every packet as a
 		heart-beat failure. (BCH, 6/3/95)
 
-	0.07	Modified to support all other 82595-based lan cards.
+	0.07	Modified to support all other 82595-based lan cards.  
 		The IRQ vector of the EtherExpress Pro will be set
 		according to the value saved in the EEPROM.  For other
 		cards, I will do autoirq_request() to grab the next
@@ -48,37 +75,37 @@
 		print out format. (BCH, 3/9/95 and 3/14/95)
 
 	0.06	First stable release that I am comfortable with. (BCH,
-		3/2/95)
+		3/2/95)	
 
-	0.05	Complete testing of multicast. (BCH, 2/23/95)
+	0.05	Complete testing of multicast. (BCH, 2/23/95)	
 
-	0.04	Adding multicast support. (BCH, 2/14/95)
+	0.04	Adding multicast support. (BCH, 2/14/95)	
 
-	0.03	First widely alpha release for public testing.
-		(BCH, 2/14/95)
+	0.03	First widely alpha release for public testing. 
+		(BCH, 2/14/95)	
 
 */
 
 static const char *version =
-	"eepro.c: v0.09 7/31/96 Bao C. Ha (bao.ha@srs.gov)\n";
+	"eepro.c: v0.11b 08/12/1998 dupuis@lei.ucl.ac.be\n";
 
 #include <linux/module.h>
 
 /*
   Sources:
 
-	This driver wouldn't have been written without the availability
-	of the Crynwr's Lan595 driver source code.  It helps me to
-	familiarize with the 82595 chipset while waiting for the Intel
-	documentation.  I also learned how to detect the 82595 using
+	This driver wouldn't have been written without the availability 
+	of the Crynwr's Lan595 driver source code.  It helps me to 
+	familiarize with the 82595 chipset while waiting for the Intel 
+	documentation.  I also learned how to detect the 82595 using 
 	the packet driver's technique.
 
 	This driver is written by cutting and pasting the skeleton.c driver
 	provided by Donald Becker.  I also borrowed the EEPROM routine from
 	Donald Becker's 82586 driver.
 
-	Datasheet for the Intel 82595 (including the TX and FX version). It
-	provides just enough info that the casual reader might think that it
+	Datasheet for the Intel 82595 (including the TX and FX version). It 
+	provides just enough info that the casual reader might think that it 
 	documents the i82595.
 
 	The User Manual for the 82595.  It provides a lot of the missing
@@ -100,23 +127,59 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+#include <asm/spinlock.h>
+#endif
 #include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/delay.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
 
+/* need to remove these asap      */
+/* 2.1.xx compatibility macros... */
+/*                                */
+
+
+#include <linux/version.h>
+
+/* For linux 2.1.xx */
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#define compat_dev_kfree_skb( skb, mode ) dev_kfree_skb( (skb) )
+/* I had reports of looong delays with SLOW_DOWN defined as udelay(2) */
+#define SLOW_DOWN inb(0x80)
+/* udelay(2) */
+
+#else 
+/* for 2.x */
+
+#define compat_dev_kfree_skb( skb, mode ) dev_kfree_skb( (skb), (mode) )
+#define test_and_set_bit(a,b) set_bit((a),(b))
+#define SLOW_DOWN SLOW_DOWN_IO
+
+#endif
+
+
 /* First, a few definitions that the brave might change. */
 /* A zero-terminated list of I/O addresses to be probed. */
-static unsigned int eepro_portlist[] __initdata =
-   { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360, 0};
+static unsigned int eepro_portlist[] =
+   { 0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0};
+/* note: 0x300 is default, the 595FX supports ALL IO Ports 
+  from 0x000 to 0x3F0, some of which are reserved in PCs */
+
+/* To try the (not-really PnP Wakeup: */
+/*
+#define PnPWakeup
+*/
 
 /* use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
-#define NET_DEBUG 3
+#define NET_DEBUG 0
 #endif
 static unsigned int net_debug = NET_DEBUG;
 
@@ -130,23 +193,107 @@
 
 /* Information that need to be kept for each board. */
 struct eepro_local {
-	struct net_device_stats stats;
+	struct enet_statistics stats;
 	unsigned rx_start;
 	unsigned tx_start; /* start of the transmit chain */
 	int tx_last;  /* pointer to last packet in the transmit chain */
 	unsigned tx_end;   /* end of the transmit chain (plus 1) */
-	int eepro;	/* a flag, TRUE=1 for the EtherExpress Pro/10,
-			   FALSE = 0 for other 82595-based lan cards. */
+	int eepro;	/* 1 for the EtherExpress Pro/10,
+			   2 for the EtherExpress Pro/10+,
+			   0 for other 82595-based lan cards. */
 	int version;	/* a flag to indicate if this is a TX or FX
 				   version of the 82595 chip. */
 	int stepping;
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+        spinlock_t lock; /* to prevent interrupt within interrupts */ 
+#endif
 };
 
 /* The station (ethernet) address prefix, used for IDing the board. */
-#define SA_ADDR0 0x00
+#define SA_ADDR0 0x00	/* Etherexpress Pro/10 */
 #define SA_ADDR1 0xaa
 #define SA_ADDR2 0x00
 
+#define GetBit(x,y) ((x & (1<<y))>>y)
+
+/* EEPROM Word 0: */
+#define ee_PnP       0  /* Plug 'n Play enable bit */
+#define ee_Word1     1  /* Word 1? */
+#define ee_BusWidth  2  /* 8/16 bit */
+#define ee_FlashAddr 3  /* Flash Address */
+#define ee_FlashMask 0x7   /* Mask */
+#define ee_AutoIO    6  /* */
+#define ee_reserved0 7  /* =0! */
+#define ee_Flash     8  /* Flash there? */
+#define ee_AutoNeg   9  /* Auto Negotiation enabled? */
+#define ee_IO0       10 /* IO Address LSB */
+#define ee_IO0Mask   0x /*...*/
+#define ee_IO1       15 /* IO MSB */
+
+/* EEPROM Word 1: */
+#define ee_IntSel    0   /* Interrupt */
+#define ee_IntMask   0x7
+#define ee_LI        3   /* Link Integrity 0= enabled */
+#define ee_PC        4   /* Polarity Correction 0= enabled */
+#define ee_TPE_AUI   5   /* PortSelection 1=TPE */
+#define ee_Jabber    6   /* Jabber prevention 0= enabled */
+#define ee_AutoPort  7   /* Auto Port Selection 1= Disabled */
+#define ee_SMOUT     8   /* SMout Pin Control 0= Input */
+#define ee_PROM      9   /* Flash EPROM / PROM 0=Flash */
+#define ee_reserved1 10  /* .. 12 =0! */
+#define ee_AltReady  13  /* Alternate Ready, 0=normal */
+#define ee_reserved2 14  /* =0! */
+#define ee_Duplex    15
+
+/* Word2,3,4: */
+#define ee_IA5       0 /*bit start for individual Addr Byte 5 */
+#define ee_IA4       8 /*bit start for individual Addr Byte 5 */
+#define ee_IA3       0 /*bit start for individual Addr Byte 5 */
+#define ee_IA2       8 /*bit start for individual Addr Byte 5 */
+#define ee_IA1       0 /*bit start for individual Addr Byte 5 */
+#define ee_IA0       8 /*bit start for individual Addr Byte 5 */
+
+/* Word 5: */
+#define ee_BNC_TPE   0 /* 0=TPE */
+#define ee_BootType  1 /* 00=None, 01=IPX, 10=ODI, 11=NDIS */
+#define ee_BootTypeMask 0x3 
+#define ee_NumConn   3  /* Number of Connections 0= One or Two */
+#define ee_FlashSock 4  /* Presence of Flash Socket 0= Present */
+#define ee_PortTPE   5
+#define ee_PortBNC   6
+#define ee_PortAUI   7
+#define ee_PowerMgt  10 /* 0= disabled */
+#define ee_CP        13 /* Concurrent Processing */
+#define ee_CPMask    0x7
+
+/* Word 6: */
+#define ee_Stepping  0 /* Stepping info */
+#define ee_StepMask  0x0F
+#define ee_BoardID   4 /* Manucaturer Board ID, reserved */
+#define ee_BoardMask 0x0FFF
+
+/* Word 7: */
+#define ee_INT_TO_IRQ 0 /* int to IRQ Mapping  = 0x1EB8 for Pro/10+ */
+#define ee_FX_INT2IRQ 0x1EB8 /* the _only_ mapping allowed for FX chips */
+
+/*..*/
+#define ee_SIZE 0x40 /* total EEprom Size */
+#define ee_Checksum 0xBABA /* initial and final value for adding checksum */
+
+
+/* Card identification via EEprom:   */
+#define ee_addr_vendor 0x10  /* Word offset for EISA Vendor ID */
+#define ee_addr_id 0x11      /* Word offset for Card ID */
+#define ee_addr_SN 0x12      /* Serial Number */
+#define ee_addr_CRC_8 0x14   /* CRC over last thee Bytes */
+
+
+#define ee_vendor_intel0 0x25  /* Vendor ID Intel */
+#define ee_vendor_intel1 0xD4
+#define ee_id_eepro10p0 0x10   /* ID for eepro/10+ */
+#define ee_id_eepro10p1 0x31
+
+
 /* Index to functions, as function prototypes. */
 
 extern int eepro_probe(struct device *dev);
@@ -158,8 +305,8 @@
 static void 	eepro_rx(struct device *dev);
 static void 	eepro_transmit_interrupt(struct device *dev);
 static int	eepro_close(struct device *dev);
-static struct net_device_stats *eepro_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev);
+static struct enet_statistics *eepro_get_stats(struct device *dev);
+static void     set_multicast_list(struct device *dev);
 
 static int read_eeprom(int ioaddr, int location);
 static void hardware_send_packet(struct device *dev, void *buf, short length);
@@ -186,10 +333,10 @@
 network traffics, the ring linked list should improve performance by
 allowing up to 8K worth of packets to be queued.
 
-The sizes of the receive and transmit buffers can now be changed via lilo
+The sizes of the receive and transmit buffers can now be changed via lilo 
 or insmod.  Lilo uses the appended line "ether=io,irq,debug,rx-buffer,eth0"
 where rx-buffer is in KB unit.  Modules uses the parameter mem which is
-also in KB unit, for example "insmod io=io-address irq=0 mem=rx-buffer."
+also in KB unit, for example "insmod io=io-address irq=0 mem=rx-buffer."  
 The receive buffer has to be more than 3K or less than 29K.  Otherwise,
 it is reset to the default of 24K, and, hence, 8K for the trasnmit
 buffer (transmit-buffer = 32K - receive-buffer).
@@ -200,11 +347,11 @@
 #define RCV_RAM         0x6000  /* 24KB default for RCV buffer */
 #define RCV_LOWER_LIMIT 0x00    /* 0x0000 */
 /* #define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) */    /* 0x5ffe */
-#define RCV_UPPER_LIMIT (((rcv_ram) - 2) >> 8)
+#define RCV_UPPER_LIMIT (((rcv_ram) - 2) >> 8)   
 /* #define XMT_RAM         (RAM_SIZE - RCV_RAM) */    /* 8KB for XMT buffer */
 #define XMT_RAM         (RAM_SIZE - (rcv_ram))    /* 8KB for XMT buffer */
 /* #define XMT_LOWER_LIMIT (RCV_RAM >> 8) */  /* 0x6000 */
-#define XMT_LOWER_LIMIT ((rcv_ram) >> 8)
+#define XMT_LOWER_LIMIT ((rcv_ram) >> 8) 
 #define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8)   /* 0x7ffe */
 #define	XMT_HEADER	8
 
@@ -218,9 +365,9 @@
 #define	XMT_CHAIN	0x04
 #define	XMT_COUNT	0x06
 
-#define	BANK0_SELECT	0x00
-#define	BANK1_SELECT	0x40
-#define	BANK2_SELECT	0x80
+#define	BANK0_SELECT	0x00		
+#define	BANK1_SELECT	0x40		
+#define	BANK2_SELECT	0x80		
 
 /* Bank 0 registers */
 #define	COMMAND_REG	0x00	/* Register 0 */
@@ -280,7 +427,7 @@
 #define	REG13		0x0d
 #define	FDX		0x00
 #define	A_N_ENABLE	0x02
-
+	
 #define	I_ADD_REG0	0x04
 #define	I_ADD_REG1	0x05
 #define	I_ADD_REG2	0x06
@@ -295,7 +442,7 @@
 #define EEDO 0x08
 
 
-/* Check for a network adaptor of this type, and return '0' iff one exists.
+/* Check for a network adaptor of this type, and return '0' if one exists.
    If dev->base_addr == 0, probe all likely locations.
    If dev->base_addr == 1, always return failure.
    If dev->base_addr == 2, allocate space for the device and return success
@@ -307,17 +454,48 @@
 struct netdev_entry netcard_drv =
 {"eepro", eepro_probe1, EEPRO_IO_EXTENT, eepro_portlist};
 #else
-__initfunc(int
-eepro_probe(struct device *dev))
+int
+eepro_probe(struct device *dev)
 {
 	int i;
 	int base_addr = dev ? dev->base_addr : 0;
 
+
+#ifdef PnPWakeup
+	/* XXXX for multiple cards should this only be run once? */
+	
+	/* Wakeup: */
+	#define WakeupPort 0x279
+	#define WakeupSeq    {0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,\
+                              0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,\
+                              0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,\
+                              0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x43}
+
+	{
+		unsigned short int WS[32]=WakeupSeq;
+
+		if (check_region(WakeupPort, 2)==0) {
+
+			if (net_debug>5)
+				printk(KERN_DEBUG "Waking UP\n");
+
+			outb_p(0,WakeupPort);
+			outb_p(0,WakeupPort);
+			for (i=0; i<32; i++) {
+				outb_p(WS[i],WakeupPort);
+				if (net_debug>5) printk(KERN_DEBUG ": %#x ",WS[i]);
+			}
+		} else printk("Checkregion Failed!\n");
+	}
+#endif
+
+
 	if (base_addr > 0x1ff)		/* Check a single specified location. */
 		return eepro_probe1(dev, base_addr);
 	else if (base_addr != 0)	/* Don't probe at all. */
 		return ENXIO;
 
+
 	for (i = 0; eepro_portlist[i]; i++) {
 		int ioaddr = eepro_portlist[i];
 		if (check_region(ioaddr, EEPRO_IO_EXTENT))
@@ -330,30 +508,89 @@
 }
 #endif
 
+void printEEPROMInfo(short ioaddr)
+{
+	unsigned short Word;
+	int i,j;
+  
+	for (i=0, j=ee_Checksum; i<ee_SIZE; i++)
+		j+=read_eeprom(ioaddr,i);
+	printk("Checksum: %#x\n",j&0xffff);
+     
+	Word=read_eeprom(ioaddr, 0);
+	printk(KERN_DEBUG "Word0:\n");
+	printk(KERN_DEBUG " Plug 'n Pray: %d\n",GetBit(Word,ee_PnP));
+	printk(KERN_DEBUG " Buswidth: %d\n",(GetBit(Word,ee_BusWidth)+1)*8 );
+	printk(KERN_DEBUG " AutoNegotiation: %d\n",GetBit(Word,ee_AutoNeg)); 
+	printk(KERN_DEBUG " IO Address: %#x\n", (Word>>ee_IO0)<<4);
+
+	if (net_debug>4)  {
+		Word=read_eeprom(ioaddr, 1);
+		printk(KERN_DEBUG "Word1:\n");
+		printk(KERN_DEBUG " INT: %d\n", Word & ee_IntMask);
+		printk(KERN_DEBUG " LI: %d\n", GetBit(Word,ee_LI));
+		printk(KERN_DEBUG " PC: %d\n", GetBit(Word,ee_PC));
+		printk(KERN_DEBUG " TPE/AUI: %d\n", GetBit(Word,ee_TPE_AUI));
+		printk(KERN_DEBUG " Jabber: %d\n", GetBit(Word,ee_Jabber));
+		printk(KERN_DEBUG " AutoPort: %d\n", GetBit(!Word,ee_Jabber));
+		printk(KERN_DEBUG " Duplex: %d\n", GetBit(Word,ee_Duplex));  
+	}
+
+  Word=read_eeprom(ioaddr, 5);
+	printk(KERN_DEBUG "Word5:\n");
+	printk(KERN_DEBUG " BNC: %d\n",GetBit(Word,ee_BNC_TPE));
+	printk(KERN_DEBUG " NumConnectors: %d\n",GetBit(Word,ee_NumConn));
+	printk(KERN_DEBUG " Has ");
+	if (GetBit(Word,ee_PortTPE)) printk("TPE ");
+	if (GetBit(Word,ee_PortBNC)) printk("BNC ");
+	if (GetBit(Word,ee_PortAUI)) printk("AUI ");
+	printk("port(s) \n");
+
+  Word=read_eeprom(ioaddr, 6);
+	printk(KERN_DEBUG "Word6:\n");
+	printk(KERN_DEBUG " Stepping: %d\n",Word & ee_StepMask);
+	printk(KERN_DEBUG " BoardID: %d\n",Word>>ee_BoardID);
+
+  Word=read_eeprom(ioaddr, 7);
+	printk(KERN_DEBUG "Word7:\n");
+	printk(KERN_DEBUG " INT to IRQ:\n");
+
+	printk(KERN_DEBUG);
+  for (i=0, j=0; i<15; i++)
+     if (GetBit(Word,i)) printk(" INT%d -> IRQ %d;",j++,i);
+
+	printk("\n");
+}
+
 /* This is the real probe routine.  Linux has a history of friendly device
-   probes on the ISA bus.  A good device probes avoids doing writes, and
+   probes on the ISA bus.  A good device probe avoids doing writes, and
    verifies that the correct device exists and functions.  */
 
-__initfunc(int eepro_probe1(struct device *dev, short ioaddr))
+int eepro_probe1(struct device *dev, short ioaddr)
 {
 	unsigned short station_addr[6], id, counter;
-	int i;
-	int eepro;	/* a flag, TRUE=1 for the EtherExpress Pro/10,
-			   FALSE = 0 for other 82595-based lan cards. */
+	int i,j, irqMask;
+	int eepro;
 	const char *ifmap[] = {"AUI", "10Base2", "10BaseT"};
 	enum iftype { AUI=0, BNC=1, TPE=2 };
 
 	/* Now, we are going to check for the signature of the
 	   ID_REG (register 2 of bank 0) */
 
-	if (((id=inb(ioaddr + ID_REG)) & ID_REG_MASK) == ID_REG_SIG) {
+
+ id=inb(ioaddr + ID_REG);
+ 
+ printk(KERN_DEBUG " id: %#x ",id);
+ printk(" io: %#x ",ioaddr);
+ 
+	if (((id) & ID_REG_MASK) == ID_REG_SIG) {
 
 		/* We seem to have the 82595 signature, let's
 		   play with its counter (last 2 bits of
 		   register 2 of bank 0) to be sure. */
-
-		counter = (id & R_ROBIN_BITS);
-		if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) ==
+	
+		counter = (id & R_ROBIN_BITS);	
+		if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) == 
 			(counter + 0x40)) {
 
 			/* Yes, the 82595 has been found */
@@ -366,79 +603,110 @@
 			station_addr[2] = read_eeprom(ioaddr, 4);
 
 			/* Check the station address for the manufacturer's code */
-
-			if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) {
-				eepro = 0;
-				printk("%s: Intel 82595-based lan card at %#x,",
+			if (net_debug>3)
+				printEEPROMInfo(ioaddr);
+				
+		        if (read_eeprom(ioaddr,7)== ee_FX_INT2IRQ) { /* int to IRQ Mask */
+				eepro = 2;
+				printk("%s: Intel EtherExpress Pro/10+ ISA\n at %#x,", 
+					dev->name, ioaddr);
+			} else
+			if (station_addr[2] == 0x00aa)  {
+				eepro = 1;
+				printk("%s: Intel EtherExpress Pro/10 ISA at %#x,", 
 					dev->name, ioaddr);
 			}
 			else {
-				eepro = 1;
-				printk("%s: Intel EtherExpress Pro/10 at %#x,",
+				eepro = 0;
+				printk("%s: Intel 82595-based lan card at %#x,", 
 					dev->name, ioaddr);
 			}
 
    			/* Fill in the 'dev' fields. */
 			dev->base_addr = ioaddr;
-
+			
 			for (i=0; i < 6; i++) {
 				dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i];
 				printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
 			}
-
+			
 			if ((dev->mem_end & 0x3f) < 3 ||	/* RX buffer must be more than 3K */
 				(dev->mem_end & 0x3f) > 29)	/* and less than 29K */
 				dev->mem_end = RCV_RAM;		/* or it will be set to 24K */
 			else dev->mem_end = 1024*dev->mem_end;  /* Maybe I should shift << 10 */
 
 			/* From now on, dev->mem_end contains the actual size of rx buffer */
-
+			
 			if (net_debug > 3)
 				printk(", %dK RCV buffer", (int)(dev->mem_end)/1024);
+				
+				
+//			outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+//			id = inb(ioaddr + REG3);
+//			if (id & TPE_BIT)
+//				dev->if_port = TPE;
+//			else dev->if_port = BNC;
+			
+			
+			/* ............... */
+
+			if (GetBit(  read_eeprom(ioaddr, 5),ee_BNC_TPE))
+				dev->if_port = BNC;
+			else dev->if_port = TPE;
+                
+			
+			/* ............... */
 
-			outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
-			id = inb(ioaddr + REG3);
-			if (id & TPE_BIT)
-				dev->if_port = TPE;
-			else dev->if_port = BNC;
+//			if (net_debug>3) 
+//				printk("id: %x\n", id);
 
-			if (dev->irq < 2 && eepro) {
+
+			if ((dev->irq < 2) && (eepro!=0)) {
 				i = read_eeprom(ioaddr, 1);
-				switch (i & 0x07) {
-					case 0:	dev->irq = 9; break;
-					case 1:	dev->irq = 3; break;
-					case 2:	dev->irq = 5; break;
-					case 3:	dev->irq = 10; break;
-					case 4:	dev->irq = 11; break;
-					default: /* should never get here !!!!! */
-						printk(" illegal interrupt vector stored in EEPROM.\n");
+				irqMask = read_eeprom(ioaddr, 7);
+				i &= 0x07; /* Mask off INT number */
+				
+				for (j=0; ((j<16) && (i>=0)); j++) {
+					if ((irqMask & (1<<j))!=0) {
+					        if (i==0) {
+					        	dev->irq = j;
+					        	break; /* found bit corresponding to irq */
+					        }
+						i--; /* count bits set in irqMask */
+ 					}
+ 				}
+ 				if (dev -> irq<2) {
+					printk(" Duh! illegal interrupt vector stored in EEPROM.\n");
 						return ENODEV;
-					}
-				}
-			else if (dev->irq == 2)
+				} else 
+				
+				if (dev->irq==2) dev->irq = 9;
+
+				else if (dev->irq == 2)
 				dev->irq = 9;
+			}
 
 			if (dev->irq > 2) {
 				printk(", IRQ %d, %s.\n", dev->irq,
 						ifmap[dev->if_port]);
-				if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro", dev)) {
+				/*if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro", dev)) {
 					printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
 					return -EAGAIN;
-				}
+				}*/
 			}
 			else printk(", %s.\n", ifmap[dev->if_port]);
-
+			
 			if ((dev->mem_start & 0xf) > 0)	/* I don't know if this is */
 				net_debug = dev->mem_start & 7; /* still useful or not */
 
 			if (net_debug > 3) {
 				i = read_eeprom(ioaddr, 5);
 				if (i & 0x2000) /* bit 13 of EEPROM word 5 */
-					printk("%s: Concurrent Processing is enabled but not used!\n",
+				  printk(KERN_DEBUG "%s: Concurrent Processing is enabled but not used!\n",
 						dev->name);
 			}
 
-			if (net_debug)
+			if (net_debug) 
 				printk(version);
 
 			/* Grab the region so we can find another board if autoIRQ fails. */
@@ -450,10 +718,10 @@
 				return -ENOMEM;
 			memset(dev->priv, 0, sizeof(struct eepro_local));
 
-			dev->open = eepro_open;
-			dev->stop = eepro_close;
-			dev->hard_start_xmit = eepro_send_packet;
-			dev->get_stats = eepro_get_stats;
+			dev->open               = eepro_open;
+			dev->stop               = eepro_close;
+			dev->hard_start_xmit    = eepro_send_packet;
+			dev->get_stats          = eepro_get_stats;
 			dev->set_multicast_list = &set_multicast_list;
 
 			/* Fill in the fields of the device structure with
@@ -481,17 +749,21 @@
    */
 
 static char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1};
+static char irqrmap2[] = {-1,-1,4,0,1,2,-1,3,-1,4,5,6,7,-1,-1,-1};
 static int	eepro_grab_irq(struct device *dev)
 {
-	int irqlist[] = { 5, 9, 10, 11, 4, 3, 0};
+	int irqlist[] = { 3, 4, 5, 7, 9, 10, 11, 12 };
 	int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr;
 
 	outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+	
+	/* Set the spinlock before activating  IRQ! */
+	((struct eepro_local *)dev->priv)->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
 
 	/* Enable the interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
 	outb(temp_reg | INT_ENABLE, ioaddr + REG1);
-
+	
 	outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
 
 	/* clear all interrupts */
@@ -507,18 +779,20 @@
 
 		outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
 
-		if (request_irq (*irqp, NULL, 0, "bogus", NULL) != EBUSY) {
+		if (request_irq (*irqp, NULL, 0, "bogus", dev) != EBUSY) {
 			/* Twinkle the interrupt, and check if it's seen */
 			autoirq_setup(0);
 
 			outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */
-
-			if (*irqp == autoirq_report(2) &&  /* It's a good IRQ line */
-				(request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro", dev) == 0))
+				
+			if (*irqp == autoirq_report(2) )//&&  /* It's a good IRQ line */
+				/* We don't take irqs on detection anymore.
+					only when actually turning on the driver (ifconfig) */
+				/* //(request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro", dev) == 0)) */
 					break;
 
 			/* clear all interrupts */
-			outb(ALL_MASK, ioaddr + STATUS_REG);
+			outb(ALL_MASK, ioaddr + STATUS_REG); 
 		}
 	} while (*++irqp);
 
@@ -526,40 +800,64 @@
 
 	/* Disable the physical interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
-	outb(temp_reg & 0x7f, ioaddr + REG1);
+	outb(temp_reg & 0x7f, ioaddr + REG1); 
 
 	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
 
 	/* Mask all the interrupts. */
-	outb(ALL_MASK, ioaddr + INT_MASK_REG);
+	outb(ALL_MASK, ioaddr + INT_MASK_REG); 
 
 	/* clear all interrupts */
-	outb(ALL_MASK, ioaddr + STATUS_REG);
+	outb(ALL_MASK, ioaddr + STATUS_REG); 
 
 	return dev->irq;
 }
 
-static int
-eepro_open(struct device *dev)
+static int eepro_open(struct device *dev)
 {
 	unsigned short temp_reg, old8, old9;
+	int irqMask;
 	int i, ioaddr = dev->base_addr, rcv_ram = dev->mem_end;
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
 
 	if (net_debug > 3)
-		printk("eepro: entering eepro_open routine.\n");
+		printk(KERN_DEBUG "eepro: entering eepro_open routine.\n");
 
-	if (dev->dev_addr[0] == SA_ADDR0 &&
+	if ((irqMask=read_eeprom(ioaddr,7))== ee_FX_INT2IRQ) /* INT to IRQ Mask */
+		{
+		  	lp->eepro = 2; /* Yes, an Intel EtherExpress Pro/10+ */
+		  	if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 2;\n"); 
+		}
+       
+	else if ((dev->dev_addr[0] == SA_ADDR0 &&
 			dev->dev_addr[1] == SA_ADDR1 &&
-			dev->dev_addr[2] == SA_ADDR2)
-		lp->eepro = 1; /* Yes, an Intel EtherExpress Pro/10 */
+			dev->dev_addr[2] == SA_ADDR2))
+		{ 
+			lp->eepro = 1;
+			if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 1;\n"); 
+		}  /* Yes, an Intel EtherExpress Pro/10 */
+
 	else lp->eepro = 0; /* No, it is a generic 82585 lan card */
 
-	/* Get the interrupt vector for the 82595 */
+	/* Get the interrupt vector for the 82595 */	
 	if (dev->irq < 2 && eepro_grab_irq(dev) == 0) {
 		printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
 		return -EAGAIN;
 	}
+		
+	if (request_irq(dev->irq , &eepro_interrupt, 0, "eepro", dev)) {
+		printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+		return -EAGAIN;
+	}
+	
+#ifdef irq2dev_map
+	if  (((irq2dev_map[dev->irq] != 0)
+		|| (irq2dev_map[dev->irq] = dev) == 0) && 
+		(irq2dev_map[dev->irq]!=dev)) {
+	    /* printk("%s: IRQ map wrong\n", dev->name); */
+	 	return -EAGAIN;
+	}
+#endif
 
 	/* Initialize the 82595. */
 
@@ -567,18 +865,18 @@
 	temp_reg = inb(ioaddr + EEPROM_REG);
 
 	lp->stepping = temp_reg >> 5;	/* Get the stepping number of the 595 */
-
+	
 	if (net_debug > 3)
-		printk("The stepping of the 82595 is %d\n", lp->stepping);
+		printk(KERN_DEBUG "The stepping of the 82595 is %d\n", lp->stepping);
 
 	if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
 		outb(temp_reg & 0xef, ioaddr + EEPROM_REG);
-	for (i=0; i < 6; i++)
-		outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);
-
+	for (i=0; i < 6; i++) 
+		outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i); 
+			
 	temp_reg = inb(ioaddr + REG1);    /* Setup Transmit Chaining */
 	outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */
-		| RCV_Discard_BadFrame, ioaddr + REG1);
+		| RCV_Discard_BadFrame, ioaddr + REG1);  
 
 	temp_reg = inb(ioaddr + REG2); /* Match broadcast */
 	outb(temp_reg | 0x14, ioaddr + REG2);
@@ -589,51 +887,70 @@
 	/* Set the receiving mode */
 	outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
 
+	/* Set the interrupt vector */	
 	temp_reg = inb(ioaddr + INT_NO_REG);
-	outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+	if (lp->eepro == 2)
+		outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG);
+	else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); 
+
+
+    temp_reg = inb(ioaddr + INT_NO_REG);
+    if (lp->eepro == 2)
+       outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG);
+    else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+
+    if (net_debug > 3)
+       printk(KERN_DEBUG "eepro_open: content of INT Reg is %x\n", temp_reg);
+       
+       
 
 	/* Initialize the RCV and XMT upper and lower limits */
-	outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);
-	outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);
-	outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG);
-	outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG);
+	outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG); 
+	outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG); 
+	outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG); 
+	outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG); 
 
 	/* Enable the interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
-	outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+	outb(temp_reg | INT_ENABLE, ioaddr + REG1); 
 
 	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
 
 	/* Let RX and TX events to interrupt */
-	outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+	outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
 	/* clear all interrupts */
-	outb(ALL_MASK, ioaddr + STATUS_REG);
+	outb(ALL_MASK, ioaddr + STATUS_REG); 
 
 	/* Initialize RCV */
-	outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);
+	outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR); 
 	lp->rx_start = (RCV_LOWER_LIMIT << 8) ;
-	outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP);
+	outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP); 
 
 	/* Initialize XMT */
-	outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR);
+	outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR); 
 
 	/* Check for the i82595TX and i82595FX */
 	old8 = inb(ioaddr + 8);
 	outb(~old8, ioaddr + 8);
 	if ((temp_reg = inb(ioaddr + 8)) == old8) {
 		if (net_debug > 3)
-			printk("i82595 detected!\n");
+			printk(KERN_DEBUG "i82595 detected!\n");
 		lp->version = LAN595;
 	}
 	else {
 		lp->version = LAN595TX;
 		outb(old8, ioaddr + 8);
 		old9 = inb(ioaddr + 9);
-		outb(~old9, ioaddr + 9);
-		if ((temp_reg = inb(ioaddr + 9)) == ~old9) {
+		/*outb(~old9, ioaddr + 9);
+		if (((temp_reg = inb(ioaddr + 9)) == ( (~old9)&0xff) )) {*/
+		
+		if (irqMask==ee_FX_INT2IRQ) {
 			enum iftype { AUI=0, BNC=1, TPE=2 };
-			if (net_debug > 3)
-				printk("i82595FX detected!\n");
+
+			if (net_debug > 3) {
+				printk(KERN_DEBUG "IrqMask: %#x\n",irqMask);
+				printk(KERN_DEBUG "i82595FX detected!\n");
+			}
 			lp->version = LAN595FX;
 			outb(old9, ioaddr + 9);
 			if (dev->if_port != TPE) {	/* Hopefully, this will fix the
@@ -647,24 +964,26 @@
 				outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
 			}
 		}
-		else if (net_debug > 3)
-			printk("i82595TX detected!\n");
+		else if (net_debug > 3) {
+		        printk(KERN_DEBUG "temp_reg: %#x  ~old9: %#x\n",temp_reg,((~old9)&0xff));
+			printk(KERN_DEBUG "i82595TX detected!\n");
+		}
 	}
-
+	
 	outb(SEL_RESET_CMD, ioaddr);
 	/* We are supposed to wait for 2 us after a SEL_RESET */
-	
-	udelay(2);
+	SLOW_DOWN;
+	SLOW_DOWN;
 
 	lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */
-	lp->tx_last = 0;
-
+	lp->tx_last = 0;  
+	
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
 
 	if (net_debug > 3)
-		printk("eepro: exiting eepro_open routine.\n");
+		printk(KERN_DEBUG "eepro: exiting eepro_open routine.\n");
 
 	outb(RCV_ENABLE_CMD, ioaddr);
 
@@ -672,121 +991,168 @@
 	return 0;
 }
 
-static int
-eepro_send_packet(struct sk_buff *skb, struct device *dev)
+static int eepro_send_packet(struct sk_buff *skb, struct device *dev)
 {
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 	int rcv_ram = dev->mem_end;
 
 	if (net_debug > 5)
-		printk("eepro: entering eepro_send_packet routine.\n");
-
+	  printk(KERN_DEBUG  "eepro: entering eepro_send_packet routine.\n");
+	
 	if (dev->tbusy) {
-		/* If we get here, some higher level has decided we are broken.
-		   There should really be a "kick me" function call instead. */
-		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 40)
-			return 1;
-		if (net_debug > 1)
-			printk("%s: transmit timed out, %s?\n", dev->name,
-				   "network cable problem");
-		lp->stats.tx_errors++;
-
-		/* Try to restart the adaptor. */
-		outb(SEL_RESET_CMD, ioaddr);
-		/* We are supposed to wait for 2 us after a SEL_RESET */
-		udelay(2);
-
-		/* Do I also need to flush the transmit buffers here? YES? */
-		lp->tx_start = lp->tx_end = rcv_ram;
-		lp->tx_last = 0;
-
-		dev->tbusy=0;
-		dev->trans_start = jiffies;
+	  /* If we get here, some higher level has decided we are broken.
+	     There should really be a "kick me" function call instead. */
+	  int tickssofar = jiffies - dev->trans_start;
+	  if (tickssofar < 40)
+	    return 1;
+	 
+	  /* if (net_debug > 1) */
+	  printk("%s: transmit timed out, %s?\n", dev->name, 
+		 "network cable problem");
+	  /* This is not a duplicate. One message for the console, one for the
+	     the log file  */
+	  printk(KERN_DEBUG "%s: transmit timed out, %s?\n", dev->name,
+		 "network cable problem");
+	  lp->stats.tx_errors++;
+
+	  /* Try to restart the adaptor. */
+	  outb(SEL_RESET_CMD, ioaddr); 
+	  /* We are supposed to wait for 2 us after a SEL_RESET */
+	  SLOW_DOWN;
+	  SLOW_DOWN;
+	  
+	  /* Do I also need to flush the transmit buffers here? YES? */
+	  lp->tx_start = lp->tx_end = rcv_ram; 
+	  lp->tx_last = 0;
+	
+	  dev->tbusy=0;
+	  dev->trans_start = jiffies;
+	  
+	  outb(RCV_ENABLE_CMD, ioaddr);
 
-		outb(RCV_ENABLE_CMD, ioaddr);
+	}
 
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20155
+	/* If some higher layer thinks we've missed an tx-done interrupt
+	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	   itself. */
+	//	if (skb == NULL) {
+	//	dev_tint(dev);
+	//	return 0;
+	// FIXME : what's this code for ?
 	}
+#endif
 
 	/* Block a timer-based transmit from overlapping. */
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
-		printk("%s: Transmitter access conflict.\n", dev->name);
+	  printk("%s: Transmitter access conflict.\n", dev->name);
 	else {
-		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-		unsigned char *buf = skb->data;
+          short length; unsigned char *buf;
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+	  unsigned long flags;
 
-		hardware_send_packet(dev, buf, length);
-		dev->trans_start = jiffies;
+	  /* Spin on the lock, until we're clear of an IRQ */
+	  spin_lock_irqsave(&lp->lock, flags);     
+#endif
+	  length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	  buf = skb->data;
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+	  lp->stats.tx_bytes+=skb->len;
+#endif
+	  hardware_send_packet(dev, buf, length);
+	  
+	  dev->trans_start = jiffies;
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+	  spin_unlock_irqrestore(&lp->lock, flags);     
+#endif	
 	}
 
-	dev_kfree_skb (skb);
+	compat_dev_kfree_skb (skb, FREE_WRITE);
 
 	/* You might need to clean up and record Tx statistics here. */
 	/* lp->stats.tx_aborted_errors++; */
 
 	if (net_debug > 5)
-		printk("eepro: exiting eepro_send_packet routine.\n");
-
+		printk(KERN_DEBUG "eepro: exiting eepro_send_packet routine.\n");
+	
 	return 0;
 }
 
-
 /*	The typical workload of the driver:
 	Handle the network interface interrupts. */
 static void
 eepro_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
-	struct device *dev = dev_id;
+	struct device *dev =  (struct device *)dev_id;
+	                      /* (struct device *)(irq2dev_map[irq]);*/
+#ifdef __SMP__
+	struct eepro_local *lp = (struct eepro_local *)dev->priv;
+#endif	
 	int ioaddr, status, boguscount = 20;
 
-	if (net_debug > 5)
-		printk("eepro: entering eepro_interrupt routine.\n");
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) 
+	{
+		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+        		dev->name);
+		dev->interrupt = 0; /* Avoid halting machine. */
+        	return;
+	}
 
+	if (net_debug > 5)
+		printk(KERN_DEBUG "eepro: entering eepro_interrupt routine.\n");
+	
 	if (dev == NULL) {
 		printk ("eepro_interrupt(): irq %d for unknown device.\n", irq);
 		return;
 	}
-	dev->interrupt = 1;
-
-	ioaddr = dev->base_addr;
-
-	do {
-		status = inb(ioaddr + STATUS_REG);
-
-		if (status & RX_INT) {
-			if (net_debug > 4)
-				printk("eepro: packet received interrupt.\n");
-
-			/* Acknowledge the RX_INT */
-			outb(RX_INT, ioaddr + STATUS_REG);
-
-			/* Get the received packets */
-			eepro_rx(dev);
-		}
-
-		else if (status & TX_INT) {
-			if (net_debug > 4)
-				printk("eepro: packet transmit interrupt.\n");
-
-			/* Acknowledge the TX_INT */
-			outb(TX_INT, ioaddr + STATUS_REG);
-
-			/* Process the status of transmitted packets */
-			eepro_transmit_interrupt(dev);
-		}
+	/* dev->interrupt = 1; */
 
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+	spin_lock(&lp->lock);
+#endif	
+        ioaddr = dev->base_addr;
+
+        do { 
+	  status = inb(ioaddr + STATUS_REG);
+		
+	  if (status & RX_INT) {
+	    if (net_debug > 4)
+	      printk(KERN_DEBUG "eepro: packet received interrupt.\n");
+
+	    /* Acknowledge the RX_INT */
+	    outb(RX_INT, ioaddr + STATUS_REG);   
+	    /* Get the received packets */
+	    eepro_rx(dev);
+	  }
+
+	  else if (status & TX_INT) {
+	    if (net_debug > 4)
+	      printk(KERN_DEBUG "eepro: packet transmit interrupt.\n");
+
+	    /* Acknowledge the TX_INT */
+	    outb(TX_INT, ioaddr + STATUS_REG); 
+	    
+	    /* Process the status of transmitted packets */
+	    eepro_transmit_interrupt(dev);
+	  }
+	
 	} while ((boguscount-- > 0) && (status & 0x06));
 
-	dev->interrupt = 0;
+	dev->interrupt = 0; 
+
 	if (net_debug > 5)
-		printk("eepro: exiting eepro_interrupt routine.\n");
+		printk(KERN_DEBUG "eepro: exiting eepro_interrupt routine.\n");
 
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+	spin_unlock(&lp->lock);
+#endif
 	return;
 }
 
-static int
-eepro_close(struct device *dev)
+static int eepro_close(struct device *dev)
 {
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
 	int ioaddr = dev->base_addr;
@@ -800,40 +1166,44 @@
 
 	/* Disable the physical interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
-	outb(temp_reg & 0x7f, ioaddr + REG1);
+	outb(temp_reg & 0x7f, ioaddr + REG1); 
 
 	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
 
 	/* Flush the Tx and disable Rx. */
-	outb(STOP_RCV_CMD, ioaddr);
+	outb(STOP_RCV_CMD, ioaddr); 
 	lp->tx_start = lp->tx_end = rcv_ram ;
-	lp->tx_last = 0;
+	lp->tx_last = 0;  
 
 	/* Mask all the interrupts. */
-	outb(ALL_MASK, ioaddr + INT_MASK_REG);
+	outb(ALL_MASK, ioaddr + INT_MASK_REG); 
 
 	/* clear all interrupts */
-	outb(ALL_MASK, ioaddr + STATUS_REG);
+	outb(ALL_MASK, ioaddr + STATUS_REG); 
 
 	/* Reset the 82595 */
-	outb(RESET_CMD, ioaddr);
+	outb(RESET_CMD, ioaddr); 
 
 	/* release the interrupt */
 	free_irq(dev->irq, dev);
 
+#ifdef irq2dev_map
+	irq2dev_map[dev->irq] = 0;
+#endif
+
 	/* Update the statistics here. What statistics? */
 
 	/* We are supposed to wait for 200 us after a RESET */
+	SLOW_DOWN;
+	SLOW_DOWN; /* May not be enough? */
 
-	udelay(200);
-	
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
 /* Get the current statistics.	This may be called with the card open or
    closed. */
-static struct net_device_stats *
+static struct enet_statistics *
 eepro_get_stats(struct device *dev)
 {
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
@@ -851,7 +1221,7 @@
 	unsigned short mode;
 	struct dev_mc_list *dmi=dev->mc_list;
 
-	if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63)
+	if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63) 
 	{
 		/*
 		 *	We must make the kernel realise we had to move
@@ -859,17 +1229,17 @@
 		 *	the cable. If it was a promisc request the
 		 *	flag is already set. If not we assert it.
 		 */
-		dev->flags|=IFF_PROMISC;
+		dev->flags|=IFF_PROMISC;		
 
 		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 		mode = inb(ioaddr + REG2);
-		outb(mode | PRMSC_Mode, ioaddr + REG2);
+		outb(mode | PRMSC_Mode, ioaddr + REG2);	
 		mode = inb(ioaddr + REG3);
 		outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
 		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
 		printk("%s: promiscuous mode enabled.\n", dev->name);
-	}
-	else if (dev->mc_count==0 )
+	} 
+	else if (dev->mc_count==0 ) 
 	{
 		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 		mode = inb(ioaddr + REG2);
@@ -878,11 +1248,11 @@
 		outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
 		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
 	}
-	else
+	else 
 	{
 		unsigned short status, *eaddrs;
 		int i, boguscount = 0;
-
+		
 		/* Disable RX and TX interrupts.  Necessary to avoid
 		   corruption of the HOST_ADDRESS_REG by interrupt
 		   service routines. */
@@ -890,7 +1260,7 @@
 
 		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 		mode = inb(ioaddr + REG2);
-		outb(mode | Multi_IA, ioaddr + REG2);
+		outb(mode | Multi_IA, ioaddr + REG2);	
 		mode = inb(ioaddr + REG3);
 		outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
 		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
@@ -899,7 +1269,7 @@
 		outw(0, ioaddr + IO_PORT);
 		outw(0, ioaddr + IO_PORT);
 		outw(6*(dev->mc_count + 1), ioaddr + IO_PORT);
-		for (i = 0; i < dev->mc_count; i++)
+		for (i = 0; i < dev->mc_count; i++) 
 		{
 			eaddrs=(unsigned short *)dmi->dmi_addr;
 			dmi=dmi->next;
@@ -916,9 +1286,9 @@
 
 		/* Update the transmit queue */
 		i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1);
-		if (lp->tx_start != lp->tx_end)
-		{
-			/* update the next address and the chain bit in the
+		if (lp->tx_start != lp->tx_end) 
+		{ 
+			/* update the next address and the chain bit in the 
 			   last packet */
 			outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
 			outw(i, ioaddr + IO_PORT);
@@ -933,26 +1303,28 @@
 
 		/* Acknowledge that the MC setup is done */
 		do { /* We should be doing this in the eepro_interrupt()! */
-			udelay(2);
-			if (inb(ioaddr + STATUS_REG) & 0x08)
+			SLOW_DOWN;
+			SLOW_DOWN;
+			if (inb(ioaddr + STATUS_REG) & 0x08) 
 			{
 				i = inb(ioaddr);
 				outb(0x08, ioaddr + STATUS_REG);
 				if (i & 0x20) { /* command ABORTed */
-					printk("%s: multicast setup failed.\n",
+					printk("%s: multicast setup failed.\n", 
 						dev->name);
 					break;
 				} else if ((i & 0x0f) == 0x03)	{ /* MC-Done */
-					printk("%s: set Rx mode to %d addresses.\n",
-						dev->name, dev->mc_count);
+				  printk("%s: set Rx mode to %d address%s.\n",
+					 dev->name, dev->mc_count,
+					 dev->mc_count > 1 ? "es":"");
 					break;
 				}
 			}
 		} while (++boguscount < 100);
 
 		/* Re-enable RX and TX interrupts */
-		outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
-
+		outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
+	
 	}
 	outb(RCV_ENABLE_CMD, ioaddr);
 }
@@ -961,7 +1333,7 @@
 /* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
 
 /* The delay between EEPROM clock transitions. */
-#define eeprom_delay()	{ udelay(40); }
+#define eeprom_delay()	{ int _i = 40; while (--_i > 0) { SLOW_DOWN; }}
 #define EE_READ_CMD (6 << 6)
 
 int
@@ -972,10 +1344,10 @@
 	short ee_addr = ioaddr + EEPROM_REG;
 	int read_cmd = location | EE_READ_CMD;
 	short ctrl_val = EECS ;
-
+	
 	outb(BANK2_SELECT, ioaddr);
 	outb(ctrl_val, ee_addr);
-
+	
 	/* Shift the read command bits out. */
 	for (i = 8; i >= 0; i--) {
 		short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI
@@ -987,7 +1359,7 @@
 		eeprom_delay();
 	}
 	outb(ctrl_val, ee_addr);
-
+	
 	for (i = 16; i > 0; i--) {
 		outb(ctrl_val | EESK, ee_addr);	 eeprom_delay();
 		retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
@@ -1013,7 +1385,7 @@
 	unsigned status, tx_available, last, end, boguscount = 100;
 
 	if (net_debug > 5)
-		printk("eepro: entering hardware_send_packet routine.\n");
+		printk(KERN_DEBUG "eepro: entering hardware_send_packet routine.\n");
 
 	while (boguscount-- > 0) {
 
@@ -1022,9 +1394,9 @@
 	   	service routines. */
 		outb(ALL_MASK, ioaddr + INT_MASK_REG);
 
-		if (dev->interrupt == 1) {
+		if (dev->interrupt == 1) {  
 			/* Enable RX and TX interrupts */
-			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
 			continue;
 		}
 
@@ -1035,13 +1407,13 @@
 			tx_available = lp->tx_start - lp->tx_end;
 		else tx_available = XMT_RAM;
 
-		if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER)
+		if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER) 
 			>= tx_available)   /* No space available ??? */
 			{
 			eepro_transmit_interrupt(dev); /* Clean up the transmiting queue */
 
 			/* Enable RX and TX interrupts */
-			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
 			continue;
 		}
 
@@ -1049,17 +1421,17 @@
 		end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
 
 		if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */
-			if ((RAM_SIZE - last) <= XMT_HEADER) {
+			if ((RAM_SIZE - last) <= XMT_HEADER) {	
 			/* Arrrr!!!, must keep the xmt header together,
 			  several days were lost to chase this one down. */
 				last = rcv_ram;
 				end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
-			}
+			}	
 			else end = rcv_ram + (end - RAM_SIZE);
 		}
 
 		outw(last, ioaddr + HOST_ADDRESS_REG);
-		outw(XMT_CMD, ioaddr + IO_PORT);
+		outw(XMT_CMD, ioaddr + IO_PORT); 
 		outw(0, ioaddr + IO_PORT);
 		outw(end, ioaddr + IO_PORT);
 		outw(length, ioaddr + IO_PORT);
@@ -1074,48 +1446,46 @@
 		}
 
 		/* A dummy read to flush the DRAM write pipeline */
-		status = inw(ioaddr + IO_PORT);
+		status = inw(ioaddr + IO_PORT); 
 
-		if (lp->tx_start == lp->tx_end) {
+		if (lp->tx_start == lp->tx_end) {	
 			outw(last, ioaddr + XMT_BAR);
 			outb(XMT_CMD, ioaddr);
 			lp->tx_start = last;   /* I don't like to change tx_start here */
 		}
 		else {
-			/* update the next address and the chain bit in the
-		   	last packet */
-			if (lp->tx_end != last) {
-				outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
-				outw(last, ioaddr + IO_PORT);
-			}
-			outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
-			status = inw(ioaddr + IO_PORT);
-			outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+		  /* update the next address and the chain bit in the 
+		     last packet */
+		  if (lp->tx_end != last) {
+		    outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+		    outw(last, ioaddr + IO_PORT); 
+		  }
+		  outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+		  status = inw(ioaddr + IO_PORT); 
+		  outw(status | CHAIN_BIT, ioaddr + IO_PORT);
 
-			/* Continue the transmit command */
-			outb(RESUME_XMT_CMD, ioaddr);
+		  /* Continue the transmit command */
+		  outb(RESUME_XMT_CMD, ioaddr);
 		}
 
 		lp->tx_last = last;
 		lp->tx_end = end;
 
-		/* Enable RX and TX interrupts */
-		outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
-
 		if (dev->tbusy) {
 			dev->tbusy = 0;
 		}
-
-		lp->stats.tx_bytes += length;
+		
+		/* Enable RX and TX interrupts */
+                outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
 
 		if (net_debug > 5)
-			printk("eepro: exiting hardware_send_packet routine.\n");
+			printk(KERN_DEBUG "eepro: exiting hardware_send_packet routine.\n");
 		return;
 	}
 
 	dev->tbusy = 1;
 	if (net_debug > 5)
-		printk("eepro: exiting hardware_send_packet routine.\n");
+		printk(KERN_DEBUG "eepro: exiting hardware_send_packet routine.\n");
 }
 
 static void
@@ -1128,75 +1498,77 @@
 	unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size;
 
 	if (net_debug > 5)
-		printk("eepro: entering eepro_rx routine.\n");
-
+		printk(KERN_DEBUG "eepro: entering eepro_rx routine.\n");
+	
 	/* Set the read pointer to the start of the RCV */
 	outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
 	rcv_event = inw(ioaddr + IO_PORT);
 
 	while (rcv_event == RCV_DONE) {
-		rcv_status = inw(ioaddr + IO_PORT);
-		rcv_next_frame = inw(ioaddr + IO_PORT);
-		rcv_size = inw(ioaddr + IO_PORT);
-
-		if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) {
-			/* Malloc up new buffer. */
-			struct sk_buff *skb;
-
-			rcv_size &= 0x3fff;
-			skb = dev_alloc_skb(rcv_size+5);
-			if (skb == NULL) {
-				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
-				lp->stats.rx_dropped++;
-				break;
-			}
-			skb->dev = dev;
-			skb_reserve(skb,2);
-
-			if (lp->version == LAN595)
-				insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1);
-			else {	/* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
-				unsigned short temp = inb(ioaddr + INT_MASK_REG);
-				outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
-				insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), (rcv_size + 3) >> 2);
-				outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
-			}
-
-
-			skb->protocol = eth_type_trans(skb,dev);
-			netif_rx(skb);
-			lp->stats.rx_packets++;
-			lp->stats.rx_bytes += rcv_size;
-		}
-		else { /* Not sure will ever reach here,
-			  I set the 595 to discard bad received frames */
-			lp->stats.rx_errors++;
-			if (rcv_status & 0x0100)
-				lp->stats.rx_over_errors++;
-			else if (rcv_status & 0x0400)
-				lp->stats.rx_frame_errors++;
-			else if (rcv_status & 0x0800)
-				lp->stats.rx_crc_errors++;
-			printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n",
-				dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size);
-		}
-		if (rcv_status & 0x1000)
-			lp->stats.rx_length_errors++;
-		if (--boguscount == 0)
-			break;
-
-		rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
-		lp->rx_start = rcv_next_frame;
-		outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
-		rcv_event = inw(ioaddr + IO_PORT);
-
-	}
+	  rcv_status = inw(ioaddr + IO_PORT); 
+	  rcv_next_frame = inw(ioaddr + IO_PORT);
+	  rcv_size = inw(ioaddr + IO_PORT); 
+
+	  if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) {
+	    /* Malloc up new buffer. */
+	    struct sk_buff *skb;
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+	    lp->stats.rx_bytes+=rcv_size;
+#endif                        
+	    rcv_size &= 0x3fff;
+	    skb = dev_alloc_skb(rcv_size+5);
+	    if (skb == NULL) {
+	      printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+	      lp->stats.rx_dropped++;
+	      break;
+	    }
+	    skb->dev = dev;
+	    skb_reserve(skb,2);
+
+	    if (lp->version == LAN595)
+	      insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1);
+	    else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
+	      unsigned short temp = inb(ioaddr + INT_MASK_REG);
+	      outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
+	      insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), 
+		   (rcv_size + 3) >> 2);
+	      outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
+	    }
+	
+	    skb->protocol = eth_type_trans(skb,dev);	
+	    netif_rx(skb);
+	    lp->stats.rx_packets++;
+	  }
+	  else { /* Not sure will ever reach here, 
+		    I set the 595 to discard bad received frames */
+	    lp->stats.rx_errors++;
+	    if (rcv_status & 0x0100)
+	      lp->stats.rx_over_errors++;
+	    else if (rcv_status & 0x0400)
+	      lp->stats.rx_frame_errors++;
+	    else if (rcv_status & 0x0800)
+	      lp->stats.rx_crc_errors++;
+	    printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n", 
+		   dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size);
+	  }
+	  if (rcv_status & 0x1000)
+	    lp->stats.rx_length_errors++;
+	  if (--boguscount == 0)
+	    break;
+	  
+	  rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
+	  lp->rx_start = rcv_next_frame;
+	  outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
+	  rcv_event = inw(ioaddr + IO_PORT);
+	  
+	} 
 	if (rcv_car == 0)
-		rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff;
+	  rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff;
 	outw(rcv_car - 1, ioaddr + RCV_STOP);
 
 	if (net_debug > 5)
-		printk("eepro: exiting eepro_rx routine.\n");
+	  printk(KERN_DEBUG "eepro: exiting eepro_rx routine.\n");
 }
 
 static void
@@ -1204,80 +1576,118 @@
 {
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
 	short ioaddr = dev->base_addr;
-	short boguscount = 20;
+	short boguscount = 20; 
 	short xmt_status;
-
-	while (lp->tx_start != lp->tx_end) {
-
-		outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG);
-		xmt_status = inw(ioaddr+IO_PORT);
-		if ((xmt_status & TX_DONE_BIT) == 0) break;
-
-		xmt_status = inw(ioaddr+IO_PORT);
-		lp->tx_start = inw(ioaddr+IO_PORT);
-
-		dev->tbusy = 0;
-		mark_bh(NET_BH);
-
-		if (xmt_status & 0x2000)
-			lp->stats.tx_packets++;
-		else {
-			lp->stats.tx_errors++;
-			if (xmt_status & 0x0400)
-				lp->stats.tx_carrier_errors++;
-			printk("%s: XMT status = %#x\n",
-				dev->name, xmt_status);
-		}
-		if (xmt_status & 0x000f) {
-			lp->stats.collisions += (xmt_status & 0x000f);
-		}
-		if ((xmt_status & 0x0040) == 0x0) {
-			lp->stats.tx_heartbeat_errors++;
-		}
-
-		if (--boguscount == 0)
-			break;
+	
+	/*
+	if (dev->tbusy == 0) {
+	  printk("%s: transmit_interrupt called with tbusy = 0 ??\n",
+		 dev->name);
+	  printk(KERN_DEBUG "%s: transmit_interrupt called with tbusy = 0 ??\n",
+		 dev->name);
+	}
+	*/
+	while (lp->tx_start != lp->tx_end) { 
+	  outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG); 
+	  xmt_status = inw(ioaddr+IO_PORT);
+	  if ((xmt_status & TX_DONE_BIT) == 0) break;
+
+	  xmt_status = inw(ioaddr+IO_PORT); 
+	  lp->tx_start = inw(ioaddr+IO_PORT);
+
+	  dev->tbusy = 0;
+	  mark_bh(NET_BH);
+
+	  if (xmt_status & 0x2000)
+	    lp->stats.tx_packets++; 
+	  else {
+	    lp->stats.tx_errors++;
+	    if (xmt_status & 0x0400)
+	      lp->stats.tx_carrier_errors++;
+	    printk("%s: XMT status = %#x\n",
+		   dev->name, xmt_status);
+	    printk(KERN_DEBUG "%s: XMT status = %#x\n",
+		   dev->name, xmt_status);
+	  }
+	  if (xmt_status & 0x000f) {
+	    lp->stats.collisions += (xmt_status & 0x000f);
+	  }
+	  if ((xmt_status & 0x0040) == 0x0) {
+	    lp->stats.tx_heartbeat_errors++;
+	  }
+	  
+	  if (--boguscount == 0)
+	    break;  
 	}
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_eepro = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, eepro_probe };
-
-static int io = 0x200;
-static int irq = 0;
-static int mem = (RCV_RAM/1024);	/* Size of the rx buffer in KB */
+#define MAX_EEPRO 8
+static char devicename[MAX_EEPRO][9];
+static struct device dev_eepro[MAX_EEPRO];
+
+static int io[MAX_EEPRO] = {
+#ifdef PnPWakeup
+  0x210,  /*: default for PnP enabled FX chips */
+#else
+  0x200,  /* Why? */
+#endif
+  -1, -1, -1, -1, -1, -1, -1};
+static int irq[MAX_EEPRO] = {0, 0, 0, 0, 0, 0, 0, 0};
+static int mem[MAX_EEPRO] = {	/* Size of the rx buffer in KB */
+  (RCV_RAM/1024),
+  (RCV_RAM/1024),
+  (RCV_RAM/1024),
+  (RCV_RAM/1024),
+  (RCV_RAM/1024),
+  (RCV_RAM/1024),
+  (RCV_RAM/1024),
+  (RCV_RAM/1024)
+};
 
+static int n_eepro = 0;
+/* For linux 2.1.xx */
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155
+MODULE_AUTHOR("Pascal Dupuis <dupuis@lei.ucl.ac.be> for the 2.1 stuff (locking,...)");
+MODULE_DESCRIPTION("Intel i82595 ISA EtherExpressPro10/10+ driver");
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
 MODULE_PARM(mem, "i");
+#endif 
 
-int
-init_module(void)
+int init_module(void)
 {
-	if (io == 0)
+	if (io[0] == 0)
 		printk("eepro: You should not use auto-probing with insmod!\n");
-	dev_eepro.base_addr = io;
-	dev_eepro.irq       = irq;
-	dev_eepro.mem_end   = mem;
+	while (n_eepro < MAX_EEPRO && io[n_eepro] >= 0) {
+		struct device *d = &dev_eepro[n_eepro];
+		d->name		= devicename[n_eepro]; /* inserted by drivers/net/net_init.c */
+		d->mem_end	= mem[n_eepro];
+		d->base_addr	= io[n_eepro];
+		d->irq		= irq[n_eepro];
+		d->init		= eepro_probe;
 
-	if (register_netdev(&dev_eepro) != 0)
-		return -EIO;
-	return 0;
+		if (register_netdev(d) == 0)
+			n_eepro++;
+	}
+	
+	return n_eepro ? 0 : -ENODEV;
 }
 
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_eepro);
-	kfree_s(dev_eepro.priv,sizeof(struct eepro_local));
-	dev_eepro.priv=NULL;
-
-	/* If we don't do this, we can't re-insmod it later. */
-	release_region(dev_eepro.base_addr, EEPRO_IO_EXTENT);
+	int i;
+	
+	for (i=0; i<n_eepro; i++) {
+		struct device *d = &dev_eepro[i];
+		unregister_netdev(d);
+		kfree_s(d->priv,sizeof(struct eepro_local));
+		d->priv=NULL;
+
+		/* If we don't do this, we can't re-insmod it later. */
+		release_region(d->base_addr, EEPRO_IO_EXTENT);
+		
+	}
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/hamradio/Config.in linux/drivers/net/hamradio/Config.in
--- v2.2.0-pre1/linux/drivers/net/hamradio/Config.in	Tue Jun  9 11:57:29 1998
+++ linux/drivers/net/hamradio/Config.in	Tue Dec 29 11:30:56 1998
@@ -13,8 +13,8 @@
     
 tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX
 tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX
-tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR
-tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP
+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
 
 tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM
 if [ "$CONFIG_SOUNDMODEM" != "n" ]; then
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/hamradio/hdlcdrv.c linux/drivers/net/hamradio/hdlcdrv.c
--- v2.2.0-pre1/linux/drivers/net/hamradio/hdlcdrv.c	Fri Nov 27 13:09:24 1998
+++ linux/drivers/net/hamradio/hdlcdrv.c	Tue Dec 29 11:30:56 1998
@@ -41,6 +41,7 @@
 /*****************************************************************************/
 
 #include <linux/config.h>
+#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/types.h>
@@ -58,10 +59,8 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/hdlcdrv.h>
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 /* prototypes for ax25_encapsulate and ax25_rebuild_header */
 #include <net/ax25.h> 
-#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
 
 /* make genksyms happy */
 #include <linux/ip.h>
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/hp100.c linux/drivers/net/hp100.c
--- v2.2.0-pre1/linux/drivers/net/hp100.c	Fri Oct  9 13:27:09 1998
+++ linux/drivers/net/hp100.c	Tue Dec 29 11:32:06 1998
@@ -2796,9 +2796,9 @@
   time=jiffies+(HZ/4);
   do{
     if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
-  } while (time>jiffies);
+  } while (time_after(time, jiffies));
 
-  if ( jiffies >= time )       /* no signal->no logout */
+  if ( time_before_eq(jiffies, time) )       /* no signal->no logout */
     return 0;
 
   /* Drop the VG Link by clearing the link up cmd and load addr.*/
@@ -2810,10 +2810,10 @@
   time=jiffies+(HZ/2);
   do{
     if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break;
-  } while(time>jiffies);
+  } while(time_after(time, jiffies));
 
 #ifdef HP100_DEBUG
-  if (jiffies>=time)
+  if (time_before_eq(jiffies, time))
     printk("hp100: %s: down_vg_link: Link does not go down?\n", dev->name);
 #endif
 
@@ -2848,7 +2848,7 @@
       time=jiffies+(HZ*5);
       do{
         if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break;
-      } while(time>jiffies);
+      } while(time_after(time, jiffies));
 
       hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */
       hp100_outl(savelan, 10_LAN_CFG_1);
@@ -2857,9 +2857,9 @@
   time=jiffies+(3*HZ); /* Timeout 3s */
   do {
     if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break;
-  } while (time>jiffies);
+  } while (time_after(time, jiffies));
   
-  if(time<=jiffies)
+  if(time_before_eq(time, jiffies))
     {
 #ifdef HP100_DEBUG
       printk( "hp100: %s: down_vg_link: timeout\n", dev->name );
@@ -2868,7 +2868,7 @@
     }
   
   time=jiffies+(2*HZ); /* This seems to take a while.... */
-  do {} while (time>jiffies);
+  do {} while (time_after(time, jiffies));
   
   return 0;
 }
@@ -2918,7 +2918,7 @@
       time = jiffies + (HZ/10); 
       do {
         if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break;
-      } while (time>jiffies);
+      } while (time_after(time, jiffies));
 
       /* Start an addressed training and optionally request promiscuous port */
       if ( (dev->flags) & IFF_PROMISC )
@@ -2955,9 +2955,9 @@
       time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */
       do {
         if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
-      } while ( jiffies < time );
+      } while ( time_before(jiffies, time) );
       
-      if ( jiffies >= time )
+      if ( time_after_eq(jiffies, time) )
 	{
 #ifdef HP100_DEBUG_TRAINING
 	  printk( "hp100: %s: Link cable status not ok? Training aborted.\n", dev->name );
@@ -2979,11 +2979,11 @@
 #endif
 		break;
 	      }
-	  } while ( time > jiffies );
+	  } while ( time_after(time, jiffies) );
 	}
       
       /* If LINK_UP_ST is set, then we are logged into the hub. */
-      if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) )
+      if ( time_before_eq(jiffies, time) && (val & HP100_LINK_UP_ST) )
         {
 #ifdef HP100_DEBUG_TRAINING
           printk( "hp100: %s: Successfully logged into the HUB.\n", dev->name);
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.2.0-pre1/linux/drivers/net/plip.c	Fri Oct 23 22:01:21 1998
+++ linux/drivers/net/plip.c	Tue Dec 29 11:51:50 1998
@@ -21,6 +21,10 @@
  *		  - Make sure other end is OK, before sending a packet.
  *		  - Fix immediate timer problem.
  *
+ *		Al Viro
+ *		  - Changed {enable,disable}_irq handling to make it work
+ *		    with new ("stack") semantics.
+ *
  *		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
@@ -120,6 +124,9 @@
 #endif
 static unsigned int net_debug = NET_DEBUG;
 
+#define ENABLE(irq) enable_irq(irq)
+#define DISABLE(irq) disable_irq(irq)
+
 /* In micro second */
 #define PLIP_DELAY_UNIT		   1
 
@@ -333,6 +340,7 @@
 #define OK        0
 #define TIMEOUT   1
 #define ERROR     2
+#define HS_TIMEOUT	3
 
 typedef int (*plip_func)(struct device *dev, struct net_local *nl,
 			 struct plip_local *snd, struct plip_local *rcv);
@@ -371,13 +379,22 @@
 		      int error)
 {
 	unsigned char c0;
+	/*
+	 * This is tricky. If we got here from the beginning of send (either
+	 * with ERROR or HS_TIMEOUT) we have IRQ enabled. Otherwise it's
+	 * already disabled. With the old variant of {enable,disable}_irq()
+	 * extra disable_irq() was a no-op. Now it became mortal - it's
+	 * unbalanced and thus we'll never re-enable IRQ (until rmmod plip,
+	 * that is). So we have to treat HS_TIMEOUT and ERROR from send
+	 * in a special way.
+	 */
 
 	spin_lock_irq(&nl->lock);
 	if (nl->connection == PLIP_CN_SEND) {
 
 		if (error != ERROR) { /* Timeout */
 			nl->timeout_count++;
-			if ((snd->state == PLIP_PK_TRIGGER
+			if ((error == HS_TIMEOUT
 			     && nl->timeout_count <= 10)
 			    || nl->timeout_count <= 3) {
 				spin_unlock_irq(&nl->lock);
@@ -387,7 +404,8 @@
 			c0 = inb(PAR_STATUS(dev));
 			printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n",
 			       dev->name, snd->state, c0);
-		}
+		} else
+			error = HS_TIMEOUT;
 		nl->enet_stats.tx_errors++;
 		nl->enet_stats.tx_aborted_errors++;
 	} else if (nl->connection == PLIP_CN_RECEIVE) {
@@ -419,8 +437,10 @@
 		snd->skb = NULL;
 	}
 	spin_unlock_irq(&nl->lock);
-	disable_irq(dev->irq);
-	synchronize_irq();
+	if (error == HS_TIMEOUT) {
+		DISABLE(dev->irq);
+		synchronize_irq();
+	}
 	outb(PAR_INTR_OFF, PAR_CONTROL(dev));
 	dev->tbusy = 1;
 	nl->connection = PLIP_CN_ERROR;
@@ -498,7 +518,7 @@
 
 	switch (rcv->state) {
 	case PLIP_PK_TRIGGER:
-		disable_irq(dev->irq);
+		DISABLE(dev->irq);
 		/* Don't need to synchronize irq, as we can safely ignore it */
 		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
 		dev->interrupt = 0;
@@ -518,7 +538,7 @@
 				nl->connection = PLIP_CN_SEND;
 				queue_task(&nl->deferred, &tq_timer);
 				outb(PAR_INTR_ON, PAR_CONTROL(dev));
-				enable_irq(dev->irq);
+				ENABLE(dev->irq);
 				return OK;
 			}
 		} else {
@@ -592,13 +612,13 @@
 			queue_task(&nl->immediate, &tq_immediate);
 			mark_bh(IMMEDIATE_BH);
 			outb(PAR_INTR_ON, PAR_CONTROL(dev));
-			enable_irq(dev->irq);
+			ENABLE(dev->irq);
 			return OK;
 		} else {
 			nl->connection = PLIP_CN_NONE;
 			spin_unlock_irq(&nl->lock);
 			outb(PAR_INTR_ON, PAR_CONTROL(dev));
-			enable_irq(dev->irq);
+			ENABLE(dev->irq);
 			return OK;
 		}
 	}
@@ -674,7 +694,7 @@
 	switch (snd->state) {
 	case PLIP_PK_TRIGGER:
 		if ((inb(PAR_STATUS(dev)) & 0xf8) != 0x80)
-			return TIMEOUT;
+			return HS_TIMEOUT;
 
 		/* Trigger remote rx interrupt. */
 		outb(0x08, data_addr);
@@ -691,12 +711,16 @@
 			c0 = inb(PAR_STATUS(dev));
 			if (c0 & 0x08) {
 				spin_unlock_irq(&nl->lock);
-				disable_irq(dev->irq);
+				DISABLE(dev->irq);
 				synchronize_irq();
 				if (nl->connection == PLIP_CN_RECEIVE) {
 					/* Interrupted.
 					   We don't need to enable irq,
 					   as it is soon disabled.    */
+					/* Yes, we do. New variant of
+					   {enable,disable}_irq *counts*
+					   them.  -- AV  */
+					ENABLE(dev->irq);
 					nl->enet_stats.collisions++;
 					return OK;
 				}
@@ -711,7 +735,7 @@
 			spin_unlock_irq(&nl->lock);
 			if (--cx == 0) {
 				outb(0x00, data_addr);
-				return TIMEOUT;
+				return HS_TIMEOUT;
 			}
 		}
 
@@ -760,7 +784,7 @@
 		nl->is_deferred = 1;
 		queue_task(&nl->deferred, &tq_timer);
 		outb(PAR_INTR_ON, PAR_CONTROL(dev));
-		enable_irq(dev->irq);
+		ENABLE(dev->irq);
 		return OK;
 	}
 	return OK;
@@ -800,7 +824,7 @@
 		dev->tbusy = 0;
 		dev->interrupt = 0;
 		outb(PAR_INTR_ON, PAR_CONTROL(dev));
-		enable_irq(dev->irq);
+		ENABLE(dev->irq);
 		mark_bh(NET_BH);
 	} else {
 		nl->is_deferred = 1;
@@ -1000,7 +1024,7 @@
 
 	dev->tbusy = 1;
 	dev->start = 0;
-	disable_irq(dev->irq);
+	DISABLE(dev->irq);
 	synchronize_irq();
 
 #ifdef NOTDEF
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/sdla.c linux/drivers/net/sdla.c
--- v2.2.0-pre1/linux/drivers/net/sdla.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/sdla.c	Tue Dec 29 11:32:06 1998
@@ -248,7 +248,7 @@
 	temp += z80_addr & SDLA_ADDR_MASK;
 	
 	resp = ~resp1;
-	while ((jiffies < done) && (resp != resp1) && (!resp2 || (resp != resp2)))
+	while (time_before(jiffies, done) && (resp != resp1) && (!resp2 || (resp != resp2)))
 	{
 		if (jiffies != now)
 		{
@@ -257,7 +257,7 @@
 			resp = *temp;
 		}
 	}
-	return(jiffies < done ? jiffies - start : -1);
+	return(time_before(jiffies, done) ? jiffies - start : -1);
 }
 
 /* constants for Z80 CPU speed */
@@ -444,7 +444,7 @@
 
 	waiting = 1;
 	len = 0;
-	while (waiting && (jiffies <= jiffs))
+	while (waiting && time_before_eq(jiffies, jiffs))
 	{
 		if (waiting++ % 3) 
 		{
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/seeq8005.c linux/drivers/net/seeq8005.c
--- v2.2.0-pre1/linux/drivers/net/seeq8005.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/seeq8005.c	Tue Dec 29 11:32:06 1998
@@ -247,10 +247,10 @@
 			outw(0x5a5a, SEEQ_BUFFER);
 		}
 		j=jiffies+HZ;
-		while ( ((inw(SEEQ_STATUS) & SEEQSTAT_FIFO_EMPTY) != SEEQSTAT_FIFO_EMPTY) && jiffies < j )
+		while ( ((inw(SEEQ_STATUS) & SEEQSTAT_FIFO_EMPTY) != SEEQSTAT_FIFO_EMPTY) && time_before(jiffies, j) )
 			mb();
 		outw( 0 , SEEQ_DMAAR);
-		while ( ((inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < j+HZ)
+		while ( ((inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && time_before(jiffies, j+HZ))
 			mb();
 		if ( (inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
 			outw( SEEQCMD_WINDOW_INT_ACK | (inw(SEEQ_STATUS)& SEEQCMD_INT_MASK), SEEQ_CMD);
@@ -707,7 +707,7 @@
 	
 	/* drain FIFO */
 	tmp = jiffies;
-	while ( (((status=inw(SEEQ_STATUS)) & SEEQSTAT_FIFO_EMPTY) == 0) && (jiffies < tmp + HZ))
+	while ( (((status=inw(SEEQ_STATUS)) & SEEQSTAT_FIFO_EMPTY) == 0) && (jiffies - tmp < HZ))
 		mb();
 	
 	/* doit ! */
@@ -729,7 +729,7 @@
 	int status;
 	
 	tmp = jiffies + HZ;
-	while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < tmp)
+	while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && time_before(jiffies, tmp))
 		mb();
 		
 	if ( (status & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/sktr.c linux/drivers/net/sktr.c
--- v2.2.0-pre1/linux/drivers/net/sktr.c	Sun Nov  8 14:03:00 1998
+++ linux/drivers/net/sktr.c	Tue Dec 29 11:32:06 1998
@@ -1020,7 +1020,7 @@
 		return;
 
 	sktr_chk_outstanding_cmds(dev);
-	if(tp->LastSendTime + SEND_TIMEOUT < jiffies
+	if(time_before(tp->LastSendTime + SEND_TIMEOUT, jiffies)
 		&& (tp->QueueSkb < MAX_TX_QUEUE || tp->TplFree != tp->TplBusy))
 	{
 		/* Anything to send, but stalled to long */
@@ -1526,11 +1526,11 @@
 {
 	long tmp;
 
-	tmp = time/(1000000/HZ);
+	tmp = jiffies + time/(1000000/HZ);
 	do {
-		current->state 		= TASK_INTERRUPTIBLE;
+  		current->state 		= TASK_INTERRUPTIBLE;
 		tmp = schedule_timeout(tmp);
-	} while(tmp);
+	} while(time_after(tmp, jiffies));
 
 	return;
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.2.0-pre1/linux/drivers/net/tulip.c	Sun Jun  7 11:16:33 1998
+++ linux/drivers/net/tulip.c	Wed Dec 30 22:00:40 1998
@@ -1,13 +1,13 @@
-/* tulip.c: A DEC 21040-family ethernet driver for linux. */
+/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
 /*
-   NOTICE: THIS IS THE ALPHA TEST VERSION!
-	Written 1994-1997 by Donald Becker.
+	Written 1994-1998 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 SMC EtherPower PCI ethernet adapter.
-	It should work with most other DEC 21*40-based ethercards.
+	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.
 
 	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
 	Center of Excellence in Space Data and Information Sciences
@@ -17,14 +17,27 @@
 	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
 */
 
-static const char *version = "tulip.c:v0.83 10/19/97 becker@cesdis.gsfc.nasa.gov\n";
+#define SMP_CHECK
+static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 25;
+
+#define MAX_UNITS 8
 /* Used to pass the full-duplex flag, etc. */
-static int full_duplex[8] = {0, };
-static int options[8] = {0, };
-static int mtu[8] = {0, };			/* Jumbo MTU for interfaces. */
+static int full_duplex[MAX_UNITS] = {0, };
+static int options[MAX_UNITS] = {0, };
+static int mtu[MAX_UNITS] = {0, };			/* Jumbo MTU for interfaces. */
+
+/*  The possible media types that can be set in options[] are: */
+static const char * const medianame[] = {
+	"10baseT", "10base2", "AUI", "100baseTx",
+	"10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
+	"100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
+	"10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
+};
 
 /* Set if the PCI BIOS detects the chips on a multiport board backwards. */
 #ifdef REVERSE_PROBE_ORDER
@@ -38,40 +51,20 @@
    bonding and packet priority.
    There are no ill effects from too-large receive rings. */
 #define TX_RING_SIZE	16
-#define RX_RING_SIZE	16
+#define RX_RING_SIZE	32
 
 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
 #ifdef __alpha__
-static const int rx_copybreak = 1518;
+static int rx_copybreak = 1518;
 #else
-static const int rx_copybreak = 100;
+static int rx_copybreak = 100;
 #endif
 
-/* The following example shows how to always use the 10base2 port. */
-#ifdef notdef
-#define TULIP_DEFAULT_MEDIA 1		/* 1 == 10base2 */
-#define TULIP_NO_MEDIA_SWITCH		/* Don't switch from this port */
-#endif
-
-/* Define to force full-duplex operation on all Tulip interfaces. */
-/* #define  TULIP_FULL_DUPLEX 1 */
-
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((2000*HZ)/1000)
+#define TX_TIMEOUT  (4*HZ)
 
-#include <linux/config.h>
-#ifdef MODULE
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
 #include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -82,6 +75,8 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#include <linux/bios32.h>
+
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -106,28 +101,13 @@
 #define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
 #endif
 
-#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
-#ifdef MODULE
-#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
-char kernel_version[] = UTS_RELEASE;
-#endif
-#else
-#undef MOD_INC_USE_COUNT
-#define MOD_INC_USE_COUNT
-#undef MOD_DEC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-#endif /* 1.3.38 */
-
 #if (LINUX_VERSION_CODE >= 0x10344)
 #define NEW_MULTICAST
 #include <linux/delay.h>
 #endif
 #if (LINUX_VERSION_CODE >= 0x20100)
-#ifdef MODULE
 char kernel_version[] = UTS_RELEASE;
 #endif
-#endif
 #ifdef SA_SHIRQ
 #define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
 #else
@@ -135,8 +115,8 @@
 #endif
 
 #if (LINUX_VERSION_CODE < 0x20123)
+#define hard_smp_processor_id() smp_processor_id()
 #define test_and_set_bit(val, addr) set_bit(val, addr)
-#include <linux/bios32.h>
 #endif
 
 /* This my implementation of shared IRQs, now only used for 1.2.13. */
@@ -167,7 +147,7 @@
 
 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 and 21142.  These chips are used on
+are the 21040, 21041, 21140, 21140A, 21142, and 21143.  These chips are used on
 many PCI boards including the SMC EtherPower series.
 
 
@@ -268,44 +248,65 @@
 
 #ifndef PCI_VENDOR_ID_LITEON
 #define PCI_VENDOR_ID_LITEON	0x11AD
-#define PCI_DEVICE_ID_PNIC		0x0002
-#define PCI_DEVICE_ID_PNIC_X	0x0168
-#else
-/* Now PCI_VENDOR_ID_LITEON is defined, but device IDs have different names */
-#define PCI_DEVICE_ID_PNIC		PCI_DEVICE_ID_LITEON_LNE100TX
-#define PCI_DEVICE_ID_PNIC_X	0x0168
+#endif
+
+#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
 
 /* The rest of these values should never change. */
 
 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);
 
 /* A table describing the chip types. */
+enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,};
 static struct tulip_chip_table {
-  int device_id;
-  char *chip_name;
-  int flags;
-  void (*media_timer)(unsigned long data);
+	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_DEVICE_ID_DEC_TULIP, "Digital DS21040 Tulip", 0, tulip_timer },
-  { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DS21041 Tulip", 0, tulip_timer },
-  { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 0, tulip_timer },
-  { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 0, tulip_timer },
-  { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 0, tulip_timer },
+  { 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", 256, 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,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
   {0, 0, 0, 0},
 };
 /* This matches the table above. */
-enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, LC82C168};
+enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
+			 LC82C168, MX98713, MX98715, MX98725};
 
-static const char * const medianame[] = {
-  "10baseT", "10base2", "AUI", "100baseTx",
-  "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
-  "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
-  "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
-};
-/* A full-duplex map for above. */
-static const char media_fd[] =
-{0,0,0,0,  0xff,0xff,0,0,  0xff,0,0xff,0x01, 0,0,0xff,0 };
+/* A full-duplex map for media types. */
+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 };
 /* 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, };
@@ -325,9 +326,9 @@
 /* The bits in the CSR5 status registers, mostly interrupt sources. */
 enum status_bits {
 	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+	NormalIntr=0x10000, AbnormalIntr=0x8000,
 	RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
-	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02,
-	TxIntr=0x01,
+	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
 };
 
 /* The Tulip Rx and Tx buffer descriptors. */
@@ -352,7 +353,7 @@
 struct mediatable {
 	u16 defaultmedia;
 	u8 leafcount, csr12dir;				/* General purpose pin directions. */
-	unsigned has_mii:1;
+	unsigned has_mii:1, has_nonmii:1;
 	struct medialeaf mleaf[0];
 };
 
@@ -360,7 +361,6 @@
 	struct mediainfo *next;
 	int info_type;
 	int index;
-	struct non_mii { char media; unsigned char csr12val; char bitnum, flags;}  non_mii;
 	unsigned char *info;
 };
 
@@ -384,32 +384,38 @@
 	struct enet_statistics stats;
 #endif
 	struct timer_list timer;	/* Media selection timer. */
-#ifdef CONFIG_NET_HW_FLOWCONTROL
-	int fc_bit;
+	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. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
 	unsigned int full_duplex_lock:1;
+	unsigned int fake_addr:1;			/* Multiport board faked address. */
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	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 csr6;					/* Current CSR6 control settings. */
 	unsigned char eeprom[128];			/* Serial EEPROM contents. */
-	signed char phys[4];				/* MII device addresses. */
+	u16 to_advertise;					/* NWay capabilities advertised.  */
+	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_device_fn;
+	unsigned char pci_bus, pci_dev_fn;
 	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
-static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+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(int ioaddr, int location);
-static int mdio_read(int ioaddr, int phy_id, int location);
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(struct device *dev, int phy_id, int location);
+static void mdio_write(struct device *dev, int phy_id, int location, int value);
 static void select_media(struct device *dev, int startup);
 static int tulip_open(struct device *dev);
 static void tulip_timer(unsigned long data);
@@ -420,24 +426,19 @@
 static void tulip_interrupt IRQ(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);
+#ifdef HAVE_PRIVATE_IOCTL
+static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+#endif
 #ifdef NEW_MULTICAST
-static void set_multicast_list(struct device *dev);
+static void set_rx_mode(struct device *dev);
 #else
-static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
-#ifdef CONFIG_NET_FASTROUTE
-#include <linux/if_arp.h>
-#include <net/ip.h>
-
-static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst);
+static void set_rx_mode(struct device *dev, int num_addrs, void *addrs);
 #endif
 
 
 
-#ifdef MODULE
 /* A list of all installed Tulip devices, for removing the driver module. */
 static struct device *root_tulip_dev = NULL;
-#endif
 
 /* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
    but now receives directly into full-sized skbuffs that are allocated
@@ -449,121 +450,118 @@
 {
 	int cards_found = 0;
 	static int pci_index = 0;	/* Static, for multiple probe calls. */
+	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 (pci_present()) {
-		unsigned char pci_bus, pci_device_fn;
-
-		for (;pci_index < 0xff; pci_index++) {
-			unsigned char pci_latency;
 #if LINUX_VERSION_CODE >= 0x20155
-			unsigned int pci_irq_line;
-			struct pci_dev *pdev;
+	if (! pci_present())
+		return -ENODEV;
 #else
-			unsigned char pci_irq_line;
+	if (! pcibios_present())
+		return -ENODEV;
 #endif
-			unsigned short pci_command, vendor, device;
-			unsigned int pci_ioaddr, chip_idx = 0;
-
-			if (pcibios_find_class
-				(PCI_CLASS_NETWORK_ETHERNET << 8,
-				 reverse_probe ? 0xfe - pci_index : pci_index,
-				 &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 (;pci_index < 0xff; pci_index++) {
+		u16 vendor, device, pci_command, new_command;
+		u32 pci_ioaddr;
+		int chip_idx = 0;
+
+		if (pcibios_find_class
+			(PCI_CLASS_NETWORK_ETHERNET << 8,
+			 reverse_probe ? 0xfe - pci_index : pci_index,
+			 &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)
+				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);
+			continue;
+		}
 #if LINUX_VERSION_CODE >= 0x20155
-			pdev = pci_find_slot(pci_bus, pci_device_fn);
-			pci_irq_line = pdev->irq;
-			pci_ioaddr = pdev->base_address[0];
+		pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0];
 #else
-			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);
+		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
+								  &pci_ioaddr);
 #endif
-			/* Remove I/O space marker in bit 0. */
-			pci_ioaddr &= ~3;
+		/* Remove I/O space marker in bit 0. */
+		pci_ioaddr &= ~3;
 
-			if (vendor != PCI_VENDOR_ID_DEC
-				&& vendor != PCI_VENDOR_ID_LITEON)
-				continue;
-			if (vendor == PCI_VENDOR_ID_LITEON)
-				device = PCI_DEVICE_ID_PNIC_X;
+		if (tulip_debug > 2)
+			printk(KERN_DEBUG "Found %s at I/O %#x.\n",
+				   tulip_tbl[chip_idx].chip_name, pci_ioaddr);
 
-			for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
-				if (device == tulip_tbl[chip_idx].device_id)
-					break;
-			if (tulip_tbl[chip_idx].chip_name == 0) {
-				printk(KERN_INFO "Unknown Digital PCI ethernet chip type"
-					   " %4.4x"" detected: not configured.\n", device);
-				continue;
-			}
-			if (tulip_debug > 2)
-				printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
-					   pci_ioaddr, pci_irq_line);
+		if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
+			continue;
 
-			if (check_region(pci_ioaddr, TULIP_TOTAL_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);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
 
-#ifdef MODULE
-			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
-						 cards_found);
-#else
-			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, -1);
-#endif
+		dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found);
 
-			if (dev) {
-			  /* Get and check the bus-master and latency values. */
-			  pcibios_read_config_word(pci_bus, pci_device_fn,
-									   PCI_COMMAND, &pci_command);
-			  if ( ! (pci_command & PCI_COMMAND_MASTER)) {
-				printk(KERN_INFO "  PCI Master Bit has not been set! Setting...\n");
-				pci_command |= PCI_COMMAND_MASTER;
-				pcibios_write_config_word(pci_bus, pci_device_fn,
-										  PCI_COMMAND, pci_command);
-			  }
-			  pcibios_read_config_byte(pci_bus, pci_device_fn,
-									   PCI_LATENCY_TIMER, &pci_latency);
-			  if (pci_latency < 10) {
-				printk(KERN_INFO "  PCI latency timer (CFLT) is unreasonably"
-					   " low at %d.  Setting to 64 clocks.\n", pci_latency);
+		/* Get and check the bus-master and latency values. */
+		if (dev) {
+			unsigned char pci_latency;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency < 10) {
+				printk(KERN_INFO "  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);
-			  } else if (tulip_debug > 1)
-				printk(KERN_INFO "  PCI latency timer (CFLT) is %#x.\n",
-					   pci_latency);
-			  /* Bring the 21143 out power-down mode. */
-			  if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
+			} 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++;
-			}
+										   0x40, 0x40000000);
+			dev = 0;
+			cards_found++;
 		}
 	}
 
 	return cards_found ? 0 : -ENODEV;
 }
 
-static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
+								   struct device *dev,
 								   int chip_id, 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 */
 	int i;
 	unsigned short sum;
 
@@ -572,7 +570,25 @@
 
 	dev = init_etherdev(dev, 0);
 
-	printk(KERN_INFO "%s: %s at %#3x,",
+#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;
+
+	printk(KERN_INFO "%s: %s at %#3lx,",
 		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
 
 	/* Stop the chip's Tx and Rx processes. */
@@ -629,6 +645,10 @@
 		for (i = 0; i < 8; i ++)
 			if (ee_data[i] != ee_data[16+i])
 				sa_offset = 20;
+		if (ee_data[0] == 0xff  &&  ee_data[1] == 0xff &&  ee_data[2] == 0) {
+			sa_offset = 2;		/* Grrr, damn Matrox boards. */
+			multiport_cnt = 4;
+		}
 		for (i = 0; i < 6; i ++) {
 			dev->dev_addr[i] = ee_data[i + sa_offset];
 			sum += ee_data[i + sa_offset];
@@ -655,13 +675,15 @@
 		irq = last_irq;
 #endif
 	}
+
 	for (i = 0; i < 6; i++)
 		printk(" %2.2x", 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. */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
+	/* Note that proper size is tulip_tbl[chip_id].chip_name, but... */
+	request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name);
 
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
@@ -671,11 +693,11 @@
 	memset(tp, 0, sizeof(*tp));
 	dev->priv = tp;
 
-#ifdef MODULE
 	tp->next_module = root_tulip_dev;
 	root_tulip_dev = dev;
-#endif
 
+	tp->pci_bus = pci_bus;
+	tp->pci_dev_fn = pci_device_fn;
 	tp->chip_id = chip_id;
 
 #ifdef TULIP_FULL_DUPLEX
@@ -690,36 +712,64 @@
 #endif
 
 	/* The lower four bits are the media type. */
-	if (board_idx >= 0) {
-		tp->full_duplex = (options[board_idx]&16) || full_duplex[board_idx]>0;
-		if (tp->full_duplex)
-			tp->full_duplex_lock = 1;
+	if (board_idx >= 0  &&  board_idx < MAX_UNITS) {
 		tp->default_port = options[board_idx] & 15;
-		if (tp->default_port)
-			tp->medialock = 1;
+		if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
+			tp->full_duplex = 1;
 		if (mtu[board_idx] > 0)
 			dev->mtu = mtu[board_idx];
 	}
+	if (dev->mem_start)
+		tp->default_port = dev->mem_start;
+	if (tp->default_port) {
+		tp->medialock = 1;
+		if (media_cap[tp->default_port] & MediaAlwaysFD)
+			tp->full_duplex = 1;
+	}
+	if (tp->full_duplex)
+		tp->full_duplex_lock = 1;
 
 	/* This is logically part of probe1(), but too complex to write inline. */
-	if (chip_id != DC21040  &&  chip_id != LC82C168)
+	if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE)
 		parse_eeprom(dev);
 
-	if (tp->mtable  &&  tp->mtable->has_mii) {
+	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;
+
+	if ((tp->mtable  &&  tp->mtable->has_mii) ||
+		( ! tp->mtable  &&  (tulip_tbl[tp->chip_id].flags & HAS_MII))) {
 		int phy, phy_idx;
 		/* 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(ioaddr, phy, 0);
-			if (mii_status != 0xffff  && mii_status != 0x0000) {
-				tp->phys[phy_idx++] = phy;
-				printk(KERN_INFO "%s:  MII transceiver found at MDIO address %d.\n",
-					   dev->name, phy);
+			int mii_status = mdio_read(dev, phy, 1);
+			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+				int mii_reg0 = mdio_read(dev, phy, 0);
+				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_DEBUG "%s:  Advertising %4.4x on PHY %d,"
+						   " previously advertising %4.4x.\n",
+						   dev->name, reg4, phy, mdio_read(dev, phy, 4));
+					mdio_write(dev, phy, 4, reg4);
+				}
+				/* Enable autonegotiation: some boards default to off. */
+				mdio_write(dev, phy, 0, mii_reg0 |
+						   (tp->full_duplex ? 0x1100 : 0x1000) |
+						   (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
 			}
 		}
-		if (phy_idx == 0) {
+		tp->mii_cnt = phy_idx;
+		if (tp->mtable  &&  tp->mtable->has_mii  &&  phy_idx == 0) {
 			printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
 				   dev->name);
 			tp->phys[0] = 1;
@@ -731,11 +781,11 @@
 	dev->hard_start_xmit = &tulip_start_xmit;
 	dev->stop = &tulip_close;
 	dev->get_stats = &tulip_get_stats;
-#ifdef HAVE_MULTICAST
-	dev->set_multicast_list = &set_multicast_list;
+#ifdef HAVE_PRIVATE_IOCTL
+	dev->do_ioctl = &private_ioctl;
 #endif
-#ifdef CONFIG_NET_FASTROUTE
-	dev->accept_fastpath = tulip_accept_fastpath;
+#ifdef HAVE_MULTICAST
+	dev->set_multicast_list = &set_rx_mode;
 #endif
 
 	/* Reset the xcvr interface and turn on heartbeat. */
@@ -744,20 +794,37 @@
 		outl(0x00000000, ioaddr + CSR13);
 		outl(0xFFFFFFFF, ioaddr + CSR14);
 		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
-		outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6);
+		outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6);
 		outl(0x0000EF05, ioaddr + CSR13);
 		break;
-	case DC21140:  case DC21142:
-		if (tp->mtable)
-			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
-		break;
 	case DC21040:
 		outl(0x00000000, ioaddr + CSR13);
 		outl(0x00000004, ioaddr + CSR13);
 		break;
+	case DC21140: default:
+		if (tp->mtable)
+			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. */
+		break;
 	case LC82C168:
-		outl(0x33, ioaddr + CSR12);
-		outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+		if ( ! tp->mii_cnt) {
+			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:
+		outl(0x00000000, ioaddr + CSR6);
+		outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
+		outl(0x00000001, ioaddr + CSR13);
 		break;
 	}
 
@@ -782,7 +849,9 @@
   {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
 							   0x0000, 0x009E, /* 10baseT */
 							   0x0903, 0x006D, /* 100baseTx */ }},
-  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f,
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
+								 0x0107, 0x8021, /* 100baseFx */
+								 0x0108, 0x8021, /* 100baseFx-FD */
 								 0x0103, 0x006D, /* 100baseTx */ }},
   {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
 							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
@@ -796,9 +865,15 @@
   {0, 0, 0, 0, {}}};
 
 static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
- "21142 non-MII PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
+ "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
 
 #define EEPROM_SIZE 128
+#if defined(__i386__)
+#define get_u16(ptr) (*(u16 *)(ptr))
+#else
+#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
+#endif
+
 static void parse_eeprom(struct device *dev)
 {
 	/* The last media info list parsed, for multiport boards.  */
@@ -806,7 +881,7 @@
 	static unsigned char *last_ee_data = NULL;
 	static int controller_index = 0;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	unsigned char *ee_data = tp->eeprom;
 	int i;
 
@@ -870,30 +945,45 @@
 	}
 subsequent_board:
 
-	if (tp->chip_id == DC21041) {
+	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 = *(u16 *)p;
-		int count = p[2];
+		short media;
+		int count;
+
+		media = get_u16(p);
+		p += 2;
+		count = *p++;
 
 		printk(KERN_INFO "%s:21041 Media information at %d, default media "
 			   "%4.4x (%s).\n", dev->name, ee_data[27], media,
 			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
 		for (i = 0; i < count; i++) {
-			unsigned char media_code = p[3 + i*7];
-			u16 *csrvals = (u16 *)&p[3 + i*7 + 1];
-			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]);
+			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]);
 		}
 	} else {
 		unsigned char *p = (void *)ee_data + ee_data[27];
 		unsigned char csr12dir = 0;
 		int count;
 		struct mediatable *mtable;
-		u16 media = *((u16 *)p)++;
+		u16 media = get_u16(p);
 
-		if (tp->chip_id == DC21140)
+		p += 2;
+		if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
 			csr12dir = *p++;
 		count = *p++;
 		mtable = (struct mediatable *)
@@ -905,7 +995,7 @@
 		mtable->defaultmedia = media;
 		mtable->leafcount = count;
 		mtable->csr12dir = csr12dir;
-		mtable->has_mii = 0;
+		mtable->has_nonmii = mtable->has_mii = 0;
 
 		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
 			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
@@ -916,14 +1006,18 @@
 				leaf->type = 0;
 				leaf->media = p[0] & 0x3f;
 				leaf->leafdata = p;
+				if ((p[2] & 0x61) == 0x01)	/* Bogus, but Znyx boards do it. */
+					mtable->has_mii = 1;
 				p += 4;
 			} else {
 				leaf->type = p[1];
 				if (p[1] & 1) {
 					mtable->has_mii = 1;
 					leaf->media = 11;
-				} else
+				} else {
+					mtable->has_nonmii = 1;
 					leaf->media = p[2] & 0x0f;
+				}
 				leaf->leafdata = p + 2;
 				p += (p[0] & 0x3f) + 1;
 			}
@@ -966,11 +1060,11 @@
 #define EE_READ_CMD		(6 << 6)
 #define EE_ERASE_CMD	(7 << 6)
 
-static int read_eeprom(int ioaddr, int location)
+static int read_eeprom(long ioaddr, int location)
 {
 	int i;
 	unsigned short retval = 0;
-	int ee_addr = ioaddr + CSR9;
+	long ee_addr = ioaddr + CSR9;
 	int read_cmd = location | EE_READ_CMD;
 	
 	outl(EE_ENB & ~EE_CS, ee_addr);
@@ -1001,6 +1095,16 @@
 	return retval;
 }
 
+/* MII transceiver control section.
+   Read and write the MII registers using software-generated serial
+   MDIO protocol.  See the MII specifications or DP83840A data sheet
+   for details. */
+
+/* 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 or future 66Mhz PCI. */
+#define mdio_delay() inl(mdio_addr)
+
 /* Read and write the MII registers using software-generated serial
    MDIO protocol.  It is just different enough from the EEPROM protocol
    to not share code.  The maxium data clock rate is 2.5 Mhz. */
@@ -1010,18 +1114,24 @@
 #define MDIO_ENB		0x00000		/* Ignore the 0x02000 databook setting. */
 #define MDIO_ENB_IN		0x40000
 #define MDIO_DATA_READ	0x80000
-#ifdef _LINUX_DELAY_H
-#define mdio_delay()	udelay(1)
-#else
-#define mdio_delay()	__SLOW_DOWN_IO
-#endif
 
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(struct device *dev, int phy_id, int location)
 {
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
 	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
-	unsigned short retval = 0;
-	int mdio_addr = ioaddr + CSR9;
+	int retval = 0;
+	long mdio_addr = dev->base_addr + CSR9;
+
+	if (tp->chip_id == LC82C168) {
+		long ioaddr = dev->base_addr;
+		int i = 1000;
+		outl(0x60020000 + (phy_id<<23) + (location<<18), 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. */ 
 	for (i = 32; i >= 0; i--) {
@@ -1031,60 +1141,78 @@
 		mdio_delay();
 	}
 	/* Shift the read command bits out. */
-	for (i = 17; i >= 0; i--) {
+	for (i = 15; i >= 0; i--) {
 		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
 
-		outl(dataval, mdio_addr);
-		mdio_delay();
-		outl(dataval | MDIO_SHIFT_CLK, mdio_addr);
+		outl(MDIO_ENB | dataval, mdio_addr);
 		mdio_delay();
-		outl(dataval, mdio_addr);
+		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
 		mdio_delay();
 	}
-	outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
-	mdio_delay();
-	outl(MDIO_ENB_IN, mdio_addr);
-
-	for (i = 16; i > 0; i--) {
-		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
-		mdio_delay();
-		retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+	/* Read the two transition, 16 data, and wire-idle bits. */
+	for (i = 19; i > 0; i--) {
 		outl(MDIO_ENB_IN, mdio_addr);
 		mdio_delay();
-	}
-	/* Clear out extra bits. */
-	for (i = 16; i > 0; i--) {
+		retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
 		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
 		mdio_delay();
-		outl(MDIO_ENB_IN, mdio_addr);
-		mdio_delay();
 	}
-	return retval;
+	return (retval>>1) & 0xffff;
 }
 
-#ifdef CONFIG_NET_HW_FLOWCONTROL
-/* Enable receiver */
-
-void tulip_xon(struct device *dev)
+static void mdio_write(struct device *dev, int phy_id, int location, int value)
 {
-	struct tulip_private *lp = (struct tulip_private *)dev->priv;
+	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;
 
-	clear_bit(lp->fc_bit, &netdev_fc_xoff);
-	if (dev->start)
-				outl(lp->csr6 | 0x2002, dev->base_addr + CSR6);
+	if (tp->chip_id == LC82C168) {
+		long ioaddr = dev->base_addr;
+		int i = 1000;
+		outl(cmd, ioaddr + 0xA0);
+		do
+			if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
+				break;
+		while (--i > 0);
+		return;
+	}
+
+	/* Establish sync by sending 32 logic ones. */ 
+	for (i = 32; i >= 0; i--) {
+		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Shift the command bits out. */
+	for (i = 31; i >= 0; i--) {
+		int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+		outl(MDIO_ENB | dataval, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Clear out extra bits. */
+	for (i = 2; i > 0; i--) {
+		outl(MDIO_ENB_IN, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	return;
 }
-#endif
 
 
 static int
 tulip_open(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int i = 0;
 
 	/* On some chip revs we must set the MII/SYM port before the reset!? */
-	if (tp->mtable  &&  tp->mtable->has_mii)
+	if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii))
 		outl(0x00040000, ioaddr + CSR6);
 
 	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
@@ -1106,31 +1234,30 @@
 	   Tx and Rx queues and the address filter list. */
 #if defined(__alpha__)
 	/* ToDo: Alpha setting could be better. */
-	outl(0x00200000 | 0xE000, ioaddr + CSR0);
+	outl(0x01A00000 | 0xE000, ioaddr + CSR0);
 #elif defined(__powerpc__)
-	outl(0x00200080 | 0x8000, ioaddr + CSR0);
+	outl(0x01A00080 | 0x8000, ioaddr + CSR0);
 #elif defined(__i386__)
 #if defined(MODULE)
 	/* When a module we don't have 'x86' to check. */
-	outl(0x00200000 | 0x4800, ioaddr + CSR0);
+	outl(0x01A00000 | 0x4800, ioaddr + CSR0);
 #else
-#ifndef ORIGINAL_TEXT
-#define x86 (boot_cpu_data.x86)
+#if (LINUX_VERSION_CODE > 0x2014c)
+#define x86 boot_cpu_data.x86
 #endif
-	outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
+	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,
-			 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000));
+			 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000));
 #endif
 #else
-	outl(0x00200000 | 0x4800, ioaddr + CSR0);
+	outl(0x01A00000 | 0x4800, ioaddr + CSR0);
 #warning Processor architecture undefined!
 #endif
 
 #ifdef SA_SHIRQ
-	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ,
-					tulip_tbl[tp->chip_id].chip_name, dev)) {
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) {
 		return -EAGAIN;
 	}
 #else
@@ -1178,7 +1305,7 @@
 	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
 
 	if (dev->if_port == 0)
-	  dev->if_port = tp->default_port;
+		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;
@@ -1186,14 +1313,16 @@
 	/* Allow selecting a default media. */
 	if (tp->mtable == NULL)
 		goto media_picked;
-	if (dev->if_port)
+	if (dev->if_port) {
+		int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 :
+			(dev->if_port == 12 ? 0 : dev->if_port);
 		for (i = 0; i < tp->mtable->leafcount; i++)
-		  if (tp->mtable->mleaf[i].media ==
-			  (dev->if_port == 12 ? 0 : dev->if_port)) {
-			printk(KERN_INFO "%s: Using user-specified media %s.\n",
-				   dev->name, medianame[dev->if_port]);
-			goto media_picked;
-		  }
+			if (tp->mtable->mleaf[i].media == looking_for) {
+				printk(KERN_INFO "%s: Using user-specified media %s.\n",
+					   dev->name, medianame[dev->if_port]);
+				goto media_picked;
+			}
+	}
 	if ((tp->mtable->defaultmedia & 0x0800) == 0)
 		for (i = 0; i < tp->mtable->leafcount; i++)
 		  if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) {
@@ -1201,55 +1330,61 @@
 				   dev->name, medianame[tp->mtable->mleaf[i].media]);
 			goto media_picked;
 		  }
+	/* Start sensing first non-full-duplex media. */
 	for (i = tp->mtable->leafcount - 1;
-		 (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--)
+		 (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
 	  ;
 media_picked:
 
-	tp->cur_index = i;
 	tp->csr6 = 0;
-	select_media(dev, 1);
+	tp->cur_index = i;
+	if (dev->if_port == 0  &&  tp->chip_id == DC21142) {
+		tp->csr6 = 0x82420200;
+		outl(0x0003FFFF, 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
+		select_media(dev, 1);
 
 	/* Start the chip's Tx to process setup frame. */
 	outl(tp->csr6, ioaddr + CSR6);
 	outl(tp->csr6 | 0x2000, ioaddr + CSR6);
 
 	dev->tbusy = 0;
-	dev->interrupt = 0;
+	tp->interrupt = 0;
 	dev->start = 1;
 
-
 	/* Enable interrupts by setting the interrupt mask. */
-	outl(0x0001ebef, ioaddr + CSR7);
+	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
+	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
 	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 	outl(0, ioaddr + CSR2);		/* Rx poll demand */
 
 	if (tulip_debug > 2) {
-		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n",
+		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
 			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
-			   inl(ioaddr + CSR13));
+			   inl(ioaddr + CSR6));
 	}
 	/* 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.expires = RUN_AT(5*HZ);
 	tp->timer.data = (unsigned long)dev;
-	tp->timer.function = &tulip_timer;				/* timer handler */
+	tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
 	add_timer(&tp->timer);
 
-#ifdef CONFIG_NET_HW_FLOWCONTROL
-	tp->fc_bit = netdev_register_fc(dev, tulip_xon);
-#endif
-#ifdef CONFIG_NET_FASTROUTE
-	dev->tx_semaphore = 1;
-#endif
 	return 0;
 }
 
 /* Set up the transceiver control registers for the selected media type. */
 static void select_media(struct device *dev, int startup)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	struct mediatable *mtable = tp->mtable;
 	u32 new_csr6;
@@ -1261,8 +1396,8 @@
 		switch (mleaf->type) {
 		case 0:					/* 21140 non-MII xcvr. */
 			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver with control"
-					   " setting %2.2x.\n",
+				printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver"
+					   " with control setting %2.2x.\n",
 					   dev->name, p[1]);
 			dev->if_port = p[0];
 			if (startup)
@@ -1270,26 +1405,11 @@
 			outl(p[1], ioaddr + CSR12);
 			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
 			break;
-		case 1:
-			if (startup) {
-				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
-				dev->if_port = 11;
-				if (tulip_debug > 2)
-					printk(KERN_DEBUG "%s:  Doing a reset sequence of length %d.\n",
-						   dev->name, p[2 + p[1]]);
-				for (i = 0; i < p[2 + p[1]]; i++)
-					outl(p[3 + p[1] + i], ioaddr + CSR12);
-				if (tulip_debug > 2)
-					printk(KERN_DEBUG "%s  Doing a transceiver setup sequence of length %d.\n",
-						   dev->name, p[1]);
-				for (i = 0; i < p[1]; i++)
-					outl(p[2 + i], ioaddr + CSR12);
-			}
-			check_mii = 1;
-			new_csr6 = 0x020C0000;
-			break;
 		case 2: case 4: {
-			u16 *setup = (u16*)&p[1];
+			u16 setup[3];
+			for (i = 0; i < 3; i++)
+				setup[i] = get_u16(&p[i*2 + 1]);
+
 			dev->if_port = p[0] & 15;
 			if (tulip_debug > 1)
 				printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
@@ -1299,59 +1419,74 @@
 				outl(setup[1], ioaddr + CSR14);
 				outl(setup[2], ioaddr + CSR15);
 				outl(setup[0], ioaddr + CSR13);
-				setup += 3;
-			} else {
+				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);
+			} else {
+				outl(0, ioaddr + CSR14);
+				outl(8, ioaddr + CSR15);
+				outl(0, ioaddr + CSR13);
 			}
 			outl(setup[0]<<16, ioaddr + CSR15);	/* Direction */
 			outl(setup[1]<<16, ioaddr + CSR15);	/* Data */
 			if (mleaf->type == 4)
-				new_csr6 = 0x02000000 | ((setup[2] & 0x71) << 18);
+				new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
 			else
 				new_csr6 = 0x82420000;
 			break;
 		}
-		case 3: {
+		case 1: case 3: {
+			int phy_num = p[0];
 			int init_length = p[1];
-			u16 * init_sequence = (u16*)(p + 2);
-			int reset_length = p[2 + init_length*2];
-			u16 * reset_sequence = (u16*)&p[3 + init_length*2];
+			u16 *misc_info;
+			u16 to_advertise;
 
 			dev->if_port = 11;
-			if (startup) {
-				if (tulip_debug > 2)
-					printk(KERN_DEBUG "%s:  Doing a 21142 reset sequence of length %d.\n",
-						   dev->name, reset_length);
-				for (i = 0; i < reset_length; i++)
-					outl(reset_sequence[i] << 16, ioaddr + CSR15);
-			}
-			if (tulip_debug > 2)
-				printk(KERN_DEBUG "%s: Doing a 21142 xcvr setup sequence of length %d.\n",
-					   dev->name, init_length);
-			for (i = 0; i < init_length; i++)
-				outl(init_sequence[i] << 16, ioaddr + CSR15);
 			check_mii = 1;
-			new_csr6 = 0x020C0000;
+			new_csr6 = 0x020E0000;
+			if (mleaf->type == 3) {	/* 21142 */
+				u16 *init_sequence = (u16*)(p+2);
+				u16 *reset_sequence = &((u16*)(p+3))[init_length];
+				int reset_length = p[2 + init_length*2];
+				misc_info = reset_sequence + reset_length;
+				if (startup)
+					for (i = 0; i < reset_length; i++)
+						outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
+				for (i = 0; i < init_length; i++)
+					outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
+			} else {
+				u8 *init_sequence = p + 2;
+				u8 *reset_sequence = p + 3 + init_length;
+				int reset_length = p[2 + init_length];
+				misc_info = (u16*)(reset_sequence + reset_length);
+				if (startup) {
+					outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+					for (i = 0; i < reset_length; i++)
+						outl(reset_sequence[i], ioaddr + CSR12);
+				}
+				for (i = 0; i < init_length; i++)
+					outl(init_sequence[i], ioaddr + CSR12);
+			}
+			to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
+			tp->advertising[phy_num] = to_advertise;
+			if (tulip_debug > 1 || 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?  */
+			mdio_write(dev, tp->phys[phy_num], 4, to_advertise);
 			break;
 		}
 		default:
-		  new_csr6 = 0x020C0000;
+		  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 == DC21140) {
-		/* Set media type to MII @ 100mbps: 0x020C0000 */
-		new_csr6 = 0x020C0000;
-		dev->if_port = 11;
-		if (tulip_debug > 1) {
-			printk(KERN_DEBUG "%s: Unknown media control, assuming MII, CSR12 %2.2x.\n",
-				   dev->name, inl(ioaddr + CSR12) & 0xff);
-		}
 	} else if (tp->chip_id == DC21041) {
 		if (tulip_debug > 1)
 			printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
@@ -1363,14 +1498,24 @@
 		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
 		new_csr6 = 0x80020000;
 	} else if (tp->chip_id == LC82C168) {
-		if (startup)
-			dev->if_port = 3;
+		if (startup && ! tp->medialock)
+			dev->if_port = tp->mii_cnt ? 11 : 0;
 		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x,"
+			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]);
-		if (dev->if_port == 3  ||  dev->if_port == 5) {
+		if (tp->mii_cnt) {
+			new_csr6 = 0x812C0000;
+			outl(0x0001, ioaddr + CSR15);
+			outl(0x0201B07A, ioaddr + 0xB8);
+		} else if (startup) {
+			/* Start with 10mbps to do autonegotiation. */
+			outl(0x32, ioaddr + CSR12);
+			new_csr6 = 0x00420000;
+			outl(0x0001B078, ioaddr + 0xB8);
+			outl(0x0201B078, ioaddr + 0xB8);
+		} else if (dev->if_port == 3  ||  dev->if_port == 5) {
 			outl(0x33, ioaddr + CSR12);
 			new_csr6 = 0x01860000;
 			if (startup)
@@ -1382,7 +1527,7 @@
 			new_csr6 = 0x00420000;
 			outl(0x1F078, ioaddr + 0xB8);
 		}
-	} else {					/* 21040 */
+	} else if (tp->chip_id == DC21040) {					/* 21040 */
 		/* Turn on the xcvr interface. */
 		int csr12 = inl(ioaddr + CSR12);
 		if (tulip_debug > 1)
@@ -1393,6 +1538,23 @@
 		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
 		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
 		outl(dev->if_port ? 0x0000000C : 0x00000004, 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;
+		if (media_cap[dev->if_port] & MediaIsMII) {
+			new_csr6 = 0x020E0000;
+		} else if (media_cap[dev->if_port] & MediaIsFx) {
+			new_csr6 = 0x028600000;
+		} else
+			new_csr6 = 0x038600000;
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: No media description table, assuming "
+				   "%s transceiver, CSR12 %2.2x.\n",
+				   dev->name, medianame[dev->if_port],
+				   inl(ioaddr + CSR12));
 	}
 
 	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
@@ -1403,7 +1565,7 @@
 {
 	struct device *dev = (struct device *)data;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	u32 csr12 = inl(ioaddr + CSR12);
 	int next_tick = 0;
 
@@ -1467,150 +1629,128 @@
 		  break;
 		}
 		break;
-	case LC82C168: {
-		int phy_reg = inl(ioaddr + 0xB8);
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
-				   dev->name, phy_reg, inl(ioaddr + CSR5));
-		if (phy_reg & 0x04000000) {	/* Remote link fault */
-			outl(0x0201F078, ioaddr + 0xB8);
-			next_tick = (24*HZ)/10;
-		} else
-			next_tick = 10*HZ;
-		if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+	case DC21140:  case DC21142: case MX98713: default: {
+		struct medialeaf *mleaf;
+		unsigned char *p;
+		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
+			/* Not much that can be done.
+			   Assume this a generic MII or SYM transceiver. */
+			next_tick = 60*HZ;
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
+					   "CSR12 0x%2.2x.\n",
+					   dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
+			break;
+		}
+		mleaf = &tp->mtable->mleaf[tp->cur_index];
+		p = mleaf->leafdata;
+		switch (mleaf->type) {
+		case 0: case 4: {
+			/* Type 0 serial or 4 SYM transceiver.  Check the link beat bit. */
+			int offset = mleaf->type == 4 ? 5 : 2;
+			s8 bitnum = p[offset];
+			if (p[offset+1] & 0x80) {
+				if (tulip_debug > 1)
+					printk(KERN_DEBUG"%s: Transceiver monitor tick "
+						   "CSR12=%#2.2x, no media sense.\n",
+						   dev->name, csr12);
+				if (mleaf->type == 4) {
+					if (mleaf->media == 3 && (csr12 & 0x02))
+						goto select_next_media;
+				}
+				break;
+			}
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
+					   " bit %d is %d, expecting %d.\n",
+					   dev->name, csr12, (bitnum >> 1) & 7,
+					   (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
+					   (bitnum >= 0));
+			/* Check that the specified bit has the proper value. */
+			if ((bitnum < 0) !=
+				((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
+				if (tulip_debug > 1)
+					printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
+						   medianame[mleaf->media]);
+				if ((p[2] & 0x61) == 0x01)	/* Bogus Znyx board. */
+					goto actually_mii;
+				break;
+			}
+			if (tp->medialock)
+				break;
+	  select_next_media:
+			if (--tp->cur_index < 0) {
+				/* We start again, but should instead look for default. */
+				tp->cur_index = tp->mtable->leafcount - 1;
+			}
+			dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
+			if (media_cap[dev->if_port] & MediaIsFD)
+				goto select_next_media; /* Skip FD entries. */
 			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));
-			if (dev->if_port == 0) {
-				dev->if_port = 3;
-			} else
-				dev->if_port = 0;
-			next_tick = (24*HZ)/10;
+				printk(KERN_DEBUG "%s: No link beat on media %s,"
+					   " trying transceiver type %s.\n",
+					   dev->name, medianame[mleaf->media & 15],
+					   medianame[tp->mtable->mleaf[tp->cur_index].media]);
 			select_media(dev, 0);
+			/* Restart the transmit process. */
 			outl(tp->csr6 | 0x0002, ioaddr + CSR6);
 			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-			dev->trans_start = jiffies;
+			next_tick = (24*HZ)/10;
+			break;
 		}
-		break;
-	}
-	case DC21140:  case DC21142: {
-		struct medialeaf *mleaf;
-		unsigned char *p;
-		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
-			/* Assume this is like a SMC card, and check its link beat bit. */
-			if ((dev->if_port == 0 && (csr12 & 0x0080)) ||
-				(dev->if_port == 1 && (csr12 & 0x0040) == 0)) {
-				dev->if_port ^= 1;
-				/* Stop the transmit process. */
-				tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000);
-				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-				printk(KERN_INFO "%s: link beat timed out, CSR12 is 0x%2.2x, switching to"
-					   " %s media.\n", dev->name,
-					   csr12 & 0xff,
-					   dev->if_port ? "100baseTx" : "10baseT");
-				outl(tp->csr6 | 0xA002, ioaddr + CSR6);
-				dev->trans_start = jiffies;
-				next_tick = (24*HZ)/10;
-			} else {
-				next_tick = 10*HZ;
-				if (tulip_debug > 2)
-					printk(KERN_DEBUG "%s: network media monitor 0x%2.2x, link"
-						   " beat detected as %s.\n", dev->name,
-						   csr12 & 0xff,
-						   dev->if_port ? "100baseTx" : "10baseT");
+		case 1:  case 3: {		/* 21140, 21142 MII */
+			int mii_reg1, mii_reg5;
+		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);
+				}
+			}
+			next_tick = 60*HZ;
+			break;
+		}
+		case 2:					/* 21142 serial block has no link beat. */
+		default:
 			break;
 		}
-	  mleaf = &tp->mtable->mleaf[tp->cur_index];
-	  p = mleaf->leafdata;
-	  switch (mleaf->type) {
-	  case 0: case 4: {
-		/* Type 0 non-MII or #4 SYM transceiver.  Check the link beat bit. */
-		  s8 bitnum = p[mleaf->type == 4 ? 5 : 2];
-		  if (tulip_debug > 2)
-			  printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
-					 " %d, expecting %d.\n",
-					 dev->name, csr12, (bitnum >> 1) & 7,
-					 (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
-					 (bitnum >= 0));
-		  /* Check that the specified bit has the proper value. */
-		  if ((bitnum < 0) !=
-			  ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
-			  if (tulip_debug > 1)
-				  printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
-						 medianame[mleaf->media]);
-			  break;
-		  }
-		  if (tp->medialock)
-			  break;
-	  select_next_media:
-		  if (--tp->cur_index < 0) {
-			/* We start again, but should instead look for default. */
-			tp->cur_index = tp->mtable->leafcount - 1;
-		  }
-		  dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
-		  if (media_fd[dev->if_port])
-			goto select_next_media; /* Skip FD entries. */
-		  if (tulip_debug > 1)
-			  printk(KERN_DEBUG "%s: No link beat on media %s,"
-					 " trying transceiver type %s.\n",
-					 dev->name, medianame[mleaf->media & 15],
-					 medianame[tp->mtable->mleaf[tp->cur_index].media]);
-		  select_media(dev, 0);
-		  /* Restart the transmit process. */
-		  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-		  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-		  next_tick = (24*HZ)/10;
-		  break;
-	  }
-	  case 1:  case 3:		/* 21140, 21142 MII */
-		  {
-			  int mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
-			  int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
-			  printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, "
-					 "MII status %4.4x, Link partner report %4.4x.\n",
-					 dev->name, csr12, mii_reg1, mii_reg5);
-#ifdef notdef
-			  if (mii_reg1 != 0xffff  &&  (mii_reg1 & 0x0004) == 0)
-				  goto select_next_media;
-#else
-			  if (mii_reg1 != 0xffff  &&  (mii_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,
-						 mdio_read(ioaddr, tp->phys[0], 1));
-#endif
-			  if (mii_reg5 == 0xffff  ||  mii_reg5 == 0x0000)
-				  ;				/* No MII device or no link partner report */
-			  else if (tp->full_duplex_lock)
-				  ;
-			  else if ((mii_reg5 & 0x0100) != 0
-					   || (mii_reg5 & 0x00C0) == 0x0040) {
-				  /* 100baseTx-FD  or  10T-FD, but not 100-HD */
-				  if (tp->full_duplex == 0) {
-					  tp->full_duplex = 1;
-					  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, full_duplex ? "full" : "half",
-							 tp->phys[0], mii_reg5);
-			  }
-		  }
-		  break;
-	  case 2:					/* 21142 serial block has no link beat. */
-	  default:
-		  break;
-	  }
 	}
 	break;
-	default:					/* Invalid chip type. */
-	  break;
 	}
 	if (next_tick) {
 		tp->timer.expires = RUN_AT(next_tick);
@@ -1618,16 +1758,237 @@
 	}
 }
 
+/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
+   of available transceivers.  */
+static void t21142_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 = 0;
+
+	if (tulip_debug > 1)
+		printk(KERN_INFO"%s: 21142 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. */
+		}
+	} 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",
+				   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);
+			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);
+			outl(1, ioaddr + CSR13);
+		}
+		if (tulip_debug > 1)
+			printk(KERN_INFO"%s: Testing new 21142 media %s.\n",
+				   dev->name, medianame[dev->if_port]);
+		if (new_csr6 != (tp->csr6 & ~0x00D5)) {
+			tp->csr6 &= 0x00D5;
+			tp->csr6 |= new_csr6;
+			outl(0x0301, ioaddr + CSR12);
+			outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+			outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		}
+	}
+	tp->timer.expires = RUN_AT(next_tick);
+	add_timer(&tp->timer);
+}
+
+static void t21142_lnk_change( struct device *dev)
+{
+	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));
+
+	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 {
+				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",
+				   dev->name);
+	} else if (!(csr12 & 4)) {		/* 10mbps link beat good. */
+			printk(KERN_INFO"%s: 21142 10mpbs 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);
+		outl(0x0301, ioaddr + CSR12);
+		outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	}
+}
+	
+
+static void mxic_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 > 3) {
+		printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
+			   inl(ioaddr + CSR12));
+	}
+	if (next_tick) {
+		tp->timer.expires = RUN_AT(next_tick);
+		add_timer(&tp->timer);
+	}
+}
+
+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));
+	} else {
+		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);
+
+		if (phy_reg & 0x04000000) {	/* Remote link fault */
+			/*outl(0x0201F078, ioaddr + 0xB8);*/
+			next_tick = 3*HZ;
+		}
+		if (inl(ioaddr + 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));
+			if (tp->medialock) {
+			} else if (dev->if_port == 0) {
+				dev->if_port = 3;
+				outl(0x33, ioaddr + CSR12);
+				new_csr6 = 0x01860000;
+				outl(0x1F868, ioaddr + 0xB8);
+			} else {
+				dev->if_port = 0;
+				outl(0x32, ioaddr + CSR12);
+				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 > 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 tulip_tx_timeout(struct device *dev)
 {
   struct tulip_private *tp = (struct tulip_private *)dev->priv;
-  int ioaddr = dev->base_addr;
+  long ioaddr = dev->base_addr;
 
-  if (tp->mtable && tp->mtable->has_mii) {
-	/* Do nothing -- the media monitor should handle this. */
-	if (tulip_debug > 1)
-	  printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
-			 dev->name);
+  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",
@@ -1637,20 +1998,6 @@
 	  }
 	  dev->trans_start = jiffies;
 	  return;
-  } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) {
-	/* Stop the transmit process. */
-	outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-	dev->if_port ^= 1;
-	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));
-	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 if (tp->chip_id == DC21041) {
 	u32 csr12 = inl(ioaddr + CSR12);
 
@@ -1659,19 +2006,38 @@
 		   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 (dev->if_port == 1 || dev->if_port == 2)
 		if (csr12 & 0x0004) {
 			dev->if_port = 2 - dev->if_port;
 		} else
 			dev->if_port = 0;
-	} else
+	else
 		dev->if_port = 1;
-
 	select_media(dev, 0);
 	tp->stats.tx_errors++;
 	dev->trans_start = jiffies;
 	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",
@@ -1753,40 +2119,12 @@
 	int entry;
 	u32 flag;
 
-#ifdef ORIGINAL_TEXT
-#ifndef final_version
-	if (skb == NULL || skb->len <= 0) {
-		printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
-			   dev->name);
-		dev_tint(dev);
-		return 0;
-	}
-#endif
-#endif
-
-#ifdef CONFIG_NET_FASTROUTE
-	cli();
-	if (xchg(&dev->tx_semaphore,0) == 0) {
-		sti();
-		/* With new queueing algorithm returning 1 when dev->tbusy == 0
-		   should not result in lockups, but I am still not sure. --ANK
-		 */
-		if (net_ratelimit())
-				printk(KERN_CRIT "Please check: are you still alive?\n");
-		return 1;
-	}
-#endif
 	/* Block a timer-based transmit from overlapping.  This could better be
 	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
-#ifdef CONFIG_NET_FASTROUTE
-		sti();
-#endif
-		if (jiffies - dev->trans_start >= TX_TIMEOUT)
-				tulip_tx_timeout(dev);
-#ifdef CONFIG_NET_FASTROUTE
-		dev->tx_semaphore = 1;
-#endif
+		if (jiffies - dev->trans_start < TX_TIMEOUT)
+			return 1;
+		tulip_tx_timeout(dev);
 		return 1;
 	}
 
@@ -1816,7 +2154,6 @@
 	if (entry == TX_RING_SIZE-1)
 		flag |= 0xe2000000;
 
-	tp->stats.tx_bytes += skb->len;
 	tp->tx_ring[entry].length = skb->len | flag;
 	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
 	tp->cur_tx++;
@@ -1824,10 +2161,6 @@
 	outl(0, dev->base_addr + CSR1);
 
 	dev->trans_start = jiffies;
-#ifdef CONFIG_NET_FASTROUTE
-	dev->tx_semaphore = 1;
-	sti();
-#endif
 
 	return 0;
 }
@@ -1842,8 +2175,9 @@
 	struct device *dev = (struct device *)(irq2dev_map[irq]);
 #endif
 
-	struct tulip_private *lp;
-	int csr5, ioaddr, boguscnt = 12;
+	struct tulip_private *tp;
+	long ioaddr;
+	int csr5, work_budget = max_interrupt_work;
 
 	if (dev == NULL) {
 		printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n",
@@ -1852,11 +2186,21 @@
 	}
 
 	ioaddr = dev->base_addr;
-	lp = (struct tulip_private *)dev->priv;
-	if (dev->interrupt)
+	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
 		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 {
 		csr5 = inl(ioaddr + CSR5);
@@ -1867,23 +2211,24 @@
 			printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
 				   dev->name, csr5, inl(dev->base_addr + CSR5));
 
-		if ((csr5 & 0x00018000) == 0)
+		if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
 			break;
 
-		if (csr5 & RxIntr)
-			tulip_rx(dev);
+		if (csr5 & (RxIntr | RxNoBuf))
+			work_budget -= tulip_rx(dev);
 
 		if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
-			int dirty_tx;
+			unsigned int dirty_tx;
 
-			for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) {
+			for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
+				 dirty_tx++) {
 				int entry = dirty_tx % TX_RING_SIZE;
-				int status = lp->tx_ring[entry].status;
+				int status = tp->tx_ring[entry].status;
 
 				if (status < 0)
 					break;			/* It still hasn't been Txed */
 				/* Check for Rx filter setup frames. */
-				if (lp->tx_skbuff[entry] == NULL)
+				if (tp->tx_skbuff[entry] == NULL)
 				  continue;
 
 				if (status & 0x8000) {
@@ -1893,74 +2238,104 @@
 						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
 							   dev->name, status);
 #endif
-					lp->stats.tx_errors++;
-					if (status & 0x4104) lp->stats.tx_aborted_errors++;
-					if (status & 0x0C00) lp->stats.tx_carrier_errors++;
-					if (status & 0x0200) lp->stats.tx_window_errors++;
-					if (status & 0x0002) lp->stats.tx_fifo_errors++;
-					if ((status & 0x0080) && lp->full_duplex == 0)
-						lp->stats.tx_heartbeat_errors++;
+					tp->stats.tx_errors++;
+					if (status & 0x4104) tp->stats.tx_aborted_errors++;
+					if (status & 0x0C00) tp->stats.tx_carrier_errors++;
+					if (status & 0x0200) tp->stats.tx_window_errors++;
+					if (status & 0x0002) tp->stats.tx_fifo_errors++;
+					if ((status & 0x0080) && tp->full_duplex == 0)
+						tp->stats.tx_heartbeat_errors++;
 #ifdef ETHER_STATS
-					if (status & 0x0100) lp->stats.collisions16++;
+					if (status & 0x0100) tp->stats.collisions16++;
 #endif
 				} else {
 #ifdef ETHER_STATS
-					if (status & 0x0001) lp->stats.tx_deferred++;
+					if (status & 0x0001) tp->stats.tx_deferred++;
 #endif
-					lp->stats.collisions += (status >> 3) & 15;
-					lp->stats.tx_packets++;
+#if LINUX_VERSION_CODE > 0x20127
+					tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
+#endif
+					tp->stats.collisions += (status >> 3) & 15;
+					tp->stats.tx_packets++;
 				}
 
 				/* Free the original skb. */
-				dev_kfree_skb(lp->tx_skbuff[entry]);
-				lp->tx_skbuff[entry] = 0;
+#if (LINUX_VERSION_CODE > 0x20155)
+				dev_kfree_skb(tp->tx_skbuff[entry]);
+#else
+				dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE);
+#endif
+				tp->tx_skbuff[entry] = 0;
 			}
 
 #ifndef final_version
-			if (lp->cur_tx - dirty_tx > TX_RING_SIZE) {
+			if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
 				printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-					   dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
+					   dev->name, dirty_tx, tp->cur_tx, tp->tx_full);
 				dirty_tx += TX_RING_SIZE;
 			}
 #endif
 
-			if (lp->tx_full && dev->tbusy
-				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+			if (tp->tx_full && dev->tbusy
+				&& tp->cur_tx - dirty_tx  < TX_RING_SIZE - 2) {
 				/* The ring is no longer full, clear tbusy. */
-				lp->tx_full = 0;
+				tp->tx_full = 0;
 				dev->tbusy = 0;
 				mark_bh(NET_BH);
 			}
 
-			lp->dirty_tx = dirty_tx;
+			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));
+				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			}
 		}
 
 		/* Log errors. */
-		if (csr5 & 0x8000) {	/* Abnormal error summary bit. */
-			if (csr5 & TxJabber) lp->stats.tx_errors++;
+		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */
+			if (csr5 & TxJabber) tp->stats.tx_errors++;
 			if (csr5 & TxFIFOUnderflow) {
-			  lp->csr6 |= 0x00200000;  /* Reconfigure to store-n-forward. */
-			  /* Restart the transmit process. */
-			  outl(lp->csr6 | 0x0002, ioaddr + CSR6);
-			  outl(lp->csr6 | 0x2002, ioaddr + CSR6);
+				if ((tp->csr6 & 0xC000) != 0xC000)
+					tp->csr6 += 0x4000;	/* Bump up the Tx threshold */
+				else
+					tp->csr6 |= 0x00200000;  /* Store-n-forward. */
+				/* Restart the transmit process. */
+				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 			}
 			if (csr5 & RxDied) {		/* Missed a Rx frame. */
-				lp->stats.rx_errors++;
-				lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+				tp->stats.rx_errors++;
+				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 			}
 			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",
+						   dev->name, csr5);
+				t21142_lnk_change(dev);
+			}
 			/* Clear all error sources, included undocumented ones! */
-			outl(0x000f7ba, ioaddr + CSR5);
+			outl(0x0800f7ba, ioaddr + CSR5);
 		}
-		if (--boguscnt < 0) {
-			printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n",
-				   dev->name, csr5);
-			/* Clear all interrupt sources. */
-			outl(0x0001ffff, ioaddr + CSR5);
+		if (--work_budget < 0) {
+			if (tulip_debug > 1)
+				printk(KERN_WARNING "%s: Too much work at 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
 			break;
 		}
 	} while (1);
@@ -1969,271 +2344,83 @@
 		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
 			   dev->name, inl(ioaddr + CSR5));
 
-	/* Code that should never be run!  Perhaps remove after testing.. */
-	{
-		static int stopit = 10;
-		if (dev->start == 0  &&  --stopit < 0) {
-			printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n"
-				   KERN_ERR "%s: Disabling interrupt handler %d to avoid "
-				   "locking up the machine.\n",
-				   dev->name, dev->name, dev->irq);
-#ifdef SA_SHIRQ
-			free_irq(irq, dev);
-#else
-			free_irq(irq);
-#endif
-		}
-	}
-
 	dev->interrupt = 0;
+	clear_bit(0, (void*)&tp->interrupt);
 	return;
 }
 
-#ifdef CONFIG_NET_FASTROUTE
-/* DMAing cards are the most easy in this respect,
-   they are able to make fast route to any device.
-
-   Now we allow to make it only to another ethernet card.
- */
-static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst)
-{
-	struct device *odev = dst->dev;
-
-	if (dst->ops->protocol != __constant_htons(ETH_P_IP))
-			return -1;
-	if (odev->type != ARPHRD_ETHER || odev->accept_fastpath == NULL)
-			return -1;
-
-	return 0;
-}
-
-/*
-   Return values:
-
-   0 - packet has gone by fast path.
-   1 - fast path is OK, but device deferred xmit. (semifast path)
-
-   2 - fast path is hit, but packet is a bit strange. (NI)
-   3 - oom
-
-   4 - fast path miss.
- */
-
-static int tulip_fast_forward(struct device *dev, int entry, int len)
-{
-	struct tulip_private *lp = (struct tulip_private *)dev->priv;
-	struct sk_buff *skb = lp->rx_skbuff[entry];
-	struct ethhdr *eth = (void*)skb->data;
-
-	if (eth->h_proto == __constant_htons(ETH_P_IP)) {
-		struct rtable *rt;
-		struct iphdr *iph;
-		unsigned h;
-
-		iph = (struct iphdr*)(skb->data + ETH_HLEN);
-		h = (*(u8*)&iph->daddr^*(u8*)&iph->saddr)&NETDEV_FASTROUTE_HMASK;
-		rt = (struct rtable*)(dev->fastpath[h]);
-		if (rt &&
-			((u16*)&iph->daddr)[0] == ((u16*)&rt->key.dst)[0] &&
-			((u16*)&iph->daddr)[1] == ((u16*)&rt->key.dst)[1] &&
-			((u16*)&iph->saddr)[0] == ((u16*)&rt->key.src)[0] &&
-			((u16*)&iph->saddr)[1] == ((u16*)&rt->key.src)[1] &&
-			rt->u.dst.obsolete == 0) {
-			struct device *odev = rt->u.dst.dev;
-
-			dev_fastroute_stat.hits++;
-
-			if (*(u8*)iph != 0x45 ||
-				(eth->h_dest[0]&1) ||
-				!neigh_is_valid(rt->u.dst.neighbour) ||
-				iph->ttl <= 1)
-					goto alas2;
-
-			ip_decrease_ttl(iph);
-
-			if (1) {
-				struct sk_buff *skb2 = DEV_ALLOC_SKB(PKT_BUF_SZ);
-				if (skb2 == NULL)
-						goto oom;
-				lp->rx_ring[entry].buffer1 = virt_to_bus(skb2->tail);
-				skb2->dev = dev;
-				lp->rx_skbuff[entry] = skb2;
-			}
-
-			skb_put(skb, len);
-
-			ip_statistics.IpInReceives++;
-			ip_statistics.IpForwDatagrams++;
-
-			/* Could use hh cache */
-			memcpy(eth->h_source, odev->dev_addr, 6);
-			memcpy(eth->h_dest, rt->u.dst.neighbour->ha, 6);
-			skb->dev = odev;
-
-#ifdef FAST_SKB_RECYCLE /* DO NOT DEFINE IT! READ COMMENT */
-			/* We could use fast buffer recycling here if odev
-			   is not DMAing.
-
-			   The only problem is that we must allocate skb2
-			   BEFORE we lose skb, otherwise we would make hole in
-			   tulip rx array. Hence, to implement FAST_SKB_RECYCLE
-			   we need always keep at least one skb in a safe place.
-			 */
-			atomic_inc(&skb->users);
-#endif
-
-			if (odev->tx_semaphore &&
-				odev->tbusy == 0 &&
-				odev->interrupt == 0 &&
-				odev->hard_start_xmit(skb, odev) == 0) {
-#ifdef FAST_SKB_RECYCLE
-				if (atomic_read(&skb->users) == 1) {
-					skb->tail = skb->data;
-					skb->len = 0;
-				}
-#endif
-				dev_fastroute_stat.succeed++;
-				return 0;
-			}
-#ifdef FAST_SKB_RECYCLE
-			atomic_dec(&skb->users);
-#endif
-
-			/* Otherwise... */
-			skb->pkt_type = PACKET_FASTROUTE;
-			skb->nh.raw = skb->data + ETH_HLEN;
-			skb->protocol = __constant_htons(ETH_P_IP);
-			dev_fastroute_stat.deferred++;
-			return 1;
-		}
-	}
-	return 4;
-
-oom:
-	return 3;
-
-alas2:
-#ifdef not_yet
-	skb->dst = dst_clone(&rt->u.dst);
-	return 2;
-#else
-	return 4;
-#endif
-}
-#endif
-
 static int
 tulip_rx(struct device *dev)
 {
-	struct tulip_private *lp = (struct tulip_private *)dev->priv;
-	int entry = lp->cur_rx % RX_RING_SIZE;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int entry = tp->cur_rx % RX_RING_SIZE;
+	int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
+	int work_done = 0;
 
 	if (tulip_debug > 4)
 		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
-			   lp->rx_ring[entry].status);
+			   tp->rx_ring[entry].status);
 	/* If we own the next entry, it's a new packet. Send it up. */
-	while (lp->rx_ring[entry].status >= 0) {
-		int status = lp->rx_ring[entry].status;
+	while (tp->rx_ring[entry].status >= 0) {
+		s32 status = tp->rx_ring[entry].status;
 
+		if (--rx_work_limit < 0)
+			break;
 		if ((status & 0x0300) != 0x0300) {
 			if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
-			  printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
-					 "multiple buffers, status %8.8x!\n", dev->name, status);
-			  lp->stats.rx_length_errors++;
+				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 & 0x8000) {
 			/* There was a fatal error. */
-			lp->stats.rx_errors++; /* end of a packet.*/
-			if (status & 0x0890) lp->stats.rx_length_errors++;
-			if (status & 0x0004) lp->stats.rx_frame_errors++;
-			if (status & 0x0002) lp->stats.rx_crc_errors++;
-			if (status & 0x0001) lp->stats.rx_fifo_errors++;
+			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 {
-			/* Malloc up new buffer, compatible with net-2e. */
 			/* Omit the four octet CRC from the length. */
-			short pkt_len = (lp->rx_ring[entry].status >> 16) - 4;
+			short pkt_len = (status >> 16) - 4;
 			struct sk_buff *skb;
-			int rx_in_place = 0;
-
-#ifdef CONFIG_NET_HW_FLOWCONTROL
-			if (netdev_dropping)
-					goto throttle;
-#endif
 
-			skb = lp->rx_skbuff[entry];
-
-#ifdef CONFIG_NET_FASTROUTE
-			switch (tulip_fast_forward(dev, entry, pkt_len)) {
-			case 0:	
-					goto gone;
-			case 1:
-					goto semi_gone;
-			case 2:
-					break;
-			case 3:
-					skb = NULL;
-					goto memory_squeeze;
-			}
-#endif
 			/* Check if the packet is long enough to just accept without
 			   copying to a properly sized skbuff. */
-			if (pkt_len > rx_copybreak) {
-				struct sk_buff *newskb;
-				char *temp;
-
-				/* Get a fresh skbuff to replace the filled one. */
-				newskb = DEV_ALLOC_SKB(PKT_BUF_SZ);
-				if (newskb == NULL) {
-					skb = NULL;		/* No memory, drop the packet. */
-					goto memory_squeeze;
-				}
-				/* Pass up the skb already on the Rx ring. */
-				temp = skb_put(skb, pkt_len);
-				if (bus_to_virt(lp->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(lp->rx_ring[entry].buffer1),
-						   skb->head, temp);
-				rx_in_place = 1;
-				lp->rx_skbuff[entry] = newskb;
-				newskb->dev = dev;
-				/* Longword alignment required: do not skb_reserve(2)! */
-				lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail);
-			} else
-				skb = DEV_ALLOC_SKB(pkt_len + 2);
-memory_squeeze:
-			if (skb == NULL) {
-				int i;
-				printk(KERN_WARNING "%s: Memory squeeze, deferring packet.\n",
-					   dev->name);
-				/* Check that at least two ring entries are free.
-				   If not, free one and mark stats->rx_dropped++. */
-				for (i = 0; i < RX_RING_SIZE; i++)
-					if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
-						break;
-
-				if (i > RX_RING_SIZE -2) {
-					lp->stats.rx_dropped++;
-					lp->rx_ring[entry].status = 0x80000000;
-					lp->cur_rx++;
-				}
-				break;
-			}
-			skb->dev = dev;
-			if (! rx_in_place) {
-				skb_reserve(skb, 2);	/* 16 byte align the data fields */
-#if LINUX_VERSION_CODE < 0x20200  || defined(__alpha__)
+			if (pkt_len < rx_copybreak
+				&& (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(lp->rx_ring[entry].buffer1), pkt_len);
+					   bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
 #else
-#ifdef ORIGINAL_TEXT
 #warning Code untested
-#else
-#error Code is wrong, and it has nothing to do with 2.2 :-)
+				eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
+								 pkt_len, 0);
+				skb_put(skb, pkt_len);
 #endif
-				eth_copy_and_sum(skb, bus_to_virt(lp->rx_ring[entry].buffer1),
-									pkt_len, 0);
+				work_done++;
+			} else { 	/* Pass up the skb already on the Rx ring. */
+				skb = tp->rx_skbuff[entry];
+				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);
 #endif
 			}
@@ -2242,56 +2429,48 @@
 #else
 			skb->len = pkt_len;
 #endif
-#ifdef CONFIG_NET_FASTROUTE
-semi_gone:
-#endif
 			netif_rx(skb);
-#ifdef CONFIG_NET_HW_FLOWCONTROL
-			if (netdev_dropping) {
-throttle:
-				if (lp->fc_bit) {
-					outl(lp->csr6 | 0x2000, dev->base_addr + CSR6);
-					set_bit(lp->fc_bit, &netdev_fc_xoff);
-				}
-			}
-#endif
-#ifdef CONFIG_NET_FASTROUTE
-gone:
-#endif
-			lp->stats.rx_packets++;
-#ifndef ORIGINAL_TEXT
-			lp->stats.rx_bytes += pkt_len;
+			dev->last_rx = jiffies;
+			tp->stats.rx_packets++;
+#if LINUX_VERSION_CODE > 0x20127
+			tp->stats.rx_bytes += pkt_len;
 #endif
 		}
+		entry = (++tp->cur_rx) % RX_RING_SIZE;
+	}
 
-		lp->rx_ring[entry].status = 0x80000000;
-		entry = (++lp->cur_rx) % RX_RING_SIZE;
+	/* Refill the Rx ring buffers. */
+	for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
+		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);
+			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
+			work_done++;
+		}
+		tp->rx_ring[entry].status = 0x80000000;
 	}
 
-	return 0;
+	return work_done;
 }
 
 static int
 tulip_close(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
 
-#ifdef CONFIG_NET_FASTROUTE
-	dev->tx_semaphore = 0;
-#endif
 	dev->start = 0;
 	dev->tbusy = 1;
 
-#ifdef CONFIG_NET_HW_FLOWCONTROL
-	if (tp->fc_bit) {
-		int bit = tp->fc_bit;
-		tp->fc_bit = 0;
-		netdev_unregister_fc(bit);
-	}
-#endif
-
 	if (tulip_debug > 1)
 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
 			   dev->name, inl(ioaddr + CSR5));
@@ -2326,12 +2505,20 @@
 #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
 		}
 	}
 	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
 		tp->tx_skbuff[i] = 0;
 	}
 
@@ -2345,7 +2532,7 @@
 tulip_get_stats(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (dev->start)
 		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
@@ -2353,6 +2540,71 @@
 	return &tp->stats;
 }
 
+#ifdef HAVE_PRIVATE_IOCTL
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	u16 *data = (u16 *)&rq->ifr_data;
+	int phy = tp->phys[0] & 0x1f;
+	long flags;
+
+	switch(cmd) {
+	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+		if (tp->mtable  &&  tp->mtable->has_mii)
+			data[0] = phy;
+		else if (tp->chip_id == DC21142)
+			data[0] = 32;
+		else
+			return -ENODEV;
+		return 0;
+	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+		if (data[0] == 32) {  /* 21142 pseudo-MII */
+			int csr12 = inl(ioaddr + CSR12);
+			int csr14 = inl(ioaddr + CSR14);
+			switch (data[1]) {
+			case 0: {
+				data[3] = ((csr14<<13)&0x4000) + ((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;
+				break;
+			}
+			case 5: data[3] = inl(ioaddr + CSR12) >> 16; break;
+			default: data[3] = 0; break;
+			}
+		} else {
+			save_flags(flags);
+			cli();
+			data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+			restore_flags(flags);
+		}
+		return 0;
+	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+		if (!suser())
+			return -EPERM;
+		if (data[0] == 32) {  /* 21142 pseudo-MII */
+		} else {
+			save_flags(flags);
+			cli();
+			mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+			restore_flags(flags);
+		}
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return -EOPNOTSUPP;
+}
+#endif  /* HAVE_PRIVATE_IOCTL */
+
 /* Set or clear the multicast filter for this adaptor.
    Note that we only use exclusion around actually queueing the
    new frame, not around filling tp->setup_frame.  This is non-deterministic
@@ -2380,12 +2632,12 @@
 }
 
 #ifdef NEW_MULTICAST
-static void set_multicast_list(struct device *dev)
+static void set_rx_mode(struct device *dev)
 #else
-static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+static void set_rx_mode(struct device *dev, int num_addrs, void *addrs)
 #endif
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
@@ -2442,9 +2694,9 @@
 		}
 		eaddrs = (u16 *)dev->dev_addr;
 		do {
-		  *setup_frm++ = eaddrs[0];
-		  *setup_frm++ = eaddrs[1];
-		  *setup_frm++ = eaddrs[2];
+			*setup_frm++ = eaddrs[0];
+			*setup_frm++ = eaddrs[1];
+			*setup_frm++ = eaddrs[2];
 		} while (++i < 15);
 		/* Now add this frame to the Tx list. */
 		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
@@ -2457,19 +2709,19 @@
 			entry = tp->cur_tx++ % TX_RING_SIZE;
 
 			if (entry != 0) {
-			  /* 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;
-			  tp->tx_ring[entry].buffer1 = 0;
-			  tp->tx_ring[entry].status = 0x80000000;
-			  entry = tp->cur_tx++ % TX_RING_SIZE;
+				/* 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;
+				tp->tx_ring[entry].buffer1 = 0;
+				tp->tx_ring[entry].status = 0x80000000;
+				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. */
+				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;
@@ -2485,15 +2737,69 @@
 	}
 }
 
+#ifdef CARDBUS
+
+#include <pcmcia/driver_ops.h>
+
+static dev_node_t *tulip_attach(dev_locator_t *loc)
+{
+	u16 dev_id;
+	u32 io;
+	u8 bus, devfn;
+	struct device *dev;
+
+	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);
+	if (dev) {
+		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
+		strcpy(node->dev_name, dev->name);
+		node->major = node->minor = 0;
+		node->next = NULL;
+		MOD_INC_USE_COUNT;
+		return node;
+	}
+	return NULL;
+}
+
+static void tulip_detach(dev_node_t *node)
+{
+	struct device **devp, **next;
+	printk(KERN_INFO "tulip_detach(%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) {
+		unregister_netdev(*devp);
+		kfree(*devp);
+		*devp = *next;
+		kfree(node);
+		MOD_DEC_USE_COUNT;
+	}
+}
+
+struct driver_operations tulip_ops = {
+	"tulip_cb", tulip_attach, NULL, NULL, 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(8) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "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... */
@@ -2505,8 +2811,12 @@
 	if (debug >= 0)
 		tulip_debug = debug;
 
-	root_tulip_dev = NULL;
+#ifdef CARDBUS
+	register_driver(&tulip_ops);
+	return 0;
+#else
 	return tulip_probe(NULL);
+#endif
 }
 
 void
@@ -2514,6 +2824,10 @@
 {
 	struct device *next_dev;
 
+#ifdef CARDBUS
+	unregister_driver(&tulip_ops);
+#endif
+
 	/* 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;
@@ -2528,7 +2842,8 @@
 
 /*
  * Local variables:
- *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
+ *  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`"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/nubus/nubus.c linux/drivers/nubus/nubus.c
--- v2.2.0-pre1/linux/drivers/nubus/nubus.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/nubus/nubus.c	Tue Dec 29 11:32:06 1998
@@ -632,7 +632,9 @@
 	nubus_init_via();
 	printk("Scanning nubus slots.\n");
 	nubus_probe_bus();
+#ifdef CONFIG_PROC_FS
 	proc_register(&proc_root, &proc_nubus);
+#endif
 }
 
 	
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.2.0-pre1/linux/drivers/pci/oldproc.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/pci/oldproc.c	Tue Dec 29 14:01:38 1998
@@ -338,6 +338,7 @@
 	DEVICE( VIA,		VIA_86C100A,	"VT 86C100A"),
 	DEVICE( VIA,		VIA_82C597_1,	"VT 82C597 Apollo VP3 AGP"),
 	DEVICE( VIA,		VIA_82C598_1,	"VT 82C598 Apollo MVP3 AGP"),
+	DEVICE( SMC2,		SMC2_1211TX,	"1211 TX"),
 	DEVICE( VORTEX,		VORTEX_GDT60x0,	"GDT 60x0"),
 	DEVICE( VORTEX,		VORTEX_GDT6000B,"GDT 6000b"),
 	DEVICE( VORTEX,		VORTEX_GDT6x10,	"GDT 6110/6510"),
@@ -482,6 +483,7 @@
 	DEVICE( S3,		S3_ViRGE_MXP,	"ViRGE/MX+"),
 	DEVICE( S3,		S3_ViRGE_MXPMV,	"ViRGE/MX+MV"),
 	DEVICE( S3,		S3_SONICVIBES,	"SonicVibes"),
+	DEVICE( DCI,		DCI_PCCOM4,	"PC COM PCI Bus 4 port serial Adapter"),
 	DEVICE( INTEL,		INTEL_82375,	"82375EB"),
 	DEVICE( INTEL,		INTEL_82424,	"82424ZX Saturn"),
 	DEVICE( INTEL,		INTEL_82378,	"82378IB"),
@@ -739,6 +741,7 @@
 	      case PCI_VENDOR_ID_INIT:		return "Initio Corp";
 	      case PCI_VENDOR_ID_TTI:		return "Triones Technologies, Inc.";
 	      case PCI_VENDOR_ID_VIA:		return "VIA Technologies";
+	      case PCI_VENDOR_ID_SMC2:		return "SMC";
 	      case PCI_VENDOR_ID_VORTEX:	return "VORTEX";
 	      case PCI_VENDOR_ID_EF:		return "Efficient Networks";
 	      case PCI_VENDOR_ID_FORE:		return "Fore Systems";
@@ -787,6 +790,7 @@
 	      case PCI_VENDOR_ID_AVANCE:	return "Avance";
 	      case PCI_VENDOR_ID_NETVIN:	return "NetVin";
 	      case PCI_VENDOR_ID_S3:		return "S3 Inc.";
+	      case PCI_VENDOR_ID_DCI:		return "Decision Computer Int.";
 	      case PCI_VENDOR_ID_INTEL:		return "Intel";
 	      case PCI_VENDOR_ID_KTI:		return "KTI";
 	      case PCI_VENDOR_ID_ADAPTEC:	return "Adaptec";
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.2.0-pre1/linux/drivers/scsi/Config.in	Tue Dec 22 14:16:56 1998
+++ linux/drivers/scsi/Config.in	Tue Dec 29 13:54:32 1998
@@ -50,7 +50,7 @@
   fi
 dep_tristate 'EATA-DMA [Obsolete] (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
-dep_tristate 'Future Domain 16xx SCSI/AHA 2920 support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
+dep_tristate 'Future Domain 16xx SCSI/AHA-2920A support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
 if [ "$CONFIG_MCA" = "y" ]; then
   if [ "$CONFIG_SCSI" = "y" ]; then
     bool 'Future Domain MCS-600/700 SCSI support' CONFIG_SCSI_FD_MCS
@@ -116,7 +116,7 @@
 dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
-  dep_tristate 'Qlogic ISP FC SCSI support' CONFIG_SCSI_QLOGIC_FC $CONFIG_SCSI
+# dep_tristate 'Qlogic ISP FC SCSI support' CONFIG_SCSI_QLOGIC_FC $CONFIG_SCSI
 fi
 dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c
--- v2.2.0-pre1/linux/drivers/scsi/advansys.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/scsi/advansys.c	Tue Dec 29 11:35:48 1998
@@ -3941,6 +3941,8 @@
 advansys_proc_info(char *buffer, char **start, off_t offset, int length, 
                    int hostno, int inout)
 {
+#ifdef CONFIG_PROC_FS
+
     struct Scsi_Host    *shp;
     asc_board_t         *boardp;
     int                 i;
@@ -4147,9 +4149,12 @@
     ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
 
     return totcnt;
+#else /* CONFIG_PROC_FS */
+    return 0;
+#endif /* CONFIG_PROC_FS */
+
 }
 #endif /* version >= v1.3.0 */
-
 /*
  * advansys_detect()
  *
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v2.2.0-pre1/linux/drivers/scsi/eata_dma.c	Tue Apr 14 14:29:22 1998
+++ linux/drivers/scsi/eata_dma.c	Tue Dec 29 11:35:48 1998
@@ -962,7 +962,7 @@
     eata_send_command((u32) cp, (u32) base, EATA_CMD_DMA_SEND_CP);
     
     i = jiffies + (3 * HZ);
-    while (fake_int_happened == FALSE && jiffies <= i) 
+    while (fake_int_happened == FALSE && time_before_eq(jiffies, i)) 
 	barrier();
     
     DBG(DBG_INTR3, printk(KERN_DEBUG "fake_int_result: %#x hbastat %#x "
@@ -973,7 +973,7 @@
     scsi_init_free((void *)cp, sizeof(struct eata_ccb));
     scsi_init_free((void *)sp, sizeof(struct eata_sp));
     
-    if ((fake_int_result & HA_SERROR) || jiffies > i){
+    if ((fake_int_result & HA_SERROR) || time_after(jiffies, i)){
 	printk(KERN_WARNING "eata_dma: trying to reset HBA at %x to clear "
 	       "possible blink state\n", base); 
 	/* hard reset the HBA  */
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/eata_dma_proc.c linux/drivers/scsi/eata_dma_proc.c
--- v2.2.0-pre1/linux/drivers/scsi/eata_dma_proc.c	Sun Dec 21 17:04:49 1997
+++ linux/drivers/scsi/eata_dma_proc.c	Tue Dec 29 11:35:48 1998
@@ -1,3 +1,4 @@
+#include <linux/config.h>
 
 void swap_statistics(u8 *p)
 {
@@ -68,6 +69,8 @@
 		   int hostno, int inout)
 {
 
+#ifdef CONFIG_PROC_FS
+
     Scsi_Device *scd, SDev;
     struct Scsi_Host *HBA_ptr;
     Scsi_Cmnd scmd;
@@ -466,6 +469,9 @@
     DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
     
     return (len);     
+#else /* CONFIG_PROC_FS */
+    return 0;
+#endif        
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/eata_pio.c linux/drivers/scsi/eata_pio.c
--- v2.2.0-pre1/linux/drivers/scsi/eata_pio.c	Wed May 20 19:10:39 1998
+++ linux/drivers/scsi/eata_pio.c	Tue Dec 29 11:35:48 1998
@@ -500,7 +500,7 @@
     HD(cmd)->state = RESET;
     
     time = jiffies;
-    while (jiffies < (time + (3 * HZ)) && limit++ < 10000000);
+    while (time_before(jiffies, time + 3 * HZ) && limit++ < 10000000);
     
     DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, "
                            "loops %d.\n", limit));
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/eata_pio_proc.c linux/drivers/scsi/eata_pio_proc.c
--- v2.2.0-pre1/linux/drivers/scsi/eata_pio_proc.c	Sun Dec 21 17:04:49 1997
+++ linux/drivers/scsi/eata_pio_proc.c	Tue Dec 29 11:35:48 1998
@@ -1,3 +1,5 @@
+#include <linux/config.h>
+
 /*
  * eata_set_info
  * buffer : pointer to the data that has been written to the hostfile
@@ -24,7 +26,7 @@
 int eata_pio_proc_info(char *buffer, char **start, off_t offset, int length, 
 		       int hostno, int inout)
 {
-
+#ifdef CONFIG_PROC_FS
     Scsi_Device *scd;
     struct Scsi_Host *HBA_ptr;
     static u8 buff[512];
@@ -108,6 +110,9 @@
     DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
     
     return (len);     
+#else
+    return 0;
+#endif        
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v2.2.0-pre1/linux/drivers/scsi/fdomain.c	Fri Oct 23 22:01:21 1998
+++ linux/drivers/scsi/fdomain.c	Tue Dec 29 11:44:53 1998
@@ -1,10 +1,8 @@
 /* fdomain.c -- Future Domain TMC-16x0 SCSI driver
  * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Wed Oct  2 11:10:55 1996 by faith@acm.org
+ * Revised: Mon Dec 28 21:59:02 1998 by faith@acm.org
  * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
- *
- * Version 5.46 (23-04-1998)
+ * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -32,13 +30,34 @@
     Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX
     Future Domain TMC-3260 (PCI)
     Quantum ISA-200S, ISA-250MG
-    Adaptec AHA-2920 (PCI)
+    Adaptec AHA-2920A (PCI) [BUT *NOT* AHA-2920C -- use aic7xxx instead]
     IBM ?
- LILO command-line options:
+ LILO/INSMOD command-line options:
     fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]
 
 
+    
+ NOTE:
+
+ The Adaptec AHA-2920C has an Adaptec AIC-7850 chip on it.
+ Use the aic7xxx driver for this board.
+       
+ The Adaptec AHA-2920A has a Future Domain chip on it, so this is the right
+ driver for that card.  Unfortunately, the boxes will probably just say
+ "2920", so you'll have to look on the card for a Future Domain logo, or a
+ letter after the 2920.
+
+ 
+ 
+ THANKS:
+
+ Thanks to Adaptec for providing PCI boards for testing.  This finally
+ enabled me to test the PCI detection and correct it for PCI boards that do
+ not have a BIOS at a standard ISA location.  For PCI boards, LILO/INSMOD
+ command-line options should no longer be needed.  --RF 18Nov98
+
 
+ 
  DESCRIPTION:
  
  This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
@@ -70,7 +89,7 @@
  your board.  Please refer to the Seagate driver for more information and
  possible support.
 
-
+ 
  
  HISTORY:
 
@@ -108,7 +127,8 @@
  2.1.1       5.45         2 Oct 1996  Update ROM accesses for 2.1.x
  2.1.97      5.46	 23 Apr 1998  Rewritten PCI detection routines [mj]
  2.1.11x     5.47	  9 Aug 1998  Touched for 8 SCSI disk majors support
-
+             5.48        18 Nov 1998  BIOS no longer needed for PCI detection
+ 2.2.0       5.50        28 Dec 1998  Support insmod parameters
  
 
  REFERENCES USED:
@@ -207,6 +227,9 @@
  patches.
 
  New PCI detection code written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+
+ Insmod parameter code based on patches from Daniel Graham
+ <graham@balance.uoregon.edu>. 
  
  All of the alpha testers deserve much thanks.
 
@@ -282,7 +305,7 @@
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
   
-#define VERSION          "$Revision: 5.45 $"
+#define VERSION          "$Revision: 5.50 $"
 
 /* START OF USER DEFINABLE OPTIONS */
 
@@ -406,6 +429,15 @@
 extern void              do_fdomain_16x0_intr( int irq, void *dev_id,
 					    struct pt_regs * regs );
 
+#ifdef MODULE
+				/* Allow insmod parameters to be like LILO
+                                   parameters.  For example:
+				   insmod fdomain fdomain=0x140,11
+				*/
+static int               fdomain[]={ 0, 0, 0 };
+MODULE_PARM(fdomain, "2-3i");
+#endif
+
 static unsigned long addresses[] = {
    0xc8000,
    0xca000,
@@ -504,10 +536,10 @@
    if (!shpnt) return;		/* This won't ever happen */
 
    if (bios_major < 0 && bios_minor < 0) {
-      printk( "scsi%d <fdomain>: No BIOS; using scsi id %d\n",
+      printk( "scsi%d: <fdomain> No BIOS; using scsi id %d\n",
 	      shpnt->host_no, shpnt->this_id );
    } else {
-      printk( "scsi%d <fdomain>: BIOS version ", shpnt->host_no );
+      printk( "scsi%d: <fdomain> BIOS version ", shpnt->host_no );
 
       if (bios_major >= 0) printk( "%d.", bios_major );
       else                 printk( "?." );
@@ -523,7 +555,7 @@
 				   boards, we will have to modify banner
 				   for additional PCI cards, but for now if
 				   it's PCI it's a TMC-3260 - JTM */
-   printk( "scsi%d <fdomain>: %s chip at 0x%x irq ",
+   printk( "scsi%d: <fdomain> %s chip at 0x%x irq ",
 	   shpnt->host_no,
 	   chip == tmc1800 ? "TMC-1800"
 	   : (chip == tmc18c50 ? "TMC-18C50"
@@ -541,8 +573,9 @@
 void fdomain_setup( char *str, int *ints )
 {
    if (setup_called++ || ints[0] < 2 || ints[0] > 3) {
-      printk( "fdomain: usage: fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]\n" );
-      printk( "fdomain: bad LILO parameters?\n" );
+      printk( "scsi: <fdomain>"
+	      " Usage: fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]\n" );
+      printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
    }
 
    port_base       = ints[0] >= 1 ? ints[1] : 0;
@@ -591,8 +624,6 @@
       if (inb( port + MSB_ID_Code ) != 0x60) return 0;
       chip = tmc18c50;
 
-#if 1
-
 				/* Try to toggle 32-bit mode.  This only
 				   works on an 18c30 chip.  (User reports
 				   say this works, so we should switch to
@@ -606,17 +637,6 @@
 	    FIFO_Size = 0x800;	/* 2k FIFO */
 	 }
       }
-#else
-
-				/* That should have worked, but appears to
-				   have problems.  Let's assume it is an
-				   18c30 if the RAM is disabled. */
-
-      if (inb( port + Configuration2 ) & 0x02) {
-	 chip      = tmc18c30;
-	 FIFO_Size = 0x800;	/* 2k FIFO */
-      }
-#endif
 				/* If that failed, we are an 18c50. */
    }
 
@@ -656,7 +676,7 @@
    int options = inb( base + Configuration1 );
 
 #if DEBUG_DETECT
-   printk( " Options = %x\n", options );
+   printk( "scsi: <fdomain> Options = %x\n", options );
 #endif
    
 				/* Check for board with lowest bios_base --
@@ -673,10 +693,31 @@
 
 static int fdomain_isa_detect( int *irq, int *iobase )
 {
-   int i;
+   int i, j;
    int base;
    int flag = 0;
 
+#if DEBUG_DETECT
+   printk( "scsi: <fdomain> fdomain_isa_detect:" );
+#endif
+
+   for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
+#if DEBUG_DETECT
+      printk( " %lx(%lx),", addresses[i], bios_base );
+#endif
+      for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
+	 if (check_signature(addresses[i] + signatures[j].sig_offset,
+			     signatures[j].signature,
+			     signatures[j].sig_length )) {
+	    bios_major = signatures[j].major_bios_version;
+	    bios_minor = signatures[j].minor_bios_version;
+	    PCI_bus    = (signatures[j].flag == 1);
+	    Quantum    = (signatures[j].flag > 1) ? signatures[j].flag : 0;
+	    bios_base  = addresses[i];
+	 }
+      }
+   }
+   
    if (bios_major == 2) {
       /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
 	 Assuming the ROM is enabled (otherwise we wouldn't have been
@@ -746,6 +787,11 @@
       if ((flag = fdomain_is_valid_port( base ))) break;
    }
 
+#if DEBUG_DETECT
+   if (flag) printk( " SUCCESS\n" );
+   else      printk( " FAILURE\n" );
+#endif
+
    if (!flag) return 0;		/* iobase not found */
 
    *irq    = fdomain_get_irq( base );
@@ -771,18 +817,21 @@
    /* Tell how to print a list of the known PCI devices from bios32 and
       list vendor and device IDs being used if in debug mode.  */
       
-   printk( "\nINFO: use lspci -v to see list of PCI devices\n" );
-   printk( "\nTMC-3260 detect:"
-	   " Using PCI Vendor ID: 0x%x, PCI Device ID: 0x%x\n",
+   printk( "scsi: <fdomain> INFO: use lspci -v to see list of PCI devices\n" );
+   printk( "scsi: <fdomain> TMC-3260 detect:"
+	   " Using Vendor ID: 0x%x and Device ID: 0x%x\n",
 	   PCI_VENDOR_ID_FD, 
 	   PCI_DEVICE_ID_FD_36C70 );
 #endif 
 
-   if ((pdev = pci_find_device(PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70, pdev)) == NULL)
+   if ((pdev = pci_find_device(PCI_VENDOR_ID_FD,
+			       PCI_DEVICE_ID_FD_36C70,
+			       pdev)) == NULL)
      return 0;
        
 #if DEBUG_DETECT
-   printk( "Future Domain 36C70 : at PCI bus %u, device %u, function %u\n",
+   printk( "scsi: <fdomain> TMC-3260 detect:"
+	   " PCI bus %u, device %u, function %u\n",
 	   pdev->bus->number,
 	   PCI_SLOT(pdev->devfn),
 	   PCI_FUNC(pdev->devfn));
@@ -793,10 +842,6 @@
 
    pci_base = pdev->base_address[0];
    pci_irq = pdev->irq;
-#if DEBUG_DETECT
-      printk( "TMC-3260 PCI: IRQ = %u, I/O base = 0x%lx\n", 
-	      pci_irq, pci_base );
-#endif
 
    /* Now we have the I/O base address and interrupt from the PCI
       configuration registers. */
@@ -805,18 +850,28 @@
    *iobase = (pci_base & PCI_BASE_ADDRESS_IO_MASK);
 
 #if DEBUG_DETECT
-   printk( "TMC-3260 fix: Masking I/O base address with 0xff00.\n" ); 
-   printk( "TMC-3260: IRQ = %d, I/O base = 0x%x\n", *irq, *iobase );
+   printk( "scsi: <fdomain> TMC-3260 detect:"
+	   " IRQ = %d, I/O base = 0x%x [0x%lx]\n", *irq, *iobase, pci_base );
 #endif
 
-   if (!fdomain_is_valid_port( *iobase )) return 0;
+   if (!fdomain_is_valid_port( *iobase )) {
+      printk( "scsi: <fdomain>"
+	      " PCI card detected, but driver not loaded (invalid port)\n" );
+      return 0;
+   }
+
+				/* Fill in a few global variables.  Ugh. */
+   bios_major = bios_minor = -1;
+   PCI_bus    = 1;
+   Quantum    = 0;
+   bios_base  = 0;
+   
    return 1;
 }
 #endif
 
 int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
 {
-   int              i, j;
    int              retcode;
    struct Scsi_Host *shpnt;
 #if DO_DETECT
@@ -829,68 +884,44 @@
    unsigned char    buf[buflen];
 #endif
 
-#if DEBUG_DETECT
-   printk( "fdomain_16x0_detect()," );
-#endif
    tpnt->proc_dir = &proc_scsi_fdomain;
 
+#ifdef MODULE
+   if (fdomain[0] || fdomain[1] || fdomain[2]) {
+      port_base       = fdomain[0];
+      interrupt_level = fdomain[1];
+      this_id         = fdomain[2];
+      bios_major = bios_minor = -1;
+      ++setup_called;
+   }
+#endif
+   
    if (setup_called) {
 #if DEBUG_DETECT
-      printk( "no BIOS, using port_base = 0x%x, irq = %d\n",
+      printk( "scsi: <fdomain> No BIOS, using port_base = 0x%x, irq = %d\n",
 	      port_base, interrupt_level );
 #endif
       if (!fdomain_is_valid_port( port_base )) {
-	 printk( "fdomain: cannot locate chip at port base 0x%x\n",
+	 printk( "scsi: <fdomain> Cannot locate chip at port base 0x%x\n",
 		 port_base );
-	 printk( "fdomain: bad LILO parameters?\n" );
+	 printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
 	 return 0;
       }
    } else {
       int flag = 0;
-      
-      for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
-#if DEBUG_DETECT
-	 printk( " %lx(%lx),", addresses[i], bios_base );
-#endif
-	 for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
-	    if (check_signature(addresses[i] + signatures[j].sig_offset,
-				signatures[j].signature,
-				signatures[j].sig_length )) {
-	       bios_major = signatures[j].major_bios_version;
-	       bios_minor = signatures[j].minor_bios_version;
-	       PCI_bus    = (signatures[j].flag == 1);
-	       Quantum    = (signatures[j].flag > 1) ? signatures[j].flag : 0;
-	       bios_base  = addresses[i];
-	    }
-	 }
-      }
-
-      if (!bios_base) {
-#if DEBUG_DETECT
-	 printk( " FAILED: NO BIOS\n" );
-#endif
-	 return 0;
-      }
 
-      if (!PCI_bus) {
-	 flag = fdomain_isa_detect( &interrupt_level, &port_base );
-      } else {
 #ifdef CONFIG_PCI
-	 flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
-#else
-	 printk(KERN_ERR "No PCI support in this kernel, giving up.\n");
-	 flag = 0;
+				/* Try PCI detection first */
+      flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
 #endif
-      }
-	 
       if (!flag) {
-#if DEBUG_DETECT
-	 printk( " FAILED: NO PORT\n" );
-#endif
-#ifdef CONFIG_PCI
-	 printk( "\nTMC-3260 36C70 PCI scsi chip detection failed.\n" );
-#endif
-	 return 0;		/* Cannot find valid set of ports */
+				/* Then try ISA bus detection */
+	 flag = fdomain_isa_detect( &interrupt_level, &port_base );
+
+	 if (!flag) {
+	    printk( "scsi: <fdomain> Detection failed (no card)\n" );
+	    return 0;
+	 }
       }
    }
 
@@ -911,13 +942,10 @@
    fdomain_16x0_reset( NULL, 0 );
 
    if (fdomain_test_loopback()) {
-#if DEBUG_DETECT
-      printk( "fdomain: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
-#endif
+      printk( "scsi: <fdomain> Detection failed"
+	      " (loopback test failed at port base 0x%x)\n", port_base );
       if (setup_called) {
-	 printk( "fdomain: loopback test failed at port base 0x%x\n",
-		 port_base );
-	 printk( "fdomain: bad LILO parameters?\n" );
+	 printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
       }
       return 0;
    }
@@ -946,7 +974,9 @@
 
 				/* Log IRQ with kernel */   
    if (!interrupt_level) {
-      panic( "fdomain: *NO* interrupt level selected!\n" );
+      printk( "scsi: <fdomain>"
+	      " Card Detected, but driver not loaded (no IRQ)\n" );
+      return 0;
    } else {
       /* Register the IRQ with the kernel */
 
@@ -955,18 +985,21 @@
 
       if (retcode < 0) {
 	 if (retcode == -EINVAL) {
-	    printk( "fdomain: IRQ %d is bad!\n", interrupt_level );
-	    printk( "         This shouldn't happen!\n" );
-	    printk( "         Send mail to faith@acm.org\n" );
+	    printk( "scsi: <fdomain> IRQ %d is bad!\n", interrupt_level );
+	    printk( "                This shouldn't happen!\n" );
+	    printk( "                Send mail to faith@acm.org\n" );
 	 } else if (retcode == -EBUSY) {
-	    printk( "fdomain: IRQ %d is already in use!\n", interrupt_level );
-	    printk( "         Please use another IRQ!\n" );
+	    printk( "scsi: <fdomain> IRQ %d is already in use!\n",
+		    interrupt_level );
+	    printk( "                Please use another IRQ!\n" );
 	 } else {
-	    printk( "fdomain: Error getting IRQ %d\n", interrupt_level );
-	    printk( "         This shouldn't happen!\n" );
-	    printk( "         Send mail to faith@acm.org\n" );
+	    printk( "scsi: <fdomain> Error getting IRQ %d\n",
+		    interrupt_level );
+	    printk( "                This shouldn't happen!\n" );
+	    printk( "                Send mail to faith@acm.org\n" );
 	 }
-	 panic( "fdomain: Driver requires interruptions\n" );
+	 printk( "scsi: <fdomain> Detected, but driver not loaded (IRQ)\n" );
+	 return 0;
       }
    }
 
@@ -987,7 +1020,7 @@
    SCinit.use_sg          = 0;
    SCinit.lun             = 0;
 
-   printk( "fdomain: detection routine scanning for devices:\n" );
+   printk( "scsi: <fdomain> detection routine scanning for devices:\n" );
    for (i = 0; i < 8; i++) {
       SCinit.target = i;
       if (i == tpnt->this_id)	/* Skip host adapter */
@@ -1031,10 +1064,10 @@
 
 const char *fdomain_16x0_info( struct Scsi_Host *ignore )
 {
-   static char buffer[80];
+   static char buffer[128];
    char        *pt;
    
-   strcpy( buffer, "Future Domain TMC-16x0 SCSI driver, version" );
+   strcpy( buffer, "Future Domain 16-bit SCSI Driver Version" );
    if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
       strcat( buffer, strchr( VERSION, ':' ) + 1 );
       pt = strrchr( buffer, '$') - 1;
@@ -1118,7 +1151,7 @@
    printk( "Arbitration failed, status = %x\n", status );
 #endif
 #if ERRORS_ONLY
-   printk( "fdomain: Arbitration failed, status = %x\n", status );
+   printk( "scsi: <fdomain> Arbitration failed, status = %x\n", status );
 #endif
    return 1;
 }
@@ -1158,7 +1191,7 @@
       if (!flag) /* Skip first failure for all chips. */
 	    ++flag;
       else
-	    printk( "fdomain: Selection failed\n" );
+	    printk( "scsi: <fdomain> Selection failed\n" );
    }
 #endif
    return 1;
@@ -1173,9 +1206,9 @@
       current_SC->result = error;
       if (current_SC->scsi_done)
 	    current_SC->scsi_done( current_SC );
-      else panic( "fdomain: current_SC->scsi_done() == NULL" );
+      else panic( "scsi: <fdomain> current_SC->scsi_done() == NULL" );
    } else {
-      panic( "fdomain: my_done() called outside of command\n" );
+      panic( "scsi: <fdomain> my_done() called outside of command\n" );
    }
 #if DEBUG_RACE
    in_interrupt_flag = 0;
@@ -1211,7 +1244,7 @@
    /* Abort calls my_done, so we do nothing here. */
    if (current_SC->SCp.phase & aborted) {
 #if DEBUG_ABORT
-      printk( "Interrupt after abort, ignoring\n" );
+      printk( "scsi: <fdomain> Interrupt after abort, ignoring\n" );
 #endif
       /*
       return; */
@@ -1311,7 +1344,7 @@
 	 if (current_SC->SCp.Status
 	     && current_SC->SCp.Status != 2
 	     && current_SC->SCp.Status != 8) {
-	    printk( "fdomain: target = %d, command = %x, status = %x\n",
+	    printk( "scsi: <fdomain> target = %d, command = %x, status = %x\n",
 		    current_SC->target,
 		    current_SC->cmnd[0],
 		    current_SC->SCp.Status );
@@ -1329,7 +1362,8 @@
 	 if (!current_SC->SCp.Message) ++done;
 #if DEBUG_MESSAGES || EVERY_ACCESS
 	 if (current_SC->SCp.Message) {
-	    printk( "fdomain: message = %x\n", current_SC->SCp.Message );
+	    printk( "scsi: <fdomain> message = %x\n",
+		    current_SC->SCp.Message );
 	 }
 #endif
 	 break;
@@ -1590,8 +1624,8 @@
 						|| code == 0x24
 						|| !code)))
 		  
-		  printk( "fdomain: REQUEST SENSE "
-			  "Key = %x, Code = %x, Qualifier = %x\n",
+		  printk( "scsi: <fdomain> REQUEST SENSE"
+			  " Key = %x, Code = %x, Qualifier = %x\n",
 			  key, code, qualifier );
 	 }
       }
@@ -1624,7 +1658,7 @@
 int fdomain_16x0_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
 {
    if (in_command) {
-      panic( "fdomain: fdomain_16x0_queue() NOT REENTRANT!\n" );
+      panic( "scsi: <fdomain> fdomain_16x0_queue() NOT REENTRANT!\n" );
    }
 #if EVERY_ACCESS
    printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
@@ -1704,7 +1738,8 @@
    unsigned int isr;
 
    if (!SCpnt || !SCpnt->host) {
-      printk( "fdomain: cannot provide detailed information\n" );
+      printk( "scsi: <fdomain> Cannot provide detailed information\n" );
+      return;
    }
    
    printk( "%s\n", fdomain_16x0_info( SCpnt->host ) );
@@ -1770,7 +1805,7 @@
 {
    unsigned long flags;
 #if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
-   printk( "fdomain: abort " );
+   printk( "scsi: <fdomain> abort " );
 #endif
 
    save_flags( flags );
@@ -1808,7 +1843,7 @@
 #endif
 
 #if ERRORS_ONLY
-   if (SCpnt) printk( "fdomain: SCSI Bus Reset\n" );
+   if (SCpnt) printk( "scsi: <fdomain> SCSI Bus Reset\n" );
 #endif
 
 #if DEBUG_RESET
@@ -1893,7 +1928,7 @@
     */
 
    if (MAJOR(dev) != SCSI_DISK0_MAJOR) {
-      printk("fdomain_16x0_biosparam: too many disks");
+      printk("scsi: <fdomain> fdomain_16x0_biosparam: too many disks");
       return 0;
    }
    drive = MINOR(dev) >> 4;
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/ibmmca.c linux/drivers/scsi/ibmmca.c
--- v2.2.0-pre1/linux/drivers/scsi/ibmmca.c	Fri Nov 27 13:09:24 1998
+++ linux/drivers/scsi/ibmmca.c	Tue Dec 29 11:40:35 1998
@@ -1567,7 +1567,7 @@
 
   /* first look for the SCSI integrated on the motherboard */
   pos2 = mca_read_stored_pos(MCA_INTEGSCSI, 2);
-  if (pos2 != 0xff) {
+//  if (pos2 != 0xff) {
     if ((pos2 & 1) == 0) {
       port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
     } else {
@@ -1584,7 +1584,7 @@
           mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
                                  shpnt);
         }
-    }
+//    }
 
   /* now look for other adapters */
   list_size = sizeof(subsys_list) / sizeof(struct subsys_list_struct);
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/mesh.c linux/drivers/scsi/mesh.c
--- v2.2.0-pre1/linux/drivers/scsi/mesh.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/scsi/mesh.c	Wed Dec 30 10:55:07 1998
@@ -1346,11 +1346,11 @@
 static void
 do_mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
 {
-	/*unsigned long flags;*/
+	unsigned long flags;
 
-	/*spin_lock_irqsave(&io_request_lock, flags);*/
+	spin_lock_irqsave(&io_request_lock, flags);
 	mesh_interrupt(irq, dev_id, ptregs);
-	/*spin_unlock_irqrestore(&io_request_lock, flags);*/
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 static void handle_error(struct mesh_state *ms)
@@ -1643,6 +1643,7 @@
 static void
 mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd)
 {
+#if 0
 	if (ms->completed_q == NULL)
 		ms->completed_q = cmd;
 	else
@@ -1651,6 +1652,9 @@
 	cmd->host_scribble = NULL;
 	queue_task(&ms->tqueue, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
+#else
+	(*cmd->scsi_done)(cmd);
+#endif
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.2.0-pre1/linux/drivers/scsi/scsi.h	Fri Oct 23 22:01:21 1998
+++ linux/drivers/scsi/scsi.h	Wed Dec 30 22:35:09 1998
@@ -319,7 +319,7 @@
 #define ASKED_FOR_SENSE 0x20
 
 
-#ifdef __mc68000__
+#if defined(__mc68000__) || defined(CONFIG_APUS)
 #include <asm/pgtable.h>
 #define CONTIGUOUS_BUFFERS(X,Y) \
 	(virt_to_phys((X)->b_data+(X)->b_size-1)+1==virt_to_phys((Y)->b_data))
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v2.2.0-pre1/linux/drivers/scsi/seagate.c	Sat Sep  5 16:46:41 1998
+++ linux/drivers/scsi/seagate.c	Tue Dec 29 11:40:35 1998
@@ -379,8 +379,8 @@
 {
   register int count = 0, start = jiffies + 1, stop = start + 25;
 
-  while (jiffies < start) ;
-  for (; jiffies < stop; ++count) ;
+  while (time_before(jiffies, start)) ;
+  for (; time_before(jiffies, stop); ++count) ;
 
 /*
  * Ok, we now have a count for .25 seconds.  Convert to a
@@ -903,9 +903,9 @@
 
       while (((STATUS | STATUS | STATUS) &
               (STAT_BSY | STAT_SEL)) &&
-             (!st0x_aborted) && (jiffies < clock));
+             (!st0x_aborted) && time_before(jiffies, clock));
 
-      if (jiffies > clock)
+      if (time_after(jiffies, clock))
         return retcode (DID_BUS_BUSY);
       else if (st0x_aborted)
         return retcode (st0x_aborted);
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.2.0-pre1/linux/drivers/scsi/wd7000.c	Fri Oct 23 22:01:21 1998
+++ linux/drivers/scsi/wd7000.c	Tue Dec 29 11:40:35 1998
@@ -791,7 +791,7 @@
     register unsigned WAITbits;
     register unsigned long WAITtimeout = jiffies + WAITnexttimeout;
 
-    while (jiffies <= WAITtimeout) {
+    while (time_before_eq(jiffies, WAITtimeout)) {
 	WAITbits = inb (port) & mask;
 
 	if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0))
@@ -806,7 +806,7 @@
 {
     register unsigned long time = jiffies + how_long;
 
-    while (jiffies < time);
+    while (time_before(jiffies, time));
 }
 
 
@@ -868,7 +868,7 @@
 	    spin_unlock_irq(&io_request_lock);
 	    for (now = jiffies; now == jiffies; );	/* wait a jiffy */
 	    spin_lock_irq(&io_request_lock);
-	} while (freescbs < needed && jiffies <= timeout);
+	} while (freescbs < needed && time_before_eq(jiffies, timeout));
 	/*
 	 *  If we get here with enough free Scbs, we can take them.
 	 *  Otherwise, we timed out and didn't get enough.
@@ -1247,7 +1247,7 @@
      */
     mail_out (host, (struct scb *) &icb);
     timeout = jiffies + WAITnexttimeout;	/* wait up to 2 seconds */
-    while (icb.phase && jiffies < timeout)
+    while (icb.phase && time_before(jiffies, timeout))
 	barrier ();		/* wait for completion */
 
     if (icb.phase) {
diff -u --recursive --new-file v2.2.0-pre1/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
--- v2.2.0-pre1/linux/drivers/sound/dmasound.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/dmasound.c	Wed Dec 30 10:55:07 1998
@@ -2084,8 +2084,7 @@
 	int utotal, ftotal;
 
 	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
+	userCount >>= (stereo? 2: 1);
 	ftotal = frameLeft;
 	utotal = userCount;
 	while (frameLeft) {
@@ -2130,8 +2129,7 @@
 	int utotal, ftotal;
 
 	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
+	userCount >>= (stereo? 2: 1);
 	ftotal = frameLeft;
 	utotal = userCount;
 	while (frameLeft) {
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/Config.in linux/fs/Config.in
--- v2.2.0-pre1/linux/fs/Config.in	Mon Dec 28 15:00:52 1998
+++ linux/fs/Config.in	Tue Dec 29 11:42:21 1998
@@ -56,7 +56,9 @@
   if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "y" ]; then
     bool '   Root file system on NFS' CONFIG_ROOT_NFS
   fi
-  tristate 'NFS server support' CONFIG_NFSD
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'NFS server support' CONFIG_NFSD
+  fi
   if [ "$CONFIG_NFSD" != "n" ]; then
     bool '   Emulate SUN NFS server' CONFIG_NFSD_SUN
   fi
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/buffer.c linux/fs/buffer.c
--- v2.2.0-pre1/linux/fs/buffer.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/buffer.c	Wed Dec 30 22:26:46 1998
@@ -424,10 +424,14 @@
 
 static inline void remove_from_hash_queue(struct buffer_head * bh)
 {
-	if (bh->b_pprev) {
-		if(bh->b_next)
-			bh->b_next->b_pprev = bh->b_pprev;
-		*bh->b_pprev = bh->b_next;
+	struct buffer_head **pprev = bh->b_pprev;
+	if (pprev) {
+		struct buffer_head * next = bh->b_next;
+		if (next) {
+			next->b_pprev = pprev;
+			bh->b_next = NULL;
+		}
+		*pprev = next;
 		bh->b_pprev = NULL;
 	}
 }
@@ -551,14 +555,19 @@
 		nr_buffers_type[bh->b_list]++;
 
 		/* Put the buffer in new hash-queue if it has a device. */
+		bh->b_next = NULL;
+		bh->b_pprev = NULL;
 		if (bh->b_dev) {
 			struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr);
-			if((bh->b_next = *bhp) != NULL)
-				(*bhp)->b_pprev = &bh->b_next;
+			struct buffer_head *next = *bhp;
+
+			if (next) {
+				bh->b_next = next;
+				next->b_pprev = &bh->b_next;
+			}
 			*bhp = bh;
-			bh->b_pprev = bhp;	/* Exists in bh hashes. */
-		} else
-			bh->b_pprev = NULL;	/* Not in bh hashes. */
+			bh->b_pprev = bhp;
+		}
 	}
 }
 
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/dcache.c linux/fs/dcache.c
--- v2.2.0-pre1/linux/fs/dcache.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/dcache.c	Wed Dec 30 17:13:57 1998
@@ -475,7 +475,7 @@
  * too much.
  *
  * Priority:
- *   0 - very urgent: schrink everything
+ *   0 - very urgent: shrink everything
  *  ...
  *   6 - base-level: try to shrink a bit.
  */
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.2.0-pre1/linux/fs/fat/inode.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/fat/inode.c	Tue Dec 29 14:07:40 1998
@@ -366,7 +366,9 @@
 			CF_LE_W(b->info_sector) * logical_sector_size + 0x1e0;
 		fsinfo = (struct fat_boot_fsinfo *)
 			&bh->b_data[MSDOS_SB(sb)->fsinfo_offset];
-		if (CF_LE_L(fsinfo->signature) != 0x61417272) {
+		if ((MSDOS_SB(sb)->fsinfo_offset - sizeof(MSDOS_SB(sb)->fsinfo_offset) + 1)> bh->b_size) 
+		    	printk("fat_read_super: Bad fsinfo_offset\n");
+		else if (CF_LE_L(fsinfo->signature) != 0x61417272) {
 			printk("fat_read_super: Did not find valid FSINFO "
 				"signature. Found 0x%x\n",
 				CF_LE_L(fsinfo->signature));
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.2.0-pre1/linux/fs/isofs/inode.c	Mon Sep 28 10:51:34 1998
+++ linux/fs/isofs/inode.c	Tue Dec 29 11:40:35 1998
@@ -409,11 +409,13 @@
   struct cdrom_multisession ms_info;
   unsigned int vol_desc_start;
   struct inode inode_fake;
+  struct file_operations *fops;
   extern struct file_operations * get_blkfops(unsigned int);
   int i;
 
   vol_desc_start=0;
-  if (get_blkfops(MAJOR(dev))->ioctl!=NULL)
+  fops = get_blkfops(MAJOR(dev));
+  if (fops && fops->ioctl)
     {
       /* Whoops.  We must save the old FS, since otherwise
        * we would destroy the kernels idea about FS on root
@@ -536,17 +538,10 @@
 	    vdp = (struct iso_volume_descriptor *)bh->b_data;
 	    hdp = (struct hs_volume_descriptor *)bh->b_data;
 	    
-	    if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) {
-		if (isonum_711 (hdp->type) != ISO_VD_PRIMARY)
-		    goto out_freebh;
-		
-		s->u.isofs_sb.s_high_sierra = 1;
-		high_sierra = 1;
-		opt.rock = 'n';
-		h_pri = (struct hs_primary_descriptor *)vdp;
-		goto root_found;
-	    }
-
+	    /* Due to the overlapping physical location of the descriptors, 
+	     * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure 
+	     * proper identification in this case, we first check for ISO.
+	     */
 	    if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
 		if (isonum_711 (vdp->type) == ISO_VD_END)
 		    break;
@@ -580,9 +575,21 @@
 		    }
 		}
 #endif
-		/* Just skip any volume descriptors we don't recognize */
+	    } else {
+	        if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) {
+		    if (isonum_711 (hdp->type) != ISO_VD_PRIMARY)
+		        goto out_freebh;
+		
+		    s->u.isofs_sb.s_high_sierra = 1;
+		    high_sierra = 1;
+		    opt.rock = 'n';
+		    h_pri = (struct hs_primary_descriptor *)vdp;
+		    goto root_found;
+		}
 	    }
 
+            /* Just skip any volume descriptors we don't recognize */
+
 	    brelse(bh);
 	    bh = NULL;
 	}
@@ -1105,8 +1112,9 @@
 	}
 
 	/* There are defective discs out there - we do this to protect
-	   ourselves.  A cdrom will never contain more than 800Mb */
-	if((inode->i_size < 0 || inode->i_size > 800000000) &&
+	   ourselves.  A cdrom will never contain more than 800Mb 
+	   .. but a DVD may be up to 1Gig (Ulrich Habel) */
+	if((inode->i_size < 0 || inode->i_size > 1073741824) &&
 	    inode->i_sb->u.isofs_sb.s_cruft == 'n') {
 	  printk("Warning: defective cdrom.  Enabling \"cruft\" mount option.\n");
 	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c
--- v2.2.0-pre1/linux/fs/lockd/clntproc.c	Sun Nov  8 14:03:06 1998
+++ linux/fs/lockd/clntproc.c	Tue Dec 29 11:42:25 1998
@@ -125,7 +125,8 @@
 
 	/* If we're cleaning up locks because the process is exiting,
 	 * perform the RPC call asynchronously. */
-	if (cmd == F_SETLK && fl->fl_type == F_UNLCK
+	if ((cmd == F_SETLK || cmd == F_SETLKW)
+	    && fl->fl_type == F_UNLCK
 	    && (current->flags & PF_EXITING)) {
 		sigfillset(&current->blocked);	/* Mask all signals */
 		recalc_sigpending(current);
@@ -144,7 +145,8 @@
 
 	if (cmd == F_GETLK) {
 		status = nlmclnt_test(call, fl);
-	} else if (cmd == F_SETLK && fl->fl_type == F_UNLCK) {
+	} else if ((cmd == F_SETLK || cmd == F_SETLKW)
+		   && fl->fl_type == F_UNLCK) {
 		status = nlmclnt_unlock(call, fl);
 	} else if (cmd == F_SETLK || cmd == F_SETLKW) {
 		call->a_args.block = (cmd == F_SETLKW)? 1 : 0;
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/lockd/host.c linux/fs/lockd/host.c
--- v2.2.0-pre1/linux/fs/lockd/host.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/lockd/host.c	Tue Dec 29 11:42:25 1998
@@ -92,7 +92,7 @@
 	/* Lock hash table */
 	down(&nlm_host_sema);
 
-	if (time_after(jiffies, next_gc))
+	if (time_after_eq(jiffies, next_gc))
 		nlm_gc_hosts();
 
 	for (hp = &nlm_hosts[hash]; (host = *hp); hp = &host->h_next) {
@@ -173,19 +173,22 @@
 	/* If we've already created an RPC client, check whether
 	 * RPC rebind is required */
 	if ((clnt = host->h_rpcclnt) != NULL) {
-		if (time_after(jiffies, host->h_nextrebind)) {
+		if (time_after_eq(jiffies, host->h_nextrebind)) {
 			clnt->cl_port = 0;
 			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
 			dprintk("lockd: next rebind in %ld jiffies\n",
 					host->h_nextrebind - jiffies);
 		}
 	} else {
-		uid_t		saved_euid = current->euid;
+		uid_t saved_fsuid = current->fsuid;
+		kernel_cap_t saved_cap = current->cap_effective;
 
 		/* Create RPC socket as root user so we get a priv port */
-		current->euid = 0;
+		current->fsuid = 0;
+		cap_raise (current->cap_effective, CAP_NET_BIND_SERVICE);
 		xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL);
-		current->euid = saved_euid;
+		current->fsuid = saved_fsuid;
+		current->cap_effective = saved_cap;
 		if (xprt == NULL)
 			goto forgetit;
 
@@ -219,7 +222,7 @@
 nlm_rebind_host(struct nlm_host *host)
 {
 	dprintk("lockd: rebind host %s\n", host->h_name);
-	if (host->h_rpcclnt && time_after(jiffies, host->h_nextrebind)) {
+	if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
 		host->h_rpcclnt->cl_port = 0;
 		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
 	}
@@ -298,7 +301,7 @@
 		q = &nlm_hosts[i];
 		while ((host = *q) != NULL) {
 			if (host->h_count || host->h_inuse
-			 || time_before_eq(jiffies, host->h_expires)) {
+			 || time_before(jiffies, host->h_expires)) {
 				q = &host->h_next;
 				continue;
 			}
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/lockd/svc.c linux/fs/lockd/svc.c
--- v2.2.0-pre1/linux/fs/lockd/svc.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/lockd/svc.c	Tue Dec 29 11:42:25 1998
@@ -136,7 +136,7 @@
 		 */
 		if (!nlmsvc_grace_period) {
 			timeout = nlmsvc_retry_blocked();
-		} else if (time_after(jiffies, nlmsvc_grace_period))
+		} else if (time_before(nlmsvc_grace_period, jiffies))
 			nlmsvc_grace_period = 0;
 
 		/*
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/lockd/svcproc.c linux/fs/lockd/svcproc.c
--- v2.2.0-pre1/linux/fs/lockd/svcproc.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/lockd/svcproc.c	Tue Dec 29 11:42:25 1998
@@ -499,15 +499,22 @@
 /*
  * NLM Server procedures.
  */
-#define nlmsvc_proc_none	NULL
-#define nlmsvc_encode_norep	NULL
-#define nlmsvc_decode_norep	NULL
-#define nlmsvc_decode_testres	NULL
-#define nlmsvc_proc_test_res	NULL
-#define nlmsvc_proc_lock_res	NULL
-#define nlmsvc_proc_cancel_res	NULL
-#define nlmsvc_proc_unlock_res	NULL
-#define nlmsvc_proc_granted_res	NULL
+
+#define nlmsvc_encode_norep	nlmsvc_encode_void
+#define nlmsvc_decode_norep	nlmsvc_decode_void
+#define nlmsvc_decode_testres	nlmsvc_decode_void
+#define nlmsvc_decode_lockres	nlmsvc_decode_void
+#define nlmsvc_decode_unlockres	nlmsvc_decode_void
+#define nlmsvc_decode_cancelres	nlmsvc_decode_void
+#define nlmsvc_decode_grantedres	nlmsvc_decode_void
+
+#define nlmsvc_proc_none	nlmsvc_proc_null
+#define nlmsvc_proc_test_res	nlmsvc_proc_null
+#define nlmsvc_proc_lock_res	nlmsvc_proc_null
+#define nlmsvc_proc_cancel_res	nlmsvc_proc_null
+#define nlmsvc_proc_unlock_res	nlmsvc_proc_null
+#define nlmsvc_proc_granted_res	nlmsvc_proc_null
+
 struct nlm_void			{ int dummy; };
 
 #define PROC(name, xargt, xrest, argt, rest)	\
@@ -533,10 +540,10 @@
   PROC(unlock_msg,	unlockargs,	norep,		args,	void),
   PROC(granted_msg,	testargs,	norep,		args,	void),
   PROC(test_res,	testres,	norep,		res,	void),
-  PROC(lock_res,	res,		norep,		res,	void),
-  PROC(cancel_res,	res,		norep,		res,	void),
-  PROC(unlock_res,	res,		norep,		res,	void),
-  PROC(granted_res,	res,		norep,		res,	void),
+  PROC(lock_res,	lockres,	norep,		res,	void),
+  PROC(cancel_res,	cancelres,	norep,		res,	void),
+  PROC(unlock_res,	unlockres,	norep,		res,	void),
+  PROC(granted_res,	grantedres,	norep,		res,	void),
   PROC(none,		void,		void,		void,	void),
   PROC(none,		void,		void,		void,	void),
   PROC(none,		void,		void,		void,	void),
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c
--- v2.2.0-pre1/linux/fs/lockd/svcsubs.c	Sun Jul 26 11:57:18 1998
+++ linux/fs/lockd/svcsubs.c	Tue Dec 29 11:42:25 1998
@@ -52,6 +52,8 @@
 	struct nlm_file	*file;
 	unsigned int	hash;
 	u32		nfserr;
+	uid_t		saved_cr_uid;
+	struct svc_cred	*cred;
 
 	dprintk("lockd: nlm_file_lookup(%s/%u)\n",
 		kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino);
@@ -80,11 +82,19 @@
 
 	/* Open the file. Note that this must not sleep for too long, else
 	 * we would lock up lockd:-) So no NFS re-exports, folks.
+	 *
+	 * We have to make sure we have the right credential to open
+	 * the file.
 	 */
+	cred = &rqstp->rq_cred;
+	saved_cr_uid = cred->cr_uid;
+	cred->cr_uid = 0;
 	if ((nfserr = nlmsvc_ops->fopen(rqstp, fh, &file->f_file)) != 0) {
 		dprintk("lockd: open failed (nfserr %ld)\n", ntohl(nfserr));
+		cred->cr_uid = saved_cr_uid;
 		goto out_free;
 	}
+	cred->cr_uid = saved_cr_uid;
 
 	file->f_next = nlm_files[hash];
 	nlm_files[hash] = file;
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/Makefile linux/fs/nfsd/Makefile
--- v2.2.0-pre1/linux/fs/nfsd/Makefile	Wed Jun 24 22:54:09 1998
+++ linux/fs/nfsd/Makefile	Tue Dec 29 11:42:25 1998
@@ -9,11 +9,8 @@
 
 O_TARGET := nfsd.o
 O_OBJS   := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
-	    export.o auth.o lockd.o nfscache.o nfsxdr.o
-
-ifdef CONFIG_PROC_FS
-  O_OBJS += stats.o
-endif
+	    export.o auth.o lockd.o nfscache.o nfsxdr.o \
+	    stats.o
 
 M_OBJS   := $(O_TARGET)
 
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/auth.c linux/fs/nfsd/auth.c
--- v2.2.0-pre1/linux/fs/nfsd/auth.c	Sun Nov  8 14:03:07 1998
+++ linux/fs/nfsd/auth.c	Tue Dec 29 11:42:25 1998
@@ -50,11 +50,10 @@
 	current->ngroups = i;
 
 	if ((cred->cr_uid)) {
-		cap_lower(current->cap_effective, CAP_DAC_OVERRIDE);
-		cap_lower(current->cap_effective, CAP_DAC_READ_SEARCH);
+		cap_t(current->cap_effective) &= ~CAP_FS_MASK;
 	} else {
-		cap_raise(current->cap_effective, CAP_DAC_OVERRIDE);
-		cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH);
+		cap_t(current->cap_effective) |= (CAP_FS_MASK &
+		                                  current->cap_permitted);
 	}
 
 	rqstp->rq_userset = 1;
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/export.c linux/fs/nfsd/export.c
--- v2.2.0-pre1/linux/fs/nfsd/export.c	Sun Nov  8 14:03:07 1998
+++ linux/fs/nfsd/export.c	Tue Dec 29 11:42:25 1998
@@ -615,6 +615,112 @@
 	return NULL;
 }
 
+struct flags {
+	int flag;
+	char *name[2];
+} expflags[] = {
+	{ NFSEXP_READONLY, {"ro", "rw"}},
+	{ NFSEXP_INSECURE_PORT, {"insecure", ""}},
+	{ NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}},
+	{ NFSEXP_ALLSQUASH, {"all_squash", ""}},
+	{ NFSEXP_ASYNC, {"async", ""}},
+	{ NFSEXP_GATHERED_WRITES, {"wdelay", ""}},
+	{ NFSEXP_UIDMAP, {"uidmap", ""}},
+	{ NFSEXP_KERBEROS, { "kerberos", ""}},
+	{ NFSEXP_SUNSECURE, { "sunsecure", ""}},
+	{ NFSEXP_CROSSMNT, {"crossmnt", ""}},
+	{ 0, {"", ""}}
+};
+
+static int
+exp_flags(char *buffer, int flag)
+{
+    int len = 0, first = 0;
+    struct flags *flg = expflags;
+
+    for (;flg->flag;flg++) {
+        int state = (flg->flag & flag)?0:1;
+        if (!flg->flag)
+		break;
+        if (*flg->name[state]) {
+		len += sprintf(buffer + len, "%s%s",
+                               first++?",":"", flg->name[state]);
+        }
+    }
+    return len;
+}
+
+int
+exp_procfs_exports(char *buffer, char **start, off_t offset,
+                             int length, int *eof, void *data)
+{
+	struct svc_clnthash	**hp, **head, *tmp;
+	struct svc_client	*clp;
+	svc_export *exp;
+	off_t	pos = 0;
+        off_t	begin = 0;
+        int	len = 0;
+	int	i,j;
+
+        len += sprintf(buffer, "# Version 1.0\n");
+        len += sprintf(buffer+len, "# Path Client(Flags) # IPs\n");
+
+	for (clp = clients; clp; clp = clp->cl_next) {
+		for (i = 0; i < NFSCLNT_EXPMAX; i++) {
+			exp = clp->cl_export[i];
+			while (exp) {
+				int first = 0;
+				len += sprintf(buffer+len, "%s\t", exp->ex_path);
+				len += sprintf(buffer+len, "%s", clp->cl_ident);
+				len += sprintf(buffer+len, "(");
+
+				len += exp_flags(buffer+len, exp->ex_flags);
+				len += sprintf(buffer+len, ") # ");
+				for (j = 0; j < clp->cl_naddr; j++) {
+					struct in_addr	addr = clp->cl_addr[j]; 
+
+					head = &clnt_hash[CLIENT_HASH(addr.s_addr)];
+					for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
+						if (tmp->h_addr.s_addr == addr.s_addr) {
+							if (first++) len += sprintf(buffer+len, "%s", " ");
+							if (tmp->h_client != clp)
+								len += sprintf(buffer+len, "(");
+							len += sprintf(buffer+len, "%ld.%ld.%ld.%ld",
+									htonl(addr.s_addr) >> 24 & 0xff,
+									htonl(addr.s_addr) >> 16 & 0xff,
+									htonl(addr.s_addr) >>  8 & 0xff,
+									htonl(addr.s_addr) >>  0 & 0xff);
+							if (tmp->h_client != clp)
+							  len += sprintf(buffer+len, ")");
+							break;
+						}
+					}
+				}
+				exp = exp->ex_next;
+
+				buffer[len++]='\n';
+
+				pos=begin+len;
+				if(pos<offset) {
+					len=0;
+					begin=pos;
+				}
+				if (pos > offset + length)
+					goto done;
+			}
+		}
+	}
+
+	*eof = 1;
+
+done:
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+	if ( len > length )
+		len = length;
+	return len;
+}
+
 /*
  * Add or modify a client.
  * Change requests may involve the list of host addresses. The list of
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/nfscache.c linux/fs/nfsd/nfscache.c
--- v2.2.0-pre1/linux/fs/nfsd/nfscache.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/nfscache.c	Tue Dec 29 11:42:25 1998
@@ -314,8 +314,8 @@
 				data->len);
 		return 0;
 	}
-	memcpy(resp->buf, data->buf, data->len);
-	resp->buf += ((data->len + 3) >> 2);
+	memcpy(resp->buf, data->buf, data->len << 2);
+	resp->buf += data->len;
 	resp->len += data->len;
 	return 1;
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c
--- v2.2.0-pre1/linux/fs/nfsd/nfsctl.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/nfsd/nfsctl.c	Tue Dec 29 11:42:25 1998
@@ -21,6 +21,7 @@
 #include <linux/version.h>
 #include <linux/unistd.h>
 #include <linux/malloc.h>
+#include <linux/proc_fs.h>
 
 #include <linux/nfs.h>
 #include <linux/sunrpc/svc.h>
@@ -53,6 +54,24 @@
 
 static int	initialized = 0;
 
+#ifdef CONFIG_PROC_FS
+
+int exp_procfs_exports(char *buffer, char **start, off_t offset,
+                             int length, int *eof, void *data);
+
+void proc_export_init(void)
+{
+	struct proc_dir_entry *nfs_export_ent = NULL;
+
+	if (!(nfs_export_ent = create_proc_entry("fs/nfs", S_IFDIR, 0)))
+		return;
+	if (!(nfs_export_ent = create_proc_entry("fs/nfs/exports", 0, 0)))
+		return;
+	nfs_export_ent->read_proc = exp_procfs_exports;
+}
+
+#endif
+
 /*
  * Initialize nfsd
  */
@@ -66,8 +85,10 @@
 	nfsd_cache_init();	/* RPC reply cache */
 	nfsd_export_init();	/* Exports table */
 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
-	nfsd_racache_init();	/* Readahead param cache */
 	nfsd_fh_init();		/* FH table */
+#ifdef CONFIG_PROC_FS
+	proc_export_init();
+#endif
 	initialized = 1;
 }
 
@@ -290,6 +311,8 @@
 	nfsd_cache_shutdown();
 	nfsd_fh_free();
 #ifdef CONFIG_PROC_FS
+	remove_proc_entry("fs/nfs/exports", NULL);
+	remove_proc_entry("fs/nfs", NULL);
 	nfsd_stat_shutdown();
 #endif
 	nfsd_lockd_shutdown();
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.2.0-pre1/linux/fs/nfsd/nfsfh.c	Mon Dec 28 15:00:53 1998
+++ linux/fs/nfsd/nfsfh.c	Tue Dec 29 11:42:25 1998
@@ -1093,32 +1093,33 @@
 	/*
 	 * Security: Check that the export is valid for dentry <gam3@acm.org>
 	 */
+	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);
-		goto out;
+		error = nfserr_stale;
 	} else if (exp->ex_dentry != dentry) {
 		struct dentry *tdentry = dentry;
-		int err2 = 0;
 
-		error = nfserr_stale;
 		do {
 			tdentry = tdentry->d_parent;
-			if (exp->ex_dentry == tdentry) {
-				error = 0;
+			if (exp->ex_dentry == tdentry)
 				break;
-			}
-			if ((err2 = nfsd_permission(exp, tdentry, MAY_READ))) {
-				error = err2;
-#ifdef NFSD_PARANOIA
-				goto out1;
-#else
-				goto out;
-#endif
+			/* 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)) {
+				error = nfserr_stale;
+dprintk("fh_verify: no root_squashed access.\n");
 			}
 		} while ((tdentry != tdentry->d_parent));
-		if (error) {
-			printk("fh_verify: Security: %s/%s bad export.\n",
+		if (exp->ex_dentry != tdentry) {
+			error = nfserr_stale;
+			printk("nfsd Security: %s/%s bad export.\n",
 			       dentry->d_parent->d_name.name,
 			       dentry->d_name.name);
 			goto out;
@@ -1126,14 +1127,14 @@
 	}
 
 	/* Finally, check access permissions. */
-	error = nfsd_permission(exp, dentry, access);
+	if (!error) {
+		error = nfsd_permission(exp, dentry, access);
+	}
 #ifdef NFSD_PARANOIA
-out1:
 if (error)
 printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name, access, error);
 #endif
-
 out:
 	return error;
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- v2.2.0-pre1/linux/fs/nfsd/nfssvc.c	Sun Nov  8 14:03:07 1998
+++ linux/fs/nfsd/nfssvc.c	Tue Dec 29 15:17:13 1998
@@ -44,6 +44,11 @@
 struct timeval			nfssvc_boot = { 0, 0 };
 static int			nfsd_active = 0;
 
+/*
+ * Maximum number of nfsd processes
+ */
+#define	NFSD_MAXSERVS		128
+
 int
 nfsd_svc(unsigned short port, int nrservs)
 {
@@ -52,19 +57,30 @@
 
 	dprintk("nfsd: creating service\n");
 	error = -EINVAL;
-	if (nrservs < 0)
+	if (nrservs <= 0)
 		goto out;
 	if (nrservs > NFSD_MAXSERVS)
 		nrservs = NFSD_MAXSERVS;
+	nfsd_nservers = nrservs;
 
 	error = -ENOMEM;
+	nfsd_racache_init();     /* Readahead param cache */
+	if (nfsd_nservers == 0)
+		goto out;
+	  
 	serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
-	if (serv == NULL) 
+	if (serv == NULL)
 		goto out;
 
-	if ((error = svc_makesock(serv, IPPROTO_UDP, port)) < 0
-	 || (error = svc_makesock(serv, IPPROTO_TCP, port)) < 0)
+	error = svc_makesock(serv, IPPROTO_UDP, port);
+	if (error < 0)
+		goto failure;
+
+#if 0	/* Don't even pretend that TCP works. It doesn't. */
+	error = svc_makesock(serv, IPPROTO_TCP, port);
+	if (error < 0)
 		goto failure;
+#endif
 
 	while (nrservs--) {
 		error = svc_create_thread(nfsd, serv);
@@ -93,6 +109,8 @@
 	exit_mm(current);
 	current->session = 1;
 	current->pgrp = 1;
+	/* Let svc_process check client's authentication. */
+	rqstp->rq_auth = 1;
 	sprintf(current->comm, "nfsd");
 
 	oldumask = current->fs->umask;		/* Set umask to 0.  */
@@ -127,22 +145,13 @@
 		 * port probes on port 2049 by unauthorized clients.
 		 */
 		rqstp->rq_client = exp_getclient(&rqstp->rq_addr);
-		if (!rqstp->rq_client) {
-			printk(KERN_WARNING "nfsd: unauthenticated request "
-				"from (%08lx:%d)\n",
-				ntohl(rqstp->rq_addr.sin_addr.s_addr),
-				ntohs(rqstp->rq_addr.sin_port));
-			svc_drop(rqstp);
-			serv->sv_stats->rpcbadclnt++;
-		} else {
-			/* Process request with signals blocked.  */
-			spin_lock_irq(&current->sigmask_lock);
-			siginitsetinv(&current->blocked, ALLOWED_SIGS);
-			recalc_sigpending(current);
-			spin_unlock_irq(&current->sigmask_lock);
-			
-			svc_process(serv, rqstp);
-		}
+		/* Process request with signals blocked.  */
+		spin_lock_irq(&current->sigmask_lock);
+		siginitsetinv(&current->blocked, ALLOWED_SIGS);
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
+
+		svc_process(serv, rqstp);
 
 		/* Unlock export hash tables */
 		exp_unlock();
@@ -166,6 +175,8 @@
 		printk("nfsd: last server exiting\n");
 		/* revoke all exports */
 		nfsd_export_shutdown();
+		/* release read-ahead cache */
+	        nfsd_racache_shutdown();
 	}
 
 	/* Destroy the thread */
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c
--- v2.2.0-pre1/linux/fs/nfsd/stats.c	Mon Jan 12 14:39:48 1998
+++ linux/fs/nfsd/stats.c	Tue Dec 29 11:42:25 1998
@@ -13,6 +13,7 @@
  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/proc_fs.h>
@@ -26,6 +27,7 @@
 struct nfsd_stats	nfsdstats;
 struct svc_stat		nfsd_svcstats = { &nfsd_program, };
 
+#ifdef CONFIG_PROC_FS
 static int
 nfsd_proc_read(char *buffer, char **start, off_t offset, int count,
 				int *eof, void *data)
@@ -88,3 +90,4 @@
 {
 	svc_proc_unregister("nfsd");
 }
+#endif /* CONFIG_PROC_FS */
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.2.0-pre1/linux/fs/nfsd/vfs.c	Tue Dec 22 14:16:57 1998
+++ linux/fs/nfsd/vfs.c	Tue Dec 29 11:42:25 1998
@@ -73,9 +73,10 @@
 				p_rawin;
 };
 
-#define FILECACHE_MAX		(2 * NFSD_MAXSERVS)
-static struct raparms		raparms[FILECACHE_MAX];
-static struct raparms *		raparm_cache = 0;
+int nfsd_nservers = 0;
+#define FILECACHE_MAX		(2 * nfsd_nservers) 
+static struct raparms *		raparml = NULL;
+static struct raparms *		raparm_cache = NULL;
 
 /*
  * Lock a parent directory following the VFS locking protocol.
@@ -148,16 +149,18 @@
 	dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name);
 
 	/* Obtain dentry and export. */
-	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP);
+	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC);
 	if (err)
 		goto out;
 
 	dparent = fhp->fh_dentry;
 	exp  = fhp->fh_export;
 
+#if 0
 	err = nfsd_permission(exp, dparent, MAY_EXEC);
 	if (err)
 		goto out;
+#endif
 	err = nfserr_noent;
 	if (fs_off_limits(dparent->d_sb))
 		goto out;
@@ -232,13 +235,17 @@
 	dentry = fhp->fh_dentry;
 	inode = dentry->d_inode;
 
+	err = inode_change_ok(inode, iap);
+	if (err)
+		goto out_nfserr;
+
 	/* The size case is special... */
 	if (iap->ia_valid & ATTR_SIZE) {
 if (!S_ISREG(inode->i_mode))
 printk("nfsd_setattr: size change??\n");
 		if (iap->ia_size < inode->i_size) {
 			err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
-			if (err != 0)
+			if (err)
 				goto out;
 		}
 		err = get_write_access(inode);
@@ -275,9 +282,17 @@
 
 	/* Change the attributes. */
 	if (iap->ia_valid) {
+		kernel_cap_t	saved_cap;
+
 		iap->ia_valid |= ATTR_CTIME;
 		iap->ia_ctime = CURRENT_TIME;
+		if (current->fsuid != 0) {
+			saved_cap = current->cap_effective;
+			cap_clear(current->cap_effective);
+		}
 		err = notify_change(dentry, iap);
+		if (current->fsuid != 0)
+			current->cap_effective = saved_cap;
 		if (err)
 			goto out_nfserr;
 		if (EX_ISSYNC(fhp->fh_export))
@@ -493,6 +508,9 @@
 	struct inode		*inode;
 	mm_segment_t		oldfs;
 	int			err = 0;
+#ifdef CONFIG_QUOTA
+	uid_t			saved_euid;
+#endif
 
 	if (!cnt)
 		goto out;
@@ -522,16 +540,31 @@
 
 	/* Write the data. */
 	oldfs = get_fs(); set_fs(KERNEL_DS);
+#ifdef CONFIG_QUOTA
+	/* This is for disk quota. */
+	saved_euid = current->euid;
+	current->euid = current->fsuid;
 	err = file.f_op->write(&file, buf, cnt, &file.f_pos);
+	current->euid = saved_euid;
+#else
+	err = file.f_op->write(&file, buf, cnt, &file.f_pos);
+#endif
 	set_fs(oldfs);
 
 	/* clear setuid/setgid flag after write */
 	if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) {
 		struct iattr	ia;
+		kernel_cap_t	saved_cap;
 
 		ia.ia_valid = ATTR_MODE;
 		ia.ia_mode  = inode->i_mode & ~(S_ISUID | S_ISGID);
+		if (current->fsuid != 0) {
+			saved_cap = current->cap_effective;
+			cap_clear(current->cap_effective);
+		}
 		notify_change(dentry, &ia);
+		if (current->fsuid != 0)
+			current->cap_effective = saved_cap;
 	}
 
 	fh_unlock(fhp);			/* unlock inode */
@@ -661,7 +694,14 @@
 		break;
 	case S_IFCHR:
 	case S_IFBLK:
+		/* The client is _NOT_ required to do security enforcement */
+		if(!capable(CAP_SYS_ADMIN))
+		{
+			err = -EPERM;
+			goto out;
+		}
 	case S_IFIFO:
+	case S_IFSOCK:
 		opfunc = dirp->i_op->mknod;
 		break;
 	}
@@ -719,6 +759,7 @@
 	struct inode	*inode;
 	struct iattr	newattrs;
 	int		err;
+	kernel_cap_t	saved_cap;
 
 	err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE | MAY_TRUNC);
 	if (err)
@@ -736,7 +777,13 @@
 	DQUOT_INIT(inode);
 	newattrs.ia_size = size;
 	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+	if (current->fsuid != 0) {
+		saved_cap = current->cap_effective;
+		cap_clear(current->cap_effective);
+	}
 	err = notify_change(dentry, &newattrs);
+	if (current->fsuid != 0)
+		current->cap_effective = saved_cap;
 	if (!err) {
 		vmtruncate(inode, size);
 		if (inode->i_op && inode->i_op->truncate)
@@ -1235,11 +1282,11 @@
 {
 	struct inode	*inode = dentry->d_inode;
 	int		err;
+	kernel_cap_t	saved_cap;
 
 	if (acc == MAY_NOP)
 		return 0;
-
-	/*
+#if 0
 	dprintk("nfsd: permission 0x%x%s%s%s%s%s mode 0%o%s%s%s\n",
 		acc,
 		(acc & MAY_READ)?	" read"  : "",
@@ -1253,8 +1300,7 @@
 		IS_RDONLY(inode)?	" ro" : "");
 	dprintk("      owner %d/%d user %d/%d\n",
 		inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
-	 */
-
+#endif
 #ifndef CONFIG_NFSD_SUN
         if (dentry->d_mounts != dentry) {
 		return nfserr_perm;
@@ -1284,15 +1330,33 @@
 	if (inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */)
 		return 0;
 
+	if (current->fsuid != 0) {
+		saved_cap = current->cap_effective;
+		cap_clear(current->cap_effective);
+	}
+
 	err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
 
 	/* Allow read access to binaries even when mode 111 */
-	if (err == -EPERM && S_ISREG(inode->i_mode) && acc == MAY_READ)
+	if (err == -EACCES && S_ISREG(inode->i_mode) && acc == MAY_READ)
 		err = permission(inode, MAY_EXEC);
 
+	if (current->fsuid != 0)
+		current->cap_effective = saved_cap;
+
 	return err? nfserrno(-err) : 0;
 }
 
+void
+nfsd_racache_shutdown(void)
+{
+	if (!raparm_cache)
+		return;
+	dprintk("nfsd: freeing %d readahead buffers.\n", FILECACHE_MAX);
+	kfree(raparml);
+	nfsd_nservers = 0;
+	raparm_cache = raparml = NULL;
+}
 /*
  * Initialize readahead param cache
  */
@@ -1303,9 +1367,19 @@
 
 	if (raparm_cache)
 		return;
-	memset(raparms, 0, sizeof(raparms));
-	for (i = 0; i < FILECACHE_MAX - 1; i++) {
-		raparms[i].p_next = raparms + i + 1;
+	raparml = kmalloc(sizeof(struct raparms) * FILECACHE_MAX, GFP_KERNEL);
+
+	if (raparml != NULL) {
+		dprintk("nfsd: allocating %d readahead buffers.\n",
+			FILECACHE_MAX);
+		memset(raparml, 0, sizeof(struct raparms) * FILECACHE_MAX);
+		for (i = 0; i < FILECACHE_MAX - 1; i++) {
+			raparml[i].p_next = raparml + i + 1;
+		}
+		raparm_cache = raparml;
+	} else {
+		printk(KERN_WARNING
+		       "nfsd: Could not allocate memory read-ahead cache.\n");
+		nfsd_nservers = 0;
 	}
-	raparm_cache = raparms;
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/open.c linux/fs/open.c
--- v2.2.0-pre1/linux/fs/open.c	Mon Dec 28 15:00:53 1998
+++ linux/fs/open.c	Tue Dec 29 11:40:35 1998
@@ -523,19 +523,27 @@
 	newattrs.ia_gid = group;
 	newattrs.ia_valid =  ATTR_UID | ATTR_GID | ATTR_CTIME;
 	/*
-	 * If the owner has been changed, remove the setuid bit
+	 * If the user or group of a non-directory has been changed by a
+	 * non-root user, remove the setuid bit.
+	 * 19981026	David C Niemi <niemi@tux.org>
+	 *
 	 */
-	if (inode->i_mode & S_ISUID) {
+	if ((inode->i_mode & S_ISUID) == S_ISUID &&
+		!S_ISDIR(inode->i_mode)
+		&& current->fsuid) 
+	{
 		newattrs.ia_mode &= ~S_ISUID;
 		newattrs.ia_valid |= ATTR_MODE;
 	}
 	/*
-	 * If the group has been changed, remove the setgid bit
-	 *
-	 * Don't remove the setgid bit if no group execute bit.
-	 * This is a file marked for mandatory locking.
+	 * Likewise, if the user or group of a non-directory has been changed
+	 * by a non-root user, remove the setgid bit UNLESS there is no group
+	 * execute bit (this would be a file marked for mandatory locking).
+	 * 19981026	David C Niemi <niemi@tux.org>
 	 */
-	if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
+	if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) 
+		&& !S_ISDIR(inode->i_mode) && current->fsuid) 
+	{
 		newattrs.ia_mode &= ~S_ISGID;
 		newattrs.ia_valid |= ATTR_MODE;
 	}
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/sysv/CHANGES linux/fs/sysv/CHANGES
--- v2.2.0-pre1/linux/fs/sysv/CHANGES	Tue Dec 22 14:16:57 1998
+++ linux/fs/sysv/CHANGES	Tue Dec 29 17:31:48 1998
@@ -38,3 +38,8 @@
 			Removed dead code - mknod is never asked to
 			create a symlink or directory. Incidentially,
 			it wouldn't do it right if it would be called.
+
+Sat Dec 26 1998   KGB
+	*    inode.c (detect_sysv4):
+			Added detection of expanded s_type field (0x10,
+			0x20 and 0x30).  Forced read-only access in this case.
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.2.0-pre1/linux/fs/sysv/inode.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/sysv/inode.c	Tue Dec 29 17:31:48 1998
@@ -15,6 +15,7 @@
  *
  *  sysv/inode.c
  *  Copyright (C) 1993  Bruno Haible
+ *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
  *
  *  This file contains code for allocating/freeing inodes and for read/writing
  *  the superblock.
@@ -179,12 +180,25 @@
 		return NULL;
 	if (sbd->s_time < 315532800) /* this is likely to happen on SystemV2 FS */
 		return NULL;
-	if (sbd->s_type > 3 || sbd->s_type < 1)
+	if ((sbd->s_type > 3 || sbd->s_type < 1) && (sbd->s_type > 0x30 || sbd->s_type < 0x10))
 		return NULL;
-	detected_bs(sbd->s_type, sb);
+
+	/* On Interactive Unix (ISC) Version 4.0/3.x s_type field = 0x10,
+	   0x20 or 0x30 indicates that symbolic links and the 14-character
+	   filename limit is gone. Due to lack of information about this
+           feature read-only mode seems to be a reasonable approach... -KGB */
+
+	if (sbd->s_type >= 0x10) {
+		printk("SysV FS: can't handle long file names on %s, "
+		       "forcing read-only mode.\n", kdevname(sb->s_dev));
+		sb->s_flags |= MS_RDONLY;
+	}
+
+	detected_bs(sbd->s_type >= 0x10 ? (sbd->s_type >> 4) : sbd->s_type, sb);
 	sb->sv_type = FSTYPE_SYSV4;
 	return "SystemV";
 }
+
 static struct super_block * detected_sysv4 (struct super_block *sb, struct buffer_head *bh)
 {
 	struct sysv4_super_block * sbd;
diff -u --recursive --new-file v2.2.0-pre1/linux/fs/sysv/namei.c linux/fs/sysv/namei.c
--- v2.2.0-pre1/linux/fs/sysv/namei.c	Tue Dec 22 14:16:57 1998
+++ linux/fs/sysv/namei.c	Tue Dec 29 17:31:48 1998
@@ -9,6 +9,7 @@
  *
  *  sysv/namei.c
  *  Copyright (C) 1993  Bruno Haible
+ *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
  */
 
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-alpha/delay.h linux/include/asm-alpha/delay.h
--- v2.2.0-pre1/linux/include/asm-alpha/delay.h	Fri May  8 23:14:54 1998
+++ linux/include/asm-alpha/delay.h	Tue Dec 29 13:56:15 1998
@@ -1,7 +1,7 @@
 #ifndef __ALPHA_DELAY_H
 #define __ALPHA_DELAY_H
 
-extern unsigned long loops_per_sec;
+#include <asm/smp.h>
 
 /*
  * Copyright (C) 1993 Linus Torvalds
@@ -9,7 +9,8 @@
  * Delay routines, using a pre-computed "loops_per_second" value.
  */
 
-extern __inline__ void __delay(unsigned long loops)
+extern __inline__ void
+__delay(unsigned long loops)
 {
 	__asm__ __volatile__(".align 3\n"
 		"1:\tsubq %0,1,%0\n\t"
@@ -24,15 +25,46 @@
  * lookup table, really, as the multiplications take much too long with
  * short delays.  This is a "reasonable" implementation, though (and the
  * first constant multiplications gets optimized away if the delay is
- * a constant)
+ * a constant).
+ *
+ * Optimize small constants further by exposing the second multiplication
+ * to the compiler.  In addition, mulq is 2 cycles faster than umulh.
  */
-extern __inline__ void udelay(unsigned long usecs)
+
+extern __inline__ void
+__udelay(unsigned long usecs, unsigned long lps)
 {
+	/* compute (usecs * 2**64 / 10**6) * loops_per_sec / 2**64 */
+
 	usecs *= 0x000010c6f7a0b5edUL;		/* 2**64 / 1000000 */
-	__asm__("umulh %1,%2,%0"
-		:"=r" (usecs)
-		:"r" (usecs),"r" (loops_per_sec));
+	__asm__("umulh %1,%2,%0" :"=r" (usecs) :"r" (usecs),"r" (lps));
+	__delay(usecs);
+}
+
+extern __inline__ void
+__small_const_udelay(unsigned long usecs, unsigned long lps)
+{
+	/* compute (usecs * 2**32 / 10**6) * loops_per_sec / 2**32 */
+
+        usecs *= 0x10c6;                /* 2^32 / 10^6 */
+	usecs *= lps;
+	usecs >>= 32;
 	__delay(usecs);
 }
+
+#ifdef __SMP__
+#define udelay(usecs)						\
+	(__builtin_constant_p(usecs) && usecs < 0x100000000UL	\
+	 ? __small_const_udelay(usecs,				\
+		cpu_data[smp_processor_id()].loops_per_sec)	\
+	 : __udelay(usecs,					\
+		cpu_data[smp_processor_id()].loops_per_sec))
+#else
+#define udelay(usecs)						\
+	(__builtin_constant_p(usecs) && usecs < 0x100000000UL	\
+	 ? __small_const_udelay(usecs, loops_per_sec)		\
+	 : __udelay(usecs, loops_per_sec))
+#endif
+
 
 #endif /* defined(__ALPHA_DELAY_H) */
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-alpha/init.h linux/include/asm-alpha/init.h
--- v2.2.0-pre1/linux/include/asm-alpha/init.h	Tue Aug 18 22:02:07 1998
+++ linux/include/asm-alpha/init.h	Tue Dec 29 13:56:15 1998
@@ -12,4 +12,6 @@
 #define __FINIT		.previous
 #define __INITDATA	.section	.data.init,"a"
 
+#define __cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES)))
+
 #endif
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-alpha/irq.h linux/include/asm-alpha/irq.h
--- v2.2.0-pre1/linux/include/asm-alpha/irq.h	Tue Dec 22 14:16:57 1998
+++ linux/include/asm-alpha/irq.h	Tue Dec 29 13:56:15 1998
@@ -93,6 +93,7 @@
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
+struct pt_regs;
 extern void (*perf_irq)(unsigned long, struct pt_regs *);
 
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h
--- v2.2.0-pre1/linux/include/asm-alpha/pgtable.h	Mon Dec 28 15:00:53 1998
+++ linux/include/asm-alpha/pgtable.h	Tue Dec 29 13:56:15 1998
@@ -175,7 +175,7 @@
 		struct vm_area_struct *	flush_vma;
 	} p;
 	unsigned long flush_addr;
-  /*	unsigned long flush_end; */ /* not used by local_flush_tlb_range */
+	unsigned long flush_end;
 };
 
 extern struct ipi_msg_flush_tb_struct ipi_msg_flush_tb;
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-alpha/smp.h linux/include/asm-alpha/smp.h
--- v2.2.0-pre1/linux/include/asm-alpha/smp.h	Wed Sep  9 14:51:10 1998
+++ linux/include/asm-alpha/smp.h	Tue Dec 29 13:56:15 1998
@@ -12,7 +12,8 @@
 	unsigned long *pgd_cache;
 	unsigned long *pte_cache;
 	unsigned long pgtable_cache_sz;
-};
+	unsigned long ipi_count;
+} __attribute__((aligned(32)));
 
 extern struct cpuinfo_alpha cpu_data[NR_CPUS];
 
@@ -35,9 +36,6 @@
 
 #define smp_processor_id()	(current->processor)
 #define cpu_logical_map(cpu)	(cpu)
-
-/* For the benefit of panic.  */
-void smp_message_pass(int target, int msg, unsigned long data, int wait);
 
 #endif /* __SMP__ */
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-alpha/system.h linux/include/asm-alpha/system.h
--- v2.2.0-pre1/linux/include/asm-alpha/system.h	Tue Dec 22 14:16:57 1998
+++ linux/include/asm-alpha/system.h	Tue Dec 29 13:56:15 1998
@@ -244,14 +244,14 @@
 
 	__asm__ __volatile__(
 	"1:	ldl_l %0,%2\n"
-	"	bis %3,%3,%1\n"
+	"	bis $31,%3,%1\n"
 	"	stl_c %1,%2\n"
 	"	beq %1,2f\n"
 	".section .text2,\"ax\"\n"
 	"2:	br 1b\n"
 	".previous"
 	: "=&r" (val), "=&r" (dummy), "=m" (*m)
-	: "r" (val), "m" (*m));
+	: "rI" (val), "m" (*m));
 
 	return val;
 }
@@ -262,14 +262,14 @@
 
 	__asm__ __volatile__(
 	"1:	ldq_l %0,%2\n"
-	"	bis %3,%3,%1\n"
+	"	bis $31,%3,%1\n"
 	"	stq_c %1,%2\n"
 	"	beq %1,2f\n"
 	".section .text2,\"ax\"\n"
 	"2:	br 1b\n"
 	".previous"
 	: "=&r" (val), "=&r" (dummy), "=m" (*m)
-	: "r" (val), "m" (*m));
+	: "rI" (val), "m" (*m));
 
 	return val;
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-alpha/timex.h linux/include/asm-alpha/timex.h
--- v2.2.0-pre1/linux/include/asm-alpha/timex.h	Fri May  8 23:14:54 1998
+++ linux/include/asm-alpha/timex.h	Tue Dec 29 13:56:15 1998
@@ -8,4 +8,23 @@
 
 #define CLOCK_TICK_RATE	1193180 /* Underlying HZ */
 
+/*
+ * Standard way to access the cycle counter.
+ * Currently only used on SMP for scheduling.
+ *
+ * Only the low 32 bits are available as a continuously counting entity. 
+ * But this only means we'll force a reschedule every 8 seconds or so,
+ * which isn't an evil thing.
+ */
+
+typedef unsigned int cycles_t;
+extern cycles_t cacheflush_time;
+
+static inline cycles_t get_cycles (void)
+{
+	cycles_t ret;
+	__asm__ __volatile__ ("rpcc %0" : "=r"(ret));
+	return ret;
+}
+
 #endif
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- v2.2.0-pre1/linux/include/asm-i386/bugs.h	Mon Oct  5 13:13:42 1998
+++ linux/include/asm-i386/bugs.h	Wed Dec 30 21:58:13 1998
@@ -312,10 +312,55 @@
 	}
 }
 
+/*
+ * Check wether we are able to run this kernel safely with this
+ * configuration.  Various configs imply certain minimum requirements
+ * of the machine:
+ *
+ * - In order to run on a i386, we need to be compiled for i386
+ *   (for due to lack of "invlpg" and working WP on a i386)
+ * - In order to run on anything without a TSC, we need to be
+ *   compiled for a i486.
+ * - In order to work on a Pentium/SMP machine, we need to be
+ *   compiled for a Pentium or lower, as a PPro config implies
+ *   a properly working local APIC without the need to do extra
+ *   reads from the APIC.
+ */
+__initfunc(static void check_config(void))
+{
+	/* Configuring for a i386 will boot on anything */
+#ifndef CONFIG_M386
+	/* Configuring for an i486 only implies 'invlpg' and a working WP bit */
+	if (boot_cpu_data.x86 == 3)
+		panic("Kernel requires i486+ for 'invlpg' and other features");
+
+#ifndef CONFIG_M486
+
+#ifndef CONFIG_M586
+	/* Configuring for a PPro implies that we have an IO-APIC without the read-before-write bug */
+
+#endif	/* CONFIG_M586 */
+#endif	/* CONFIG_M486 */
+#endif	/* CONFIG_M386 */
+
+/* If we configured ourselves for a TSC, we'd better have one! */
+#ifdef CONFIG_TSC
+	if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC))
+		panic("Kernel compiled for Pentium+, requires TSC");
+#endif
+
+/* If we were told we had a good APIC for SMP, we'd better be a PPro */
+#ifdef CONFIG_GOOD_APIC
+	if (smp_found_config && boot_cpu_data.x86 <= 5)
+		panic("Kernel compiled for PPro+, assumes local APIC without read-before-write bug");
+#endif
+}
+
 __initfunc(static void check_bugs(void))
 {
 	check_cyrix_cpu();
 	identify_cpu(&boot_cpu_data);
+	check_config();
 #ifndef __SMP__
 	printk("CPU: ");
 	print_cpu_info(&boot_cpu_data);
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/byteorder.h linux/include/asm-i386/byteorder.h
--- v2.2.0-pre1/linux/include/asm-i386/byteorder.h	Fri Jul 31 17:05:52 1998
+++ linux/include/asm-i386/byteorder.h	Wed Dec 30 14:42:47 1998
@@ -12,7 +12,7 @@
 
 static __inline__ __const__ __u32 ___arch__swab32(__u32 x)
 {
-#if defined(__KERNEL__) && !defined(CONFIG_M386)
+#ifdef CONFIG_BSWAP
 	__asm__("bswap %0" : "=r" (x) : "0" (x));
 #else
 	__asm__("xchgb %b0,%h0\n\t"	/* swap lower bytes	*/
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/elf.h linux/include/asm-i386/elf.h
--- v2.2.0-pre1/linux/include/asm-i386/elf.h	Wed Jun 24 22:54:10 1998
+++ linux/include/asm-i386/elf.h	Wed Dec 30 11:34:24 1998
@@ -32,8 +32,16 @@
    This provides a mean for the dynamic linker to call DT_FINI functions for
    shared libraries that have been loaded before the code runs.
 
-   A value of 0 tells we have no such handler.  */
-#define ELF_PLAT_INIT(_r)	_r->edx = 0
+   A value of 0 tells we have no such handler. 
+
+   We might as well make sure everything else is cleared too (except for %esp),
+   just to make things more deterministic.
+ */
+#define ELF_PLAT_INIT(_r)	do { \
+	_r->ebx = 0; _r->ecx = 0; _r->edx = 0; \
+	_r->esi = 0; _r->edi = 0; _r->ebp = 0; \
+	_r->eax = 0; \
+} while (0)
 
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	4096
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v2.2.0-pre1/linux/include/asm-i386/pgtable.h	Wed Sep  9 14:51:12 1998
+++ linux/include/asm-i386/pgtable.h	Wed Dec 30 21:58:13 1998
@@ -41,7 +41,7 @@
 #define __flush_tlb() \
 do { unsigned long tmpreg; __asm__ __volatile__("movl %%cr3,%0\n\tmovl %0,%%cr3":"=r" (tmpreg) : :"memory"); } while (0)
 
-#ifdef CONFIG_M386
+#ifndef CONFIG_INVLPG
 #define __flush_tlb_one(addr) flush_tlb()
 #else
 #define __flush_tlb_one(addr) \
@@ -218,18 +218,16 @@
  * memory. 
  */
 #define _PAGE_PRESENT	0x001
-#define _PAGE_PROTNONE	0x002		/* If not present */
-#define _PAGE_RW	0x002		/* If present */
+#define _PAGE_RW	0x002
 #define _PAGE_USER	0x004
 #define _PAGE_WT	0x008
 #define _PAGE_PCD	0x010
 #define _PAGE_ACCESSED	0x020
 #define _PAGE_DIRTY	0x040
-#define _PAGE_4M	0x080	/* 4 MB page, Pentium+.. */
+#define _PAGE_4M	0x080	/* 4 MB page, Pentium+, if present.. */
 #define _PAGE_GLOBAL	0x100	/* Global TLB entry PPro+ */
 
-#define _PAGE_READABLE  (_PAGE_PRESENT)             
-#define _PAGE_WRITABLE  (_PAGE_PRESENT | _PAGE_RW)
+#define _PAGE_PROTNONE	0x080	/* If not present */
 
 #define _PAGE_TABLE	(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define _KERNPG_TABLE	(_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
@@ -340,21 +338,17 @@
 extern inline int pte_exec(pte_t pte)		{ return pte_val(pte) & _PAGE_USER; }
 extern inline int pte_dirty(pte_t pte)		{ return pte_val(pte) & _PAGE_DIRTY; }
 extern inline int pte_young(pte_t pte)		{ return pte_val(pte) & _PAGE_ACCESSED; }
+extern inline int pte_write(pte_t pte)		{ return pte_val(pte) & _PAGE_RW; }
 
 extern inline pte_t pte_rdprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
 extern inline pte_t pte_exprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
 extern inline pte_t pte_mkclean(pte_t pte)	{ pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
 extern inline pte_t pte_mkold(pte_t pte)	{ pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
+extern inline pte_t pte_wrprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_RW; return pte; }
 extern inline pte_t pte_mkread(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
 extern inline pte_t pte_mkexec(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
 extern inline pte_t pte_mkdirty(pte_t pte)	{ pte_val(pte) |= _PAGE_DIRTY; return pte; }
 extern inline pte_t pte_mkyoung(pte_t pte)	{ pte_val(pte) |= _PAGE_ACCESSED; return pte; }
-
-/*
- * These are harder, as writability is two bits, not one..
- */
-extern inline int pte_write(pte_t pte)		{ return (pte_val(pte) & _PAGE_WRITABLE) == _PAGE_WRITABLE; }
-extern inline pte_t pte_wrprotect(pte_t pte)	{ pte_val(pte) &= ~((pte_val(pte) & _PAGE_PRESENT) << 1); return pte; }
 extern inline pte_t pte_mkwrite(pte_t pte)	{ pte_val(pte) |= _PAGE_RW; return pte; }
 
 /*
@@ -589,9 +583,9 @@
 {
 }
 
-#define SWP_TYPE(entry) (((entry) >> 2) & 0x3f)
+#define SWP_TYPE(entry) (((entry) >> 1) & 0x3f)
 #define SWP_OFFSET(entry) ((entry) >> 8)
-#define SWP_ENTRY(type,offset) (((type) << 2) | ((offset) << 8))
+#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
 
 #define module_map      vmalloc
 #define module_unmap    vfree
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v2.2.0-pre1/linux/include/asm-i386/smp.h	Mon Dec 28 15:00:53 1998
+++ linux/include/asm-i386/smp.h	Wed Dec 30 21:58:13 1998
@@ -159,6 +159,7 @@
 extern unsigned long smp_alloc_memory(unsigned long mem_base);
 extern unsigned char boot_cpu_id;
 extern unsigned long cpu_present_map;
+extern unsigned long cpu_online_map;
 extern volatile int cpu_number_map[NR_CPUS];
 extern volatile unsigned long smp_invalidate_needed;
 extern void smp_flush_tlb(void);
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v2.2.0-pre1/linux/include/asm-i386/string.h	Wed Jun 24 22:54:10 1998
+++ linux/include/asm-i386/string.h	Tue Dec 29 11:18:18 1998
@@ -30,20 +30,22 @@
 #define __HAVE_ARCH_STRCPY
 extern inline char * strcpy(char * dest,const char *src)
 {
+int d0, d1, d2;
 __asm__ __volatile__(
 	"cld\n"
 	"1:\tlodsb\n\t"
 	"stosb\n\t"
 	"testb %%al,%%al\n\t"
 	"jne 1b"
-	: /* no output */
-	:"S" (src),"D" (dest):"si","di","ax","memory");
+	: "=&S" (d0), "=&D" (d1), "=&a" (d2)
+	:"0" (src),"1" (dest) : "memory");
 return dest;
 }
 
 #define __HAVE_ARCH_STRNCPY
 extern inline char * strncpy(char * dest,const char *src,size_t count)
 {
+int d0, d1, d2, d3;
 __asm__ __volatile__(
 	"cld\n"
 	"1:\tdecl %2\n\t"
@@ -55,14 +57,15 @@
 	"rep\n\t"
 	"stosb\n"
 	"2:"
-	: /* no output */
-	:"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory");
+	: "=&S" (d0), "=&D" (d1), "=&c" (d2), "=&a" (d3)
+	:"0" (src),"1" (dest),"2" (count) : "memory");
 return dest;
 }
 
 #define __HAVE_ARCH_STRCAT
 extern inline char * strcat(char * dest,const char * src)
 {
+int d0, d1, d2, d3;
 __asm__ __volatile__(
 	"cld\n\t"
 	"repne\n\t"
@@ -72,20 +75,21 @@
 	"stosb\n\t"
 	"testb %%al,%%al\n\t"
 	"jne 1b"
-	: /* no output */
-	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx");
+	: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
+	: "0" (src), "1" (dest), "2" (0), "3" (0xffffffff):"memory");
 return dest;
 }
 
 #define __HAVE_ARCH_STRNCAT
 extern inline char * strncat(char * dest,const char * src,size_t count)
 {
+int d0, d1, d2, d3;
 __asm__ __volatile__(
 	"cld\n\t"
 	"repne\n\t"
 	"scasb\n\t"
 	"decl %1\n\t"
-	"movl %4,%3\n"
+	"movl %8,%3\n"
 	"1:\tdecl %3\n\t"
 	"js 2f\n\t"
 	"lodsb\n\t"
@@ -94,15 +98,16 @@
 	"jne 1b\n"
 	"2:\txorl %2,%2\n\t"
 	"stosb"
-	: /* no output */
-	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
-	:"si","di","ax","cx","memory");
+	: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
+	: "0" (src),"1" (dest),"2" (0),"3" (0xffffffff), "g" (count)
+	: "memory");
 return dest;
 }
 
 #define __HAVE_ARCH_STRCMP
 extern inline int strcmp(const char * cs,const char * ct)
 {
+int d0, d1;
 register int __res;
 __asm__ __volatile__(
 	"cld\n"
@@ -116,7 +121,8 @@
 	"2:\tsbbl %%eax,%%eax\n\t"
 	"orb $1,%%al\n"
 	"3:"
-	:"=a" (__res):"S" (cs),"D" (ct):"si","di");
+	:"=a" (__res), "=&S" (d0), "=&D" (d1)
+		     :"1" (cs),"2" (ct));
 return __res;
 }
 
@@ -124,6 +130,7 @@
 extern inline int strncmp(const char * cs,const char * ct,size_t count)
 {
 register int __res;
+int d0, d1, d2;
 __asm__ __volatile__(
 	"cld\n"
 	"1:\tdecl %3\n\t"
@@ -138,13 +145,15 @@
 	"3:\tsbbl %%eax,%%eax\n\t"
 	"orb $1,%%al\n"
 	"4:"
-	:"=a" (__res):"S" (cs),"D" (ct),"c" (count):"si","di","cx");
+		     :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
+		     :"1" (cs),"2" (ct),"3" (count));
 return __res;
 }
 
 #define __HAVE_ARCH_STRCHR
 extern inline char * strchr(const char * s, int c)
 {
+int d0;
 register char * __res;
 __asm__ __volatile__(
 	"cld\n\t"
@@ -157,13 +166,14 @@
 	"movl $1,%1\n"
 	"2:\tmovl %1,%0\n\t"
 	"decl %0"
-	:"=a" (__res):"S" (s),"0" (c):"si");
+	:"=a" (__res), "=&S" (d0) : "1" (s),"0" (c));
 return __res;
 }
 
 #define __HAVE_ARCH_STRRCHR
 extern inline char * strrchr(const char * s, int c)
 {
+int d0, d1;
 register char * __res;
 __asm__ __volatile__(
 	"cld\n\t"
@@ -174,104 +184,24 @@
 	"leal -1(%%esi),%0\n"
 	"2:\ttestb %%al,%%al\n\t"
 	"jne 1b"
-	:"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
-return __res;
-}
-
-#define __HAVE_ARCH_STRSPN
-extern inline size_t strspn(const char * cs, const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 1b\n"
-	"2:\tdecl %0"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res-cs;
-}
-
-#define __HAVE_ARCH_STRCSPN
-extern inline size_t strcspn(const char * cs, const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 1b\n"
-	"2:\tdecl %0"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res-cs;
-}
-
-#define __HAVE_ARCH_STRPBRK
-extern inline char * strpbrk(const char * cs,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 1b\n\t"
-	"decl %0\n\t"
-	"jmp 3f\n"
-	"2:\txorl %0,%0\n"
-	"3:"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
+	:"=g" (__res), "=&S" (d0), "=&a" (d1) :"0" (0),"1" (s),"2" (c));
 return __res;
 }
 
 #define __HAVE_ARCH_STRSTR
 extern inline char * strstr(const char * cs,const char * ct)
 {
+int d0, d1, d2, d3;
 register char * __res;
 __asm__ __volatile__(
 	"cld\n\t" \
-	"movl %4,%%edi\n\t"
+	"movl %8,%%edi\n\t"
 	"repne\n\t"
 	"scasb\n\t"
 	"notl %%ecx\n\t"
 	"decl %%ecx\n\t"	/* NOTE! This also sets Z if searchstring='' */
 	"movl %%ecx,%%edx\n"
-	"1:\tmovl %4,%%edi\n\t"
+	"1:\tmovl %8,%%edi\n\t"
 	"movl %%esi,%%eax\n\t"
 	"movl %%edx,%%ecx\n\t"
 	"repe\n\t"
@@ -283,14 +213,14 @@
 	"jne 1b\n\t"
 	"xorl %%eax,%%eax\n\t"
 	"2:"
-	:"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
-	:"cx","dx","di","si");
+	:"=a" (__res), "=&c" (d0), "=&S" (d1), "=&d" (d2), "=&D" (d3) : "0" (0),"1" (0xffffffff),"2" (cs),"g" (ct));
 return __res;
 }
 
 #define __HAVE_ARCH_STRLEN
 extern inline size_t strlen(const char * s)
 {
+int d0;
 register int __res;
 __asm__ __volatile__(
 	"cld\n\t"
@@ -298,86 +228,26 @@
 	"scasb\n\t"
 	"notl %0\n\t"
 	"decl %0"
-	:"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di");
-return __res;
-}
-
-#define __HAVE_ARCH_STRTOK
-extern inline char * strtok(char * s,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"testl %1,%1\n\t"
-	"jne 1f\n\t"
-	"testl %0,%0\n\t"
-	"je 8f\n\t"
-	"movl %0,%1\n"
-	"1:\txorl %0,%0\n\t"
-	"movl $-1,%%ecx\n\t"
-	"xorl %%eax,%%eax\n\t"
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"je 7f\n\t"			/* empty delimiter-string */
-	"movl %%ecx,%%edx\n"
-	"2:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 7f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 2b\n\t"
-	"decl %1\n\t"
-	"cmpb $0,(%1)\n\t"
-	"je 7f\n\t"
-	"movl %1,%0\n"
-	"3:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 5f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 3b\n\t"
-	"decl %1\n\t"
-	"cmpb $0,(%1)\n\t"
-	"je 5f\n\t"
-	"movb $0,(%1)\n\t"
-	"incl %1\n\t"
-	"jmp 6f\n"
-	"5:\txorl %1,%1\n"
-	"6:\tcmpb $0,(%0)\n\t"
-	"jne 7f\n\t"
-	"xorl %0,%0\n"
-	"7:\ttestl %0,%0\n\t"
-	"jne 8f\n\t"
-	"movl %0,%1\n"
-	"8:"
-	:"=b" (__res),"=S" (___strtok)
-	:"0" (___strtok),"1" (s),"g" (ct)
-	:"ax","cx","dx","di","memory");
+	:"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff));
 return __res;
 }
 
 extern inline void * __memcpy(void * to, const void * from, size_t n)
 {
+int d0, d1, d2;
 __asm__ __volatile__(
 	"cld\n\t"
 	"rep ; movsl\n\t"
-	"testb $2,%b1\n\t"
+	"testb $2,%b4\n\t"
 	"je 1f\n\t"
 	"movsw\n"
-	"1:\ttestb $1,%b1\n\t"
+	"1:\ttestb $1,%b4\n\t"
 	"je 2f\n\t"
 	"movsb\n"
 	"2:"
-	: /* no output */
-	:"c" (n/4), "q" (n),"D" ((long) to),"S" ((long) from)
-	: "cx","di","si","memory");
+	: "=&c" (d0), "=&D" (d1), "=&S" (d2)
+	:"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
+	: "memory");
 return (to);
 }
 
@@ -431,19 +301,23 @@
 			return to;
 	}
 #define COMMON(x) \
-__asm__("cld\n\t" \
+__asm__ __volatile__( \
+	"cld\n\t" \
 	"rep ; movsl" \
 	x \
-	: /* no outputs */ \
-	: "c" (n/4),"D" ((long) to),"S" ((long) from) \
-	: "cx","di","si","memory");
-
+	: "=&c" (d0), "=&D" (d1), "=&S" (d2) \
+	: "0" (n/4),"1" ((long) to),"2" ((long) from) \
+	: "memory");
+{
+	int d0, d1, d2;
 	switch (n % 4) {
 		case 0: COMMON(""); return to;
 		case 1: COMMON("\n\tmovsb"); return to;
 		case 2: COMMON("\n\tmovsw"); return to;
 		default: COMMON("\n\tmovsw\n\tmovsb"); return to;
 	}
+}
+  
 #undef COMMON
 }
 
@@ -456,25 +330,26 @@
 #define __HAVE_ARCH_MEMMOVE
 extern inline void * memmove(void * dest,const void * src, size_t n)
 {
+int d0, d1, d2;
 if (dest<src)
 __asm__ __volatile__(
 	"cld\n\t"
 	"rep\n\t"
 	"movsb"
-	: /* no output */
-	:"c" (n),"S" (src),"D" (dest)
-	:"cx","si","di");
+	: "=&c" (d0), "=&S" (d1), "=&D" (d2)
+	:"0" (n),"1" (src),"2" (dest)
+	: "memory");
 else
 __asm__ __volatile__(
 	"std\n\t"
 	"rep\n\t"
 	"movsb\n\t"
 	"cld"
-	: /* no output */
-	:"c" (n),
-	 "S" (n-1+(const char *)src),
-	 "D" (n-1+(char *)dest)
-	:"cx","si","di","memory");
+	: "=&c" (d0), "=&S" (d1), "=&D" (d2)
+	:"0" (n),
+	 "1" (n-1+(const char *)src),
+	 "2" (n-1+(char *)dest)
+	:"memory");
 return dest;
 }
 
@@ -483,6 +358,7 @@
 #define __HAVE_ARCH_MEMCHR
 extern inline void * memchr(const void * cs,int c,size_t count)
 {
+int d0;
 register void * __res;
 if (!count)
 	return NULL;
@@ -493,20 +369,20 @@
 	"je 1f\n\t"
 	"movl $1,%0\n"
 	"1:\tdecl %0"
-	:"=D" (__res):"a" (c),"D" (cs),"c" (count)
-	:"cx");
+	:"=D" (__res), "=&c" (d0) : "a" (c),"0" (cs),"1" (count));
 return __res;
 }
 
 extern inline void * __memset_generic(void * s, char c,size_t count)
 {
+int d0, d1;
 __asm__ __volatile__(
 	"cld\n\t"
 	"rep\n\t"
 	"stosb"
-	: /* no output */
-	:"a" (c),"D" (s),"c" (count)
-	:"cx","di","memory");
+	: "=&c" (d0), "=&D" (d1)
+	:"a" (c),"1" (s),"0" (count)
+	:"memory");
 return s;
 }
 
@@ -520,19 +396,20 @@
  */
 extern inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
 {
+int d0, d1;
 __asm__ __volatile__(
 	"cld\n\t"
 	"rep ; stosl\n\t"
-	"testb $2,%b1\n\t"
+	"testb $2,%b3\n\t"
 	"je 1f\n\t"
 	"stosw\n"
-	"1:\ttestb $1,%b1\n\t"
+	"1:\ttestb $1,%b3\n\t"
 	"je 2f\n\t"
 	"stosb\n"
 	"2:"
-	: /* no output */
-	:"a" (c), "q" (count), "c" (count/4), "D" ((long) s)
-	:"cx","di","memory");
+	: "=&c" (d0), "=&D" (d1)
+	:"a" (c), "q" (count), "0" (count/4), "1" ((long) s)
+	:"memory");
 return (s);	
 }
 
@@ -540,20 +417,20 @@
 #define __HAVE_ARCH_STRNLEN
 extern inline size_t strnlen(const char * s, size_t count)
 {
+int d0;
 register int __res;
 __asm__ __volatile__(
-	"movl %1,%0\n\t"
+	"movl %2,%0\n\t"
 	"jmp 2f\n"
 	"1:\tcmpb $0,(%0)\n\t"
 	"je 3f\n\t"
 	"incl %0\n"
-	"2:\tdecl %2\n\t"
-	"cmpl $-1,%2\n\t"
+	"2:\tdecl %1\n\t"
+	"cmpl $-1,%1\n\t"
 	"jne 1b\n"
-	"3:\tsubl %1,%0"
-	:"=a" (__res)
-	:"c" (s),"d" (count)
-	:"dx");
+	"3:\tsubl %2,%0"
+	:"=a" (__res), "=&d" (d0)
+	:"c" (s),"1" (count));
 return __res;
 }
 /* end of additional stuff */
@@ -582,19 +459,22 @@
 			return s;
 	}
 #define COMMON(x) \
-__asm__("cld\n\t" \
+__asm__  __volatile__("cld\n\t" \
 	"rep ; stosl" \
 	x \
-	: /* no outputs */ \
-	: "a" (pattern),"c" (count/4),"D" ((long) s) \
-	: "cx","di","memory")
-
+	: "=&c" (d0), "=&D" (d1) \
+	: "a" (pattern),"0" (count/4),"1" ((long) s) \
+	: "memory")
+{
+	int d0, d1;
 	switch (count % 4) {
 		case 0: COMMON(""); return s;
 		case 1: COMMON("\n\tstosb"); return s;
 		case 2: COMMON("\n\tstosw"); return s;
 		default: COMMON("\n\tstosw\n\tstosb"); return s;
 	}
+}
+  
 #undef COMMON
 }
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/timex.h linux/include/asm-i386/timex.h
--- v2.2.0-pre1/linux/include/asm-i386/timex.h	Mon Dec 28 15:00:53 1998
+++ linux/include/asm-i386/timex.h	Wed Dec 30 21:58:13 1998
@@ -6,6 +6,8 @@
 #ifndef _ASMi386_TIMEX_H
 #define _ASMi386_TIMEX_H
 
+#include <linux/config.h>
+
 #define CLOCK_TICK_RATE	1193180 /* Underlying HZ */
 #define CLOCK_TICK_FACTOR	20	/* Factor of both 1000000 and CLOCK_TICK_RATE */
 #define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
@@ -32,7 +34,7 @@
 
 static inline cycles_t get_cycles (void)
 {
-#if CPU < 586
+#ifndef CONFIG_TSC
 	return 0;
 #else
 	unsigned long eax, edx;
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.2.0-pre1/linux/include/asm-i386/uaccess.h	Mon Dec 28 15:00:53 1998
+++ linux/include/asm-i386/uaccess.h	Wed Dec 30 22:00:23 1998
@@ -4,6 +4,7 @@
 /*
  * User space memory access functions
  */
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <asm/page.h>
 
@@ -44,7 +45,7 @@
 		:"1" (addr),"g" (size),"g" (current->addr_limit.seg)); \
 	flag; })
 
-#if CPU > 386
+#ifdef CONFIG_WP_WORKS_OK
 
 #define access_ok(type,addr,size) (__range_ok(addr,size) == 0)
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/checksum.h linux/include/asm-ppc/checksum.h
--- v2.2.0-pre1/linux/include/asm-ppc/checksum.h	Sat May  2 14:19:54 1998
+++ linux/include/asm-ppc/checksum.h	Wed Dec 30 10:55:07 1998
@@ -83,13 +83,13 @@
 						   unsigned int sum) 
 {
     __asm__("
-	add %0,%0,%1
-	add %0,%0,%2
-	add %0,%0,%0
-	addi %0,%0,0
+	addc %0,%0,%1
+	adde %0,%0,%2
+	adde %0,%0,%3
+	addze %0,%0
 	"
 	: "=r" (sum)
-	: "r" (daddr), "r"(saddr), "r"((ntohs(len)<<16)+proto*256), "0"(sum));
+	: "r" (daddr), "r"(saddr), "r"((proto<<16)+len), "0"(sum));
     return sum;
 }
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/ide.h linux/include/asm-ppc/ide.h
--- v2.2.0-pre1/linux/include/asm-ppc/ide.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/ide.h	Tue Dec 29 11:14:57 1998
@@ -200,14 +200,54 @@
 		id->word79         = __le16_to_cpu(id->word79);
 		id->word80         = __le16_to_cpu(id->word80);
 		id->word81         = __le16_to_cpu(id->word81);
-		id->word82         = __le16_to_cpu(id->word82);
+		id->command_sets   = __le16_to_cpu(id->command_sets);
 		id->word83         = __le16_to_cpu(id->word83);
 		id->word84         = __le16_to_cpu(id->word84);
 		id->word85         = __le16_to_cpu(id->word85);
 		id->word86         = __le16_to_cpu(id->word86);
 		id->word87         = __le16_to_cpu(id->word87);
 		id->dma_ultra      = __le16_to_cpu(id->dma_ultra);
-		for (i=0; i<167; i++)
+		id->word89         = __le16_to_cpu(id->word89);
+		id->word90         = __le16_to_cpu(id->word90);
+		id->word91         = __le16_to_cpu(id->word91);
+		id->word92         = __le16_to_cpu(id->word92);
+		id->word93         = __le16_to_cpu(id->word93);
+		id->word94         = __le16_to_cpu(id->word94);
+		id->word95         = __le16_to_cpu(id->word95);
+		id->word96         = __le16_to_cpu(id->word96);
+		id->word97         = __le16_to_cpu(id->word97);
+		id->word98         = __le16_to_cpu(id->word98);
+		id->word99         = __le16_to_cpu(id->word99);
+		id->word100        = __le16_to_cpu(id->word100);
+		id->word101        = __le16_to_cpu(id->word101);
+		id->word102        = __le16_to_cpu(id->word102);
+		id->word103        = __le16_to_cpu(id->word103);
+		id->word104        = __le16_to_cpu(id->word104);
+		id->word105        = __le16_to_cpu(id->word105);
+		id->word106        = __le16_to_cpu(id->word106);
+		id->word107        = __le16_to_cpu(id->word107);
+		id->word108        = __le16_to_cpu(id->word108);
+		id->word109        = __le16_to_cpu(id->word109);
+		id->word110        = __le16_to_cpu(id->word110);
+		id->word111        = __le16_to_cpu(id->word111);
+		id->word112        = __le16_to_cpu(id->word112);
+		id->word113        = __le16_to_cpu(id->word113);
+		id->word114        = __le16_to_cpu(id->word114);
+		id->word115        = __le16_to_cpu(id->word115);
+		id->word116        = __le16_to_cpu(id->word116);
+		id->word117        = __le16_to_cpu(id->word117);
+		id->word118        = __le16_to_cpu(id->word118);
+		id->word119        = __le16_to_cpu(id->word119);
+		id->word120        = __le16_to_cpu(id->word120);
+		id->word121        = __le16_to_cpu(id->word121);
+		id->word122        = __le16_to_cpu(id->word122);
+		id->word123        = __le16_to_cpu(id->word123);
+		id->word124        = __le16_to_cpu(id->word124);
+		id->word125        = __le16_to_cpu(id->word125);
+		id->word126        = __le16_to_cpu(id->word126);
+		id->word127        = __le16_to_cpu(id->word127);
+		id->security       = __le16_to_cpu(id->security);
+		for (i=0; i<127; i++)
 			id->reserved[i] = __le16_to_cpu(id->reserved[i]);
 	}
 }
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/init.h linux/include/asm-ppc/init.h
--- v2.2.0-pre1/linux/include/asm-ppc/init.h	Fri May  8 23:14:56 1998
+++ linux/include/asm-ppc/init.h	Wed Dec 30 10:56:58 1998
@@ -31,6 +31,9 @@
 #define __FINIT	.previous
 #define __INITDATA	.section	".data.init",#alloc,#write
 
+#define __cacheline_aligned __attribute__ \
+			 ((__section__ (".data.cacheline_aligned")))
+
 #else /* not egcs */
 
 #define __init
@@ -52,5 +55,7 @@
 #define __openfirmware
 #define __openfirmwaredata
 #define __openfirmwarefunc(x) x
+
+#define __cacheline_aligned
 #endif /* egcs */
 #endif
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/pgtable.h linux/include/asm-ppc/pgtable.h
--- v2.2.0-pre1/linux/include/asm-ppc/pgtable.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/pgtable.h	Wed Dec 30 10:55:07 1998
@@ -121,6 +121,7 @@
 #define _PAGE_DIRTY	0x080	/* C: page changed */
 #define _PAGE_ACCESSED	0x100	/* R: page referenced */
 #define _PAGE_HWWRITE	0x200	/* software: _PAGE_RW & _PAGE_DIRTY */
+#define _PAGE_SHARED	0
 
 #else
 #define _PAGE_PRESENT	0x0001	/* Page is valid */
@@ -147,28 +148,23 @@
 
 #define _PAGE_CHG_MASK	(PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
 
+#ifdef __SMP__
+#define _PAGE_BASE	_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT
+#else
+#define _PAGE_BASE	_PAGE_PRESENT | _PAGE_ACCESSED
+#endif
+#define _PAGE_WRENABLE	_PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE
+
 #define PAGE_NONE	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
 
-#ifndef CONFIG_8xx
-#define PAGE_SHARED	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
-				 _PAGE_ACCESSED)
-#define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_READONLY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | \
-				 _PAGE_HWWRITE | _PAGE_ACCESSED)
-#define PAGE_KERNEL_CI	__pgprot(_PAGE_PRESENT | _PAGE_NO_CACHE | _PAGE_RW | \
-				 _PAGE_HWWRITE | _PAGE_DIRTY | _PAGE_ACCESSED)
-#else
-#define PAGE_SHARED	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
-				 _PAGE_ACCESSED | _PAGE_SHARED)
-#define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_READONLY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | \
-				 _PAGE_SHARED | _PAGE_ACCESSED)
-#define PAGE_KERNEL_CI	__pgprot(_PAGE_PRESENT | _PAGE_NO_CACHE | _PAGE_RW | \
-				 _PAGE_SHARED | _PAGE_DIRTY | _PAGE_ACCESSED)
-#endif /* CONFIG_8xx */
-     
+#define PAGE_SHARED	__pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER | \
+				 _PAGE_SHARED)
+#define PAGE_COPY	__pgprot(_PAGE_BASE | _PAGE_USER)
+#define PAGE_READONLY	__pgprot(_PAGE_BASE | _PAGE_USER)
+#define PAGE_KERNEL	__pgprot(_PAGE_BASE | _PAGE_WRENABLE | _PAGE_SHARED)
+#define PAGE_KERNEL_CI	__pgprot(_PAGE_BASE | _PAGE_WRENABLE | _PAGE_SHARED | \
+				 _PAGE_NO_CACHE )
+
 /*
  * The PowerPC can only do execute protection on a segment (256MB) basis,
  * not on a page basis.  So we consider execute permission the same as read.
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/spinlock.h linux/include/asm-ppc/spinlock.h
--- v2.2.0-pre1/linux/include/asm-ppc/spinlock.h	Fri May  8 23:14:56 1998
+++ linux/include/asm-ppc/spinlock.h	Wed Dec 30 10:55:07 1998
@@ -3,12 +3,23 @@
 
 #ifndef __SMP__
 
-typedef struct { } spinlock_t;
-#define SPIN_LOCK_UNLOCKED { }
+/*
+ * Your basic spinlocks, allowing only a single CPU anywhere
+ *
+ * Gcc-2.7.x has a nasty bug with empty initializers.
+ */
+#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+  typedef struct { } spinlock_t;
+  #define SPIN_LOCK_UNLOCKED (spinlock_t) { }
+#else
+  typedef struct { int gcc_is_buggy; } spinlock_t;
+  #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 }
+#endif
 
 #define spin_lock_init(lock)	do { } while(0)
 #define spin_lock(lock)		do { } while(0)
 #define spin_trylock(lock)	do { } while(0)
+#define spin_unlock_wait(lock)	do { } while(0)
 #define spin_unlock(lock)	do { } while(0)
 #define spin_lock_irq(lock)	cli()
 #define spin_unlock_irq(lock)	sti()
@@ -27,9 +38,16 @@
  * can "mix" irq-safe locks - any writer needs to get a
  * irq-safe write-lock, but readers can get non-irqsafe
  * read-locks.
+ *
+ * Gcc-2.7.x has a nasty bug with empty initializers.
  */
-typedef struct { } rwlock_t;
-#define RW_LOCK_UNLOCKED { }
+#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+  typedef struct { } rwlock_t;
+  #define RW_LOCK_UNLOCKED (rwlock_t) { }
+#else
+  typedef struct { int gcc_is_buggy; } rwlock_t;
+  #define RW_LOCK_UNLOCKED (rwlock_t) { 0 }
+#endif
 
 #define read_lock(lock)		do { } while(0)
 #define read_unlock(lock)	do { } while(0)
@@ -63,8 +81,8 @@
 	volatile unsigned long owner_cpu;
 } spinlock_t;
 
-#define SPIN_LOCK_UNLOCKED	{ 0, 0, 0 }
-#define spin_lock_init(lp) do { (lp)->lock = 0; } while(0)
+#define SPIN_LOCK_UNLOCKED	(spinlock_t) { 0, 0, 0 }
+#define spin_lock_init(lp) 	do { (lp)->lock = 0; } while(0)
 #define spin_unlock_wait(lp)	do { barrier(); } while((lp)->lock)
 
 extern void _spin_lock(spinlock_t *lock);
@@ -99,7 +117,7 @@
 	volatile unsigned long owner_pc;
 } rwlock_t;
 
-#define RW_LOCK_UNLOCKED { 0, 0 }
+#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 }
 
 extern void _read_lock(rwlock_t *rw);
 extern void _read_unlock(rwlock_t *rw);
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- v2.2.0-pre1/linux/include/asm-ppc/system.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/system.h	Wed Dec 30 10:55:07 1998
@@ -5,7 +5,15 @@
 #include <asm/processor.h>
 #include <asm/atomic.h>
 
+/*
+ * Memory barrier.
+ * The sync instruction guarantees that all memory accesses initiated
+ * by this processor have been performed (with respect to all other
+ * mechanisms that access memory).
+ */
 #define mb()  __asm__ __volatile__ ("sync" : : : "memory")
+#define rmb()  __asm__ __volatile__ ("sync" : : : "memory")
+#define wmb()  __asm__ __volatile__ ("sync" : : : "memory")
 
 #define __save_flags(flags)	({\
 	__asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); })
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/termios.h linux/include/asm-ppc/termios.h
--- v2.2.0-pre1/linux/include/asm-ppc/termios.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/termios.h	Wed Dec 30 10:55:07 1998
@@ -182,6 +182,8 @@
 #define N_MASC		8	/* Reserved for Mobitex module <kaz@cafe.net> */
 #define N_R3964		9	/* Reserved for Simatic R3964 module */
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
+#define N_IRDA		11	/* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
+#define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/asm-ppc/timex.h linux/include/asm-ppc/timex.h
--- v2.2.0-pre1/linux/include/asm-ppc/timex.h	Fri May  8 23:14:56 1998
+++ linux/include/asm-ppc/timex.h	Wed Dec 30 16:04:22 1998
@@ -12,4 +12,28 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
+typedef unsigned long cycles_t;
+
+/*
+ * For the "cycle" counter we use the timebase lower half.
+ * Currently only used on SMP.
+ *
+ * Since SMP kernels won't run on the PPC601 CPU (which doesn't have
+ * the timebase register) anyway, we don't bother checking the CPU version.
+ */
+
+extern cycles_t cacheflush_time;
+
+static inline cycles_t get_cycles(void)
+{
+#ifdef __SMP__
+	cycles_t ret;
+
+	__asm__("mftb %0" : "=r" (ret) : );
+	return ret;
+#else
+	return 0;
+#endif
+}
+
 #endif
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/arcdevice.h linux/include/linux/arcdevice.h
--- v2.2.0-pre1/linux/include/linux/arcdevice.h	Sun Jun  7 11:16:39 1998
+++ linux/include/linux/arcdevice.h	Tue Dec 29 11:18:18 1998
@@ -192,7 +192,7 @@
 
 
 
-#define JIFFER(time) for (delayval=jiffies+time; jiffies<delayval;) ;
+#define JIFFER(time) for (delayval=jiffies+time; time_before(jiffies,delayval);) ;
 
 	/* a complete ARCnet packet */
 union ArcPacket
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/consolemap.h linux/include/linux/consolemap.h
--- v2.2.0-pre1/linux/include/linux/consolemap.h	Tue Jul 28 14:21:09 1998
+++ linux/include/linux/consolemap.h	Tue Dec 29 14:28:37 1998
@@ -11,5 +11,5 @@
 struct vc_data;
 
 extern unsigned char inverse_translate(struct vc_data *conp, int glyph);
-extern unsigned short *set_translate(int m);
+extern unsigned short *set_translate(int m,int currcons);
 extern int conv_uni_to_pc(struct vc_data *conp, long ucs);
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v2.2.0-pre1/linux/include/linux/cyclades.h	Fri Oct  9 13:27:15 1998
+++ linux/include/linux/cyclades.h	Wed Dec 30 21:20:31 1998
@@ -581,6 +581,7 @@
 #define	CyISA_Ywin	0x2000
 
 #define CyPCI_Ywin 	0x4000
+#define CyPCI_Yctl 	0x80
 #define CyPCI_Zctl 	CTRL_WINDOW_SIZE
 #define CyPCI_Zwin 	0x80000
 #define CyPCI_Ze_win 	(2 * CyPCI_Zwin)
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.2.0-pre1/linux/include/linux/dcache.h	Mon Sep 28 10:51:35 1998
+++ linux/include/linux/dcache.h	Tue Dec 29 10:59:21 1998
@@ -42,7 +42,7 @@
 }
 
 /* Compute the hash for a name string. */
-static __inline__ unsigned int full_name_hash(const char * name, unsigned int len)
+static __inline__ unsigned int full_name_hash(const unsigned char * name, unsigned int len)
 {
 	unsigned long hash = init_name_hash();
 	while (len--)
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.2.0-pre1/linux/include/linux/hdreg.h	Thu Sep 17 17:53:38 1998
+++ linux/include/linux/hdreg.h	Wed Dec 30 21:58:13 1998
@@ -52,6 +52,13 @@
 #define WIN_SETIDLE1		0xE3
 #define WIN_SETIDLE2		0x97
 
+#define WIN_STANDBYNOW1		0xE0
+#define WIN_STANDBYNOW2		0x94
+#define WIN_SLEEPNOW1		0xE6
+#define WIN_SLEEPNOW2		0x99
+#define WIN_CHECKPOWERMODE1	0xE5
+#define WIN_CHECKPOWERMODE2	0x98
+
 #define WIN_DOORLOCK		0xde	/* lock door on removable drives */
 #define WIN_DOORUNLOCK		0xdf	/* unlock door on removable drives */
 
@@ -59,6 +66,7 @@
 #define WIN_MULTWRITE		0xC5	/* write sectors using multiple mode */
 #define WIN_SETMULT		0xC6	/* enable/disable multiple mode */
 #define WIN_IDENTIFY		0xEC	/* ask drive to identify itself	*/
+#define WIN_IDENTIFY_DMA	0xEE	/* same as WIN_IDENTIFY, but DMA */
 #define WIN_SETFEATURES		0xEF	/* set special drive features */
 #define WIN_READDMA		0xc8	/* read sectors using DMA transfers */
 #define WIN_WRITEDMA		0xca	/* write sectors using DMA transfers */
@@ -85,6 +93,14 @@
 #define SMART_STATUS		0xda
 #define SMART_AUTO_OFFLINE	0xdb
 
+/* WIN_SECURITY sub-commands */
+#define SECURITY_SET_PASSWORD		0xBA	/* 0xF1 */
+#define SECURITY_UNLOCK			0xBB	/* 0xF2 */
+#define SECURITY_ERASE_PREPARE		0xBC	/* 0xF3 */
+#define SECURITY_ERASE_UNIT		0xBD	/* 0xF4 */
+#define SECURITY_FREEZE_LOCK		0xBE	/* 0xF5 */
+#define SECURITY_DISABLE_PASSWORD	0xBF	/* 0xF6 */
+
 /* Bits for HD_ERROR */
 #define MARK_ERR	0x01	/* Bad address mark */
 #define TRK0_ERR	0x02	/* couldn't find track 0 */
@@ -184,14 +200,54 @@
 	unsigned short  word79;
 	unsigned short  word80;
 	unsigned short  word81;
-	unsigned short  word82;
-	unsigned short  word83;
+	unsigned short  command_sets;	/* bits 0:Smart 1:Security 2:Removable 3:PM */
+	unsigned short  word83;		/* bits 14:Smart Enabled 13:0 zero */
 	unsigned short  word84;
 	unsigned short  word85;
 	unsigned short  word86;
 	unsigned short  word87;
 	unsigned short  dma_ultra;
-	unsigned short  reserved[167];
+	unsigned short	word89;		/* reserved (word 89) */
+	unsigned short	word90;		/* reserved (word 90) */
+	unsigned short	word91;		/* reserved (word 91) */
+	unsigned short	word92;		/* reserved (word 92) */
+	unsigned short	word93;		/* reserved (word 93) */
+	unsigned short	word94;		/* reserved (word 94) */
+	unsigned short	word95;		/* reserved (word 95) */
+	unsigned short	word96;		/* reserved (word 96) */
+	unsigned short	word97;		/* reserved (word 97) */
+	unsigned short	word98;		/* reserved (word 98) */
+	unsigned short	word99;		/* reserved (word 99) */
+	unsigned short	word100;	/* reserved (word 100) */
+	unsigned short	word101;	/* reserved (word 101) */
+	unsigned short	word102;	/* reserved (word 102) */
+	unsigned short	word103;	/* reserved (word 103) */
+	unsigned short	word104;	/* reserved (word 104) */
+	unsigned short	word105;	/* reserved (word 105) */
+	unsigned short	word106;	/* reserved (word 106) */
+	unsigned short	word107;	/* reserved (word 107) */
+	unsigned short	word108;	/* reserved (word 108) */
+	unsigned short	word109;	/* reserved (word 109) */
+	unsigned short	word110;	/* reserved (word 110) */
+	unsigned short	word111;	/* reserved (word 111) */
+	unsigned short	word112;	/* reserved (word 112) */
+	unsigned short	word113;	/* reserved (word 113) */
+	unsigned short	word114;	/* reserved (word 114) */
+	unsigned short	word115;	/* reserved (word 115) */
+	unsigned short	word116;	/* reserved (word 116) */
+	unsigned short	word117;	/* reserved (word 117) */
+	unsigned short	word118;	/* reserved (word 118) */
+	unsigned short	word119;	/* reserved (word 119) */
+	unsigned short	word120;	/* reserved (word 120) */
+	unsigned short	word121;	/* reserved (word 121) */
+	unsigned short	word122;	/* reserved (word 122) */
+	unsigned short	word123;	/* reserved (word 123) */
+	unsigned short	word124;	/* reserved (word 124) */
+	unsigned short	word125;	/* reserved (word 125) */
+	unsigned short	word126;	/* reserved (word 126) */
+	unsigned short	word127;	/* reserved (word 127) */
+	unsigned short	security;	/* bits 0:support 1:enabled 2:locked 3:frozen */
+	unsigned short	reserved[127];
 };
 
 /*
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/kernel.h linux/include/linux/kernel.h
--- v2.2.0-pre1/linux/include/linux/kernel.h	Thu Nov 12 16:21:24 1998
+++ linux/include/linux/kernel.h	Wed Dec 30 21:19:20 1998
@@ -71,10 +71,10 @@
  */
 
 #define NIPQUAD(addr) \
-	(int)(((addr) >> 0)  & 0xff), \
-	(int)(((addr) >> 8)  & 0xff), \
-	(int)(((addr) >> 16) & 0xff), \
-	(int)(((addr) >> 24) & 0xff)
+	((unsigned char *)&addr)[0], \
+	((unsigned char *)&addr)[1], \
+	((unsigned char *)&addr)[2], \
+	((unsigned char *)&addr)[3]
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h
--- v2.2.0-pre1/linux/include/linux/nfsd/nfsd.h	Fri Jul 31 17:10:04 1998
+++ linux/include/linux/nfsd/nfsd.h	Wed Dec 30 21:59:06 1998
@@ -26,11 +26,6 @@
  */
 #define NFSD_VERSION		"0.4"
 
-/*
- * Maximum number of nfsd processes
- */
-#define NFSD_MAXSERVS		16
-
 #ifdef __KERNEL__
 /*
  * Special flags for nfsd_permission. These must be different from MAY_READ,
@@ -76,6 +71,7 @@
 /* nfsd/vfs.c */
 int		fh_lock_parent(struct svc_fh *, struct dentry *);
 void		nfsd_racache_init(void);
+void		nfsd_racache_shutdown(void);
 int		nfsd_lookup(struct svc_rqst *, struct svc_fh *,
 				const char *, int, struct svc_fh *);
 int		nfsd_setattr(struct svc_rqst *, struct svc_fh *,
@@ -166,6 +162,11 @@
  * Time of server startup
  */
 extern struct timeval	nfssvc_boot;
+
+/*
+ * The number of nfsd threads.
+ */
+extern int		nfsd_nservers;
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.2.0-pre1/linux/include/linux/pci.h	Tue Dec 22 14:16:58 1998
+++ linux/include/linux/pci.h	Wed Dec 30 21:58:13 1998
@@ -542,6 +542,7 @@
 #define PCI_VENDOR_ID_QLOGIC		0x1077
 #define PCI_DEVICE_ID_QLOGIC_ISP1020	0x1020
 #define PCI_DEVICE_ID_QLOGIC_ISP1022	0x1022
+#define PCI_DEVICE_ID_QLOGIC_ISP2100	0x2100
 
 #define PCI_VENDOR_ID_CYRIX		0x1078
 #define PCI_DEVICE_ID_CYRIX_5510	0x0000
@@ -734,6 +735,9 @@
 #define PCI_DEVICE_ID_VIA_82C597_1	0x8597
 #define PCI_DEVICE_ID_VIA_82C598_1      0x8598
 
+#define PCI_VENDOR_ID_SMC2             0x1113
+#define PCI_DEVICE_ID_SMC2_1211TX      0x1211
+
 #define PCI_VENDOR_ID_VORTEX		0x1119
 #define PCI_DEVICE_ID_VORTEX_GDT60x0	0x0000
 #define PCI_DEVICE_ID_VORTEX_GDT6000B	0x0001
@@ -984,6 +988,9 @@
 #define PCI_DEVICE_ID_S3_ViRGE_MXP	0x8c02
 #define PCI_DEVICE_ID_S3_ViRGE_MXPMV	0x8c03
 #define PCI_DEVICE_ID_S3_SONICVIBES	0xca00
+
+#define PCI_VENDOR_ID_DCI       0x6666
+#define PCI_DEVICE_ID_DCI_PCCOM4    0x0001
 
 #define PCI_VENDOR_ID_INTEL		0x8086
 #define PCI_DEVICE_ID_INTEL_82375	0x0482
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/sunrpc/sched.h linux/include/linux/sunrpc/sched.h
--- v2.2.0-pre1/linux/include/linux/sunrpc/sched.h	Thu Aug 20 17:05:19 1998
+++ linux/include/linux/sunrpc/sched.h	Wed Dec 30 21:59:08 1998
@@ -128,7 +128,7 @@
 void		rpc_execute(struct rpc_task *);
 void		rpc_run_child(struct rpc_task *parent, struct rpc_task *child,
 					rpc_action action);
-void		rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *);
+int		rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *);
 void		rpc_remove_wait_queue(struct rpc_task *);
 void		rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
 					rpc_action action, rpc_action timer);
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/sunrpc/svc.h linux/include/linux/sunrpc/svc.h
--- v2.2.0-pre1/linux/include/linux/sunrpc/svc.h	Fri Jul 31 17:10:04 1998
+++ linux/include/linux/sunrpc/svc.h	Wed Dec 30 21:59:48 1998
@@ -106,7 +106,8 @@
 	u32			rq_prot;	/* IP protocol */
 	unsigned short		rq_verfed  : 1,	/* reply has verifier */
 				rq_userset : 1,	/* auth->setuser OK */
-				rq_secure  : 1;	/* secure port */
+				rq_secure  : 1,	/* secure port */
+				rq_auth    : 1;	/* check client */
 
 	void *			rq_argp;	/* decoded arguments */
 	void *			rq_resp;	/* xdr'd results */
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.2.0-pre1/linux/include/linux/sysctl.h	Tue Dec 22 14:16:58 1998
+++ linux/include/linux/sysctl.h	Tue Dec 29 11:18:18 1998
@@ -112,7 +112,7 @@
 	VM_PAGECACHE=7,		/* struct: Set cache memory thresholds */
 	VM_PAGERDAEMON=8,	/* struct: Control kswapd behaviour */
 	VM_PGT_CACHE=9,		/* struct: Set page table cache parameters */
-	VM_PAGE_CLUSTER=10	/* int: set log2 number of pages to swap together */
+	VM_PAGE_CLUSTER=10	/* int: set number of pages to swap together */
 };
 
 
@@ -376,11 +376,12 @@
 
 /* /proc/sys/net/decnet */
 enum {
-	NET_DECNET_NODE_TYPE=1,
-	NET_DECNET_NODE_ADDRESS=2,
-	NET_DECNET_NODE_NAME=3,
-	NET_DECNET_DEFAULT_DEVICE=4,
-	NET_DECNET_DEBUG_LEVEL=255
+	NET_DECNET_DEF_T3_BROADCAST=1,
+	NET_DECNET_DEF_T3_POINTTOPOINT=2,
+	NET_DECNET_DEF_T1=3,
+	NET_DECNET_DEF_BCT1=4,
+	NET_DECNET_CACHETIMEOUT=5,
+	NET_DECNET_DEBUG_LEVEL=6
 };
 
 /* CTL_PROC names: */
@@ -397,7 +398,7 @@
 	FS_MAXFILE=7,	/* int:maximum number of filedescriptors that can be allocated */
 	FS_DENTRY=8,
 	FS_NRSUPER=9,	/* int:current number of allocated super_blocks */
-	FS_MAXSUPER=10	/* int:maximum number of super_blocks that can be allocated */
+	FS_MAXSUPER=10 	/* int:maximum number of super_blocks that can be allocated */
 };
 
 /* CTL_DEBUG names: */
diff -u --recursive --new-file v2.2.0-pre1/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v2.2.0-pre1/linux/include/linux/sysv_fs.h	Thu Sep 17 17:53:38 1998
+++ linux/include/linux/sysv_fs.h	Wed Dec 30 22:36:02 1998
@@ -98,7 +98,9 @@
 	char		s_fill[371];
 	s32		s_magic;	/* version of file system */
 	s32		s_type;		/* type of file system: 1 for 512 byte blocks
-								2 for 1024 byte blocks */
+								2 for 1024 byte blocks
+								3 for 2048 byte blocks */
+								
 };
 
 /* Xenix free list block on disk */
diff -u --recursive --new-file v2.2.0-pre1/linux/ipc/shm.c linux/ipc/shm.c
--- v2.2.0-pre1/linux/ipc/shm.c	Tue Dec 22 14:16:59 1998
+++ linux/ipc/shm.c	Tue Dec 29 11:21:49 1998
@@ -624,6 +624,7 @@
 	unsigned int id, idx;
 
 	id = SWP_OFFSET(code) & SHM_ID_MASK;
+#ifdef DEBUG_SHM
 	if (id != (SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK)) {
 		printk ("shm_swap_in: code id = %d and shmd id = %ld differ\n",
 			id, SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK);
@@ -633,12 +634,17 @@
 		printk ("shm_swap_in: id=%d too big. proc mem corrupted\n", id);
 		return BAD_PAGE;
 	}
+#endif
 	shp = shm_segs[id];
+
+#ifdef DEBUG_SHM
 	if (shp == IPC_UNUSED || shp == IPC_NOID) {
 		printk ("shm_swap_in: id=%d invalid. Race.\n", id);
 		return BAD_PAGE;
 	}
+#endif
 	idx = (SWP_OFFSET(code) >> SHM_IDX_SHIFT) & SHM_IDX_MASK;
+#ifdef DEBUG_SHM
 	if (idx != (offset >> PAGE_SHIFT)) {
 		printk ("shm_swap_in: code idx = %u and shmd idx = %lu differ\n",
 			idx, offset >> PAGE_SHIFT);
@@ -648,6 +654,7 @@
 		printk ("shm_swap_in : too large page index. id=%d\n", id);
 		return BAD_PAGE;
 	}
+#endif
 
 	pte = __pte(shp->shm_pages[idx]);
 	if (!pte_present(pte)) {
diff -u --recursive --new-file v2.2.0-pre1/linux/kernel/acct.c linux/kernel/acct.c
--- v2.2.0-pre1/linux/kernel/acct.c	Fri Nov 27 13:09:29 1998
+++ linux/kernel/acct.c	Tue Dec 29 17:32:38 1998
@@ -124,12 +124,12 @@
 	if (acct_active) {
 		if (act < 0) {
 			acct_active = 0;
-			printk(KERN_INFO "Process accounting paused\r\n");
+			printk(KERN_INFO "Process accounting paused\n");
 		}
 	} else {
 		if (act > 0) {
 			acct_active = 1;
-			printk(KERN_INFO "Process accounting resumed\r\n");
+			printk(KERN_INFO "Process accounting resumed\n");
 		}
 	}
 
diff -u --recursive --new-file v2.2.0-pre1/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.2.0-pre1/linux/kernel/ksyms.c	Mon Dec 28 15:00:53 1998
+++ linux/kernel/ksyms.c	Wed Dec 30 11:35:53 1998
@@ -129,6 +129,7 @@
 EXPORT_SYMBOL(get_empty_filp);
 EXPORT_SYMBOL(init_private_file);
 EXPORT_SYMBOL(fput);
+EXPORT_SYMBOL(put_filp);
 EXPORT_SYMBOL(check_disk_change);
 EXPORT_SYMBOL(invalidate_buffers);
 EXPORT_SYMBOL(invalidate_inodes);
@@ -360,6 +361,7 @@
 EXPORT_SYMBOL(__down);
 EXPORT_SYMBOL(__down_interruptible);
 EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(brw_page);
 
 /* all busmice */
 EXPORT_SYMBOL(add_mouse_randomness);
diff -u --recursive --new-file v2.2.0-pre1/linux/kernel/sched.c linux/kernel/sched.c
--- v2.2.0-pre1/linux/kernel/sched.c	Mon Dec 28 15:00:53 1998
+++ linux/kernel/sched.c	Tue Dec 29 18:27:23 1998
@@ -155,6 +155,15 @@
 }
 #endif /* __SMP__ */
 
+/*
+ * If there is a dependency between p1 and p2,
+ * don't be too eager to go into the slow schedule.
+ * In particular, if p1 and p2 both want the kernel
+ * lock, there is no point in trying to make them
+ * extremely parallel..
+ */
+#define related(p1,p2) ((p1)->lock_depth && (p2)->lock_depth)
+
 static inline void reschedule_idle(struct task_struct * p)
 {
 
@@ -197,7 +206,7 @@
 	 *
 	 * [We can switch to something more finegrained in 2.3.]
 	 */
-	if ((current->avg_slice < cacheflush_time) && !in_interrupt())
+	if ((current->avg_slice < cacheflush_time) && related(current, p))
 		return;
 
 	reschedule_idle_slow(p);
diff -u --recursive --new-file v2.2.0-pre1/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.2.0-pre1/linux/kernel/sysctl.c	Tue Dec 22 14:16:59 1998
+++ linux/kernel/sysctl.c	Tue Dec 29 15:31:09 1998
@@ -28,7 +28,7 @@
 #include <linux/nfs_fs.h>
 #endif
 
-#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
+#if defined(CONFIG_SYSCTL)
 
 /* External variables not in a header file. */
 extern int panic_timeout;
@@ -36,6 +36,8 @@
 extern int bdf_prm[], bdflush_min[], bdflush_max[];
 extern char binfmt_java_interpreter[], binfmt_java_appletviewer[];
 extern int sysctl_overcommit_memory;
+extern int nr_queued_signals, max_queued_signals;
+
 #ifdef CONFIG_KMOD
 extern char modprobe_path[];
 #endif
@@ -70,7 +72,9 @@
 
 static ctl_table kern_table[];
 static ctl_table vm_table[];
+#ifdef CONFIG_NET
 extern ctl_table net_table[];
+#endif
 static ctl_table proc_table[];
 static ctl_table fs_table[];
 static ctl_table debug_table[];
@@ -123,18 +127,20 @@
 
 extern struct proc_dir_entry proc_sys_root;
 
-extern int inodes_stat[];
-extern int dentry_stat[];
 static void register_proc_table(ctl_table *, struct proc_dir_entry *);
 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
 #endif
+extern int inodes_stat[];
+extern int dentry_stat[];
 
 /* The default sysctl tables: */
 
 static ctl_table root_table[] = {
 	{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
 	{CTL_VM, "vm", NULL, 0, 0555, vm_table},
+#ifdef CONFIG_NET
 	{CTL_NET, "net", NULL, 0, 0555, net_table},
+#endif
 	{CTL_PROC, "proc", NULL, 0, 0555, proc_table},
 	{CTL_FS, "fs", NULL, 0, 0555, fs_table},
 	{CTL_DEBUG, "debug", NULL, 0, 0555, debug_table},
@@ -195,6 +201,10 @@
 	{KERN_ACCT, "acct", &acct_parm, 3*sizeof(int),
 	0644, NULL, &proc_dointvec},
 #endif
+	{KERN_RTSIGNR, "rtsig-nr", &nr_queued_signals, sizeof(int),
+	 0444, NULL, &proc_dointvec},
+	{KERN_RTSIGMAX, "rtsig-max", &max_queued_signals, sizeof(int),
+	 0644, NULL, &proc_dointvec},
 	{0}
 };
 
@@ -868,14 +878,14 @@
 
 #else /* CONFIG_PROC_FS */
 
-int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
-			  void *buffer, size_t *lenp)
+int proc_dostring(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
 {
-  return -ENOSYS; 
+	return -ENOSYS;
 }
 
-int proc_dostring(ctl_table *table, int write, struct file *filp,
-		  void *buffer, size_t *lenp)
+static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
+			    void *buffer, size_t *lenp)
 {
 	return -ENOSYS;
 }
@@ -1055,7 +1065,7 @@
 }
 
 
-#else /* CONFIG_PROC_FS && CONFIG_SYSCTL */
+#else /* CONFIG_SYSCTL */
 
 
 extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
@@ -1111,7 +1121,4 @@
 {
 }
 
-#endif /* CONFIG_PROC_FS && CONFIG_SYSCTL */
-
-
-
+#endif /* CONFIG_SYSCTL */
diff -u --recursive --new-file v2.2.0-pre1/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.2.0-pre1/linux/mm/vmscan.c	Tue Dec 22 14:16:59 1998
+++ linux/mm/vmscan.c	Wed Dec 30 17:15:40 1998
@@ -564,10 +564,10 @@
 	
 		priority = 5;
 		do {
+			shrink_dcache_memory(priority, gfp_mask);
 			free_memory(shrink_mmap(priority, gfp_mask));
 			free_memory(shm_swap(priority, gfp_mask));
 			free_memory(swap_out(priority, gfp_mask));
-			shrink_dcache_memory(priority, gfp_mask);
 		} while (--priority >= 0);
 		retval = 0;
 done:
diff -u --recursive --new-file v2.2.0-pre1/linux/net/Config.in linux/net/Config.in
--- v2.2.0-pre1/linux/net/Config.in	Thu Sep 17 17:53:39 1998
+++ linux/net/Config.in	Tue Dec 29 11:21:49 1998
@@ -54,11 +54,14 @@
   bool 'Forwarding between high speed interfaces' CONFIG_NET_HW_FLOWCONTROL
   bool 'CPU is too slow to handle full bandwidth' CONFIG_CPU_IS_SLOW
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'QoS and/or fair queueing'
   bool 'QoS and/or fair queueing' CONFIG_NET_SCHED
   if [ "$CONFIG_NET_SCHED" = "y" ]; then
     source net/sched/Config.in
   fi
 #  bool 'Network code profiler' CONFIG_NET_PROFILE
+  endmenu
   fi
 fi
 endmenu
diff -u --recursive --new-file v2.2.0-pre1/linux/net/core/Makefile linux/net/core/Makefile
--- v2.2.0-pre1/linux/net/core/Makefile	Sat May  2 14:19:54 1998
+++ linux/net/core/Makefile	Tue Dec 29 11:21:49 1998
@@ -12,7 +12,9 @@
 O_OBJS	:= sock.o skbuff.o iovec.o datagram.o scm.o
 
 ifeq ($(CONFIG_SYSCTL),y)
+ifeq ($(CONFIG_NET),y)
 O_OBJS += sysctl_net_core.o
+endif
 endif
 
 ifdef CONFIG_FILTER
diff -u --recursive --new-file v2.2.0-pre1/linux/net/core/firewall.c linux/net/core/firewall.c
--- v2.2.0-pre1/linux/net/core/firewall.c	Mon Feb 23 18:12:12 1998
+++ linux/net/core/firewall.c	Tue Dec 29 11:21:49 1998
@@ -10,6 +10,7 @@
 #include <linux/skbuff.h>
 #include <linux/firewall.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <asm/semaphore.h>
 
 struct semaphore firewall_sem = MUTEX; 
@@ -89,6 +90,7 @@
 			struct firewall_ops *f=fw->next;
 			*nl = f;
 			up(&firewall_sem); 
+			synchronize_bh();
 			return 0;
 		}
 		nl=&((*nl)->next);
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.2.0-pre1/linux/net/ipv4/devinet.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/devinet.c	Tue Dec 29 17:33:35 1998
@@ -208,9 +208,16 @@
 {
 	struct in_ifaddr *ifa1, **ifap, **last_primary;
 
-	if (ifa->ifa_local == 0) {
-		inet_free_ifa(ifa);
-		return 0;
+	/* Allow 0.0.0.0, but it must be the only address to avoid
+           multiple matches. */
+	if (in_dev->ifa_list) {
+		if (ifa->ifa_local == 0) {
+			inet_free_ifa(ifa);
+			return 0;
+		}
+
+		if (in_dev->ifa_list->ifa_local == 0)
+			inet_del_ifa(in_dev, &in_dev->ifa_list, 1);
 	}
 
 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
@@ -1001,18 +1008,12 @@
 
 __initfunc(int inet_add_bootp_addr(struct device *dev))
 {
-	struct in_device *in_dev = dev->ip_ptr;
 	struct in_ifaddr *ifa;
 
-	if (!in_dev && !(in_dev = inetdev_init(dev)))
-		return -ENOBUFS;
 	if (!(ifa = inet_alloc_ifa()))
 		return -ENOBUFS;
-	ifa->ifa_dev = in_dev;
-	in_dev->ifa_list = ifa;
-	rtmsg_ifa(RTM_NEWADDR, ifa);
-	notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
-	return 0;
+
+	return inet_set_ifa(dev, ifa);
 }
 
 __initfunc(void inet_del_bootp_addr(struct device *dev))
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.2.0-pre1/linux/net/ipv4/fib_frontend.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/fib_frontend.c	Tue Dec 29 17:33:35 1998
@@ -443,13 +443,13 @@
 	if (ifa->ifa_broadcast && ifa->ifa_broadcast != 0xFFFFFFFF)
 		fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
 
-	if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
+	if (!(ifa->ifa_flags&IFA_F_SECONDARY) &&
 	    (prefix != addr || ifa->ifa_prefixlen < 32)) {
 		fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
 			  RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
 
 		/* Add network specific broadcasts, when it takes a sense */
-		if (ifa->ifa_prefixlen < 31) {
+		if (!ZERONET(prefix) && ifa->ifa_prefixlen < 31) {
 			fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
 			fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
 		}
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.2.0-pre1/linux/net/ipv4/ipconfig.c	Sun Nov  8 14:03:14 1998
+++ linux/net/ipv4/ipconfig.c	Tue Dec 29 17:33:35 1998
@@ -411,96 +411,6 @@
 static struct bootp_pkt *ic_recv_bootp __initdata = NULL; /* Packet being received */
 
 /*
- *  Dirty tricks for BOOTP packet routing.  We replace the standard lookup function
- *  for the local fib by our version which does fake lookups and returns our private
- *  fib entries. Ugly, but it seems to be the simplest way to do the job.
- */
-
-static void *ic_old_local_lookup __initdata = NULL;	/* Old local routing table lookup function */
-static struct fib_info *ic_bootp_tx_fib __initdata = NULL; /* Our fake fib entries */
-static struct fib_info *ic_bootp_rx_fib __initdata = NULL;
-
-__initfunc(static int ic_bootp_route_lookup(struct fib_table *tb, const struct rt_key *key,
-	struct fib_result *res))
-{
-	static u32 ic_brl_zero = 0;
-
-	DBG(("BOOTP: Route lookup: %d:%08x -> %d:%08x: ", key->iif, key->src, key->oif, key->dst));
-	res->scope = RT_SCOPE_UNIVERSE;
-	res->prefix = &ic_brl_zero;
-	res->prefixlen = 0;
-	res->nh_sel = 0;
-	if (key->src == 0 && key->dst == 0xffffffff && key->iif == loopback_dev.ifindex) { /* Packet output */
-		DBG(("Output\n"));
-		res->type = RTN_UNICAST;
-		res->fi = ic_bootp_tx_fib;
-	} else if (key->iif && key->iif != loopback_dev.ifindex && key->oif == 0) {	/* Packet input */
-		DBG(("Input\n"));
-		res->type = RTN_LOCAL;
-		res->fi = ic_bootp_rx_fib;
-	} else if (!key->iif && !key->oif && !key->src) {	/* Address check by inet_addr_type() */
-		DBG(("Check\n"));
-		res->type = RTN_UNICAST;
-		res->fi = ic_bootp_tx_fib;
-	} else {
-		DBG(("Drop\n"));
-		return -EINVAL;
-	}
-	return 0;
-}
-
-__initfunc(static int ic_set_bootp_route(struct ic_device *d))
-{
-	struct fib_info *f = ic_bootp_tx_fib;
-	struct fib_nh *n = &f->fib_nh[0];
-
-	n->nh_dev = d->dev;
-	n->nh_oif = n->nh_dev->ifindex;
-	rt_cache_flush(0);
-	return 0;
-}
-
-__initfunc(static int ic_bootp_route_init(void))
-{
-	int size = sizeof(struct fib_info) + sizeof(struct fib_nh);
-	struct fib_info *rf, *tf;
-	struct fib_nh *nh;
-
-	if (!(rf = ic_bootp_rx_fib = kmalloc(size, GFP_KERNEL)) ||
-	    !(tf = ic_bootp_tx_fib = kmalloc(size, GFP_KERNEL)))
-		return -1;
-
-	memset(rf, 0, size);
-	rf->fib_nhs = 1;
-	nh = &rf->fib_nh[0];
-	nh->nh_scope = RT_SCOPE_UNIVERSE;
-
-	memset(tf, 0, size);
-	rf->fib_nhs = 1;
-	nh = &rf->fib_nh[0];
-	nh->nh_dev = ic_first_dev->dev;
-	nh->nh_scope = RT_SCOPE_UNIVERSE;
-	nh->nh_oif = nh->nh_dev->ifindex;
-
-	/* Dirty trick: replace standard routing table lookup by our function */
-	ic_old_local_lookup = local_table->tb_lookup;
-	local_table->tb_lookup = ic_bootp_route_lookup;
-
-	return 0;
-}
-
-__initfunc(static void ic_bootp_route_cleanup(void))
-{
-	if (ic_old_local_lookup)
-		local_table->tb_lookup = ic_old_local_lookup;
-	if (ic_bootp_rx_fib)
-		kfree_s(ic_bootp_rx_fib, sizeof(struct fib_info) + sizeof(struct fib_nh));
-	if (ic_bootp_tx_fib)
-		kfree_s(ic_bootp_tx_fib, sizeof(struct fib_info) + sizeof(struct fib_nh));
-}
-
-
-/*
  *  Allocation and freeing of BOOTP packet buffers.
  */
 __initfunc(static int ic_bootp_alloc(void))
@@ -677,10 +587,9 @@
 	/* Add fake zero addresses to all interfaces */
 	if (ic_bootp_addrs_add() < 0)
 		return -1;
-
-	/* Initialize BOOTP routing */
-	if (ic_bootp_route_init() < 0)
-		return -1;
+	
+	/* Setting the addresses automatically creates appropriate
+	   routes. */	
 
 	/* Initialize common portion of BOOTP request */
 	memset(ic_xmit_bootp, 0, sizeof(struct bootp_pkt));
@@ -699,7 +608,6 @@
 	ic_bootp_xmit_sock->sk->broadcast = 1;
 	ic_bootp_xmit_sock->sk->reuse = 1;
 	ic_bootp_recv_sock->sk->reuse = 1;
-	ic_set_bootp_route(ic_first_dev);
 	if (ic_udp_bind(ic_bootp_recv_sock, INADDR_ANY, 68) ||
 	    ic_udp_bind(ic_bootp_xmit_sock, INADDR_ANY, 68) ||
 	    ic_udp_connect(ic_bootp_xmit_sock, INADDR_BROADCAST, 67))
@@ -718,7 +626,6 @@
 	ic_udp_close(ic_bootp_recv_sock);
 	ic_bootp_addrs_del();
 	ic_bootp_free();
-	ic_bootp_route_cleanup();
 }
 
 
@@ -735,7 +642,6 @@
 	memset(b->hw_addr, 0, sizeof(b->hw_addr));
 	memcpy(b->hw_addr, dev->dev_addr, dev->addr_len);
 	b->secs = htons(jiffies / HZ);
-	ic_set_bootp_route(d);
 	return ic_udp_send(ic_bootp_xmit_sock, b, sizeof(struct bootp_pkt));
 }
 
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.2.0-pre1/linux/net/ipv4/route.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/route.c	Tue Dec 29 17:33:35 1998
@@ -950,6 +950,10 @@
 	if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr))
 		goto martian_destination;
 
+	/* Accept anything arriving at 0.0.0.0 */
+	if (in_dev->ifa_list && in_dev->ifa_list->ifa_local == 0)
+		goto local_input;
+
 	/*
 	 *	Now we are ready to route packet.
 	 */
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.2.0-pre1/linux/net/ipv4/tcp.c	Thu Nov 19 09:56:29 1998
+++ linux/net/ipv4/tcp.c	Wed Dec 30 21:19:20 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.132 1998/11/08 13:21:14 davem Exp $
+ * Version:	$Id: tcp.c,v 1.133 1998/11/30 15:13:06 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1612,19 +1612,15 @@
 	if(sk->keepopen)
 		tcp_inc_slow_timer(TCP_SLT_KEEPALIVE);
 
-	/*
-	 * This does not pass any already set errors on the new socket
-	 * to the user, but they will be returned on the first socket operation
-	 * after the accept.
-	 *
-	 * Once linux gets a multithreaded net_bh or equivalent there will be a race
-	 * here - you'll have to check for sk->zapped as set by the ICMP handler then.
-	 */
+	release_sock(sk);
+	return newsk;
 
-	error = 0;
 out:
+	/* sk should be in LISTEN state, thus accept can use sk->err for
+	 * internal purposes without stomping one anyone's feed.
+	 */ 
+	sk->err = error; 
 	release_sock(sk);
-	sk->err = error;
 	return newsk;
 }
 
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.2.0-pre1/linux/net/ipv4/tcp_input.c	Thu Nov 19 09:56:29 1998
+++ linux/net/ipv4/tcp_input.c	Wed Dec 30 21:19:20 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.141 1998/11/18 02:12:07 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.143 1998/12/20 20:20:20 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -301,7 +301,7 @@
 			/* The retransmission queue is always in order, so
 			 * we can short-circuit the walk early.
 			 */
-			if(!before(start_seq, TCP_SKB_CB(skb)->end_seq))
+			if(after(TCP_SKB_CB(skb)->seq, start_seq))
 				break;
 
 			/* We play conservative, we don't allow SACKS to partially
@@ -598,6 +598,13 @@
 	unsigned long now = jiffies;
 	int acked = 0;
 
+	/* If we are retransmitting, and this ACK clears up to
+	 * the retransmit head, or further, then clear our state.
+	 */
+	if (tp->retrans_head != NULL &&
+	    !before(ack, TCP_SKB_CB(tp->retrans_head)->end_seq))
+		tp->retrans_head = NULL;
+
 	while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) {
 		struct tcp_skb_cb *scb = TCP_SKB_CB(skb); 
 		__u8 sacked = scb->sacked;
@@ -625,6 +632,7 @@
 			if(tp->fackets_out)
 				tp->fackets_out--;
 		} else {
+			/* This is pure paranoia. */
 			tp->retrans_head = NULL;
 		}		
 		tp->packets_out--;
@@ -633,9 +641,6 @@
 		__skb_unlink(skb, skb->list);
 		kfree_skb(skb);
 	}
-
-	if (acked)
-		tp->retrans_head = NULL;
 	return acked;
 }
 
@@ -740,7 +745,6 @@
 	 * congestion window is handled properly by that code.
 	 */
 	if (tp->retransmits) {
-		tp->retrans_head = NULL;
 		tcp_xmit_retransmit_queue(sk);
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 	} else {
@@ -1657,9 +1661,12 @@
 	}
 }
 
-/*
- * Clean first the out_of_order queue, then the receive queue until
- * the socket is in its memory limits again.
+/* Clean the out_of_order queue if we can, trying to get
+ * the socket within its memory limits again.
+ *
+ * Return less than zero if we should stop dropping frames
+ * until the socket owning process reads some of the data
+ * to stabilize the situation.
  */
 static int prune_queue(struct sock *sk)
 {
@@ -1670,8 +1677,8 @@
 
 	net_statistics.PruneCalled++; 
 
-	/* First Clean the out_of_order queue. */
-	/* Start with the end because there are probably the least
+	/* Clean the out_of_order queue.
+	 * Start with the end because there are probably the least
 	 * useful packets (crossing fingers).
 	 */
 	while ((skb = __skb_dequeue_tail(&tp->out_of_order_queue))) { 
@@ -1681,35 +1688,32 @@
 			return 0;
 	}
 	
-	/* Now continue with the receive queue if it wasn't enough.
-	 * But only do this if we are really being abused.
+	/* If we are really being abused, tell the caller to silently
+	 * drop receive data on the floor.  It will get retransmitted
+	 * and hopefully then we'll have sufficient space.
+	 *
+	 * We used to try to purge the in-order packets too, but that
+	 * turns out to be deadly and fraught with races.  Consider:
+	 *
+	 * 1) If we acked the data, we absolutely cannot drop the
+	 *    packet.  This data would then never be retransmitted.
+	 * 2) It is possible, with a proper sequence of events involving
+	 *    delayed acks and backlog queue handling, to have the user
+	 *    read the data before it gets acked.  The previous code
+	 *    here got this wrong, and it lead to data corruption.
+	 * 3) Too much state changes happen when the FIN arrives, so once
+	 *    we've seen that we can't remove any in-order data safely.
+	 *
+	 * The net result is that removing in-order receive data is too
+	 * complex for anyones sanity.  So we don't do it anymore.  But
+	 * if we are really having our buffer space abused we stop accepting
+	 * new receive data.
 	 */
-	while ((atomic_read(&sk->rmem_alloc) >= (sk->rcvbuf * 2)) &&
-	       (skb = skb_peek_tail(&sk->receive_queue))) {
-		/* Never toss anything when we've seen the FIN.
-		 * It's just too complex to recover from it.
-		 */
-		if(skb->h.th->fin)
-			break;
-
-		/* Never remove packets that have been already acked */
-		if (before(TCP_SKB_CB(skb)->end_seq, tp->last_ack_sent+1)) {
-			SOCK_DEBUG(sk, "prune_queue: hit acked data c=%x,%x,%x\n",
-				   tp->copied_seq, TCP_SKB_CB(skb)->end_seq,
-				   tp->last_ack_sent);
-			return -1;
-		}
-
-		net_statistics.RcvPruned += skb->len; 
+	if(atomic_read(&sk->rmem_alloc) < (sk->rcvbuf << 1))
+		return 0;
 
-		__skb_unlink(skb, skb->list);
-		tp->rcv_nxt = TCP_SKB_CB(skb)->seq;
-		SOCK_DEBUG(sk, "prune_queue: removing %x-%x (c=%x)\n",
-			   TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
-			   tp->copied_seq); 
-		kfree_skb(skb);
-	}
-	return 0;
+	/* Massive buffer overcommit. */
+	return -1;
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.2.0-pre1/linux/net/ipv4/tcp_ipv4.c	Wed Dec 16 10:32:56 1998
+++ linux/net/ipv4/tcp_ipv4.c	Wed Dec 30 21:19:20 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.162 1998/11/07 11:50:26 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.163 1998/11/30 15:24:22 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -265,7 +265,7 @@
 	struct tcp_bind_bucket *tb;
 	int low = sysctl_local_port_range[0];
 	int high = sysctl_local_port_range[1];
-	int remaining = high - low + 1;
+	int remaining = (high - low) + 1;
 	int rover;
 
 	SOCKHASH_LOCK();
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.2.0-pre1/linux/net/ipv4/tcp_output.c	Thu Nov 19 09:56:29 1998
+++ linux/net/ipv4/tcp_output.c	Wed Dec 30 21:19:20 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.97 1998/11/08 13:21:27 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.98 1998/12/12 06:43:35 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -80,15 +80,28 @@
 		struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
 		int tcp_header_size = tp->tcp_header_len;
 		struct tcphdr *th;
+		int sysctl_flags;
 
+#define SYSCTL_FLAG_TSTAMPS	0x1
+#define SYSCTL_FLAG_WSCALE	0x2
+#define SYSCTL_FLAG_SACK	0x4
+
+		sysctl_flags = 0;
 		if(tcb->flags & TCPCB_FLAG_SYN) {
 			tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
-			if(sysctl_tcp_timestamps)
+			if(sysctl_tcp_timestamps) {
 				tcp_header_size += TCPOLEN_TSTAMP_ALIGNED;
-			if(sysctl_tcp_window_scaling)
+				sysctl_flags |= SYSCTL_FLAG_TSTAMPS;
+			}
+			if(sysctl_tcp_window_scaling) {
 				tcp_header_size += TCPOLEN_WSCALE_ALIGNED;
-			if(sysctl_tcp_sack && !sysctl_tcp_timestamps)
-				tcp_header_size += TCPOLEN_SACKPERM_ALIGNED;
+				sysctl_flags |= SYSCTL_FLAG_WSCALE;
+			}
+			if(sysctl_tcp_sack) {
+				sysctl_flags |= SYSCTL_FLAG_SACK;
+				if(!sysctl_tcp_timestamps)
+					tcp_header_size += TCPOLEN_SACKPERM_ALIGNED;
+			}
 		} else if(tp->sack_ok && tp->num_sacks) {
 			/* A SACK is 2 pad bytes, a 2 byte header, plus
 			 * 2 32-bit sequence numbers for each SACK block.
@@ -118,9 +131,9 @@
 			 */
 			th->window	= htons(tp->rcv_wnd);
 			tcp_syn_build_options((__u32 *)(th + 1), tp->mss_clamp,
-					      sysctl_tcp_timestamps,
-					      sysctl_tcp_sack,
-					      sysctl_tcp_window_scaling,
+					      (sysctl_flags & SYSCTL_FLAG_TSTAMPS),
+					      (sysctl_flags & SYSCTL_FLAG_SACK),
+					      (sysctl_flags & SYSCTL_FLAG_WSCALE),
 					      tp->rcv_wscale,
 					      TCP_SKB_CB(skb)->when);
 		} else {
@@ -134,6 +147,9 @@
 		tcp_statistics.TcpOutSegs++;
 		tp->af_specific->queue_xmit(skb);
 	}
+#undef SYSCTL_FLAG_TSTAMPS
+#undef SYSCTL_FLAG_WSCALE
+#undef SYSCTL_FLAG_SACK
 }
 
 /* This is the main buffer sending routine. We queue the buffer
diff -u --recursive --new-file v2.2.0-pre1/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.2.0-pre1/linux/net/ipv4/tcp_timer.c	Sun Nov  8 14:03:15 1998
+++ linux/net/ipv4/tcp_timer.c	Wed Dec 30 21:19:20 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.55 1998/11/07 11:55:42 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.56 1998/11/30 15:18:12 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -170,8 +170,13 @@
 
 	if(!sk->zapped &&
 	   sk->tp_pinfo.af_tcp.delayed_acks &&
-	   sk->state != TCP_CLOSE)
-		tcp_send_ack(sk);
+	   sk->state != TCP_CLOSE) {
+		/* If socket is currently locked, defer the ACK. */
+		if (!atomic_read(&sk->sock_readers))
+			tcp_send_ack(sk);
+		else
+			tcp_send_delayed_ack(&(sk->tp_pinfo.af_tcp), HZ/10);
+	}
 }
 
 void tcp_probe_timer(unsigned long data)
diff -u --recursive --new-file v2.2.0-pre1/linux/net/netsyms.c linux/net/netsyms.c
--- v2.2.0-pre1/linux/net/netsyms.c	Thu Nov 19 09:56:29 1998
+++ linux/net/netsyms.c	Tue Dec 29 11:21:49 1998
@@ -11,17 +11,16 @@
 #include <linux/types.h>
 #include <linux/net.h>
 #include <linux/in.h>
-#include <net/sock.h>
-#include <net/dst.h>
-#include <net/checksum.h>
-#include <net/pkt_sched.h>
 #include <linux/netdevice.h>
-#include <linux/etherdevice.h>
 #include <linux/fddidevice.h>
 #include <linux/trdevice.h>
 #include <linux/ioport.h>
 #include <net/neighbour.h>
 #include <net/snmp.h>
+#include <net/dst.h>
+#include <net/checksum.h>
+#include <linux/etherdevice.h>
+#include <net/pkt_sched.h>
 
 #ifdef CONFIG_BRIDGE
 #include <net/br.h>
@@ -29,6 +28,7 @@
 
 #ifdef CONFIG_INET
 #include <linux/ip.h>
+#include <linux/etherdevice.h>
 #include <net/protocol.h>
 #include <net/arp.h>
 #include <net/ip.h>
@@ -38,19 +38,13 @@
 #include <net/route.h>
 #include <net/scm.h>
 #include <net/inet_common.h>
+#include <net/pkt_sched.h>
 #include <linux/inet.h>
 #include <linux/mroute.h>
 #include <linux/igmp.h>
 
 extern struct net_proto_family inet_family_ops;
 
-#ifdef CONFIG_DLCI_MODULE
-extern int (*dlci_ioctl_hook)(unsigned int, void *);
-EXPORT_SYMBOL(dlci_ioctl_hook);
-#endif
-
-#endif
-
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 #include <linux/in6.h>
 #include <linux/icmpv6.h>
@@ -62,6 +56,7 @@
 extern int tcp_tw_death_row_slot;
 #endif
 
+#endif
 
 #include <linux/rtnetlink.h>
 
@@ -87,6 +82,10 @@
 
 #ifdef CONFIG_ATALK_MODULE
 #include <net/sock.h>
+#include <net/dst.h>
+#include <net/checksum.h>
+#include <linux/etherdevice.h>
+#include <net/pkt_sched.h>
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -136,6 +135,9 @@
 EXPORT_SYMBOL(sock_no_recvmsg);
 EXPORT_SYMBOL(sock_rfree);
 EXPORT_SYMBOL(sock_wfree);
+EXPORT_SYMBOL(sock_wmalloc);
+EXPORT_SYMBOL(sock_rmalloc);
+EXPORT_SYMBOL(sock_rspace);
 EXPORT_SYMBOL(skb_recv_datagram);
 EXPORT_SYMBOL(skb_free_datagram);
 EXPORT_SYMBOL(skb_copy_datagram);
@@ -222,6 +224,7 @@
 EXPORT_SYMBOL(icmp_send);
 EXPORT_SYMBOL(ip_options_compile);
 EXPORT_SYMBOL(arp_send);
+EXPORT_SYMBOL(arp_broken_ops);
 EXPORT_SYMBOL(ip_id_count);
 EXPORT_SYMBOL(ip_send_check);
 EXPORT_SYMBOL(ip_fragment);
@@ -233,13 +236,16 @@
 EXPORT_SYMBOL(inet_dgram_ops);
 EXPORT_SYMBOL(ip_cmsg_recv);
 EXPORT_SYMBOL(__release_sock);
-EXPORT_SYMBOL(arp_find);
-EXPORT_SYMBOL(ip_rcv);
-EXPORT_SYMBOL(arp_rcv);
 
 /* needed for ip_gre -cw */
 EXPORT_SYMBOL(ip_statistics);
 
+#ifdef CONFIG_DLCI_MODULE
+extern int (*dlci_ioctl_hook)(unsigned int, void *);
+EXPORT_SYMBOL(dlci_ioctl_hook);
+#endif
+
+
 #ifdef CONFIG_IPV6
 EXPORT_SYMBOL(ipv6_addr_type);
 EXPORT_SYMBOL(icmpv6_send);
@@ -273,7 +279,6 @@
 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
 EXPORT_SYMBOL(net_timer);
 /* UDP/TCP exported functions for TCPv6 */
-EXPORT_SYMBOL(sock_rspace);
 EXPORT_SYMBOL(udp_ioctl);
 EXPORT_SYMBOL(udp_connect);
 EXPORT_SYMBOL(udp_sendmsg);
@@ -370,8 +375,17 @@
 EXPORT_SYMBOL(rtnl_lock);
 EXPORT_SYMBOL(rtnl_unlock);
 
-EXPORT_SYMBOL(sock_wmalloc);
-EXPORT_SYMBOL(sock_rmalloc);
+                  
+/* Used by at least ipip.c.  */
+EXPORT_SYMBOL(ipv4_config);
+EXPORT_SYMBOL(dev_open);
+
+EXPORT_SYMBOL(ip_rcv);
+EXPORT_SYMBOL(arp_rcv);
+EXPORT_SYMBOL(arp_tbl);
+EXPORT_SYMBOL(arp_find);
+
+#endif  /* CONFIG_INET */
 
 #if	defined(CONFIG_ULTRA)	||	defined(CONFIG_WD80x3)		|| \
 	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
@@ -395,12 +409,6 @@
 EXPORT_SYMBOL(init_trdev);
 EXPORT_SYMBOL(tr_freedev);
 #endif
-                  
-/* Used by at least ipip.c.  */
-EXPORT_SYMBOL(ipv4_config);
-EXPORT_SYMBOL(dev_open);
-
-#endif  /* CONFIG_INET */
 
 /* Device callback registration */
 EXPORT_SYMBOL(register_netdevice_notifier);
@@ -445,10 +453,11 @@
 EXPORT_SYMBOL(dev_base);
 EXPORT_SYMBOL(dev_close);
 EXPORT_SYMBOL(dev_mc_add);
+EXPORT_SYMBOL(dev_mc_delete);
+EXPORT_SYMBOL(dev_mc_upload);
 EXPORT_SYMBOL(n_tty_ioctl);
 EXPORT_SYMBOL(tty_register_ldisc);
 EXPORT_SYMBOL(kill_fasync);
-EXPORT_SYMBOL(dev_mc_delete);
 
 EXPORT_SYMBOL(if_port_text);
 
diff -u --recursive --new-file v2.2.0-pre1/linux/net/sunrpc/auth.c linux/net/sunrpc/auth.c
--- v2.2.0-pre1/linux/net/sunrpc/auth.c	Mon Dec 28 15:00:53 1998
+++ linux/net/sunrpc/auth.c	Tue Dec 29 11:42:25 1998
@@ -117,7 +117,7 @@
 				printk("RPC: rpcauth_gc_credcache looping!\n");
 				break;
 			}
-			if (!cred->cr_count && time_after(jiffies, cred->cr_expire)) {
+			if (!cred->cr_count && time_before(cred->cr_expire, jiffies)) {
 				*q = cred->cr_next;
 				cred->cr_next = free;
 				free = cred;
@@ -160,7 +160,7 @@
 
 	nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR);
 
-	if (time_after(jiffies, auth->au_nextgc))
+	if (time_before(auth->au_nextgc, jiffies))
 		rpcauth_gc_credcache(auth);
 
 	q = &auth->au_credcache[nr];
diff -u --recursive --new-file v2.2.0-pre1/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.2.0-pre1/linux/net/sunrpc/sched.c	Sun Nov  8 14:03:16 1998
+++ linux/net/sunrpc/sched.c	Tue Dec 29 11:42:25 1998
@@ -79,13 +79,16 @@
  * improve overall performance.
  * Everyone else gets appended to the queue to ensure proper FIFO behavior.
  */
-void
+int
 rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
 {
 	if (task->tk_rpcwait) {
 		if (task->tk_rpcwait != queue)
+		{
 			printk(KERN_WARNING "RPC: doubly enqueued task!\n");
-		return;
+			return -EWOULDBLOCK;
+		}
+		return 0;
 	}
 	if (RPC_IS_SWAPPER(task))
 		rpc_insert_list(&queue->task, task);
@@ -95,6 +98,8 @@
 
 	dprintk("RPC: %4d added to queue %p \"%s\"\n",
 				task->tk_pid, queue, rpc_qname(queue));
+
+	return 0;
 }
 
 /*
@@ -168,7 +173,13 @@
 		return;
 	}
 	if (RPC_IS_ASYNC(task)) {
-		rpc_add_wait_queue(&schedq, task);
+		int status;
+		status = rpc_add_wait_queue(&schedq, task);
+		if (status)
+		{
+			printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
+			task->tk_status = status;
+		}
 		wake_up(&rpciod_idle);
 	} else {
 		wake_up(&task->tk_wait);
@@ -202,6 +213,7 @@
 			rpc_action action, rpc_action timer)
 {
 	unsigned long	oldflags;
+	int status;
 
 	dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid,
 				rpc_qname(q), jiffies);
@@ -211,11 +223,20 @@
 	 */
 	save_flags(oldflags); cli();
 
-	rpc_add_wait_queue(q, task);
-	task->tk_callback = action;
-	if (task->tk_timeout)
-		rpc_add_timer(task, timer);
-	task->tk_flags &= ~RPC_TASK_RUNNING;
+	status = rpc_add_wait_queue(q, task);
+	if (status)
+	{
+		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
+		task->tk_status = status;
+		task->tk_flags |= RPC_TASK_RUNNING;
+	}
+	else
+	{
+		task->tk_callback = action;
+		if (task->tk_timeout)
+			rpc_add_timer(task, timer);
+		task->tk_flags &= ~RPC_TASK_RUNNING;
+	}
 
 	restore_flags(oldflags);
 	return;
diff -u --recursive --new-file v2.2.0-pre1/linux/net/sunrpc/svc.c linux/net/sunrpc/svc.c
--- v2.2.0-pre1/linux/net/sunrpc/svc.c	Fri Dec 19 10:54:10 1997
+++ linux/net/sunrpc/svc.c	Tue Dec 29 11:42:25 1998
@@ -244,6 +244,12 @@
 	argp->buf += 5;
 	argp->len -= 5;
 
+	/* Used by nfsd to only allow the NULL procedure for amd. */
+	if (rqstp->rq_auth && !rqstp->rq_client && proc) {
+		auth_stat = rpc_autherr_badcred;
+		goto err_bad_auth;
+	}
+
 	/*
 	 * Decode auth data, and add verifier to reply buffer.
 	 * We do this before anything else in order to get a decent
diff -u --recursive --new-file v2.2.0-pre1/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- v2.2.0-pre1/linux/net/sunrpc/xprt.c	Mon Dec 28 15:00:53 1998
+++ linux/net/sunrpc/xprt.c	Tue Dec 29 11:42:25 1998
@@ -543,7 +543,7 @@
 		pkt_cnt++;
 		pkt_len += req->rq_slen + copied;
 		pkt_rtt += jiffies - req->rq_xtime;
-		if (time_after(jiffies, nextstat)) {
+		if (time_before(nextstat, jiffies)) {
 			printk("RPC: %lu %ld cwnd\n", jiffies, xprt->cwnd);
 			printk("RPC: %ld %ld %ld %ld stat\n",
 					jiffies, pkt_cnt, pkt_len, pkt_rtt);
@@ -936,6 +936,7 @@
 	struct rpc_timeout *timeo;
 	struct rpc_rqst	*req = task->tk_rqstp;
 	struct rpc_xprt	*xprt = req->rq_xprt;
+	int status;
 
 	/*DEBUG*/int ac_debug=xprt->snd_sent;
 	
@@ -993,9 +994,17 @@
 	 * the pending list now:
 	 */
 	start_bh_atomic();
-	rpc_add_wait_queue(&xprt->pending, task);
-	task->tk_callback = NULL;
+	status = rpc_add_wait_queue(&xprt->pending, task);
+	if (!status)
+		task->tk_callback = NULL;
 	end_bh_atomic();
+
+	if (status)
+	{
+		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
+		task->tk_status = status;
+		return;
+	}
 
 	/* Continue transmitting the packet/record. We must be careful
 	 * to cope with writespace callbacks arriving _after_ we have
diff -u --recursive --new-file v2.2.0-pre1/linux/net/wanrouter/wanmain.c linux/net/wanrouter/wanmain.c
--- v2.2.0-pre1/linux/net/wanrouter/wanmain.c	Mon Dec 28 15:00:53 1998
+++ linux/net/wanrouter/wanmain.c	Wed Dec 30 15:07:00 1998
@@ -32,6 +32,7 @@
 *                               kernel space (for big firmwares)
 *****************************************************************************/
 
+#include <linux/config.h>
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/kernel.h>
@@ -87,10 +88,8 @@
  *	Global Data
  */
 
-#ifdef MODULE
 static char fullname[]		= "WAN Router";
 static char copyright[]		= "(c) 1995-1997 Sangoma Technologies Inc.";
-#endif
 static char modname[]		= ROUTER_NAME;	/* short module name */
 wan_device_t * router_devlist	= NULL;	/* list of registered devices */
 static int devcnt		= 0;
