diff -u --recursive --new-file v2.1.8/linux/CREDITS linux/CREDITS
--- v2.1.8/linux/CREDITS	Sun Nov 10 20:12:06 1996
+++ linux/CREDITS	Tue Nov 12 10:40:04 1996
@@ -1047,6 +1047,17 @@
 E: greg@caldera.com
 D: IPX development and support
 
+N: Barak A. Pearlmutter
+E: bap@cs.unm.edu
+W: http://www.cs.unm.edu/~bap/
+P: 512/602D785D 9B A1 83 CD EE CB AD 93  20 C6 4C B7 F5 E9 60 D4
+D: Author of mark-and-sweep GC integrated by Alan Cox
+S: Computer Science Department
+S: FEC 313
+S: University of New Mexico
+S: Albuquerque, NM  87131
+S: USA
+
 N: Avery Pennarun
 E: apenwarr@foxnet.net
 D: ARCnet driver
diff -u --recursive --new-file v2.1.8/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.8/linux/Documentation/Changes	Tue Oct 29 19:58:01 1996
+++ linux/Documentation/Changes	Sat Nov  9 18:24:41 1996
@@ -1,7 +1,7 @@
 Intro
 =====
 
-This document contains a list of the latest releases of the most
+This document contains a list of the latest stable releases of the most
 important packages for Linux as well as instructions for newcomers to
 the 2.0.x series of kernels.  By glancing through it, you should be
 able to find out what you need to upgrade in order to successfully run
@@ -28,13 +28,16 @@
 translation).
 
    Tamas also maintains a version of this file at
-http://www.datanet.hu/generations/linux/Changes.html (English).
+http://www.datanet.hu/generations/linux/Changes.html (English version).
 
    For people who prefer Japanese (thanks to Mitsuhiro Kojima): Kono
 bunshou no nihongo ban wa
 http://jf.gee.kyoto-u.ac.jp/JF/v2.0/Changes-2.0.html ni arimasu.
 
-Last updated: September 10, 1996.
+   Voyez le site http://www.linux-kheops.com/traduc/kernels/ pour la
+traduction francais (merci, David Bourgin).  (French translation)
+
+Last updated: November 5, 1996.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Releases
@@ -43,16 +46,16 @@
 - Kernel modules	 2.0.0
 - PPP daemon		 2.2.0f
 - Dynamic linker (ld.so) 1.7.14
-- GNU CC		 2.7.2
-- Binutils		 2.6.0.14
-- Linux C Library	 Stable:  5.2.18, Beta:  5.3.12
-- Linux C++ Library	 2.7.1.4
+- GNU CC		 2.7.2.1
+- Binutils		 2.7.0.3
+- Linux C Library	 Stable:  5.2.18, Beta:  5.4.10
+- Linux C++ Library	 2.7.2.1
 - Termcap		 2.0.8
 - Procps		 1.01
 - Gpm			 1.10
 - SysVinit		 2.64
 - Util-linux		 2.5
-- Mount                  2.5k
+- Mount                  2.5p
 - Net-tools              1.32-alpha
 - Kbd                    0.91
 
@@ -69,10 +72,11 @@
 127.0.0.1' in your network configuration files and change it to `route
 add -net 127.0.0.0'.
 
-   This error is present in all Red Hat distributions (and derivative
-distributions like Caldera).  If you're running one of these, edit
-/etc/sysconfig/network-scripts/ifup-lo, changing the line `route add
--net $(IPADDR)' to `route add -net 127.0.0.0' and you should be fine.
+   This error is present in all Red Hat distributions through Red Hat
+3.03 (and derivative distributions like Caldera).  If you're running
+one of these, edit /etc/sysconfig/network-scripts/ifup-lo, changing the
+line `route add -net $(IPADDR)' to `route add -net 127.0.0.0' and you
+should be fine.
 
    People have also reported problems due to the naming of the dummy
 network interface driver.  If the dummy driver is compiled into the
@@ -103,67 +107,58 @@
 this option in their lilo.config file.  Comment it out and re-run lilo
 if you need ramdisks.
 
-Module load errors
-==================
-
-   The 386 Linux kernel versions 2.1.0 and above have moved to address
-0xc0000000.  This means that syscalls that return an address in the
-kernel area will return a value that is a negative 32 bit number.
-Versions of libc prior to libc-5.4.8 will interpret the negative value
-as an error.  In particular, this prevents modules from loading.  You
-need at least libc-5.4.10 to load modules successfully again.
+   The definition of SIOCSARP in /usr/include/linux/sockios.h was
+changed.  This means bootpd has to be re-compiled in order to work.
 
+   The kernel reboot method is now, by default, a cold reboot so that
+the kernel will work on systems that don't support other methods.  If
+you want to be able to do a warm reboot, add a reboot=warm option to
+lilo.conf.
 
 The Linux C Library
 ===================
 
-   Before upgrading your C library, be sure to carefully read the
-`release.libc-x.y.z' file. (x.y.z is the version number eg. 5.4.7)
-
-- GNU make, perl, and a few other important utils can be broken by the
-  upgrade.  A dirent bug, which erroneously defined d->reclen to
-  d->namlen if USE_GNU was defined, has been fixed.  Unfortunately,
-  some GNU packages depend on this bug.  GNU make 3.xx is one of them.
-  To fix that you need to patch and recompile those programs (a patch
-  for make is included in the file `release.libc-x.y.z', and the
-  address to obtain a precompiled binary is at the end of this file).
-
-- Upgrading libc can also break xterm support.  If it does, you need
-  to recompile xterm.
-
-- There have been some important changes to libc that may cause
-  trouble with buggy programs.  Programs that call free() on a pointer
-  not returned by malloc() work with older versions of libc, but not
-  with newer versions of libc.
+   The latest stable Linux C Library release is 5.2.18.  If you upgrade
+to this from 5.0.9 or earlier, be sure to read the
+`release.libc-5.2.18' file, since GNU make and a few other fairly
+important utils can be broken by the upgrade.
+
+   The current (beta) Linux C Library release is 5.3.12. In this
+release there are some important changes that may cause troubles to
+buggy programs (programs that call free() on a pointer not returned by
+malloc() work with previous libc, but not with this release) so read the
+`release.libc-5.3.12' file carefully!  In the latest libc releases a
+dirent bug, which erroneously defined d->reclen to d->namlen if USE_GNU
+was defined, has been fixed.  Unfortunately, some GNU packages depend
+on this bug.  GNU make 3.xx is one of them.  To fix that you need to
+patch and recompile those programs (a patch for make is included in the
+file `release.libc-.5.3.9', and the address to obtain a precompiled
+binary is at the end of this file).
+
+   Also, the libc-5.3.x line has a known security hole relating to
+rlogin.  Libc-5.3.12 fixes this, so if you're going to run an
+experimental libc, be sure to upgrade to 5.3.12.  Libc-5.4.10 is
+currently available as well, but it may have problems, so caveat emptor.
 
-
-There are good reasons to upgrade your libc though!
-
-- If you use modules, you'll need libc-5.4.10 or later with version
-  2.1.x i386 kernels
-
-- The libc-5.3.x line before libc-5.3.12 have a known security hole
-  relating to rlogin.  Another security fix was done in libc-5.4.7.
-
-- If you're getting an error message that is something like
+   If you're getting an error message that is something to the effect of
 
    `fcntl_setlk() called by process 123 with broken flock() emulation'
 
-  then you need to upgrade to at least libc-5.2.18.  A proper (in
-  other words, BSD-style ;-) flock system call was added to 2.0.x, and
-  older libc's will now give this error.  It doesn't *really* matter,
-  so you can just ignore it. If it really annoys you, upgrade libc
-  (and recompile any static binaries you might have that are linked
-  against the old libc).  If you're feeling lazy, just comment out
-
-  `printk(KERN_WARNING
-    "fcntl_setlk() called by process %d with broken flock() emulation\n",
-     current->pid);'
-
-  in linux/fs/locks.c and recompile.  If you're still running a.out,
-  there's an unofficial libc-4.7.6 to which you can upgrade to fix
-  this problem.
-
+   then you need to upgrade to at least libc-5.2.18 as well.  A proper
+(in other words, BSD-style ;-) flock system call was added to 2.0.x,
+and older libc's will now give this error.  It doesn't *really* matter,
+so you can just ignore it. If it really annoys you, upgrade libc (and
+recompile any static binaries you might have that are linked against
+the old libc).  If you're feeling lazy, just comment out
+
+   `                printk(KERN_WARNING
+"fcntl_setlk() called by process %d with broken flock()
+  emulation\n",                        current->pid);'
+
+   in linux/fs/locks.c and recompile.  If you're still running a.out,
+there's an unofficial libc-4.7.6 release out to which you can upgrade
+to fix this problem.  Libc is available from
+ftp://sunsite.unc.edu/pub/Linux/GCC/.
 
 GCC Signal 11 error
 ===================
@@ -278,7 +273,7 @@
 details.  Among the programs this has impacted are older sendmails.  If
 you get a message that sendmail cannot lock aliases.dir (or other
 files), you'll need to upgrade to at least 8.7.x. The latest sendmail
-is at ftp://ftp.cs.berkeley.edu/ucb/src/sendmail/sendmail.8.7.5.tar.gz.
+is at ftp://ftp.cs.berkeley.edu/ucb/src/sendmail/sendmail.8.8.2.tar.gz.
 
 Uugetty
 =======
@@ -300,7 +295,7 @@
 currently at release 2.5.  Some may find, especially when using the
 loop or xiafs file system, NFS, or automounting, that they need to
 upgrade to the latest release of mount, available from
-ftp://ftp.win.tue.nl/pub/linux/util/mount-2.5k.tar.gz.
+ftp://ftp.win.tue.nl/pub/linux/util/mount-2.5p.tar.gz.
 
 Console
 =======
@@ -434,9 +429,11 @@
    If make no longer works, you need to read the release notes for the
 libc you upgraded to.  The latest libc and release notes can be found at
 ftp://tsx-11.mit.edu/pub/linux/packages/GCC.  This is NOT an error due
-to the kernel, though many people have mistakenly thought it was.  When
-you upgrade to libc-5.3.9 or later, from previous versions, you have
-to patch make to get it to work.
+to the kernel, though many people have mistakenly thought it is.  When
+you upgrade to libc-5.3.9, you have to patch make to get it to work.
+All of this is documented in the release notes with libc.  Upgrading
+libc can also break xterm support.  If it does, you need to recompile
+xterm.
 
 Loop device
 ===========
@@ -509,7 +506,7 @@
 
    For a version of Dosemu that works (well, at least as well as DOS
 ever works ;-), get
-ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/Development/dosemu-0.63.1.55.tgz
+ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/Development/dosemu-0.64.0.3.tgz
 or check out http://www.ednet.ns.ca/auto/rddc. Be sure to follow the
 instructions in README.newkernels about patching your include files, or
 it will not compile.
@@ -583,13 +580,14 @@
    e2fsprogs 1.02 will work with the latest kernels, but it cannot be
 compiled on them.  If you need (or want) to compile your own copy,
 you'll need to get the latest version, currently available at
-ftp://tsx-11.mit.edu/pub/linux/packages/ext2fs/e2fsprogs-1.04.tar.gz.
+ftp://tsx-11.mit.edu/pub/linux/packages/ext2fs/e2fsprogs-1.06.tar.gz.
 
 How to know the version of the installed programs
 *************************************************
 
    There are some simple methods useful to know the version of the
-installed programs and libraries.
+installed programs and libraries.  The SysVinit version display
+requires that you be logged in as root.
 
 GNU CC: gcc -v and gcc --version
 PPP: pppd -h (wrong but it show the version)
@@ -600,6 +598,8 @@
 termcap: ls -l /lib/libtermcap.so.*
 modules: insmod -V
 procps: ps --version
+SysVinit: cat /proc/`cat /var/run/syslog.pid`/environ|strings|awk '$1 ~
+/INIT_VERSION/ {print}'
 
 Where to get the files
 **********************
@@ -607,29 +607,29 @@
 Binutils
 ========
 
-ftp://sunsite.unc.edu/pub/Linux/GCC/binutils-2.6.0.14.bin.tar.gz
+ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/binutils-2.7.0.3.bin.tar.gz
 Installation notes:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.binutils-2.6.0.14
+ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/release.binutils-2.7.0.3
 
 GNU CC
 ======
 
-ftp://sunsite.unc.edu/pub/Linux/GCC/gcc-2.7.2.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/gcc-2.7.2.1.bin.tar.gz
 Installation notes:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.gcc-2.7.2
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.gcc-2.7.2.1
 
 Linux C Library
 ===============
 
-The stable 5.4.7 release:
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.7.bin.tar.gz
-Installation notes for 5.4.7:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.7
-
-The latest release:
-ftp://sunsite.unc.edu/pub/Linux/GCC/private/tofu/libc-x.y.z.bin.tar.gz
-Installation notes for x.y.z:
-ftp://sunsite.unc.edu/pub/Linux/GCC/private/tofu/release.libc-x.y.z
+The stable 5.2.18 release:
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.2.18.bin.tar.gz
+Installation notes for 5.2.18:
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.2.18
+
+The latest 5.4.10 release:
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.10.bin.tar.gz
+Installation notes for 5.4.10:
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.10
 
 Patched make sources:
 ftp://sunsite.unc.edu/pub/Linux/devel/make/make-3.74.patched.tar.gz
@@ -640,15 +640,8 @@
 =================
 
 ftp://sunsite.unc.edu/pub/Linux/GCC/libg++-2.7.2.1.bin.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/libg++-2.7.1.4.bin.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/libg++-2.7.1.3.bin.tar.gz
 Installation notes:
 ftp://sunsite.unc.edu/pub/Linux/GCC/release.libg++-2.7.2.1
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libg++-2.7.1.4
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libg++-2.7.1.3
-Use libg++-2.7.2.1 with libc-5.4.4 and above,
-    libg++-2.7.1.4 with libc-5.3.5 to libc-5.4.3,
-    libg++-2.7.1.3 with libc 5.2.14 to libc-5.3.4
 
 Dynamic Linker
 ==============
@@ -721,11 +714,9 @@
    For others, David Bourgin has put together a package of everything
 necessary to quickly and easily upgrade to 2.0.x.  See
 ftp://ftp.wsc.com/pub/freeware/linux/update.linux/ for more information
-and the files.  This package also includes many bug-fixes, including
-one for a recently discovered bug in sendmail-8.7.5 (just look at
-/pub/freeware/linux/update.linux/updat2-0.addon1/sendmail-8.7.5a.tar.gz
-if you only need the bug fix).  There's also an alternate lightweight
-termcap in the same directory that works well for many people.
+and the files.  This package also includes many bug-fixes, such as the
+latest sendmail. There's also an alternate lightweight termcap in the
+same directory that works well for many people.
 
 Please send info about any other packages that 2.0.x "broke" or about
 any new features of 2.0.x that require extra or new packages for use to
@@ -733,3 +724,4 @@
 modified texinfo setup, so you don't need to bother generating a diff
 against the current version before you send the additional information
 to me.
+
diff -u --recursive --new-file v2.1.8/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.8/linux/Documentation/Configure.help	Sun Nov 10 20:12:07 1996
+++ linux/Documentation/Configure.help	Tue Nov 12 13:08:43 1996
@@ -3813,6 +3813,16 @@
   machine see http://cap.anu.edu.au/cap/projects/linux or mail to
   hackers@cafe.anu.edu.au
 
+Video mode selection support
+CONFIG_VIDEO_SELECT
+  This enables support for text mode selection on kernel startup. If you
+  want to take advantage of some high-resolution text mode your card's
+  BIOS offers, but the traditional Linux utilities like SVGATextMode
+  don't, you can enable this and set the mode using the "vga=" option
+  from your boot loader (LILO or LOADLIN) or set "vga=ask" which brings
+  up a video mode menu on kernel startup. Read Documentation/svga.txt
+  for more information. If unsure, say "n".
+
 # need an empty line after last entry, for sed script in Configure.
 
 #
diff -u --recursive --new-file v2.1.8/linux/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex
--- v2.1.8/linux/Documentation/cdrom/cdrom-standard.tex	Wed Aug 14 10:21:03 1996
+++ linux/Documentation/cdrom/cdrom-standard.tex	Tue Nov 12 10:32:39 1996
@@ -1,5 +1,5 @@
 \documentclass{article}
-\def\version{$Id: cdrom-standard.tex,v 0.8 1996/08/10 10:57:16 david Exp $}
+\def\version{$Id: cdrom-standard.tex,v 1.2 1996/09/22 20:18:00 david Exp $}
 
 \evensidemargin=0pt
 \oddsidemargin=0pt
@@ -138,7 +138,7 @@
 \section{Standardizing through another software level}
 \label{cdrom.c}
 
-At the time this document is written, all drivers directly implement
+At the time this document was conceived, all drivers directly implement
 the $ioctl()$ calls through their own routines, with the danger of
 forgetting calls to $verify_area()$ and the risk of divergence in
 implementation. 
@@ -160,7 +160,8 @@
 
 The extra interfacing level routines are implemented in a file
 \cdromc, and a low-level \cdrom\ driver hands over the interfacing to
-the kernel by registering the following general $struct\ file_operations$:
+the kernel by registering the following general $struct\ 
+file_operations$:
 $$
 \halign{$#$\ \hfil&$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
 struct& file_operations\ cdrom_fops = \{\hidewidth\cr
@@ -189,97 +190,150 @@
 are typical to \cdrom\ (removable-media) devices.
 
 Registration of the \cdrom\ device driver should now be to the general
-routines in \cdromc, not to the VFS any more. This is done though the
-call
-$$register_cdrom(int\ major, char * name, 
-  struct\ cdrom_device_ops\ device_options)
-$$
+routines in \cdromc, not to the VFS any more. The interfacing with
+\cdromc\ is implemented trough two general structures, that contain
+information about the capabilities of the driver, and the specific
+drives on which the driver operates. The structures are seperated to
+contain information about
+\begin{description}
+\item[the low-level driver] It lists the routines that actually
+  implement cdrom operations, and hence the structure is called
+  $cdrom_device_ops$. The structure is conceptually connected to the
+  major number of the device (although some drivers may have have
+  different major numbers, as is the case for the IDE driver).
+\item[the specific drive] It lists the variables informative of the
+  drive that is driven, and hence the structure is called
+  $cdrom_device_info$. The structure is conceptually connected to the
+  minor number of the device.
+\end{description}
 
-The device operations structure lists the implemented routines for
-interfacing to the hardware, and some specifications of capabilities
-of the device, such as the maximum head-transfer rate.  [It is
-impossible to come up with a complete list of all capabilities of
-(future) \cdrom\ drives, as the developments in technology follow-up
-at an incredible rate. Maybe write-operation (WORM devices) will
-become very popular in the future.]  The list now is:
+The registration is done for each drive found by the driver (and hence
+for each minor number) though the call
+$$register_cdrom(kdev_t\ dev, char * name, 
+  struct\ cdrom_device_info\ <device>_info)
+$$
+This device information structure (described shortly) contains all
+information needed for the kernel to interface with the low-level
+cdrom device driver. One of the main entries of this structure is a
+pointer to the $cdrom_device_ops$ structure of the driver.
+
+This device operations structure lists the implemented routines for
+interfacing to the hardware. [It is impossible to come up with a
+complete list of all capabilities of (future) \cdrom\ drives, as the
+developments in technology follow-up at an incredible rate. Maybe
+write-operation (WORM devices) will become very popular in the
+future.]  The list now is:
 $$
 \halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}&
   $/*$ \rm# $*/$\hfil\cr
 struct& cdrom_device_ops\ \{ \hidewidth\cr
-  &int& (* open)(kdev_t, int)\cr
-  &void& (* release)(kdev_t);\cr 
-  &int& (* open_files)(kdev_t);  \cr
-  &int& (* drive_status)(kdev_t);\cr     
-  &int& (* disc_status)(kdev_t);\cr      
-  &int& (* media_changed)(kdev_t);\cr 
-  &int& (* tray_move)(kdev_t, int);\cr
-  &int& (* lock_door)(kdev_t, int);\cr
-  &int& (* select_speed)(kdev_t, int);\cr
-  &int& (* select_disc)(kdev_t, int);\cr
-  &int& (* get_last_session) (kdev_t, struct\ cdrom_multisession *{});\cr
-  &int& (* get_mcn)(kdev_t, struct\ cdrom_mcn *{});\cr
-  &int& (* reset)(kdev_t);\cr
-  &int& (* audio_ioctl)(kdev_t, unsigned\ int, void *{});\cr 
-  &int& (* dev_ioctl)(kdev_t, unsigned\ int, unsigned\ long);\cr
+  &int& (* open)(struct\ cdrom_device_info *, int)\cr
+  &void& (* release)(struct\ cdrom_device_info *);\cr 
+  &int& (* drive_status)(struct\ cdrom_device_info *);\cr     
+  &int& (* disc_status)(struct\ cdrom_device_info *);\cr      
+  &int& (* media_changed)(struct\ cdrom_device_info *, int);\cr 
+  &int& (* tray_move)(struct\ cdrom_device_info *, int);\cr
+  &int& (* lock_door)(struct\ cdrom_device_info *, int);\cr
+  &int& (* select_speed)(struct\ cdrom_device_info *, int);\cr
+  &int& (* select_disc)(struct\ cdrom_device_info *, int);\cr
+  &int& (* get_last_session) (struct\ cdrom_device_info *, struct\ cdrom_multisession *{});\cr
+  &int& (* get_mcn)(struct\ cdrom_device_info *, struct\ cdrom_mcn *{});\cr
+  &int& (* reset)(struct\ cdrom_device_info *);\cr
+  &int& (* audio_ioctl)(struct\ cdrom_device_info *, unsigned\ int, void *{});\cr 
+  &int& (* dev_ioctl)(struct\ cdrom_device_info *, unsigned\ int, unsigned\ long);\cr
 \noalign{\medskip}
   &\llap{const\ }int& capability;&  capability flags \cr
-  &int& mask;& mask of capability: disables them \cr
-  &\llap{$const\ $}int& speed;&  maximum speed for reading data \cr
-  &\llap{$const\ $}int& minors;& number of supported minor devices \cr
-  &\llap{$const\ $}int& capacity;& number of discs in jukebox \cr
-\noalign{\medskip}
-  &int& options;& options flags \cr
-  &long& mc_flags;& media-change buffer flags ($2\times16$) \cr
-\}\cr
+  &int& n_minors;& number of supported minor devices \cr
 }
-$$ The \cdrom-driver should simply implement (some of) these
+$$
+The \cdrom-driver should simply implement (some of) these
 functions, and register the functions to the global \cdrom\ driver,
 which performs interfacing with the Virtual File System and system
 $ioctl$s. The flags $capability$ specify the hardware-capabilities on
-registration of the device, the flags $mask$ can be used to mask some
-of those capabilities (for one reason or another). The value $minors$
-should be a positive value indicating the number of minor devices that
-are supported by the driver, normally~1.  (They are supposed to be
-numbered from 0 upwards). The value $capacity$ should be the number of
-discs the drive can hold simultaneously, if it is designed as a
-juke-box, or otherwise~1.
-
-Two registers contain variables local to the \cdrom\ device. The flags
-$options$ are used to specify how the general \cdrom\ routines
-should behave. These various flags registers should provide enough
-flexibility to adapt to the different user's wishes (and {\em not\/}
-the `arbitrary' wishes of the author of the low-level device driver,
-as is the case in the old scheme). The register $mc_flags$ is used to
-buffer the information from $media_changed()$ to two separate queues. 
+registration of the device. The value $n_minors$ should be a positive
+value indicating the number of minor devices that are supported by the
+driver, normally~1. Although these two variables are `informative'
+rather than `operational,' they are included in $cdrom_device_ops$
+because they describe the cabability of the {\em driver\/} rather than
+the {\em drive}. Nomenclature has always been difficult in computer
+programming. 
 
 Note that most functions have fewer parameters than their
 $blkdev_fops$ counterparts. This is because very little of the
 information in the structures $inode$ and $file$ are used, the main
 parameter is the device $dev$, from which the minor-number can be
 extracted. (Most low-level \cdrom\ drivers don't even look at that value
-as only one device is supported.)
+as only one device is supported.) This will be available through $dev$
+in $cdrom_device_info$ described below. 
+
+The drive-specific, minor-like information that is registered to
+\cdromc, contains the following fields:
+$$
+\halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}&
+  $/*$ \rm# $*/$\hfil\cr
+struct& cdrom_device_info\ \{ \hidewidth\cr
+  & \llap{$const\ $}struct\ cdrom_device_ops *& ops;& device operations for this major\cr
+  & struct\ cdrom_device_info *& next;& next device_info for this major\cr
+  & void *&  handle;& driver-dependent data\cr
+\noalign{\medskip}
+  & \llap{$const\ $}kdev_t&  dev;& device number (incorporates minor)/\cr
+  & int& mask;& mask of capability: disables them \cr
+  &\llap{$const\ $}int& speed;&  maximum speed for reading data \cr
+  &\llap{$const\ $}int& n_discs;& number of discs in jukebox \cr
+\noalign{\medskip}
+  &int& options : 30;& options flags \cr
+  &long& mc_flags : 2;& media-change buffer flags \cr
+  & int& use_count;& number of times devices is opened\cr
+\}\cr
+}$$
+
+With this $struct$, a linked list of minor devices registrered with
+the same low-level driver is built, though the field $next$.  The
+device number, the device operations struct and specifications of
+properties of the drive are stored in this structure.
+
+The flags $mask$ can be used to mask out some of the capabilities
+listed in $ops\to capability$, if a specific drive doesn't support a
+feature of the driver. The value $speed$ specifies the maximum
+head-rate of the drive, measured in units of normal audio speed
+(176\,kB/sec raw data or 150\,kB/sec filesystem data).  The value
+$n_discs$ should reflect the number of discs the drive can hold
+simultaneously, if it is designed as a juke-box, or otherwise~1.
+The parameters are declared $const$ because they describe properties
+of the drive, which don't change after registration. 
+
+A few registers contain variables local to the \cdrom\ drive. The
+flags $options$ are used to specify how the general \cdrom\ routines
+should behave. These various flags registers should provide enough
+flexibility to adapt to the different user's wishes (and {\em not\/}
+the `arbitrary' wishes of the author of the low-level device driver,
+as is the case in the old scheme). The register $mc_flags$ is used to
+buffer the information from $media_changed()$ to two separate queues.
+Other data that is specific to minor drive, can be accessed through
+$handle$, which can point to a data structure specific to the
+low-level driver. The fields $use_count$, $next$, $options$ and
+$mc_flags$ need not be initialized.
 
 The intermediate software layer that \cdromc\ forms will performs some
-additional bookkeeping. The minor number of the device is checked
-against the maximum registered in $<device>_dops$. The function
-$cdrom_ioctl()$ will verify the appropriate user-memory regions for
-read and write, and in case a location on the CD is transferred, it
-will `sanitize' the format by making requests to the low-level drivers
-in a standard format, and translating all formats between the
-user-software and low level drivers. This relieves much of the drivers
-memory checking and format checking and translation. Also, the
-necessary structures will be declared on the program stack.
+additional bookkeeping. The use count of the device (the number of
+processes that have the device opened) is registered in $use_count$.
+The function $cdrom_ioctl()$ will verify the appropriate user-memory
+regions for read and write, and in case a location on the CD is
+transferred, it will `sanitize' the format by making requests to the
+low-level drivers in a standard format, and translating all formats
+between the user-software and low level drivers. This relieves much of
+the drivers memory checking and format checking and translation. Also,
+the necessary structures will be declared on the program stack.
 
 The implementation of the functions should be as defined in the
-following sections. Three functions {\em must\/} be implemented,
-namely $open()$, $release()$ and $open_files()$. Other functions may
-be omitted, their corresponding capability flags will be cleared upon
-registration. Generally, a function returns zero on success and
-negative on error. A function call should return only after the
-command has completed, but of course waiting for the device should not
-use processor time.
+following sections. Two functions {\em must\/} be implemented, namely
+$open()$ and $release()$. Other functions may be omitted, their
+corresponding capability flags will be cleared upon registration.
+Generally, a function returns zero on success and negative on error. A
+function call should return only after the command has completed, but
+of course waiting for the device should not use processor time.
 
-\subsection{$Open(kdev_t\ dev, int\ purpose)$}
+\subsection{$Open(struct\ cdrom_device_info * cdi, int\ purpose)$}
 
 $Open()$ should try to open the device for a specific $purpose$, which
 can be either:
@@ -289,36 +343,27 @@
 \item[1] Open for $ioctl$ commanding, as is used for audio-CD playing
 programs mostly. 
 \end{itemize}
-In this routine, a static counter should be updated, reflecting the
-number of times the specific device is successfully opened (and in
-case the driver supports modules, the call $MOD_INC_USE_COUNT$
-should be performed exactly once, if successful). The return value is
-negative on error, and zero on success. The open-for-ioctl call can
-only fail if there is no hardware.
+In case the driver supports modules, the call $MOD_INC_USE_COUNT$
+should be performed exactly once, if the $open()$ was successful. The
+return value is negative on error, and zero on success. The
+open-for-ioctl call can only fail if there is no hardware.
 
 Notice that any strategic code (closing tray upon $open()$, etc.)\ is
 done by the calling routine in \cdromc, so the low-level routine
 should only be concerned with proper initialization and device-use
 count.
 
-\subsection{$Release(kdev_t\ dev)$}
+\subsection{$Release(struct\ cdrom_device_info * cdi)$}
 
-The use-count of the device $dev$ should be decreased by 1, and a
-single call $MOD_DEC_USE_COUNT$ should be coded here.  Possibly other
-device-specific actions should be taken such as spinning down the
-device. However, strategic actions such as ejection of the tray, or
-unlocking the door, should be left over to the general routine
-$cdrom_release()$. Also, the invalidation of the allocated buffers in
-the VFS is taken care of by the routine in \cdromc.
-
-\subsection{$Open_files(kdev_t\ dev)$}
-
-This function should return the internal variable use-count of the
-device $dev$. The use-count is not implemented in the routines in
-\cdromc\ itself, because there may be many minor devices connected to
-a single low-level driver.
+In case of module support, a single call $MOD_DEC_USE_COUNT$ should be
+coded here.  Possibly other device-specific actions should be taken
+such as spinning down the device. However, strategic actions such as
+ejection of the tray, or unlocking the door, should be left over to
+the general routine $cdrom_release()$. Also, the invalidation of the
+allocated buffers in the VFS is taken care of by the routine in
+\cdromc.
 
-\subsection{$Drive_status(kdev_t\ dev)$}
+\subsection{$Drive_status(struct\ cdrom_device_info * cdi)$}
 \label{drive status}
 
 The function $drive_status$, if implemented, should provide
@@ -334,15 +379,19 @@
 CDS_DISC_OK& a disc is loaded and everything is fine\cr
 }
 $$
+%For a juke-box, the second argument $drive_nr$ specifies information
+%is requested for another than the default disc ($drive_nr=0$),
+%possibly only a subset of the return values can be returned.
 
-\subsection{$Disc_status(kdev_t\ dev)$}
+\subsection{$Disc_status(struct\ cdrom_device_info * cdi)$}
 \label{disc status}
 
 As a complement to $drive_status()$, this function can provide the
-general \cdrom-routines with information about the current disc that is
-inserted in the drive represented by $dev$. The history of development
-of the CD's use as a carrier medium for various digital information
-has lead to many different disc types, hence this function can return:
+general \cdrom-routines with information about the current disc that
+is inserted in the drive represented by $cdi\to dev$. The history of
+development of the CD's use as a carrier medium for various digital
+information has lead to many different disc types, hence this function
+can return:
 $$
 \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
 CDS_NO_INFO& no information available\cr
@@ -358,16 +407,18 @@
 some information concerning frame layout of the various disc types, see
 a recent version of {\tt cdrom.h}. 
 
-\subsection{$Media_changed(dev\_t\ dev)$}
+\subsection{$Media_changed(struct\ cdrom_device_info * cdi, int disc_nr)$}
 
-This function is very similar to the original function in $struct\
-file_operations$. It returns 1 if the medium of the device $dev$ has
-changed since the last call, and 0 otherwise. Note that by `re-routing'
-this function through $cdrom_media_changed()$, we can implement
-separate queues for the VFS and a new $ioctl()$ function that can
-report device changes to software (e.g., an auto-mounting daemon). 
+This function is very similar to the original function in $struct\ 
+file_operations$. It returns 1 if the medium of the device $cdi\to
+dev$ has changed since the last call, and 0 otherwise. The parameter
+$disc_nr$ identifies a specific slot in a juke-box, it should be
+ignored for single-disc drives.  Note that by `re-routing' this
+function through $cdrom_media_changed()$, we can implement separate
+queues for the VFS and a new $ioctl()$ function that can report device
+changes to software (e.g., an auto-mounting daemon).
 
-\subsection{$Tray_move(kdev_t\ dev, int\ position)$}
+\subsection{$Tray_move(struct\ cdrom_device_info * cdi, int\ position)$}
 
 This function, if implemented, should control the tray movement. (No
 other function should control this.) The parameter $position$ controls
@@ -380,7 +431,7 @@
 error. Note that if the tray is already in the desired position, no
 action need be taken, and the return value should be 0. 
 
-\subsection{$Lock_door(kdev_t\ dev, int\ lock)$}
+\subsection{$Lock_door(struct\ cdrom_device_info * cdi, int\ lock)$}
 
 This function (and no other code) controls locking of the door, if the
 drive allows this. The value of $lock$ controls the desired locking
@@ -391,7 +442,7 @@
 \end{itemize}
 Return values are as for $tray_move()$.
 
-\subsection{$Select_speed(kdev_t\ dev, int\ speed)$}
+\subsection{$Select_speed(struct\ cdrom_device_info * cdi, int\ speed)$}
 
 Although none of the drivers has implemented this function so far,
 some drives are capable of head-speed selection, and hence this is a
@@ -407,48 +458,48 @@
 high-speed copying of audio tracks). Badly pressed \cdrom s may
 benefit from less-than-maximum head rate.
 
-\subsection{$Select_disc(kdev_t\ dev, int\ number)$}
+\subsection{$Select_disc(struct\ cdrom_device_info * cdi, int\ number)$}
 
-If the drive can store multiple discs (a juke-box), it is likely that
-a disc selection can be made by software. This function should perform
-disc selection. It should return the number of the selected disc on
-success, a negative value on error. Currently, none of the \linux\ 
-\cdrom\ drivers appears to support such functionality, but it is defined
-here for future purposes.
+If the drive can store multiple discs (a juke-box) this function
+should perform disc selection. It should return the number of the
+selected disc on success, a negative value on error. Currently, only
+the IDE-cd driver supports such functionality.
 
-\subsection{$Get_last_session(kdev_t\ dev, struct\ cdrom_multisession *
-ms_info)$}
+\subsection{$Get_last_session(struct\ cdrom_device_info * cdi, struct\
+  cdrom_multisession * ms_info)$}
 
 This function should implement the old corresponding $ioctl()$. For
-device $dev$, the start of the last session of the current disc should
-be returned in the pointer argument $ms_info$. Note that routines in \cdromc\ have sanitized this argument: its
-requested format will {\em always\/} be of the type $CDROM_LBA$
-(linear block addressing mode), whatever the calling software
-requested. But sanitization goes even further: the low-level
-implementation may return the requested information in $CDROM_MSF$
-format if it wishes so (setting the $ms_info\rightarrow addr_format$
-field appropriately, of course) and the routines in \cdromc\ will make
-the transform if necessary. The return value is 0 upon success.
+device $cdi->dev$, the start of the last session of the current disc
+should be returned in the pointer argument $ms_info$. Note that
+routines in \cdromc\ have sanitized this argument: its requested
+format will {\em always\/} be of the type $CDROM_LBA$ (linear block
+addressing mode), whatever the calling software requested. But
+sanitization goes even further: the low-level implementation may
+return the requested information in $CDROM_MSF$ format if it wishes so
+(setting the $ms_info\rightarrow addr_format$ field appropriately, of
+course) and the routines in \cdromc\ will make the transform if
+necessary. The return value is 0 upon success.
 
-\subsection{$Get_mcn(kdev_t\ dev, struct\ cdrom_mcn * mcn)$}
+\subsection{$Get_mcn(struct\ cdrom_device_info * cdi, struct\
+  cdrom_mcn * mcn)$}
 
 Some discs carry a `Media Catalog Number' (MCN), also called
-`Universal Product Code' (UPC). This number should reflect the number that
-is generally found in the bar-code on the product. Unfortunately, the
-few discs that carry such a number on the disc don't even use the same
-format. The return argument to this function is a pointer to a
+`Universal Product Code' (UPC). This number should reflect the number
+that is generally found in the bar-code on the product. Unfortunately,
+the few discs that carry such a number on the disc don't even use the
+same format. The return argument to this function is a pointer to a
 pre-declared memory region of type $struct\ cdrom_mcn$. The MCN is
 expected as a 13-character string, terminated by a null-character.
 
-\subsection{$Reset(kdev_t dev)$}
+\subsection{$Reset(struct\ cdrom_device_info * cdi)$}
 
 This call should implement hard-resetting the drive (although in
 circumstances that a hard-reset is necessary, a drive may very well
 not listen to commands anymore). Preferably, control is returned to the
 caller only after the drive has finished resetting.
 
-\subsection{$Audio_ioctl(kdev_t\ dev, unsigned\ int\ cmd, void *
-arg)$}
+\subsection{$Audio_ioctl(struct\ cdrom_device_info * cdi, unsigned\
+  int\ cmd, void * arg)$}
 
 Some of the \cdrom-$ioctl$s defined in {\tt cdrom.h} can be
 implemented by the routines described above, and hence the function
@@ -471,8 +522,8 @@
 may decide to sanitize the return value in $cdrom_ioctl()$, in order
 to guarantee a uniform interface to the audio-player software.)
 
-\subsection{$Dev_ioctl(kdev_t\ dev, unsigned\ int\ cmd, unsigned\ long\
-arg)$}
+\subsection{$Dev_ioctl(struct\ cdrom_device_info * cdi, unsigned\ int\
+  cmd, unsigned\ long\ arg)$}
 
 Some $ioctl$s seem to be specific to certain \cdrom\ drives. That is,
 they are introduced to service some capabilities of certain drives. In
@@ -485,17 +536,18 @@
 so either the audio-file-system should ask for 75264 bytes at once
 (the least common multiple of 512 and 2352), or the drivers should
 bend their backs to cope with this incoherence (to which I would be
-opposed). Once this question is resolved, this code should be standardized in 
-\cdromc.
+opposed). Once this question is resolved, this code should be
+standardized in \cdromc.
 
 Because there are so many $ioctl$s that seem to be introduced to
-satisfy certain drivers,\footnote{Is there software around that actually uses
-these? I'd be interested!} any `non-standard' $ioctl$s are routed through
-the call $dev_ioctl()$. In principle, `private' $ioctl$s should be
-numbered after the device's major number, and not the general \cdrom\
-$ioctl$ number, {\tt 0x53}. Currently the non-supported $ioctl$s are:
-{\it CDROMREADMODE1, CDROMREADMODE2, CDROMREADAUDIO, CDROMREADRAW,
-CDROMREADCOOKED, CDROMSEEK, CDROMPLAY\-BLK and CDROMREADALL}. 
+satisfy certain drivers,\footnote{Is there software around that
+  actually uses these? I'd be interested!} any `non-standard' $ioctl$s
+are routed through the call $dev_ioctl()$. In principle, `private'
+$ioctl$s should be numbered after the device's major number, and not
+the general \cdrom\ $ioctl$ number, {\tt 0x53}. Currently the
+non-supported $ioctl$s are: {\it CDROMREADMODE1, CDROMREADMODE2,
+  CDROMREADAUDIO, CDROMREADRAW, CDROMREADCOOKED, CDROMSEEK,
+  CDROMPLAY\-BLK and CDROMREADALL}.
 
 \subsection{\cdrom\ capabilities}
 
@@ -518,24 +570,24 @@
 }
 $$
 The capability flag is declared $const$, to prevent drivers from
-accidentally tampering with the contents. However, upon registration,
-some (claimed) capability flags may be cleared if the supporting
-function has not been implemented (see $register_cdrom()$ in
-\cdromc). 
-
-If you want to disable any of the capabilities, there is a special
-flag register $<device>_dops.mask$ that may (temporarily) disable
-certain capabilities. In the file \cdromc\ you will encounter many
-constructions of the type 
+accidentally tampering with the contents. The capability fags actually
+inform \cdromc\ on what the driver is capable of. If the drive found
+by the driver does not have the capability, is can be masked out by
+the $cdrom_device_info$ variable $mask$. For instance, the SCSI cdrom
+driver has implemeted the code for loading and ejecting cdrom's, and
+hence its corresponding flags in $capability$ will be set. But a SCSI
+cdrom drive might be a caddy system, which can't load the tray, and
+hence for this drive the $cdrom_device_info$ struct will have set
+the $CDC_CLOSE_TRAY$ bit in $mask$.
+
+In the file \cdromc\ you will encounter many constructions of the type
 $$\it
-if\ (cdo\rightarrow capability \mathrel\& \mathord{\sim} cdo\rightarrow mask 
+if\ (cdo\rightarrow capability \mathrel\& \mathord{\sim} cdi\rightarrow mask 
    \mathrel{\&} CDC_<capability>) \ldots
 $$
-The $mask$ could be set in the low-level driver code to disable
-certain capabilities for special brands of the device that can't
-perform the actions.  However, there is not (yet) an $ioctl$ to set
-the mask\dots The reason is that I think it is better to control the
-{\em behavior\/} rather than the {\em capabilities}.
+There is no $ioctl$ to set the mask\dots The reason is that
+I think it is better to control the {\em behavior\/} rather than the
+{\em capabilities}.
 
 \subsection{Options}
 
@@ -707,22 +759,37 @@
 register_blkdev(major, <name>, \&cdrom_fops);
 $$
 
-\subsection{$Int\ register_cdrom(int\ major, char * name, struct\
-cdrom_device_ops\ * cdo)$}
+\subsection{$Int\ register_cdrom(kdev_t\ dev, char * name, struct\
+cdrom_device_info\ * cdi)$}
 
 Similar to registering $cdrom_fops$ to the kernel, the device
-operations structure, as described in section~\ref{cdrom.c}, should be
-registered to the general \cdrom\ interface:
-$$
-register_cdrom(major, <name>, \&<device>_dops);
-$$
-This function returns zero upon success, and non-zero upon failure. 
-
-\subsection{$Int\ unregister_cdrom(int\ major, char * name)$}
-
-Unregistering device $name$ with major number $major$ disconnects the
-registered device-operation routines from the \cdrom\ interface.
-This function returns zero upon success, and non-zero upon failure. 
+operations and information structures, as described in
+section~\ref{cdrom.c}, should be registered to the general \cdrom\ 
+interface:
+$$
+register_cdrom(dev, <name>, \&<device>_info);
+$$
+This function returns zero upon success, and non-zero upon
+failure. The structure $<device>_info$ should have a pointer the
+driver's $<device>_dops$, as in 
+$$
+\vbox{\halign{&$#$\hfil\cr
+struct\ &cdrom_device_info\ <device>_info = \{\cr
+& <device>_dops;\cr
+&\ldots\cr
+\}\cr
+}}$$
+Note that a drivers has one static structure, $<device>_dops$, while
+it has as many structures $<device>_info$ as there are minor devices
+active. $Register_cdrom()$ builds a linked list from these. 
+
+\subsection{$Int\ unregister_cdrom(struct\ cdrom_device_info * cdi)$}
+
+Unregistering device $cdi$ with minor number $MINOR(cdi\to dev)$
+removes the minor device from the list. If it was the last minor for
+the driver, this disconnects the registered device-operation routines
+from the \cdrom\ interface. This function returns zero upon success,
+and non-zero upon failure.
 
 \subsection{$Int\ cdrom_open(struct\ inode * ip, struct\ file * fp)$}
 
@@ -786,7 +853,7 @@
 \item[CDROMPLAYMSF] Play audio fragment specified in Minute, Second,
 Frame format, delimited by $arg$ of type $struct\ cdrom_msf *{}$.
 \item[CDROMPLAYTRKIND] Play audio fragment in track-index format
-delimited by $arg$ of type $struct cdrom_ti *{}$.
+delimited by $arg$ of type $struct\ cdrom_ti *{}$.
 \item[CDROMVOLCTRL] Set volume specified by $arg$ of type $struct\
 cdrom_volctrl *{}$.
 \item[CDROMVOLREAD] Read volume into by $arg$ of type $struct\
@@ -887,12 +954,18 @@
 
 \section{Thanks}
 
-Thanks to all the people involved. Thanks to Thomas Quinot, Jon Tombs,
-Ken Pizzini, Eberhard M\"onkeberg and Andrew Kroll, the \linux\ 
-\cdrom\ device driver developers who were kind enough to give
+Thanks to all the people involved. Thanks to Scott Snyder and Gerd
+Knorr, who were the first to implement this interface for SCSI and
+IDE-CD drivers and added many ideas for extension of the data
+structures relative to kernel~2.0. Further thanks to Thomas Quinot,
+Jon Tombs, Ken Pizzini, Eberhard M\"onkeberg and Andrew Kroll, the
+\linux\ \cdrom\ device driver developers who were kind enough to give
 suggestions and criticisms during the writing. Finally of course, I
 want to thank Linus Torvalds for making this possible in the first
 place.
 
+\vfill
+$\version$
+\eject
 \end{document}
 
diff -u --recursive --new-file v2.1.8/linux/Documentation/cdrom/ide-cd linux/Documentation/cdrom/ide-cd
--- v2.1.8/linux/Documentation/cdrom/ide-cd	Wed Sep 25 11:11:47 1996
+++ linux/Documentation/cdrom/ide-cd	Tue Nov 12 10:32:40 1996
@@ -32,10 +32,11 @@
    function; the only ones which i've heard of successes with are Sony
    and Toshiba drives.
 
- - There is now rudimentary support for cdrom changers which comply
-   with the ATAPI 2.6 draft standard (such as the NEC CDR-251).  This
-   merely adds a function to switch between the slots of the changer
-   under control of an external program.  A sample such program is
+ - There is now support for cdrom changers which comply with the 
+   ATAPI 2.6 draft standard (such as the NEC CDR-251).  This additional
+   functionality includes a function call to query which slot is the
+   currently selected slot, a function call to query which slots contain
+   CDs, etc. A sample program which demonstrates this functionality is
    appended to the end of this file.  The Sanyo 3-disc changer
    (which does not conform to the standard) is also now supported.
    Please note the driver refers to the first CD as slot # 0.
@@ -150,11 +151,11 @@
 this are Sony and Toshiba drives.  You will get errors if you try to
 use this function on a drive which does not support it.
 
-For supported changers, you can use the `cdload' program (appended to
+For supported changers, you can use the `cdchange' program (appended to
 the end of this file) to switch between changer slots.  Note that the
 drive should be unmounted before attempting this.  The program takes
-two arguments: the cdrom device, and the slot number to which to change.
-If the slot number is -1, the drive is unloaded.
+two arguments: the cdrom device, and the slot number to which you wish
+to change.  If the slot number is -1, the drive is unloaded.
 
 
 4. Compilation options
@@ -360,16 +361,22 @@
     expense of low system performance.
 
 
-6. cdload.c
------------
+6. cdchange.c
+-------------
 
 /*
- * cdload.c  <device>  <slot>
+ * cdchange.c  [-v]  <device>  [<slot>]
  *
- * Load a cdrom from a specified slot in a changer.  The drive should be
- * unmounted before executing this.
+ * This load a cdrom from a specified slot in a changer, and displays 
+ * information about the changer status.  The drive should be unmounted before 
+ * using this program.
+ *
+ * Changer information is displayed if either the -v flag is specified
+ * or no slot was specified.
  *
  * Based on code originally from Gerhard Zuber <zuber@berlin.snafu.de>.
+ * Changer status information, and rewrite for the new common cdrom driver
+ * interface by Erik Andersen <andersee@et.byu.edu>.
  */
 
 #include <stdlib.h>
@@ -377,7 +384,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <stdio.h>
-#include <linux/cdrom.h>
 #include <linux/ucdrom.h>
 
 
@@ -386,19 +392,34 @@
 {
 	char *program;
 	char *device;
-	int x_slot;
 	int fd;           /* file descriptor for CD-ROM device */
 	int status;       /* return status for system calls */
+	int verbose = 0;
+	int x_slot = -1;
+	int total_slots_available;
 
 	program = argv[0];
 
-	if (argc != 3) {
-		fprintf (stderr, "usage: %s <device> <slot>\n", program);
+	++argv;
+	--argc;
+
+	if (argc < 1 || argc > 3) {
+		fprintf (stderr, "usage: %s [-v] <device> [<slot>]\n",
+			 program);
+		fprintf (stderr, "       Slots are numbered 1 -- n.\n");
 		exit (1);
 	}
-
-	device = argv[1];
-	x_slot = atoi (argv[2]);
+ 
+       if (strcmp (argv[0], "-v") == 0) {
+                verbose = 1;
+                ++argv;
+                --argc;
+        }
+ 
+	device = argv[0];
+ 
+	if (argc == 2)
+		x_slot = atoi (argv[1]) - 1;
 
 	/* open device */ 
 	fd = open (device, 0);
@@ -408,15 +429,77 @@
 		exit (1);
 	}
 
-	/* load */ 
-	status = ioctl (fd, CDROM_SELECT_DISC, x_slot);
-	if (status != 0) {
-		fprintf (stderr,
-			 "%s: CDROM_SELECT_DISC ioctl failed for `%s': %s\n",
-			 program, device, strerror (errno));
+	/* Check CD player status */ 
+	total_slots_available = ioctl (fd, CDROM_CHANGER_NSLOTS);
+	if (total_slots_available <= 1 ) {
+		fprintf (stderr, "%s: Device `%s' is not an ATAPI "
+			"compliant CD changer.\n", program, device);
 		exit (1);
 	}
- 
+
+	if (x_slot >= 0) {
+		if (x_slot >= total_slots_available) {
+			fprintf (stderr, "Bad slot number.  "
+				 "Should be 1 -- %d.\n",
+				 total_slots_available);
+			exit (1);
+		}
+
+		/* load */ 
+		status = ioctl (fd, CDROM_SELECT_DISC, x_slot);
+	}
+
+	if (x_slot < 0 || verbose) {
+
+		status = ioctl (fd, CDROM_SELECT_DISC, CDSL_CURRENT);
+
+		printf ("Current slot: %d\n", status+1);
+		printf ("Total slots available: %d\n",
+			total_slots_available);
+
+		printf ("Drive status: ");
+		switch (ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) {
+		case CDS_DISC_OK:
+			printf ("Ready.\n");
+			break;
+		case CDS_TRAY_OPEN:
+			printf ("Tray Open.\n");
+			break;
+		case CDS_DRIVE_NOT_READY:
+			printf ("Drive Not Ready.\n");
+			break;
+		default:
+			printf ("This Should not happen!\n");
+			break;
+		}
+
+		for (x_slot=0; x_slot<total_slots_available; x_slot++) {
+			printf ("Slot %2d: ", x_slot+1);
+			switch (ioctl (fd, CDROM_DRIVE_STATUS, x_slot)) {
+			case CDS_DISC_OK:
+				printf ("Disc present.");
+				break;
+			case CDS_NO_DISC: 
+				printf ("Empty slot.");
+				break;
+			case CDS_NO_INFO:
+				printf ("No Information.");
+				break;
+			default:
+				printf ("This Should not happen!\n");
+				break;
+			}
+			switch (ioctl (fd, CDROM_MEDIA_CHANGED, x_slot)) {
+			case 1:
+				printf ("\t\tChanged.\n");
+				break;
+			default:
+				printf ("\n");
+				break;
+			}
+		}
+	}
+
 	/* close device */
 	status = close (fd);
 	if (status != 0) {
diff -u --recursive --new-file v2.1.8/linux/Documentation/exception.txt linux/Documentation/exception.txt
--- v2.1.8/linux/Documentation/exception.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/exception.txt	Mon Nov 11 11:07:43 1996
@@ -0,0 +1,286 @@
+     Kernel level exception handling in Linux 2.1.8
+  Commentary by Joerg Pommnitz <joerg@raleigh.ibm.com>
+
+When a process runs in kernel mode, it often has to access user 
+mode memory whose address has been passed by an untrusted program. 
+To protect itself the kernel has to verify this address.
+
+In older versions of Linux this was done with the 
+int verify_area(int type, const void * addr, unsigned long size) 
+function.
+
+This function verified, that the memory area starting at address 
+addr and of size size was accessible for the operation specified 
+in type (read or write). To do this, verify_read had to look up the 
+virtual memory area (vma) that contained the address addr. In the 
+normal case (correctly working program), this test was successful. 
+It only failed for the (hopefully) rare, buggy program. In some kernel 
+profiling tests, this normally unneeded verification used up a 
+considerable amount of time.
+
+To overcome this situation, Linus decided to let the virtual memory 
+hardware present in every Linux capable CPU handle this test.
+
+How does this work?
+
+Whenever the kernel tries to access an address that is currently not 
+accessible, the CPU generates a page fault exception and calls the 
+page fault handler 
+
+void do_page_fault(struct pt_regs *regs, unsigned long error_code)
+
+in arch/i386/mm/fault.c. The parameters on the stack are set up by 
+the low level assembly glue in arch/i386/kernel/entry.S. The parameter
+regs is a pointer to the saved registers on the stack, error_code 
+contains a reason code for the exception.
+
+do_page_fault first obtains the unaccessible address from the CPU 
+control register CR2. If the address is within the virtual address 
+space of the process, the fault probably occured, because the page 
+was not swapped in, write protected or something similiar. However, 
+we are interested in the other case: the address is not valid, there 
+is no vma that contains this address. In this case, the kernel jumps 
+to the bad_area label. 
+
+There it uses the address of the instruction that caused the exception 
+(i.e. regs->eip) to find an address where the excecution can continue 
+(fixup). If this search is successful, the fault handler modifies the 
+return address (again regs->eip) and returns. The execution will 
+continue at the address in fixup.
+
+Where does fixup point to?
+
+Since we jump to the the contents of fixup, fixup obviously points 
+to executable code. This code is hidden inside the user access macros. 
+I have picked the get_user macro defined in include/asm/uacess.h as an
+example. The definition is somewhat hard to follow, so lets peek at 
+the code generated by the preprocessor and the compiler. I selected
+the get_user call in drivers/char/console.c for a detailed examination.
+
+The original code in console.c line 1405:
+        get_user(c, buf);
+
+The preprocessor output (edited to become somewhat readable):
+
+(
+  {        
+    long __gu_err = - 14 , __gu_val = 0;        
+    const __typeof__(*( (  buf ) )) *__gu_addr = ((buf));        
+    if (((((0 + current_set[0])->tss.segment) == 0x18 )  || 
+       (((sizeof(*(buf))) <= 0xC0000000UL) && 
+       ((unsigned long)(__gu_addr ) <= 0xC0000000UL - (sizeof(*(buf)))))))        
+      do {
+        __gu_err  = 0;        
+        switch ((sizeof(*(buf)))) {        
+          case 1: 
+            __asm__ __volatile__(        
+              "1:      mov" "b" " %2,%" "b" "1\n"        
+              "2:\n"        
+              ".section .fixup,\"ax\"\n"        
+              "3:      movl %3,%0\n"        
+              "        xor" "b" " %" "b" "1,%" "b" "1\n"        
+              "        jmp 2b\n"        
+              ".section __ex_table,\"a\"\n"        
+              "        .align 4\n"        
+              "        .long 1b,3b\n"        
+              ".text"        : "=r"(__gu_err), "=q" (__gu_val): "m"((*(struct __large_struct *)
+                            (   __gu_addr   )) ), "i"(- 14 ), "0"(  __gu_err  )) ; 
+              break;        
+          case 2: 
+            __asm__ __volatile__(
+              "1:      mov" "w" " %2,%" "w" "1\n"        
+              "2:\n"        
+              ".section .fixup,\"ax\"\n"        
+              "3:      movl %3,%0\n"        
+              "        xor" "w" " %" "w" "1,%" "w" "1\n"        
+              "        jmp 2b\n"        
+              ".section __ex_table,\"a\"\n"        
+              "        .align 4\n"        
+              "        .long 1b,3b\n"        
+              ".text"        : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
+                            (   __gu_addr   )) ), "i"(- 14 ), "0"(  __gu_err  )); 
+              break;        
+          case 4: 
+            __asm__ __volatile__(        
+              "1:      mov" "l" " %2,%" "" "1\n"        
+              "2:\n"        
+              ".section .fixup,\"ax\"\n"        
+              "3:      movl %3,%0\n"        
+              "        xor" "l" " %" "" "1,%" "" "1\n"        
+              "        jmp 2b\n"        
+              ".section __ex_table,\"a\"\n"        
+              "        .align 4\n"        "        .long 1b,3b\n"        
+              ".text"        : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
+                            (   __gu_addr   )) ), "i"(- 14 ), "0"(__gu_err)); 
+              break;        
+          default: 
+            (__gu_val) = __get_user_bad();        
+        }        
+      } while (0) ;        
+    ((c)) = (__typeof__(*((buf))))__gu_val;        
+    __gu_err;
+  }
+);
+
+WOW! Black GCC/assembly magic. This is impossible to follow, so lets
+see what code gcc generates:
+
+ >         xorl %edx,%edx
+ >         movl current_set,%eax
+ >         cmpl $24,788(%eax)        
+ >         je .L1424        
+ >         cmpl $-1073741825,64(%esp)
+ >         ja .L1423                
+ > .L1424:
+ >         movl %edx,%eax                        
+ >         movl 64(%esp),%ebx
+ > #APP
+ > 1:      movb (%ebx),%dl                /* this is the actual user access */
+ > 2:
+ > .section .fixup,"ax"
+ > 3:      movl $-14,%eax
+ >         xorb %dl,%dl
+ >         jmp 2b
+ > .section __ex_table,"a"
+ >         .align 4
+ >         .long 1b,3b
+ > .text
+ > #NO_APP
+ > .L1423:
+ >         movzbl %dl,%esi
+
+The optimizer does a good job and gives us something we can actually 
+understand. Can we? The actual user access is quite obvious. Thanks 
+to the unified address space we can just access the address in user 
+memory. But what does the .section stuff do?????
+
+To understand this we have to look at the final kernel:
+
+ > objdump --section-headers vmlinux
+ > 
+ > vmlinux:     file format elf32-i386
+ > 
+ > Sections:
+ > Idx Name          Size      VMA       LMA       File off  Algn
+ >   0 .text         00098f40  c0100000  c0100000  00001000  2**4
+ >                   CONTENTS, ALLOC, LOAD, READONLY, CODE
+ >   1 .fixup        000016bc  c0198f40  c0198f40  00099f40  2**0
+ >                   CONTENTS, ALLOC, LOAD, READONLY, CODE
+ >   2 .rodata       0000f127  c019a5fc  c019a5fc  0009b5fc  2**2
+ >                   CONTENTS, ALLOC, LOAD, READONLY, DATA
+ >   3 __ex_table    000015c0  c01a9724  c01a9724  000aa724  2**2
+ >                   CONTENTS, ALLOC, LOAD, READONLY, DATA
+ >   4 .data         0000ea58  c01abcf0  c01abcf0  000abcf0  2**4
+ >                   CONTENTS, ALLOC, LOAD, DATA
+ >   5 .bss          00018e21  c01ba748  c01ba748  000ba748  2**2
+ >                   ALLOC
+ >   6 .comment      00000ec4  00000000  00000000  000ba748  2**0
+ >                   CONTENTS, READONLY
+ >   7 .note         00001068  00000ec4  00000ec4  000bb60c  2**0
+ >                   CONTENTS, READONLY
+
+There are obviously 2 non standard ELF sections in the generated object
+file. But first we want to find out what happened to our code in the
+final kernel executable:
+
+ > objdump --disassemble --section=.text vmlinux
+ >
+ > c017e785 <do_con_write+c1> xorl   %edx,%edx
+ > c017e787 <do_con_write+c3> movl   0xc01c7bec,%eax
+ > c017e78c <do_con_write+c8> cmpl   $0x18,0x314(%eax)
+ > c017e793 <do_con_write+cf> je     c017e79f <do_con_write+db>
+ > c017e795 <do_con_write+d1> cmpl   $0xbfffffff,0x40(%esp,1)
+ > c017e79d <do_con_write+d9> ja     c017e7a7 <do_con_write+e3>
+ > c017e79f <do_con_write+db> movl   %edx,%eax
+ > c017e7a1 <do_con_write+dd> movl   0x40(%esp,1),%ebx
+ > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
+ > c017e7a7 <do_con_write+e3> movzbl %dl,%esi
+
+The whole user memory access is reduced to 10 x86 machine instructions.
+The instructions bracketed in the .section directives are not longer
+in the normal execution path. They are located in a different section 
+of the executable file:
+
+ > objdump --disassemble --section=.fixup vmlinux
+ > 
+ > c0199ff5 <.fixup+10b5> movl   $0xfffffff2,%eax
+ > c0199ffa <.fixup+10ba> xorb   %dl,%dl
+ > c0199ffc <.fixup+10bc> jmp    c017e7a7 <do_con_write+e3>
+
+And finally:
+ > objdump --full-contents --section=__ex_table vmlinux
+ > 
+ >  c01aa7c4 93c017c0 e09f19c0 97c017c0 99c017c0  ................
+ >  c01aa7d4 f6c217c0 e99f19c0 a5e717c0 f59f19c0  ................
+ >  c01aa7e4 080a18c0 01a019c0 0a0a18c0 04a019c0  ................
+
+or in human readable byte order:
+
+ >  c01aa7c4 c017c093 c0199fe0 c017c097 c017c099  ................
+ >  c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5  ................
+                               ^^^^^^^^^^^^^^^^^
+                               this is the interesting part!
+ >  c01aa7e4 c0180a08 c019a001 c0180a0a c019a004  ................
+
+What happened? The assembly directives
+
+.section .fixup,"ax"
+.section __ex_table,"a"
+
+told the assembler to move the following code to the specified
+sections in the ELF object file. So the instructions
+3:      movl $-14,%eax
+        xorb %dl,%dl
+        jmp 2b
+ended up in the .fixup section of the object file and the addresses
+        .long 1b,3b
+ended up in the __ex_table section of the object file. 1b and 3b
+are local labels. The local label 1b (1b stands for next label 1 
+backward) is the address of the instruction that might fault, i.e. 
+in our case the address of the label 1 is c017e7a5:
+the original assembly code: > 1:      movb (%ebx),%dl
+and linked in vmlinux     : > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
+
+The local label 3 (backwards again) is the address of the code to handle
+the fault, in our case the actual value is c0199ff5:
+the original assembly code: > 3:      movl $-14,%eax
+and linked in vmlinux     : > c0199ff5 <.fixup+10b5> movl   $0xfffffff2,%eax
+
+The assembly code
+ > .section __ex_table,"a"
+ >         .align 4
+ >         .long 1b,3b
+
+becomes the value pair
+ >  c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5  ................
+                               ^this is ^this is
+                               1b       3b 
+c017e7a5,c0199ff5 in the exception table of the kernel.
+
+So, what actually happens if a fault from kernel mode with no suitable
+vma occurs?
+
+1.) access to invalid address:
+ > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
+2.) MMU generates exception
+3.) CPU calls do_page_fault
+4.) do page fault calls search_exception_table (regs->eip == c017e7a5);
+5.) search_exception_table looks up the address c017e7a5 in the
+    exception table (i.e. the contents of the ELF section __ex_table 
+    and returns the address of the associated fault handle code c0199ff5.
+6.) do_page_fault modifies its own return address to point to the fault 
+    handle code and returns.
+7.) execution continues in the fault handling code.
+8.) 8a) EAX becomes -EFAULT (== -14)
+    8b) DL  becomes zero (the value we "read" from user space)
+    8c) execution continues at local label 2 (address of the
+        instruction immediately after the faulting user access).
+
+The steps 8a to 8c in a certain way emulate the faulting instruction.
+
+That's it, mostely. If you look at our example, you might ask, why
+we set EAX to -EFAULT in the exception handler code. Well, the
+get_user macro actually returns a value: 0, if the user access was
+successful, -EFAULT on failure. Our original code did not test this
+return value, however the inline assembly code in get_user tries to
+return -EFAULT. GCC selected EAX to return this value.
diff -u --recursive --new-file v2.1.8/linux/Documentation/svga.txt linux/Documentation/svga.txt
--- v2.1.8/linux/Documentation/svga.txt	Mon May 13 12:58:55 1996
+++ linux/Documentation/svga.txt	Tue Nov 12 13:08:43 1996
@@ -1,4 +1,4 @@
-			Video Mode Selection Support 2.9
+		       Video Mode Selection Support 2.10
 	      (c) 1995, 1996 Martin Mares, <mj@k332.feld.cvut.cz>
 --------------------------------------------------------------------------------
 
@@ -118,6 +118,7 @@
 	0x0f05	VGA 80x30 (480 scans, 16-point font)
 	0x0f06	VGA 80x34 (480 scans, 14-point font)
 	0x0f07	VGA 80x60 (480 scans, 8-point font)
+	0x0f08	Graphics hack (see the CONFIG_VIDEO_HACK paragraph below)
 
    0x1000 to 0x7fff - modes specified by resolution. The code has a "0xRRCC"
 	form where RR is a number of rows and CC is a number of columns.
@@ -163,6 +164,19 @@
 of the table (which also includes a video card name to be displayed on the
 top of the menu).
 
+   CONFIG_VIDEO_400_HACK - force setting of 400 scan lines for standard VGA
+modes. This option is intended to be used on certain buggy BIOS'es which draw
+some useless logo using font download and then fail to reset the correct mode.
+Don't use unless needed as it forces resetting the video card.
+
+   CONFIG_VIDEO_GFX_HACK - includes special hack for setting of graphics modes
+to be used later by special drivers (e.g., 800x600 on IBM ThinkPad -- see
+ftp://ftp.phys.keio.ac.jp/pub/XFree86/800x600/XF86Configs/XF86Config.IBM_TP560).
+Allows to set _any_ BIOS mode including graphic ones and forcing specific
+text screen resolution instead of peeking it from BIOS variables. Don't use
+unless you think you know what you're doing. To activate this setup, use
+mode number 0x0f08 (see section 3).
+
 5. Adding more cards
 ~~~~~~~~~~~~~~~~~~~~
    If you have a card not detected by the driver and you are a good programmer,
@@ -202,6 +216,10 @@
 end setting". Adding 0x8000 to the mode ID might fix the problem. Unfortunately,
 this must be done manually -- no autodetection mechanisms are available.
 
+   If you have a VGA card and your display still looks as on EGA, your BIOS
+is probably broken and you need to set the CONFIG_VIDEO_400_HACK switch to
+force setting of the correct mode.
+
 7. History
 ~~~~~~~~~~
 1.0 (??-Nov-95)	First version supporting all adapters supported by the old
@@ -244,3 +262,7 @@
 2.8 (14-Apr-96)	- Previous release was not compilable without CONFIG_VIDEO_SVGA.
 		- Better recognition of text modes during mode scan.
 2.9 (12-May-96)	- Ignored VESA modes 0x80 - 0xff (more VESA BIOS bugs!)
+2.10 (11-Nov-96)- The whole thing made optional.
+		- Added the CONFIG_VIDEO_400_HACK switch.
+		- Added the CONFIG_VIDEO_GFX_HACK switch.
+		- Code cleanup.
diff -u --recursive --new-file v2.1.8/linux/Documentation/unicode.txt linux/Documentation/unicode.txt
--- v2.1.8/linux/Documentation/unicode.txt	Sat Mar  2 13:18:47 1996
+++ linux/Documentation/unicode.txt	Tue Nov 12 10:30:57 1996
@@ -20,18 +20,20 @@
 
 In accordance with the Unicode standard/ISO 10646 the range U+F000 to
 U+F8FF has been reserved for OS-wide allocation (the Unicode Standard
-refers to this as a "Corporate Zone").  U+F000 was picked as the
-starting point since it lets the direct-mapping area start on a large
-power of two (in case 1024- or 2048-character fonts ever become
-necessary).  This leaves U+E000 to U+EFFF as End User Zone.
+refers to this as a "Corporate Zone", since this is inaccurate for
+Linux we call it the "Linux Zone").  U+F000 was picked as the starting
+point since it lets the direct-mapping area start on a large power of
+two (in case 1024- or 2048-character fonts ever become necessary).
+This leaves U+E000 to U+EFFF as End User Zone.
 
 The Unicodes in the range U+F000 to U+F1FF have been hard-coded to map
 directly to the loaded font, bypassing the translation table.  The
 user-defined map now defaults to U+F000 to U+F1FF, emulating the
-previous behaviour.
+previous behaviour.  This range may expand in the future should it be
+warranted.
 
-Actual characters assigned in the Corporate Zone
-------------------------------------------------
+Actual characters assigned in the Linux Zone
+--------------------------------------------
 
 In addition, the following characters not present in Unicode 1.1.4 (at
 least, I have not found them!) have been defined; these are used by
@@ -58,7 +60,7 @@
 about the whole 16-bit concept to begin with.)  However, with Linux
 being a hacker-driven OS it seems this is a brilliant linguistic hack
 worth supporting.  Hence I have chosen to add it to the list in the
-Linux "Corporate" Zone.
+Linux Zone.
 
 Several glyph forms for the Klingon alphabet has been proposed.
 However, since the set of symbols appear to be consistent throughout,
@@ -122,5 +124,16 @@
 U+F8F8	KLINGON DIGIT EIGHT
 U+F8F9	KLINGON DIGIT NINE
 
+Other Fictional and Artificial Scripts
+--------------------------------------
 
-	H. Peter Anvin <hpa@storm.net>
+Since the assignment of the Klingon Linux Unicode block, a registry of
+fictional and artificial scripts has been established by John Cowan,
+<cowan@ccil.org>.  The ConScript Unicode Registry is accessible at
+http://locke.ccil.org/~cowan/csur/; the ranges used fall at the bottom
+of the End User Zone and can hence not be normatively assigned, but it
+is recommended that people who wish to encode fictional scripts use
+these codes, in the interest of interoperability.  For Klingon, CSUR
+has adopted the Linux encoding.
+
+	H. Peter Anvin <hpa@zytor.com>
diff -u --recursive --new-file v2.1.8/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.8/linux/MAINTAINERS	Tue Oct 29 19:58:01 1996
+++ linux/MAINTAINERS	Tue Nov 12 13:08:43 1996
@@ -213,6 +213,12 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+IDE/ATAPI CDROM DRIVER 
+P:	Erik Andersen
+M:	andersee@debian.org
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 ISDN SUBSYSTEM
 P:	Fritz Elfert
 M:	fritz@wuemaus.franken.de
@@ -289,7 +295,7 @@
 SVGA HANDLING:
 P:	Martin Mares
 M:	mj@k332.feld.cvut.cz
-L:	linux-kernel@vger.rutgers.edu
+L:	linux-video@atrey.karlin.mff.cuni.cz
 S:	Maintained
 
 VFAT FILESYSTEM:
diff -u --recursive --new-file v2.1.8/linux/Makefile linux/Makefile
--- v2.1.8/linux/Makefile	Sun Nov 10 20:12:08 1996
+++ linux/Makefile	Tue Nov 12 10:32:40 1996
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 8
+SUBLEVEL = 9
 
 ARCH = i386
 
@@ -126,12 +126,12 @@
 
 DRIVERS := $(DRIVERS) drivers/net/net.a
 
-ifdef CONFIG_CD_NO_IDESCSI
-DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
-endif
-
 ifeq ($(CONFIG_SCSI),y)
 DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
+endif
+
+ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
+DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
 endif
 
 ifeq ($(CONFIG_SOUND),y)
diff -u --recursive --new-file v2.1.8/linux/arch/alpha/lib/csum_ipv6_magic.S linux/arch/alpha/lib/csum_ipv6_magic.S
--- v2.1.8/linux/arch/alpha/lib/csum_ipv6_magic.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/alpha/lib/csum_ipv6_magic.S	Sun Nov 10 19:29:33 1996
@@ -0,0 +1,68 @@
+/*
+ * arch/alpha/lib/csum_ipv6_magic.S
+ * Contributed by Richard Henderson <rth@tamu.edu>
+ *
+ * unsigned short csum_ipv6_magic(struct in6_addr *saddr,
+ *                                struct in6_addr *daddr,
+ *                                __u16 len,
+ *                                unsigned short proto,
+ *                                unsigned int csum);
+ */
+
+	.globl csum_ipv6_magic
+	.align 4
+	.ent csum_ipv6_magic
+	.frame $30,0,$26,0
+csum_ipv6_magic:
+	.prologue 0
+
+	ldq	$0,0($16)	# e0    : load src & dst addr words
+	zapnot	$20,15,$20	# .. e1 : zero extend incoming csum
+	extwh	$18,7,$4	# e0    : byte swap len & proto while we wait
+	ldq	$1,8($16)	# .. e1 :
+	extbl	$18,1,$18	# e0    :
+	ldq	$2,0($17)	# .. e1 :
+	extwh	$19,7,$5	# e0    :
+	ldq	$3,8($17)	# .. e1 :
+	extbl	$19,1,$19	# e0    :
+	or	$18,$4,$18	# .. e1 :
+	addq	$20,$0,$20	# e0    : begin summing the words
+	or	$19,$5,$19	# .. e1 :
+	sll	$18,48,$18	# e0    :
+	cmpult	$20,$0,$0	# .. e1 :
+	sll	$19,48,$19	# e0    :
+	addq	$20,$1,$20	# .. e1 :
+	sra	$18,32,$18	# e0    : len complete
+	cmpult	$20,$1,$1	# .. e1 :
+	sra	$19,32,$19	# e0    : proto complete
+	addq	$20,$2,$20	# .. e1 :
+	cmpult	$20,$2,$2	# e0    :
+	addq	$20,$3,$20	# .. e1 :
+	cmpult	$20,$3,$3	# e0    :
+	addq	$20,$18,$20	# .. e1 :
+	cmpult	$20,$18,$18	# e0    :
+	addq	$20,$19,$20	# .. e1 :
+	cmpult	$20,$19,$19	# e0    :
+	addq	$0,$1,$0	# .. e1 : merge the carries back into the csum
+	addq	$2,$3,$2	# e0    :
+	addq	$18,$19,$18	# .. e1 :
+	addq	$0,$2,$0	# e0    :
+	addq	$20,$18,$20	# .. e1 :
+	addq	$0,$20,$0	# e0    :
+	unop			#       :
+	extwl	$0,2,$2		# e0    : begin folding the 64-bit value
+	zapnot	$0,3,$3		# .. e1 :
+	extwl	$0,4,$1		# e0    :
+	addq	$2,$3,$3	# .. e1 :
+	extwl	$0,6,$0		# e0    :
+	addq	$3,$1,$3	# .. e1 :
+	addq	$0,$3,$0	# e0    :
+	unop			#       :
+	extwl	$0,2,$1		# e0    :
+	zapnot	$0,3,$0		# .. e1 :
+	addq	$0,$1,$0	# e0    :
+	unop			#       :
+	not	$0,$0		# e0    :
+	ret			# .. e1 :
+
+	.end csum_ipv6_magic
diff -u --recursive --new-file v2.1.8/linux/arch/i386/boot/video.S linux/arch/i386/boot/video.S
--- v2.1.8/linux/arch/i386/boot/video.S	Sat May 18 11:15:10 1996
+++ linux/arch/i386/boot/video.S	Tue Nov 12 13:08:43 1996
@@ -1,8 +1,8 @@
 !
-!	Display adapter & video mode setup, version 2.9 (12-May-96)
+!	Display adapter & video mode setup, version 2.10 (11-Nov-96)
 !
 !	Copyright (C) 1995, 1996 Martin Mares <mj@k332.feld.cvut.cz>
-!	Based on the original setup.S code (C) Linus Torvalds
+!	Based on the original setup.S code (C) Linus Torvalds and Mats Anderson
 !
 
 ! Enable autodetection of SVGA adapters and modes
@@ -20,6 +20,18 @@
 ! Enable local mode list
 #undef CONFIG_VIDEO_LOCAL
 
+! Force 400 scan lines for standard modes (hack to fix bad behaviour
+! of certain broken BIOS'es -- don't use unless needed)
+#undef CONFIG_VIDEO_400_HACK
+
+! A special hack allowing to force specific BIOS mode ID along with specific
+! dimensions. Especially useful for certain X-Window graphics mode hacks
+! (e.g., 800x600 modes on IBM ThinkPad).
+#undef CONFIG_VIDEO_GFX_HACK
+#define VIDEO_GFX_BIOS_AX 0x4f02	/* 800x600 on ThinkPad */
+#define VIDEO_GFX_BIOS_BX 0x0102
+#define VIDEO_GFX_DUMMY_RESOLUTION 0x6425	/* 100x37 */
+
 ! This code uses an extended set of video mode numbers. These include:
 ! Aliases for standard modes
 !	NORMAL_VGA (-1)
@@ -44,7 +56,8 @@
 #define VIDEO_80x30 0x0f05
 #define VIDEO_80x34 0x0f06
 #define VIDEO_80x60 0x0f07
-#define VIDEO_LAST_SPECIAL 0x0f08
+#define VIDEO_GFX_HACK 0x0f08
+#define VIDEO_LAST_SPECIAL 0x0f09
 ! Video modes given by resolution
 #define VIDEO_FIRST_RESOLUTION 0x1000
 
@@ -66,7 +79,7 @@
 #define DO_STORE call store_screen
 #else
 #define DO_STORE
-#endif
+#endif /* CONFIG_VIDEO_RETAIN */
 
 !
 ! This is the main entry point called by setup.S
@@ -85,6 +98,7 @@
 	mov	gs,ax		! GS is zero
 	cld
 	call	basic_detect	! Basic adapter type testing (EGA/VGA/MDA/CGA)
+#ifdef CONFIG_VIDEO_SELECT
 	seg	fs		! User-selected video mode
 	mov	ax,[0x01fa]
 	cmp	ax,#ASK_VGA	! Bring up the menu
@@ -97,7 +111,8 @@
 vid1:
 #ifdef CONFIG_VIDEO_RETAIN
 	call	restore_screen	! Restore screen contents
-#endif
+#endif /* CONFIG_VIDEO_RETAIN */
+#endif /* CONFIG_VIDEO_SELECT */
 	call	mode_params	! Store mode parameters
 	pop	ds		! Restore original DS
 	ret
@@ -176,6 +191,8 @@
 	movb	[PARAM_VIDEO_LINES],al
 	ret
 
+#ifdef CONFIG_VIDEO_SELECT
+
 !
 ! The video mode menu
 !
@@ -335,6 +352,8 @@
 	stc
 	ret
 
+_setrec:	br	setrec	! Ugly...
+
 !
 ! Aliases for backward compatibility.
 !
@@ -358,7 +377,7 @@
 	cmp	ah,#0xff
 	jz	setalias
 	test	ah,#VIDEO_RECALC>>8
-	jnz	setrec
+	jnz	_setrec
 	cmp	ah,#VIDEO_FIRST_RESOLUTION>>8
 	jnc	setres
 	cmp	ah,#VIDEO_FIRST_SPECIAL>>8
@@ -404,6 +423,8 @@
 	.word	0xa7ff, spec_inits	! JMP [BX+spec_inits]
 
 setmenu:
+	or	al,al		! 80x25 is an exception
+	jz	set_80x25
 	push	bx		! Set mode chosen from menu
 	call	mode_table	! Build the mode table
 	pop	ax
@@ -477,6 +498,7 @@
 	.word	set_80x30
 	.word	set_80x34
 	.word	set_80x60
+	.word	set_gfx
 
 !
 ! Set the 80x25 mode. If already set, do nothing.
@@ -485,6 +507,11 @@
 set_80x25:
 	mov	[force_size],#0x5019	! Override possibly broken BIOS vars
 use_80x25:
+#ifdef CONFIG_VIDEO_400_HACK
+	mov	ax,#0x1202	! Force 400 scan lines
+	mov	bl,#0x30
+	int	0x10
+#else
 	mov	ah,#0x0f	! Get current mode ID
 	int	0x10
 	cmp	ax,#0x5007	! Mode 7 (80x25 mono) is the only one available
@@ -499,6 +526,7 @@
 	jz	set80
 	cmp	al,#24		! It's hopefully correct
 	jz	set80
+#endif /* CONFIG_VIDEO_400_HACK */
 force3:	DO_STORE
 	mov	ax,#0x0003	! Forced set
 	int	0x10
@@ -623,6 +651,20 @@
 	mov	[force_size],#0x503c
 	jmp	setvde
 
+!
+! Special hack for ThinkPad graphics
+!
+
+set_gfx:
+#ifdef CONFIG_VIDEO_GFX_HACK
+	mov	ax,# VIDEO_GFX_BIOS_AX
+	mov	bx,# VIDEO_GFX_BIOS_BX
+	int	0x10
+	mov	[force_size],# VIDEO_GFX_DUMMY_RESOLUTION
+	stc
+#endif
+	ret
+
 #ifdef CONFIG_VIDEO_RETAIN
 
 !
@@ -780,9 +822,9 @@
 mtab1x:	jmp	mtab1
 
 mtabv:	lea	si,vga_modes			! All modes for std VGA
-	mov	cx,#12
-	rep
-	movsw
+	mov	cx,#vga_modes_end-vga_modes
+	rep	! I'm unable to use movsw as I don't know how to store a half
+	movsb	! of the expression above to cx without using explicit shr.
 
 	cmpb	[scanning],#0			! Mode scan requested?
 	jz	mscan1
@@ -791,16 +833,16 @@
 
 #ifdef CONFIG_VIDEO_LOCAL
 	call	local_modes
-#endif
+#endif /* CONFIG_VIDEO_LOCAL */
 #ifdef CONFIG_VIDEO_VESA
 	call	vesa_modes			! Detect VESA VGA modes
-#endif
+#endif /* CONFIG_VIDEO_VESA */
 #ifdef CONFIG_VIDEO_SVGA
 	cmpb	[scanning],#0			! Bypass when scanning
 	jnz	mscan2
 	call	svga_modes			! Detect SVGA cards & modes
 mscan2:
-#endif
+#endif /* CONFIG_VIDEO_SVGA */
 
 mtabe:
 
@@ -848,6 +890,11 @@
 	.word	0x5022		! 80x34
 	.word	VIDEO_80x60
 	.word	0x503c		! 80x60
+#ifdef CONFIG_VIDEO_GFX_HACK
+	.word	VIDEO_GFX_HACK
+	.word	VIDEO_GFX_DUMMY_RESOLUTION
+#endif
+vga_modes_end:
 
 !
 ! Detect VESA modes.
@@ -1738,19 +1785,20 @@
 	pop	ax
 	ret
 
-! Variables:
+!
+! VIDEO_SELECT-only variables
+!
 
-adapter:	.byte	0	! Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA
 mt_end:		.word	0	! End of video mode table if built
 edit_buf:	.space	6	! Line editor buffer
 card_name:	.word	0	! Pointer to adapter name
 scanning:	.byte	0	! Performing mode scan
 do_restore:	.byte	0	! Screen contents altered during mode change
 svga_prefix:	.byte	VIDEO_FIRST_BIOS>>8	! Default prefix for BIOS modes
-video_segment:	.word	0xb800	! Video memory segment
-force_size:	.word	0	! Use this size instead of the one in BIOS vars
 
+!
 ! Messages:
+!
 
 keymsg:		.ascii	"Press <RETURN> to see video modes available, "
 		.ascii	"<SPACE> to continue or wait 30 secs"
@@ -1779,3 +1827,13 @@
 		db	0
 name_bann:	.ascii	"Video adapter: "
 		db	0
+
+#endif /* CONFIG_VIDEO_SELECT */
+
+!
+! Other variables:
+!
+
+adapter:	.byte	0	! Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA
+video_segment:	.word	0xb800	! Video memory segment
+force_size:	.word	0	! Use this size instead of the one in BIOS vars
diff -u --recursive --new-file v2.1.8/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.8/linux/arch/i386/config.in	Fri Sep 20 18:40:25 1996
+++ linux/arch/i386/config.in	Tue Nov 12 13:08:43 1996
@@ -42,6 +42,7 @@
 	 486		CONFIG_M486	\
 	 Pentium	CONFIG_M586	\
 	 PPro		CONFIG_M686" Pentium
+bool 'Video mode selection support' CONFIG_VIDEO_SELECT
 endmenu
 
 source drivers/block/Config.in
diff -u --recursive --new-file v2.1.8/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.8/linux/arch/i386/defconfig	Sun Nov 10 20:12:08 1996
+++ linux/arch/i386/defconfig	Tue Nov 12 14:49:39 1996
@@ -28,6 +28,7 @@
 # CONFIG_M486 is not set
 CONFIG_M586=y
 # CONFIG_M686 is not set
+# CONFIG_VIDEO_SELECT is not set
 
 #
 # Floppy, IDE, and other block devices
@@ -43,7 +44,6 @@
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDE_PCMCIA is not set
 CONFIG_BLK_DEV_CMD640=y
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_RZ1000=y
@@ -144,6 +144,7 @@
 CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
+# CONFIG_AFFS_FS is not set
 # CONFIG_UFS_FS is not set
 
 #
diff -u --recursive --new-file v2.1.8/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.8/linux/arch/i386/kernel/entry.S	Sun Nov 10 20:12:08 1996
+++ linux/arch/i386/kernel/entry.S	Sun Nov 10 19:08:30 1996
@@ -317,6 +317,7 @@
 	RESTORE_ALL
 	ALIGN
 tracesys:
+	movl $-ENOSYS,EAX(%esp)
 	call SYMBOL_NAME(syscall_trace)
 	movl ORIG_EAX(%esp),%eax
 	call SYMBOL_NAME(sys_call_table)(,%eax,4)
diff -u --recursive --new-file v2.1.8/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.1.8/linux/arch/i386/kernel/time.c	Sun Nov 10 20:12:08 1996
+++ linux/arch/i386/kernel/time.c	Mon Nov 11 15:38:07 1996
@@ -232,6 +232,14 @@
 	return offset + count;
 }
 
+/*
+ * this is only used if we have fast gettimeoffset:
+ */
+void do_x86_get_fast_time(struct timeval * tv)
+{
+	do_gettimeofday(tv);
+}
+
 static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
 
 /*
@@ -465,15 +473,16 @@
                                    needs more debugging. */
 	if (x86_capability & 16) {
 		do_gettimeoffset = do_fast_gettimeoffset;
+		do_get_fast_time = do_x86_get_fast_time;
 
 		if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) {
 			if( x86 == 5 ) {
 				if( x86_model == 0 ) {
 					/* turn on cycle counters during power down */
 					__asm__ __volatile__ (" movl $0x83, %%ecx \n \
-								rdmsr \n \
+								.byte 0x0f,0x32 \n \
 								orl $1,%%eax \n \
-								wrmsr \n " 
+								.byte 0x0f,0x30 \n " 
                                                                 : : : "ax", "cx", "dx" );
 					udelay(500);
 				}
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/boot/Makefile linux/arch/sparc/boot/Makefile
--- v2.1.8/linux/arch/sparc/boot/Makefile	Sat Nov 25 02:57:38 1995
+++ linux/arch/sparc/boot/Makefile	Sat Nov  9 10:11:31 1996
@@ -1,21 +1,12 @@
-# $Id: Makefile,v 1.2 1995/11/25 00:57:37 davem Exp $
+# $Id: Makefile,v 1.3 1996/08/04 08:40:58 ecd Exp $
 # Makefile for the Sparc low level /boot module.
 #
 # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
 
-OBJS	=bare.o init_me.o ../kernel/promops.o ../lib/lib.a
-BOOTLINKFLAGS = -N -Ttext 0x200000 -e _first_adr_in_text
-
-.S.s:
-	$(CC) -D__ASSEMBLY__ -D__KERNEL__ -ansi -E -o $*.o $<
-.S.o:
-	$(CC) -D__ASSEMBLY__ -D__KERNEL__ -ansi -c -o $*.o $<
-
 all: boot
 
-boot: $(OBJS)
-	$(LD) $(BOOTLINKFLAGS) $(OBJS) -o boot
+boot:
+	@echo "Nothing special to be done for 'boot' on Linux/SPARC."
 
 dep:
-	$(CPP) -M *.c > .depend
-	$(CPP) -M -D__ASSEMBLY__ -ansi *.S >>.depend
+
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.8/linux/arch/sparc/config.in	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/config.in	Sat Nov  9 10:11:26 1996
@@ -1,15 +1,35 @@
-# $Id: config.in,v 1.12 1996/04/24 03:15:38 davem Exp $
+# $Id: config.in,v 1.23 1996/10/28 01:24:40 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
-mainmenu_name "Kernel configuration of Linux for SparcStations"
+mainmenu_name "Linux/SPARC Kernel Configuration"
+
+mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
+comment 'Loadable module support'
+bool 'Enable loadable module support' CONFIG_MODULES
+if [ "$CONFIG_MODULES" = "y" ]; then
+  bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
+  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+fi
+endmenu
 
 mainmenu_option next_comment
 comment 'General setup'
 
 bool 'Support for AP1000 multicomputer' CONFIG_AP1000
 
-if [ "$CONFIG_AP1000" = "n" ]; then
+if [ "$CONFIG_AP1000" = "y" ]; then
+	define_bool CONFIG_NO_KEYBOARD y
+	define_bool CONFIG_APFDDI y
+	define_bool CONFIG_APBLOCK y
+	define_bool CONFIG_APBIF y
+	define_bool CONFIG_DDV y
+else
 	# Global things across all Sun machines.
 	define_bool CONFIG_SBUS y
 	define_bool CONFIG_SBUSCHAR y
@@ -20,21 +40,23 @@
 	define_bool CONFIG_SUN_CONSOLE y
 	define_bool CONFIG_SUN_AUXIO y
 	define_bool CONFIG_SUN_IO y
+	source drivers/sbus/char/Config.in
 fi
 
-define_bool CONFIG_NET_ALIAS n
-define_bool CONFIG_BINFMT_AOUT y
-
-
+tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
+fi
 endmenu
 
 mainmenu_option next_comment
 comment 'Floppy, IDE, and other block devices'
 
-tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
+bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD
 
 bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
@@ -49,6 +71,8 @@
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
 
+endmenu
+
 if [ "$CONFIG_NET" = "y" ]; then
 	source net/Config.in
 fi
@@ -75,7 +99,8 @@
 	mainmenu_option next_comment
 	comment 'SCSI low-level drivers'
 
-	dep_tristate 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI
+	bool 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI
+	endmenu
 fi
 endmenu
 
@@ -86,16 +111,21 @@
 	bool 'Network device support' CONFIG_NETDEVICES
 	if [ "$CONFIG_NETDEVICES" = "y" ]; then
 		tristate 'Dummy net driver support' CONFIG_DUMMY
+		tristate 'PPP (point-to-point) support' CONFIG_PPP
+		if [ ! "$CONFIG_PPP" = "n" ]; then
+		   comment 'CCP compressors for PPP are only built as modules.'
+		fi
 		tristate 'SLIP (serial line) support' CONFIG_SLIP
 		if [ "$CONFIG_SLIP" != "n" ]; then
 		  bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
 		  bool ' Keepalive and linefill' CONFIG_SLIP_SMART
-		fi
-		tristate 'PPP (point-to-point) support' CONFIG_PPP
-		if [ ! "$CONFIG_PPP" = "n" ]; then
-		  comment 'CCP compressors for PPP are only built as modules.'
+		  bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6
 		fi
 		bool 'Sun LANCE support' CONFIG_SUNLANCE
+		bool 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL
+#		bool 'FDDI driver support' CONFIG_FDDI
+#		if [ "$CONFIG_FDDI" = "y" ]; then
+#		fi
 	fi
 	endmenu
 fi
@@ -109,3 +139,4 @@
 if [ "$CONFIG_PROFILE" = "y" ]; then
 	int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+endmenu
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.1.8/linux/arch/sparc/defconfig	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/defconfig	Sun Nov 10 17:55:51 1996
@@ -3,6 +3,18 @@
 #
 
 #
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KERNELD=y
+
+#
 # General setup
 #
 # CONFIG_AP1000 is not set
@@ -15,46 +27,86 @@
 CONFIG_SUN_CONSOLE=y
 CONFIG_SUN_AUXIO=y
 CONFIG_SUN_IO=y
-# CONFIG_NET_ALIAS is not set
-CONFIG_BINFMT_AOUT=y
+
+#
+# SBUS Frame Buffer support
+#
+SUN_FBS_IN_PROCFS=y
+CONFIG_SUN_FB_DISPLAY=y
+SUN_FB_CGSIX=y
+SUN_FB_TCX=y
+SUN_FB_CGTHREE=y
+SUN_FB_CGFOURTEEN=y
+SUN_FB_BWTWO=y
+SUN_FB_LEO=y
+TADPOLE_FB_WEITEK=y
+SUN_FB_FAST_ONE=y
+SUN_FB_FAST_TWO=y
+SUN_FB_FAST_MONO=y
+SUN_FB_GENERIC=y
+
+#
+# Misc Linux/SPARC drivers
+#
+CONFIG_SUN_OPENPROMIO=m
+CONFIG_SUN_MOSTEK_RTC=y
+CONFIG_SUN_OPENPROMFS=m
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_JAVA=m
 
 #
 # Floppy, IDE, and other block devices
 #
 CONFIG_BLK_DEV_FD=y
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=y
+CONFIG_MD_STRIPED=y
 CONFIG_BLK_DEV_RAM=y
-# CONFIG_BLK_DEV_INITRD is not set
-# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BLK_DEV_LOOP=m
 
 #
 # Networking options
 #
-# CONFIG_FIREWALL is not set
-# CONFIG_NET_ALIAS is not set
+CONFIG_FIREWALL=y
+CONFIG_NET_ALIAS=y
 CONFIG_INET=y
-# CONFIG_IP_FORWARD is not set
-# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_FORWARD=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_FIREWALL=y
+# CONFIG_IP_FIREWALL_VERBOSE is not set
+CONFIG_IP_MASQUERADE=y
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+# CONFIG_IP_TRANSPARENT_PROXY is not set
+# CONFIG_IP_ALWAYS_DEFRAG is not set
 # CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+CONFIG_NET_IPIP=m
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=m
 
 #
 # (it is safe to leave these untouched)
 #
 # CONFIG_INET_PCTCP is not set
-# CONFIG_INET_RARP is not set
+CONFIG_INET_RARP=m
 # CONFIG_NO_PATH_MTU_DISCOVERY is not set
-# CONFIG_TCP_NAGLE_OFF is not set
 CONFIG_IP_NOSR=y
-# CONFIG_SKB_LARGE is not set
+CONFIG_SKB_LARGE=y
+CONFIG_IPV6=m
 
 #
 #  
 #
-# CONFIG_IPX is not set
-# CONFIG_ATALK is not set
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+CONFIG_ATALK=m
 # CONFIG_AX25 is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_NETLINK is not set
@@ -68,9 +120,9 @@
 # SCSI support type (disk, tape, CDrom)
 #
 CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
+CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
-# CONFIG_CHR_DEV_SG is not set
+CONFIG_CHR_DEV_SG=y
 
 #
 # Some SCSI devices (e.g. CD jukebox) support multiple LUNs
@@ -87,34 +139,47 @@
 # Network device support
 #
 CONFIG_NETDEVICES=y
-CONFIG_DUMMY=y
-# CONFIG_SLIP is not set
-# CONFIG_PPP is not set
+CONFIG_DUMMY=m
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+# CONFIG_SLIP_MODE_SLIP6 is not set
 CONFIG_SUNLANCE=y
+CONFIG_HAPPYMEAL=y
 
 #
 # Filesystems
 #
-# CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_EXT_FS is not set
+CONFIG_QUOTA=y
+CONFIG_MINIX_FS=m
+CONFIG_EXT_FS=m
 CONFIG_EXT2_FS=y
-# CONFIG_XIA_FS is not set
-# CONFIG_FAT_FS is not set
-# CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
-# CONFIG_UMSDOS_FS is not set
+CONFIG_XIA_FS=m
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_UMSDOS_FS=m
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 CONFIG_ROOT_NFS=y
 CONFIG_RNFS_BOOTP=y
 CONFIG_RNFS_RARP=y
-# CONFIG_SMB_FS is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_LONG=y
+CONFIG_NCP_FS=m
 CONFIG_ISO9660_FS=y
-# CONFIG_HPFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
+CONFIG_HPFS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_AFFS_FS=m
+CONFIG_AMIGA_PARTITION=y
 CONFIG_UFS_FS=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_SMD_DISKLABEL=y
 
 #
 # Kernel hacking
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile
--- v2.1.8/linux/arch/sparc/kernel/Makefile	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/Makefile	Sat Nov  9 10:11:33 1996
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.30 1996/04/22 10:37:53 davem Exp $
+# $Id: Makefile,v 1.34 1996/09/21 04:07:31 davem Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -31,11 +31,14 @@
 
 O_TARGET := kernel.o
 IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o
-O_OBJS   := entry.o wof.o wuf.o etrap.o rtrap.o switch.o traps.o ${IRQ_OBJS} \
+O_OBJS   := entry.o wof.o wuf.o etrap.o rtrap.o traps.o ${IRQ_OBJS} \
 	    process.o signal.o ioport.o setup.o idprom.o \
 	    sys_sparc.o sunos_asm.o sparc-stub.o systbls.o sys_sunos.o  \
-	    sunos_ioctl.o time.o windows.o cpu.o devices.o ksyms.o \
-	    sclow.o solaris.o tadpole.o tick14.o ptrace.o
+	    sunos_ioctl.o time.o windows.o cpu.o devices.o \
+	    sclow.o solaris.o tadpole.o tick14.o ptrace.o sys_solaris.o \
+	    unaligned.o
+
+OX_OBJS  := sparc_ksyms.o
 
 ifdef SMP
 O_OBJS += trampoline.o smp.o rirq.o
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/auxio.c linux/arch/sparc/kernel/auxio.c
--- v2.1.8/linux/arch/sparc/kernel/auxio.c	Tue Apr 23 12:31:45 1996
+++ linux/arch/sparc/kernel/auxio.c	Sat Nov  9 10:11:34 1996
@@ -5,6 +5,7 @@
 
 #include <asm/oplib.h>
 #include <asm/io.h>
+#include <asm/auxio.h>
 
 /* Probe and map in the Auxiliary I/O register */
 unsigned char *auxio_register;
@@ -15,6 +16,10 @@
 	int node, auxio_nd;
 	struct linux_prom_registers auxregs[1];
 
+	if (sparc_cpu_model == sun4d) {
+		auxio_register = 0;
+		return;
+	}
 	node = prom_getchild(prom_root_node);
 	auxio_nd = prom_searchsiblings(node, "auxiliary-io");
 	if(!auxio_nd) {
@@ -37,4 +42,6 @@
 	if((((unsigned long) auxregs[0].phys_addr) & 3) == 3 ||
 	   sparc_cpu_model == sun4c)
 		auxio_register = (unsigned char *) ((int)auxio_register | 3);
+
+	TURN_ON_LED;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/cpu.c linux/arch/sparc/kernel/cpu.c
--- v2.1.8/linux/arch/sparc/kernel/cpu.c	Sun Apr 21 12:30:29 1996
+++ linux/arch/sparc/kernel/cpu.c	Sat Nov  9 10:11:34 1996
@@ -66,6 +66,7 @@
   { 5, 5, "reserved"},
   { 5, 6, "reserved"},
   { 5, 7, "No FPU"},
+  { 9, 3, "Weitek on-chip FPU"},
 };
 
 #define NSPARCFPU  (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info))
@@ -100,7 +101,10 @@
   { 7, 0, "Harvest VLSI Design Center, Inc. - unknown"},
   /* Gallium arsenide 200MHz, BOOOOGOOOOMIPS!!! */
   { 8, 0, "Systems and Processes Engineering Corporation (SPEC)"},
-  { 9, 0, "Fujitsu #3"},
+  { 9, 0, "Weitek Power-UP"},
+  { 9, 1, "Weitek Power-UP"},
+  { 9, 2, "Weitek Power-UP"},
+  { 9, 3, "Weitek Power-UP"},
   { 0xa, 0, "UNKNOWN CPU-VENDOR/TYPE"},
   { 0xb, 0, "UNKNOWN CPU-VENDOR/TYPE"},
   { 0xc, 0, "UNKNOWN CPU-VENDOR/TYPE"},
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/devices.c linux/arch/sparc/kernel/devices.c
--- v2.1.8/linux/arch/sparc/kernel/devices.c	Mon Apr 22 10:59:39 1996
+++ linux/arch/sparc/kernel/devices.c	Sat Nov  9 10:11:35 1996
@@ -18,6 +18,7 @@
 
 extern void cpu_probe(void);
 extern void clock_stop_probe(void); /* tadpole.c */
+extern void sun4c_probe_memerr_reg(void);
 
 unsigned long
 device_scan(unsigned long mem_start)
@@ -30,7 +31,7 @@
 #if CONFIG_AP1000
         printk("Not scanning device list for CPUs\n");
 	linux_num_cpus = 1;
-	return;
+	return mem_start;
 #endif
 
 	prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str));
@@ -75,6 +76,9 @@
 	}
 #endif
 	clock_stop_probe();
+
+	if (sparc_cpu_model == sun4c)
+		sun4c_probe_memerr_reg();
 
 	return mem_start;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S
--- v2.1.8/linux/arch/sparc/kernel/entry.S	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/entry.S	Sat Nov  9 10:11:36 1996
@@ -1,7 +1,9 @@
-/* $Id: entry.S,v 1.93 1996/04/25 06:08:32 davem Exp $
+/* $Id: entry.S,v 1.116 1996/10/27 08:35:47 davem Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  */
 
 #include <linux/config.h>
@@ -23,6 +25,8 @@
 
 #include <asm/asmmacro.h>
 
+#define curptr      g6
+
 #define NR_SYSCALLS 255      /* Each OS is different... */
 
 /* First, KGDB low level things.  This is a rewrite
@@ -108,48 +112,26 @@
 
 
 #ifdef CONFIG_BLK_DEV_FD
-#ifdef TRACE_FLOPPY_HARDINT
-/* Useful tracing */
-	.data
-	.align	4
-	.globl	C_LABEL(floppy_hardint_trace)
-C_LABEL(floppy_hardint_trace):
-	.skip	32
-	.globl	C_LABEL(floppy_hardint_index)
-C_LABEL(floppy_hardint_index):
-	.word	0
-#endif
-
 	.text
 	.align	4
 	.globl	C_LABEL(floppy_hardint)
 C_LABEL(floppy_hardint):
-	/* Can only use regs %l3->%l7:
+	/*
+	 * This code cannot touch registers %l0 %l1 and %l2
+	 * because SAVE_ALL depends on their values. It depends
+	 * on %l3 also, but we regenerate it before a call.
+	 * Other registers are:
 	 * %l3 -- base address of fdc registers
 	 * %l4 -- pdma_vaddr
 	 * %l5 -- scratch for ld/st address
 	 * %l6 -- pdma_size
-	 * %l7 -- floppy_softint
+	 * %l7 -- scratch [floppy byte, ld/st address, aux. data]
 	 */
 
-#ifdef TRACE_FLOPPY_HARDINT
-        sethi   %hi(C_LABEL(floppy_hardint_trace)), %l5
-        or      %l5, %lo(C_LABEL(floppy_hardint_trace)), %l5
-        ld      [%l5 + 32], %l7
-        add     %l7, 1, %l7
-        and     %l7, 31, %l7
-        st      %l7, [%l5 + 32]
-        sub     %l7, 1, %l7
-        and     %l7, 31, %l7
-        add     %l7, %l5, %l5
-	or	%g0, 0xf, %l7
-	stb	%l7, [%l5]
-#endif
-
 	/* Do we have work to do? */
-	sethi	%hi(C_LABEL(doing_pdma)), %l4
-	ld	[%l4 + %lo(C_LABEL(doing_pdma))], %l4
-	cmp	%l4, 0
+	sethi	%hi(C_LABEL(doing_pdma)), %l7
+	ld	[%l7 + %lo(C_LABEL(doing_pdma))], %l7
+	cmp	%l7, 0
 	be	floppy_dosoftint
 	 nop
 
@@ -163,21 +145,7 @@
 	sethi	%hi(C_LABEL(pdma_size)), %l5	! bytes to go
 	ld	[%l5 + %lo(C_LABEL(pdma_size))], %l6
 next_byte:
-#ifdef TRACE_FLOPPY_HARDINT
-	sethi	%hi(C_LABEL(floppy_hardint_trace)), %l5
-	or	%l5, %lo(C_LABEL(floppy_hardint_trace)), %l5
-	ld	[%l5 + 32], %l7
-	add	%l7, 1, %l7
-	and	%l7, 31, %l7
-	st	%l7, [%l5 + 32]
-	sub	%l7, 1, %l7
-	and	%l7, 31, %l7
-	add	%l7, %l5, %l5
-	ldub	[%l3], %l7
-	stb	%l7, [%l5]
-#else
   	ldub	[%l3], %l7
-#endif
 
 	andcc	%l7, 0x80, %g0		! Does fifo still have data
 	bz	floppy_fifo_emptied	! fifo has been emptied...
@@ -212,24 +180,24 @@
 	sethi	%hi(C_LABEL(pdma_size)), %l5
 	st	%l6, [%l5 + %lo(C_LABEL(pdma_size))]
 	/* Flip terminal count pin */
-	set	C_LABEL(auxio_register), %l4
-	ld	[%l4], %l4
+	set	C_LABEL(auxio_register), %l7
+	ld	[%l7], %l7
 
 	set	C_LABEL(sparc_cpu_model), %l5
 	ld	[%l5], %l5
 	subcc   %l5, 1, %g0		/* enum { sun4c = 1 }; */
 	be	1f
-	 ldub	[%l4], %l5
+	 ldub	[%l7], %l5
 
 	or	%l5, 0xc2, %l5
-	stb	%l5, [%l4]
+	stb	%l5, [%l7]
 	andn    %l5, 0x02, %l5
 	b	2f
 	 nop
 
 1:
 	or      %l5, 0xf4, %l5
-	stb     %l5, [%l4]
+	stb     %l5, [%l7]
 	andn    %l5, 0x04, %l5
 
 2:
@@ -237,12 +205,12 @@
 	WRITE_PAUSE
 	WRITE_PAUSE
 
-	stb     %l5, [%l4]
+	stb     %l5, [%l7]
 
 	/* Prevent recursion */
-	sethi	%hi(C_LABEL(doing_pdma)), %l4
+	sethi	%hi(C_LABEL(doing_pdma)), %l7
 	b	floppy_dosoftint
-	 st	%g0, [%l4 + %lo(C_LABEL(doing_pdma))]
+	 st	%g0, [%l7 + %lo(C_LABEL(doing_pdma))]
 
 	/* We emptied the FIFO, but we haven't read everything
 	 * as of yet.  Store the current transfer address and
@@ -268,8 +236,8 @@
 	sethi	%hi(C_LABEL(pdma_size)), %l5
 	st	%l6, [%l5 + %lo(C_LABEL(pdma_size))]
 	/* Prevent recursion */
-	sethi	%hi(C_LABEL(doing_pdma)), %l4
-	st	%g0, [%l4 + %lo(C_LABEL(doing_pdma))]
+	sethi	%hi(C_LABEL(doing_pdma)), %l7
+	st	%g0, [%l7 + %lo(C_LABEL(doing_pdma))]
 
 	/* fall through... */
 floppy_dosoftint:
@@ -283,9 +251,10 @@
 	wr	%l4, PSR_ET, %psr
 	WRITE_PAUSE
 
-	mov	11, %o0			! floppy irq level
+	mov	11, %o0			! floppy irq level (unused anyway)
+	mov	%g0, %o1		! devid is not used in fast interrupts
 	call	C_LABEL(floppy_interrupt)
-	 add	%sp, REGWIN_SZ, %o1	! struct pt_regs *regs
+	 add	%sp, REGWIN_SZ, %o2	! struct pt_regs *regs
 
 	LEAVE_IRQ
 	RESTORE_ALL
@@ -333,8 +302,7 @@
 	be	1f
 	 nop
 
-	b	linux_trap_ipi9_sun4m
-	 nop
+	b,a	linux_trap_ipi9_sun4m
 
 1:
 #endif
@@ -357,59 +325,25 @@
 	be	1f
 	 nop
 
-	b	linux_trap_ipi13_sun4m
-	 nop
+	b,a	linux_trap_ipi13_sun4m
 
 1:	
 
 #endif
 
 	/* start atomic operation with respect to software interrupts */
-	sethi	%hi(C_LABEL(intr_count)), %l4
-	ld	[%l4 + %lo(C_LABEL(intr_count))], %l5
-	add	%l5, 0x1, %l5
-	st	%l5, [%l4 + %lo(C_LABEL(intr_count))]
-
-	/* Enable traps w/IRQs off, so we can call c-code properly.
-	 * Note how we are increasing PIL so we need to do two writes
-	 * to work around a MicroSPARC bug of sorts.
-	 */
-	or	%l0, PSR_PIL, %l4
-
-	wr	%l4, 0x0, %psr
-	WRITE_PAUSE
-	wr	%l4, PSR_ET, %psr
-	WRITE_PAUSE
-
+	sethi	%hi(C_LABEL(intr_count)), %l6
+	ld	[%l6 + %lo(C_LABEL(intr_count))], %l5
+	or	%l0, PSR_PIL, %g2
+	add	%l5, 0x1, %l4
+	wr	%g2, 0x0, %psr
+	st	%l4, [%l6 + %lo(C_LABEL(intr_count))]
+	wr	%g2, PSR_ET, %psr
 	mov	%l7, %o0		! irq level
 	call	C_LABEL(handler_irq)
 	 add	%sp, REGWIN_SZ, %o1	! pt_regs ptr
-
-rie_checkbh:
-	sethi	%hi(C_LABEL(intr_count)), %l4
-	ld	[%l4 + %lo(C_LABEL(intr_count))], %l5
-	subcc	%l5, 0x1, %l5
-	bne	2f	/* IRQ within IRQ, get out of here... */
-	 nop
-
-	sethi	%hi(C_LABEL(bh_active)), %l3
-	ld	[%l3 + %lo(C_LABEL(bh_active))], %g2
-	sethi	%hi(C_LABEL(bh_mask)), %l3
-	ld	[%l3 + %lo(C_LABEL(bh_mask))], %g3
-	andcc	%g2, %g3, %g0
-	be	2f
-	 nop
-
-	call	C_LABEL(do_bottom_half)	
-	 nop
-
-	/* Try again... */
-	b	rie_checkbh
-	 nop
-	
-2:
-	st	%l5, [%l4 + %lo(C_LABEL(intr_count))]
-
+	wr	%l0, PSR_ET, %psr
+	st	%l5, [%l6 + %lo(C_LABEL(intr_count))]
 	LEAVE_IRQ
 	RESTORE_ALL
 
@@ -450,22 +384,36 @@
 
 	RESTORE_ALL
 
-	/* This routine handles unaligned data accesses.
-	 */
+	/* This routine handles unaligned data accesses. */
 	.align	4
 	.globl	mna_handler
 mna_handler:
+	andcc	%l0, PSR_PS, %g0
+	be	mna_fromuser
+	 ld	[%l1], %l7
+
+	SAVE_ALL
+	ENTER_SYSCALL
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	mov	%l7, %o1
+	call	C_LABEL(kernel_unaligned_trap)
+	 add	%sp, REGWIN_SZ, %o0
+
+	RESTORE_ALL
+
+mna_fromuser:
 	SAVE_ALL
 	ENTER_SYSCALL
 
 	wr	%l0, PSR_ET, %psr		! re-enable traps
 	WRITE_PAUSE
 
-	add	%sp, REGWIN_SZ, %o0
-	mov	%l1, %o1
-	mov	%l2, %o2
-	call	C_LABEL(do_memaccess_unaligned)
-	 mov	%l0, %o3
+	mov	%l7, %o1
+	call	C_LABEL(user_unaligned_trap)
+	 add	%sp, REGWIN_SZ, %o0
 
 	RESTORE_ALL
 
@@ -677,8 +625,11 @@
 
 	RESTORE_ALL
 
+	.globl	flush_patch_one
+
 	/* We get these for debugging routines using __builtin_return_address() */
 dfw_kernel:
+flush_patch_one:
 	FLUSH_ALL_KERNEL_WINDOWS
 
 	/* Advance over the trap instruction. */
@@ -711,7 +662,7 @@
 setcc_trap_handler:
 	sll	%g1, 0x14, %l4
 	set	PSR_ICC, %l5
-	andn	%l0, %l5, %l0	! clear ICC bits in current %psr
+	andn	%l0, %l5, %l0	! clear ICC bits in %psr
 	and	%l4, %l5, %l4	! clear non-ICC bits in user value
 	or	%l4, %l0, %l4	! or them in... mix mix mix
 
@@ -901,26 +852,194 @@
 
 #endif
 
+
+	.align	4
+	.globl	C_LABEL(invalid_segment_patch1_ff)
+	.globl	C_LABEL(invalid_segment_patch2_ff)
+C_LABEL(invalid_segment_patch1_ff):	cmp	%l4, 0xff
+C_LABEL(invalid_segment_patch2_ff):	mov	0xff, %l4
+
+	.align	4
+	.globl	C_LABEL(num_context_patch1_16), C_LABEL(num_context_patch2_16)
+C_LABEL(num_context_patch1_16):		mov	0x10, %l7
+C_LABEL(num_context_patch2_16):		mov	0x10, %l7
+
+	.align	4
+	.globl	C_LABEL(sun4c_kernel_buckets_patch_32)
+C_LABEL(sun4c_kernel_buckets_patch_32):	andn	%l7, 256, %l3
+
+	.globl	C_LABEL(invalid_segment_patch1), C_LABEL(invalid_segment_patch2)
+	.globl	C_LABEL(num_context_patch1), C_LABEL(num_context_patch2)
+	.globl	C_LABEL(sun4c_kernel_buckets_patch)
+
 	.align	4
 	.globl	sun4c_fault
 sun4c_fault:
-	SAVE_ALL
-	ENTER_SYSCALL
-
-	/* XXX This needs to be scheduled better */
 	sethi	%hi(AC_SYNC_ERR), %l4
-	add	%l4, 0x4, %l5		! AC_SYNC_VA in %l5
-	lda	[%l5] ASI_CONTROL, %o3		/* Address */
+	add	%l4, 0x4, %l6			! AC_SYNC_VA in %l6
+	lda	[%l6] ASI_CONTROL, %l5		! Address
 	lda	[%l4] ASI_CONTROL, %l6
-	srl	%l6, 15, %l6
-	and	%l6, 1, %o2	/* Write? */
+
+	andn	%l5, 0xfff, %l5			! Encode all info into l7
+	srl	%l6, 14, %l6
+
+	and	%l6, 2, %l6
+	or	%l5, %l6, %l6
+
+	or	%l6, %l7, %l7			! l7 = [addr,write,txtfault]
+
+	andcc	%l0, PSR_PS, %g0
+	be	sun4c_fault_fromuser
+	 andcc	%l7, 1, %g0			! Text fault?
+
+	be	1f
+	 sethi	%hi(KERNBASE), %l6
+
+	mov	%l1, %l5			! PC
+
+1:
+	cmp	%l5, %l6
+	blu	sun4c_fault_fromuser
+	 sethi	%hi(0xfffc0000), %l4		! SUN4C_REAL_PGDIR_MASK
+
+	and	%l5, %l4, %l5
+
+	lduba	[%l5] ASI_SEGMAP, %l4
+C_LABEL(invalid_segment_patch1):
+	cmp	%l4, 0x7f
+	bne	1f
+	 sethi	%hi(C_LABEL(sun4c_kernel_next)), %l4
+
+	ld	[%l4 + %lo(C_LABEL(sun4c_kernel_next))], %l6	! entry
+
+	ld	[%l6], %l3		! entry->vaddr
+	cmp	%l3, 0			! is this segment available?
+	be	4f			! Yes, use it.
+	 st	%l5, [%l6]		! entry->vaddr = address
+
+	! use entry->vaddr to unmap the old segment
+	mov	%l3, %l5
+
+C_LABEL(num_context_patch1):
+	mov	0x08, %l7
+
+C_LABEL(invalid_segment_patch2):
+	mov	0x7f, %l4
+
+	sethi	%hi(AC_CONTEXT), %l3
+	lduba	[%l3] ASI_CONTROL, %l6
+
+3:
+	deccc	%l7
+	stba	%l7, [%l3] ASI_CONTROL
+	bne	3b
+	 stba	%l4, [%l5] ASI_SEGMAP
+
+	stba	%l6, [%l3] ASI_CONTROL
+
+	! reload the entry
+
+	sethi	%hi(C_LABEL(sun4c_kernel_next)), %l4
+	ld	[%l4 + %lo(C_LABEL(sun4c_kernel_next))], %l6
+
+	ld	[%l6], %l5	! restore address from entry->vaddr
+
+4:
+	! advance sun4c_kernel_next
+	add	%l6, 8, %l7
+C_LABEL(sun4c_kernel_buckets_patch):
+	andn	%l7, 128, %l3
+	st	%l3, [%l4 + %lo(C_LABEL(sun4c_kernel_next))]
+
+C_LABEL(num_context_patch2):
+	mov	0x08, %l7
+
+	ldub	[%l6 + 0x4], %l4	! entry->pseg
+
+	sethi	%hi(AC_CONTEXT), %l3
+	lduba	[%l3] ASI_CONTROL, %l6
+
+3:
+	deccc	%l7
+	stba	%l7, [%l3] ASI_CONTROL
+	bne	3b
+	 stba	%l4, [%l5] ASI_SEGMAP
+
+	stba	%l6, [%l3] ASI_CONTROL
+
+1:
+	sethi	%hi(0xfe200000), %l4		! SUN4C_VMALLOC_START
+	cmp	%l5, %l4
+
+	bgeu	1f
+	 mov	0x40, %l7	! SUN4C_REAL_PGDIR_SIZE / PAGE_SIZE
+
+	sethi	%hi(KERNBASE), %l6
+
+	sub	%l5, %l6, %l4
+	srl	%l4, PAGE_SHIFT, %l4
+	sethi	%hi(0xf3000000), %l3		! SUN4C_PAGE_KERNEL
+	or	%l3, %l4, %l3
+
+	sethi	%hi(PAGE_SIZE), %l4
+
+2:
+	sta	%l3, [%l5] ASI_PTE
+	deccc	%l7
+	inc	%l3
+	bne	2b
+	 add	%l5, %l4, %l5
+
+	/* Restore condition codes */
+	wr	%l0, 0x0, %psr
+	WRITE_PAUSE
+	jmp	%l1
+	 rett	%l2
+
+1:
+	srl	%l5, 22, %l3			! SUN4C_PGDIR_SHIFT
+	sethi	%hi(C_LABEL(swapper_pg_dir)), %l4
+	or	%l4, %lo(C_LABEL(swapper_pg_dir)), %l4
+	sll	%l3, 2, %l3
+	ld	[%l4 + %l3], %l4
+	andn	%l4, 0xfff, %l4			! PAGE_MASK
+
+	srl	%l5, PAGE_SHIFT - 2, %l6
+	and	%l6, 0xffc, %l6			! (SUN4C_PTRS_PER_PTE - 1) << 2
+	add	%l6, %l4, %l6
+
+	sethi	%hi(PAGE_SIZE), %l4
+
+2:
+	ld	[%l6], %l3
+	deccc	%l7
+	sta	%l3, [%l5] ASI_PTE
+	add	%l6, 0x4, %l6
+	bne	2b
+	 add	%l5, %l4, %l5
+
+	/* Restore condition codes */
+	wr	%l0, 0x0, %psr
+	WRITE_PAUSE
+	jmp	%l1
+	 rett	%l2
+
+sun4c_fault_fromuser:
+	SAVE_ALL
+	ENTER_SYSCALL
+	
+	mov	%l7, %o1		! Decode the info from %l7
+	mov	%l7, %o2
+	and	%o1, 1, %o1		! arg2 = text_faultp
+	mov	%l7, %o3
+	and	%o2, 2, %o2		! arg3 = writep
+	andn	%o3, 0xfff, %o3		! arg4 = faulting address
 
 	wr	%l0, PSR_ET, %psr
 	WRITE_PAUSE
 
-	mov	%l7, %o1			/* Text fault? */
-	call	C_LABEL(do_sparc_fault)
-	 add	%sp, REGWIN_SZ, %o0		/* pt_regs */
+	call	C_LABEL(do_sun4c_fault)
+	 add	%sp, REGWIN_SZ, %o0	! arg1 = pt_regs ptr
 
 	RESTORE_ALL
 
@@ -965,34 +1084,35 @@
 	 */
 	.globl	C_LABEL(sunos_indir)
 C_LABEL(sunos_indir):
-	ld	[%sp + REGWIN_SZ + PT_I0], %g1
-	cmp	%g1, NR_SYSCALLS
+	mov	%o7, %l4
+	cmp	%o0, NR_SYSCALLS
 	blu,a	1f
-	 sll	%g1, 0x2, %g1
+	 sll	%o0, 0x2, %o0
 
-	set	C_LABEL(sunos_nosys), %l6
+	sethi	%hi(C_LABEL(sunos_nosys)), %l6
 	b	2f
-	 nop
+	 or	%l6, %lo(C_LABEL(sunos_nosys)), %l6
 
 1:
 	set	C_LABEL(sunos_sys_table), %l7
-	ld	[%l7 + %g1], %l6
+	ld	[%l7 + %o0], %l6
 
 2:	
-	ld	[%sp + REGWIN_SZ + PT_I1], %o0
-	ld	[%sp + REGWIN_SZ + PT_I2], %o1
-	ld	[%sp + REGWIN_SZ + PT_I3], %o2
-	mov	%o7, %l5
-	ld	[%sp + REGWIN_SZ + PT_I4], %o3
+	mov	%o1, %o0
+	mov	%o2, %o1
+	mov	%o3, %o2
+	mov	%o4, %o3
+	mov	%o5, %o4
 	call	%l6
-	 ld	[%sp + REGWIN_SZ + PT_I5], %o4
-
-	jmp	%l5 + 0x8		/* so stupid... */
-	 nop
+	 mov	%l4, %o7
 
-	/* Note how we really return to ret_syscall because we share the
-	 * register window with our caller.
-	 */
+	.align	4
+	.globl	C_LABEL(sys_nis_syscall)
+C_LABEL(sys_nis_syscall):
+	mov	%o7, %l5
+	add	%sp, REGWIN_SZ, %o0		! pt_regs *regs arg
+	call	C_LABEL(c_sys_nis_syscall)
+	 mov	%l5, %o7
 
 	.align 4
 	.globl	C_LABEL(sys_ptrace)
@@ -1000,8 +1120,7 @@
 	call	C_LABEL(do_ptrace)
 	 add	%sp, REGWIN_SZ, %o0
 
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 0x14], %l5
+	ld	[%curptr + 0x14], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1016,32 +1135,26 @@
 	.globl	C_LABEL(sys_execve)
 C_LABEL(sys_execve):
 	mov	%o7, %l5
+	add	%sp, REGWIN_SZ, %o0		! pt_regs *regs arg
 	call	C_LABEL(sparc_execve)
-	 add	%sp, REGWIN_SZ, %o0		! pt_regs *regs arg
-
-	jmp	%l5 + 0x8
-	 nop
+	 mov	%l5, %o7
 
 	.align	4
 	.globl	C_LABEL(sys_pipe)
 C_LABEL(sys_pipe):
 	mov	%o7, %l5
-
+	add	%sp, REGWIN_SZ, %o0		! pt_regs *regs arg
 	call	C_LABEL(sparc_pipe)
-	 add	%sp, REGWIN_SZ, %o0		! pt_regs *regs arg
-
-	jmp	%l5 + 0x8
-	 nop
+	 mov	%l5, %o7
 
 	.align	4
 	.globl	C_LABEL(sys_sigpause)
 C_LABEL(sys_sigpause):
-	ld	[%sp + REGWIN_SZ + PT_I0], %o0
+	/* Note: %o0 already has correct value... */
 	call	C_LABEL(do_sigpause)
 	 add	%sp, REGWIN_SZ, %o1
 
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 0x14], %l5
+	ld	[%curptr + 0x14], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1059,8 +1172,7 @@
 	call	C_LABEL(do_sigsuspend)
 	 add	%sp, REGWIN_SZ, %o0
 
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 0x14], %l5
+	ld	[%curptr + 0x14], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1078,8 +1190,7 @@
 	call	C_LABEL(do_sigreturn)
 	 add	%sp, REGWIN_SZ, %o0
 
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 0x14], %l5
+	ld	[%curptr + 0x14], %l5
 	andcc	%l5, 0x20, %g0
 	be	1f
 	 nop
@@ -1098,94 +1209,57 @@
 	 * of SunOS vfork() will use sys_clone() instead.
 	 */
 	.align	4
-	.globl	C_LABEL(sys_fork), C_LABEL(sys_vfork)
+	.globl	C_LABEL(sys_fork), C_LABEL(sys_vfork), flush_patch_two
 C_LABEL(sys_vfork):
 C_LABEL(sys_fork):
 	mov	%o7, %l5
-
-	/* Save the kernel state as of now. */
+flush_patch_two:
 	FLUSH_ALL_KERNEL_WINDOWS;
-	STORE_WINDOW(sp)
-	LOAD_CURRENT(g6, g5)
 	rd	%psr, %g4
-	rd	%wim, %g5
-	std	%g4, [%g6 + THREAD_FORK_KPSR]
-
 	mov	SIGCHLD, %o0			! arg0:	clone flags
-	ld	[%sp + REGWIN_SZ + PT_FP], %o1	! arg1:	usp
+	rd	%wim, %g5
+	mov	%fp, %o1			! arg1:	usp
+	std	%g4, [%curptr + THREAD_FORK_KPSR]
+	add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
 	call	C_LABEL(do_fork)
-	 add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
-
-	jmp	%l5 + 0x8
-	 nop
+	 mov	%l5, %o7
 	
 	/* Whee, kernel threads! */
-	.globl	C_LABEL(sys_clone)
+	.globl	C_LABEL(sys_clone), flush_patch_three
 C_LABEL(sys_clone):
 	mov	%o7, %l5
-
-	/* Save the kernel state as of now. */
+flush_patch_three:
 	FLUSH_ALL_KERNEL_WINDOWS;
-	STORE_WINDOW(sp)
-	LOAD_CURRENT(g6, g5)
 	rd	%psr, %g4
-	rd	%wim, %g5
-	std	%g4, [%g6 + THREAD_FORK_KPSR]
 
-	ldd	[%sp + REGWIN_SZ + PT_I0], %o0	! arg0,1: flags,usp
+	/* arg0,1: flags,usp  -- loaded already */
 	cmp	%o1, 0x0			! Is new_usp NULL?
+	rd	%wim, %g5
 	be,a	1f
-	 ld	[%sp + REGWIN_SZ + PT_FP], %o1	! yes, use current usp
+	 mov	%fp, %o1			! yes, use callers usp
+	andn	%o1, 7, %o1			! no, align to 8 bytes
 1:
+	std	%g4, [%curptr + THREAD_FORK_KPSR]
+	add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
 	call	C_LABEL(do_fork)
-	 add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
-
-	jmp	%l5 + 0x8
-	 nop
+	 mov	%l5, %o7
 
 	/* Linux native and SunOS system calls enter here... */
 	.align	4
 	.globl	linux_sparc_syscall
 linux_sparc_syscall:
-	/* While we are here trying to optimize our lives
-	 * away, handle the easy bogus cases like a
-	 * ni_syscall or sysnum > NR_SYSCALLS etc.
-	 * In the cases where we cannot optimize the
-	 * call inline we don't really lose anything
-	 * performance wise because we are doing here
-	 * things which we did anyway in the original
-	 * routine.  The only added complexity is a
-	 * bit test, compare, and branch to decide
-	 * if we need to save process state or not.
-	 */
-
-	/* XXX TODO: When we have ptrace working test
-	 * XXX       test for PF_TRACESYS in task flags.
-	 */
-
 	/* Direct access to user regs, must faster. */
 	cmp	%g1, NR_SYSCALLS
 	blu,a	1f
 	 sll	%g1, 2, %l4
 
-	set	C_LABEL(sys_ni_syscall), %l7
+	sethi	%hi(C_LABEL(sys_ni_syscall)), %l7
 	b	syscall_is_too_hard
-	 nop
+	 or	%l7, %lo(C_LABEL(sys_ni_syscall)), %l7
 
 1:
 	ld	[%l7 + %l4], %l7
 
-	/* If bit-1 is set, this is a "fast" syscall.
-	 * This is the _complete_ overhead of this optimization,
-	 * and we save ourselves a load, so it evens out to nothing.
-	 */
-	andcc	%l7, 0x1, %g0
-	be	syscall_is_too_hard
-	 andn	%l7, 0x1, %l7
-
-	jmpl	%l7, %g0
-	 nop
-
 	.globl	syscall_is_too_hard
 syscall_is_too_hard:
 	rd	%wim, %l3
@@ -1195,108 +1269,99 @@
 	wr	%l0, PSR_ET, %psr
 	WRITE_PAUSE
 
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 0x14], %l5
+	ld	[%curptr + 0x14], %l5
 	andcc	%l5, 0x20, %g0
-	be	2f
-	 nop
+	be,a	2f
+	 mov	%i0, %o0
 
 	call	C_LABEL(syscall_trace)
 	 nop
 
+	mov	%i0, %o0
 2:
-	ldd	[%sp + REGWIN_SZ + PT_I0], %o0
-	st	%o0, [%sp + REGWIN_SZ + PT_G0]	! for restarting syscalls
-	ldd	[%sp + REGWIN_SZ + PT_I2], %o2
+	mov	%i1, %o1
+	mov	%i2, %o2
+	mov	%i0, %l5
+	mov	%i3, %o3
+	mov	%i4, %o4
 	call	%l7
-	 ldd	[%sp + REGWIN_SZ + PT_I4], %o4
+	 mov	%i5, %o5
 
 	st	%o0, [%sp + REGWIN_SZ + PT_I0]
 
 	.globl	C_LABEL(ret_sys_call)
 C_LABEL(ret_sys_call):
 	ld	[%sp + REGWIN_SZ + PT_I0], %o0
-	set	PSR_C, %l6
+	set	PSR_C, %g2
 	cmp	%o0, -ENOIOCTLCMD
 	bgeu	1f
-	 ld	[%sp + REGWIN_SZ + PT_PSR], %l5
+	 ld	[%sp + REGWIN_SZ + PT_PSR], %g3
 
 	/* System call success, clear Carry condition code. */		
-	andn	%l5, %l6, %l5
+	andn	%g3, %g2, %g3
+	clr	%l6
 	b	2f
-	 st	%l5, [%sp + REGWIN_SZ + PT_PSR]	
+	 st	%g3, [%sp + REGWIN_SZ + PT_PSR]	
 
 1:
 	/* System call failure, set Carry condition code.
 	 * Also, get abs(errno) to return to the process.
 	 */
 	sub	%g0, %o0, %o0
+	or	%g3, %g2, %g3
 	st	%o0, [%sp + REGWIN_SZ + PT_I0]
-	or	%l5, %l6, %l5
-	st	%l5, [%sp + REGWIN_SZ + PT_PSR]
+	mov	1, %l6
+	st	%g3, [%sp + REGWIN_SZ + PT_PSR]
 
 2:
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 0x14], %l5
-	andcc	%l5, 0x20, %g0
-	be	3f
-	 nop
+	ld	[%curptr + 0x14], %g2
+	andcc	%g2, 0x20, %g0
+	be,a	3f
+	 ld	[%sp + REGWIN_SZ + PT_NPC], %l1	/* pc  = npc   */
 
 	call	C_LABEL(syscall_trace)
 	 nop
 
 	/* Advance the pc and npc over the trap instruction. */
-3:
 	ld	[%sp + REGWIN_SZ + PT_NPC], %l1	/* pc  = npc   */
+3:
 	add	%l1, 0x4, %l2			/* npc = npc+4 */
 	st	%l1, [%sp + REGWIN_SZ + PT_PC]
-	st	%l2, [%sp + REGWIN_SZ + PT_NPC]
-
-	RESTORE_ALL
+	b	ret_trap_entry
+	 st	%l2, [%sp + REGWIN_SZ + PT_NPC]
 
-	/* Solaris system calls enter here... */
+	/*
+	 * Solaris system calls and indirect system calls enter here.
+         *
+	 * I have named the solaris indirect syscalls like that because
+	*  it seems like Solaris has some fast path syscalls that can
+	 * be handled as indirect system calls. - mig
+	 */
+	
+	.align  4
+	.globl  solaris_indirect_syscall
+solaris_indirect_syscall:
+	/* sethi done on the macro */
+        /* or %l7, %lo(C_LABEL(sys_call_table)), %l7; -- really needed? */
+	
 	.align	4
 	.globl	solaris_syscall
 solaris_syscall:
-	/* While we are here trying to optimize our lives
-	 * away, handle the easy bogus cases like a
-	 * ni_syscall or sysnum > NR_SYSCALLS etc.
-	 * In the cases where we cannot optimize the
-	 * call inline we don't really lose anything
-	 * performance wise because we are doing here
-	 * things which we did anyway in the original
-	 * routine.  The only added complexity is a
-	 * bit test, compare, and branch to decide
-	 * if we need to save process state or not.
-	 */
-
-	/* XXX TODO: When we have ptrace working test
-	 * XXX       test for PF_TRACESYS in task flags.
-	 */
-
 	/* Direct access to user regs, must faster. */
 	cmp	%g1, NR_SYSCALLS
 	blu,a	1f
+#ifdef OLD_SOLARIS
 	 sll	%g1, 2, %l4
-
-	set	C_LABEL(sys_ni_syscall), %l7
-	b	solaris_is_too_hard
+#else
 	 nop
-
+#endif
+	sethi	%hi(C_LABEL(sys_ni_syscall)), %l7
+	b	solaris_is_too_hard
+	 or	%l7, %lo(C_LABEL(sys_ni_syscall)), %l7
 1:
+#ifdef OLD_SOLARIS
 	ld	[%l7 + %l4], %l7
-
-	/* If bit-1 is set, this is a "fast" syscall.
-	 * This is the _complete_ overhead of this optimization,
-	 * and we save ourselves a load, so it evens out to nothing.
-	 */
-	andcc	%l7, 0x1, %g0
-	be	solaris_is_too_hard
-	 andn	%l7, 0x1, %l7
-
-	jmpl	%l7, %g0
-	 nop
-
+#endif
 	.globl	solaris_is_too_hard
 solaris_is_too_hard:
 	rd	%wim, %l3
@@ -1307,22 +1372,32 @@
 	WRITE_PAUSE
 
 2:
-	ldd	[%sp + REGWIN_SZ + PT_I0], %o0
-	st	%o0, [%sp + REGWIN_SZ + PT_G0]	! for restarting syscalls
-	ldd	[%sp + REGWIN_SZ + PT_I2], %o2
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov	%i2, %o2
+	mov	%i0, %l5
+	mov	%i3, %o3
+	mov	%i4, %o4
+#ifdef OLD_SOLARIS
 	call	%l7
-	 ldd	[%sp + REGWIN_SZ + PT_I4], %o4
+	 mov	%i5, %o5
+#else
+	mov	%i5, %o5
+	call	C_LABEL(do_solaris_syscall)
+	 add	%sp, REGWIN_SZ, %o0
+#endif
 
 	st	%o0, [%sp + REGWIN_SZ + PT_I0]
-	set	PSR_C, %l6
+	set	PSR_C, %g2
 	cmp	%o0, -ENOIOCTLCMD
 	bgeu	1f
-	 ld	[%sp + REGWIN_SZ + PT_PSR], %l5
+	 ld	[%sp + REGWIN_SZ + PT_PSR], %g3
 
 	/* System call success, clear Carry condition code. */		
-	andn	%l5, %l6, %l5
+	andn	%g3, %g2, %g3
+	clr	%l6
 	b	2f
-	 st	%l5, [%sp + REGWIN_SZ + PT_PSR]	
+	 st	%g3, [%sp + REGWIN_SZ + PT_PSR]	
 
 1:
 	/* System call failure, set Carry condition code.
@@ -1333,39 +1408,23 @@
 	or	%o3, %lo(C_LABEL(solaris_xlatb_rorl)), %o3
 	sll	%o0, 2, %o0
 	ld	[%o3 + %o0], %o0
+	mov	1, %l6
 	st	%o0, [%sp + REGWIN_SZ + PT_I0]
-	or	%l5, %l6, %l5
-	st	%l5, [%sp + REGWIN_SZ + PT_PSR]
+	or	%g3, %g2, %g3
+	st	%g3, [%sp + REGWIN_SZ + PT_PSR]
 
 	/* Advance the pc and npc over the trap instruction. */
 2:
 	ld	[%sp + REGWIN_SZ + PT_NPC], %l1	/* pc  = npc   */
 	add	%l1, 0x4, %l2			/* npc = npc+4 */
 	st	%l1, [%sp + REGWIN_SZ + PT_PC]
-	st	%l2, [%sp + REGWIN_SZ + PT_NPC]
-
-	RESTORE_ALL
+	b	ret_trap_entry
+	 st	%l2, [%sp + REGWIN_SZ + PT_NPC]
 
 	/* {net, open}bsd system calls enter here... */
 	.align	4
 	.globl	bsd_syscall
 bsd_syscall:
-	/* While we are here trying to optimize our lives
-	 * away, handle the easy bogus cases like a
-	 * ni_syscall or sysnum > NR_SYSCALLS etc.
-	 * In the cases where we cannot optimize the
-	 * call inline we don't really lose anything
-	 * performance wise because we are doing here
-	 * things which we did anyway in the original
-	 * routine.  The only added complexity is a
-	 * bit test, compare, and branch to decide
-	 * if we need to save process state or not.
-	 */
-
-	/* XXX TODO: When we have ptrace working test
-	 * XXX       test for PF_TRACESYS in task flags.
-	 */
-
 	/* Direct access to user regs, must faster. */
 	cmp	%g1, NR_SYSCALLS
 	blu,a	1f
@@ -1378,17 +1437,6 @@
 1:
 	ld	[%l7 + %l4], %l7
 
-	/* If bit-1 is set, this is a "fast" syscall.
-	 * This is the _complete_ overhead of this optimization,
-	 * and we save ourselves a load, so it evens out to nothing.
-	 */
-	andcc	%l7, 0x1, %g0
-	be	bsd_is_too_hard
-	 andn	%l7, 0x1, %l7
-
-	jmpl	%l7, %g0
-	 nop
-
 	.globl	bsd_is_too_hard
 bsd_is_too_hard:
 	rd	%wim, %l3
@@ -1399,22 +1447,26 @@
 	WRITE_PAUSE
 
 2:
-	ldd	[%sp + REGWIN_SZ + PT_I0], %o0
-	st	%o0, [%sp + REGWIN_SZ + PT_G0]	! for restarting syscalls
-	ldd	[%sp + REGWIN_SZ + PT_I2], %o2
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov	%i2, %o2
+	mov	%i0, %l5
+	mov	%i3, %o3
+	mov	%i4, %o4
 	call	%l7
-	 ldd	[%sp + REGWIN_SZ + PT_I4], %o4
+	 mov	%i5, %o5
 
 	st	%o0, [%sp + REGWIN_SZ + PT_I0]
-	set	PSR_C, %l6
+	set	PSR_C, %g2
 	cmp	%o0, -ENOIOCTLCMD
 	bgeu	1f
-	 ld	[%sp + REGWIN_SZ + PT_PSR], %l5
+	 ld	[%sp + REGWIN_SZ + PT_PSR], %g3
 
 	/* System call success, clear Carry condition code. */		
-	andn	%l5, %l6, %l5
+	andn	%g3, %g2, %g3
+	clr	%l6
 	b	2f
-	 st	%l5, [%sp + REGWIN_SZ + PT_PSR]	
+	 st	%g3, [%sp + REGWIN_SZ + PT_PSR]	
 
 1:
 	/* System call failure, set Carry condition code.
@@ -1427,18 +1479,18 @@
 	sll	%o0, 2, %o0
 	ld	[%o3 + %o0], %o0
 #endif
+	mov	1, %l6
 	st	%o0, [%sp + REGWIN_SZ + PT_I0]
-	or	%l5, %l6, %l5
-	st	%l5, [%sp + REGWIN_SZ + PT_PSR]
+	or	%g3, %g2, %g3
+	st	%g3, [%sp + REGWIN_SZ + PT_PSR]
 
 	/* Advance the pc and npc over the trap instruction. */
 2:
 	ld	[%sp + REGWIN_SZ + PT_NPC], %l1	/* pc  = npc   */
 	add	%l1, 0x4, %l2			/* npc = npc+4 */
 	st	%l1, [%sp + REGWIN_SZ + PT_PC]
-	st	%l2, [%sp + REGWIN_SZ + PT_NPC]
-
-	RESTORE_ALL
+	b	ret_trap_entry
+	 st	%l2, [%sp + REGWIN_SZ + PT_NPC]
 
 /* Saving and restoring the FPU state is best done from lowlevel code.
  *
@@ -1549,5 +1601,31 @@
 	
 	ret
 	restore
+
+	/* Handle a software breakpoint */
+	/* We have to inform parent that child has stopped */
+	.align 4
+	.globl breakpoint_trap
+breakpoint_trap:
+	rd	%wim,%l3
+	SAVE_ALL
+	ENTER_SYSCALL
+	wr 	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	st	%i0, [%sp + REGWIN_SZ + PT_G0] ! for restarting syscalls
+	call	C_LABEL(sparc_breakpoint)
+	 add	%sp, REGWIN_SZ, %o0
+
+	RESTORE_ALL
+
+	.align	4
+	.globl	C_LABEL(__handle_exception), flush_patch_exception
+C_LABEL(__handle_exception):
+flush_patch_exception:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	ldd	[%o0], %o6
+	jmpl	%o7 + 0xc, %g0			! see asm-sparc/processor.h
+	 mov	1, %g1				! signal EFAULT condition
 
 /* End of entry.S */
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/etrap.S linux/arch/sparc/kernel/etrap.S
--- v2.1.8/linux/arch/sparc/kernel/etrap.S	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/etrap.S	Sat Nov  9 10:11:37 1996
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.18 1996/04/25 06:08:35 davem Exp $
+/* $Id: etrap.S,v 1.21 1996/10/11 00:59:40 davem Exp $
  * etrap.S: Sparc trap window preparation for entry into the
  *          Linux kernel.
  *
@@ -23,7 +23,7 @@
 #define t_kstack     l5 /* Set right before pt_regs frame is built */
 #define t_retpc      l6 /* If you change this, change winmacro.h header file */
 #define t_systable   l7 /* Never touch this, could be the syscall table ptr. */
-#define curptr       g4 /* Set after pt_regs frame is built */
+#define curptr       g6 /* Set after pt_regs frame is built */
 
 	.text
 	.align 4
@@ -78,10 +78,9 @@
 	 * or kernel and branch conditionally.
 	 */
 	mov	1, %t_twinmask
-	sll	%t_twinmask, %t_psr, %t_twinmask ! t_twinmask = (1 << psr)
 	andcc	%t_psr, PSR_PS, %g0		 ! fromsupv_p = (psr & PSR_PS)
 	be	trap_setup_from_user		 ! nope, from user mode
-	 nop
+	 sll	%t_twinmask, %t_psr, %t_twinmask ! t_twinmask = (1 << psr)
 
 	/* From kernel, allocate more kernel stack and
 	 * build a pt_regs trap frame.
@@ -94,8 +93,7 @@
 	be	1f
 	 nop
 
-	b	trap_setup_kernel_spill		! in trap window, clean up
-	 nop
+	b,a	trap_setup_kernel_spill		! in trap window, clean up
 
 	/* Trap from kernel with a window available.
 	 * Just do it...
@@ -105,7 +103,6 @@
 	 mov	%t_kstack, %sp		! jump onto new stack
 
 trap_setup_kernel_spill:
-	LOAD_CURRENT(curptr, g1)
 	ld	[%curptr + THREAD_UMASK], %g1
 	orcc	%g0, %g1, %g0
 	bne	trap_setup_user_spill	! there are some user windows, yuck
@@ -123,7 +120,6 @@
 
 	/* Set new %wim value */
 	wr	%g2, 0x0, %wim
-	WRITE_PAUSE
 
 	/* Save the kernel window onto the corresponding stack. */
 	STORE_WINDOW(sp)
@@ -198,7 +194,6 @@
 	save	%g0, %g0, %g0
 
 	wr	%g2, 0x0, %wim
-	WRITE_PAUSE
 
 	/* Call MMU-architecture dependent stack checking
 	 * routine.
@@ -239,8 +234,7 @@
 	be	1f
 	 sra	%sp, 29, %glob_tmp
 
-	b	trap_setup_user_stack_is_bolixed
-	 nop
+	b,a	trap_setup_user_stack_is_bolixed
 
 1:
 	add	%glob_tmp, 0x1, %glob_tmp
@@ -248,8 +242,7 @@
 	be	1f
 	 and	%sp, 0xfff, %glob_tmp		! delay slot
 
-	b	trap_setup_user_stack_is_bolixed
-	 nop
+	b,a	trap_setup_user_stack_is_bolixed
 
 	/* See if our dump area will be on more than one
 	 * page.
@@ -267,8 +260,7 @@
 	be	1f
 	 add	%sp, 0x38, %glob_tmp		/* Is second page in vma hole? */
 
-	b	trap_setup_user_stack_is_bolixed
-	 nop
+	b,a	trap_setup_user_stack_is_bolixed
 
 1:
 	sra	%glob_tmp, 29, %glob_tmp
@@ -277,8 +269,7 @@
 	be	1f
 	 add	%sp, 0x38, %glob_tmp
 
-	b	trap_setup_user_stack_is_bolixed
-	 nop
+	b,a	trap_setup_user_stack_is_bolixed
 
 1:
 	lda	[%glob_tmp] ASI_PTE, %glob_tmp
@@ -289,20 +280,25 @@
 	be	trap_setup_good_ustack			! success
 	 nop
 
-	b	trap_setup_user_stack_is_bolixed
-	 nop
+	b,a	trap_setup_user_stack_is_bolixed
 
 	.globl	C_LABEL(tsetup_srmmu_stackchk)
 C_LABEL(tsetup_srmmu_stackchk):
 	/* Check results of callers andcc %sp, 0x7, %g0 */
-	bne	trap_setup_user_stack_is_bolixed
-	 sethi	%hi(KERNBASE), %glob_tmp
+	sethi	%hi(C_LABEL(page_offset)), %glob_tmp
+	be	1f
+	 ld	[%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp
+
+	b,a	trap_setup_user_stack_is_bolixed
+1:
 	cmp	%glob_tmp, %sp
-	bleu	trap_setup_user_stack_is_bolixed
-	 nop
+	bgu,a	1f
+	 lda	[%g0] ASI_M_MMUREGS, %glob_tmp		! read MMU control
+
+	b,a	trap_setup_user_stack_is_bolixed
 
+1:
 	/* Clear the fault status and turn on the no_fault bit. */
-	lda	[%g0] ASI_M_MMUREGS, %glob_tmp		! read MMU control
 	or	%glob_tmp, 0x2, %glob_tmp		! or in no_fault bit
 	sta	%glob_tmp, [%g0] ASI_M_MMUREGS		! set it
 
@@ -317,9 +313,7 @@
 	mov	AC_M_SFSR, %glob_tmp
 	lda	[%glob_tmp] ASI_M_MMUREGS, %glob_tmp	! save away status of winstore
 	andcc	%glob_tmp, 0x2, %g0			! did we fault?
-	be	trap_setup_finish_up			! cool beans, success
-	 nop
-
-	b	trap_setup_user_stack_is_bolixed	! we faulted, ugh
-	 nop
+	be,a	trap_setup_finish_up + 0x4		! cool beans, success
+	 restore %g0, %g0, %g0
 
+	b,a	trap_setup_user_stack_is_bolixed	! we faulted, ugh
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.1.8/linux/arch/sparc/kernel/head.S	Mon May  6 12:26:03 1996
+++ linux/arch/sparc/kernel/head.S	Sat Nov  9 10:11:39 1996
@@ -1,8 +1,9 @@
-/* $Id: head.S,v 1.57 1996/04/25 06:08:38 davem Exp $
+/* $Id: head.S,v 1.70 1996/10/31 06:28:29 davem Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Peter Zaitcev   (Zaitcev@ipmce.su)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  */
 
 #include <linux/version.h>
@@ -60,10 +61,6 @@
 	.asciz  "Sparc-Linux sun4 support not implemented yet\n\n"
 	.align 4
 
-sun4d_notsup:
-        .asciz  "Sparc-Linux sun4d support does not exist\n\n"
-	.align 4
-
 sun4e_notsup:
         .asciz  "Sparc-Linux sun4e support does not exist\n\n"
 	.align 4
@@ -147,7 +144,7 @@
 t_bad79:BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
 t_bad7e:BAD_TRAP(0x7e) BAD_TRAP(0x7f)
 t_sunos:SUNOS_SYSCALL_TRAP                  /* SunOS System Call             */
-t_sbkpt:BAD_TRAP(0x81)                      /* Software Breakpoint/KGDB      */
+t_sbkpt:BREAKPOINT_TRAP                     /* Software Breakpoint/KGDB      */
 t_divz:	BAD_TRAP(0x82)                      /* Divide by zero trap           */
 t_flwin:TRAP_ENTRY(0x83, do_flush_windows)  /* Flush Windows Trap            */
 t_clwin:BAD_TRAP(0x84)                      /* Clean Windows Trap            */
@@ -165,7 +162,8 @@
 t_getcc:GETCC_TRAP                          /* Get Condition Codes           */
 t_setcc:SETCC_TRAP                          /* Set Condition Codes           */
 t_bada2:BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
-t_bada7:BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+t_bada7:INDIRECT_SOLARIS_SYSCALL(156)
+t_bada8:BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
 t_badac:BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
 t_badb1:BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
 t_badb6:BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
@@ -229,7 +227,9 @@
 	BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
 	BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
 	BAD_TRAP(0x7e) BAD_TRAP(0x7f)
-	SUNOS_SYSCALL_TRAP BAD_TRAP(0x81) BAD_TRAP(0x82)
+	SUNOS_SYSCALL_TRAP 
+	BREAKPOINT_TRAP
+	BAD_TRAP(0x82)
 	TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
 	BAD_TRAP(0x86) BAD_TRAP(0x87) SOLARIS_SYSCALL_TRAP
 	NETBSD_SYSCALL_TRAP BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
@@ -239,7 +239,7 @@
 	BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
 	BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP
 	BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
-	BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+	INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
 	BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
 	BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
 	BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
@@ -295,7 +295,9 @@
 	BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
 	BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
 	BAD_TRAP(0x7e) BAD_TRAP(0x7f)
-	SUNOS_SYSCALL_TRAP BAD_TRAP(0x81) BAD_TRAP(0x82)
+	SUNOS_SYSCALL_TRAP 
+	BREAKPOINT_TRAP
+	BAD_TRAP(0x82)
 	TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
 	BAD_TRAP(0x86) BAD_TRAP(0x87) SOLARIS_SYSCALL_TRAP
 	NETBSD_SYSCALL_TRAP BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
@@ -305,7 +307,7 @@
 	BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
 	BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP
 	BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
-	BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+	INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
 	BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
 	BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
 	BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
@@ -361,7 +363,9 @@
 	BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
 	BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
 	BAD_TRAP(0x7e) BAD_TRAP(0x7f)
-	SUNOS_SYSCALL_TRAP BAD_TRAP(0x81) BAD_TRAP(0x82)
+	SUNOS_SYSCALL_TRAP  
+	BREAKPOINT_TRAP
+	BAD_TRAP(0x82)
 	TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
 	BAD_TRAP(0x86) BAD_TRAP(0x87) SOLARIS_SYSCALL_TRAP
 	NETBSD_SYSCALL_TRAP BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
@@ -371,7 +375,7 @@
 	BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
 	BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP
 	BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
-	BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+	INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
 	BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
 	BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
 	BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
@@ -417,6 +421,30 @@
 C_LABEL(empty_bad_page_table):		.skip 0x1000
 C_LABEL(empty_zero_page):		.skip 0x1000
 
+	.global C_LABEL(root_flags)
+	.global C_LABEL(ram_flags)
+	.global C_LABEL(root_dev)
+	.global C_LABEL(ramdisk_image)
+	.global C_LABEL(ramdisk_size)
+
+/* This stuff has to be in sync with SILO and other potential boot loaders
+ * Fields should be kept upward compatible and whenever any change is made,
+ * HdrS version should be incremented.
+ */
+	.ascii	"HdrS"
+	.word	LINUX_VERSION_CODE
+	.half	0x0201		/* HdrS version */
+C_LABEL(root_flags):
+	.half	1
+C_LABEL(root_dev):
+	.half	0
+C_LABEL(ram_flags):
+	.half	0
+C_LABEL(ramdisk_image):
+	.word	0
+C_LABEL(ramdisk_size):
+	.word	0
+	.word	C_LABEL(reboot_command)
 
 /* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
  * %g7 and at prom_vector_p. And also quickly check whether we are on
@@ -460,10 +488,9 @@
 		/* %l6 will hold the offset we have to subtract
 		 * from absolute symbols in order to access areas
 		 * in our own image.  If already mapped this is
-		 * just plain zero, else it is PAGE_OFFSET which is
-		 * also KERNBASE.
+		 * just plain zero, else it is KERNBASE.
 		 */
-		set	PAGE_OFFSET, %l6
+		set	KERNBASE, %l6
 		b	copy_prom_lvl14
 		 nop
 
@@ -519,6 +546,9 @@
 		be	sun4_mutant_remap	! Ugh, it is...
 		 nop
 
+		b	sun4_normal_remap	! regular sun4, 2 level mmu
+		 nop
+
 remap_not_a_sun4:
 		lda	[%g0] ASI_M_MMUREGS, %g1 ! same as ASI_PTE on sun4c
 		and	%g1, 0x1, %g1		! Test SRMMU Enable bit ;-)
@@ -669,7 +699,24 @@
 		b	go_to_highmem		! Jump to high memory.
 		 nop
 
-/* The following works for normal (ie. non Sun4/400) Sun4 MMU's */
+		/* The following is for non-4/4xx sun4 MMU's. */
+sun4_normal_remap:
+		mov	0, %g3			! source base
+		set	KERNBASE, %g4		! destination base
+		set	0x300000, %g5		! upper bound 3MB
+		mov	1, %l6
+		sll	%l6, 18, %l6		! sun4 mmu segmap size
+sun4_normal_loop:
+		lduha	[%g3] ASI_SEGMAP, %g6	! load phys_seg
+		stha	%g6, [%g4] ASI_SEGMAP	! stort new virt mapping
+		add	%g3, %l6, %g3		! increment source pointer
+		subcc	%g3, %g5, %g0		! reached limit?
+		blu	sun4_normal_loop	! nope, loop again
+		 add	%g4, %l6, %g4		! delay, increment dest ptr
+		b	go_to_highmem
+		 nop
+
+		/* The following works for Sun4c MMU's */
 sun4c_remap:
 		mov	0, %g3			! source base
 		set	KERNBASE, %g4		! destination base
@@ -780,10 +827,6 @@
 		be	1f
 		 nop
 
-		cmp	%l1, 'd'
-		be	no_sun4d_here		! God bless the person who
-		 nop				! tried to run this on sun4d.
-	
 		cmp	%l1, 'e'
 		be	no_sun4e_here		! Could be a sun4e.
 		 nop
@@ -797,6 +840,8 @@
 		ldub	[%l1 + 0x4], %l1
 		cmp	%l1, 'm'		! Test for sun4d, sun4e ?
 		be	sun4m_init
+		 cmp	%l1, 'd'		! Let us see how the beast will die
+		be	sun4m_init
 		 nop
 
 		/* Jump into mmu context zero. */
@@ -807,6 +852,50 @@
 		 nop
 
 sun4m_init:
+	/* All sun4m processors can do hw mul/div/rem, patch 'em. */
+#define PATCH_IT(dst, src)	\
+	set	(dst), %g5;	\
+	set	(src), %g4;	\
+	ld	[%g4], %g3;	\
+	st	%g3, [%g5];	\
+	ld	[%g4+0x4], %g3;	\
+	st	%g3, [%g5+0x4];
+
+	/* Signed multiply. */
+	PATCH_IT(.mul, .mul_patch)
+	PATCH_IT(.mul+0x08, .mul_patch+0x08)
+
+	/* Signed remainder. */
+	PATCH_IT(.rem, .rem_patch)
+	PATCH_IT(.rem+0x08, .rem_patch+0x08)
+	PATCH_IT(.rem+0x10, .rem_patch+0x10)
+	PATCH_IT(.rem+0x18, .rem_patch+0x18)
+	PATCH_IT(.rem+0x20, .rem_patch+0x20)
+	PATCH_IT(.rem+0x28, .rem_patch+0x28)
+
+	/* Signed division. */
+	PATCH_IT(.div, .div_patch)
+	PATCH_IT(.div+0x08, .div_patch+0x08)
+	PATCH_IT(.div+0x10, .div_patch+0x10)
+	PATCH_IT(.div+0x18, .div_patch+0x18)
+	PATCH_IT(.div+0x20, .div_patch+0x20)
+
+	/* Unsigned multiply. */
+	PATCH_IT(.umul, .umul_patch)
+	PATCH_IT(.umul+0x08, .umul_patch+0x08)
+
+	/* Unsigned remainder. */
+	PATCH_IT(.urem, .urem_patch)
+	PATCH_IT(.urem+0x08, .urem_patch+0x08)
+	PATCH_IT(.urem+0x10, .urem_patch+0x10)
+	PATCH_IT(.urem+0x18, .urem_patch+0x18)
+
+	/* Unsigned division. */
+	PATCH_IT(.udiv, .udiv_patch)
+	PATCH_IT(.udiv+0x08, .udiv_patch+0x08)
+	PATCH_IT(.udiv+0x10, .udiv_patch+0x10)
+
+#undef PATCH_IT
 
 /* Ok, the PROM could have done funny things and apple cider could still
  * be sitting in the fault status/address registers.  Read them all to
@@ -873,13 +962,13 @@
 		/* Initialize the umask value for init_task just in case.
 		 * But first make current_set[0] point to something useful.
 		 */
-		set	C_LABEL(init_task), %g4
+		set	C_LABEL(init_task), %g6
 		set	C_LABEL(current_set), %g2
-		st	%g4, [%g2]
+		st	%g6, [%g2]
 
 		set	C_LABEL(bootup_kernel_stack), %g3
-		st	%g3, [%g4 + TASK_KSTACK_PG]
-		st	%g0, [%g4 + THREAD_UMASK]
+		st	%g3, [%g6 + TASK_KSTACK_PG]
+		st	%g0, [%g6 + THREAD_UMASK]
 
 /* Compute NWINDOWS and stash it away. Now uses %wim trick explained
  * in the V8 manual. Ok, this method seems to work, Sparc is cool...
@@ -912,9 +1001,9 @@
 
 #define		PATCH_INSN(src, dest) \
 		set	src, %g5; \
-		set	dest, %g6; \
+		set	dest, %g2; \
 		ld	[%g5], %g4; \
-		st	%g4, [%g6];
+		st	%g4, [%g2];
 	
 		/* Patch for window spills... */
 		PATCH_INSN(spnwin_patch1_7win, spnwin_patch1)
@@ -950,6 +1039,25 @@
 		PATCH_INSN(rirq_7win_patch5, rirq_patch5)
 
 #endif
+		/* Now patch the kernel window flush sequences.
+		 * This saves 2 traps on every switch and fork.
+		 */
+		set	0x01000000, %g4
+		set	flush_patch_one, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_two, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_three, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_exception, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_switch, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
 
 2:		
 		sethi	%hi( C_LABEL(nwindows) ), %g4
@@ -998,14 +1106,6 @@
 		 nop
 1:
 		ba	1b			! Cannot exit into KMON
-		 nop
-
-no_sun4d_here:
-		ld	[%g7 + 0x68], %o1
-		set	sun4d_notsup, %o0
-		call	%o1
-		 nop
-		b	halt_me
 		 nop
 
 no_sun4e_here:
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/idprom.c linux/arch/sparc/kernel/idprom.c
--- v2.1.8/linux/arch/sparc/kernel/idprom.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/idprom.c	Sat Nov  9 10:11:39 1996
@@ -1,10 +1,7 @@
-/* $Id: idprom.c,v 1.19 1996/04/25 06:08:41 davem Exp $
+/* $Id: idprom.c,v 1.21 1996/10/12 13:12:48 davem Exp $
  * idprom.c: Routines to load the idprom into kernel addresses and
  *           interpret the data contained within.
  *
- * Because they use the IDPROM's machine type field, some of the
- * virtual address cache probings on the sun4c are done here.
- *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
@@ -14,10 +11,9 @@
 #include <asm/oplib.h>
 #include <asm/idprom.h>
 #include <asm/machines.h>  /* Fun with Sun released architectures. */
-#include <asm/system.h>    /* For halt() macro */
 
-struct idp_struct *idprom;
-static struct idp_struct idprom_buff;
+struct idprom *idprom;
+static struct idprom idprom_buffer;
 
 /* Here is the master table of Sun machines which use some implementation
  * of the Sparc CPU and have a meaningful IDPROM machtype value that we
@@ -44,127 +40,61 @@
 /* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */
 { "Sun4M OBP based system", (SM_SUN4M_OBP | 0x0) } };
 
-void
-sparc_display_systype(unsigned char machtyp)
+static void display_system_type(unsigned char machtype)
 {
-	char system_name[128];
-	int i;
+	char sysname[128];
+	register int i;
 
-	for(i = 0; i<NUM_SUN_MACHINES; i++) {
-		if(Sun_Machines[i].id_machtype == machtyp) {
-			if(machtyp!=(SM_SUN4M_OBP | 0x0)) {
+	for (i = 0; i < NUM_SUN_MACHINES; i++) {
+		if(Sun_Machines[i].id_machtype == machtype) {
+			if (machtype != (SM_SUN4M_OBP | 0x00))
 				printk("TYPE: %s\n", Sun_Machines[i].name);
-				break;
-			} else {
+			else {
 				prom_getproperty(prom_root_node, "banner-name",
-						 system_name, sizeof(system_name));
-				printk("TYPE: %s\n", system_name);
-				break;
+						 sysname, sizeof(sysname));
+				printk("TYPE: %s\n", sysname);
 			}
+			return;
 		}
 	}
-	if(i == NUM_SUN_MACHINES)
-		printk("Uh oh, IDPROM had bogus id_machtype value <%x>\n", machtyp);
-	return;
-}
-
-void
-get_idprom(void)
-{
-	prom_getidp((char *) &idprom_buff, sizeof(idprom_buff));
-
-	idprom = &idprom_buff;
 
-	sparc_display_systype(idprom->id_machtype);
-
-	printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
-		    idprom->id_eaddr[0], idprom->id_eaddr[1], idprom->id_eaddr[2],
-		    idprom->id_eaddr[3], idprom->id_eaddr[4], idprom->id_eaddr[5]);
-
-	return;
+	prom_printf("IDPROM: Bogus id_machtype value, 0x%x\n", machtype);
+	prom_halt();
 }
 
-/* find_vac_size() returns the number of bytes in the VAC (virtual
- * address cache) on this machine.
- */
-
-int
-find_vac_size(void)
+/* Calculate the IDPROM checksum (xor of the data bytes). */
+static unsigned char calc_idprom_cksum(struct idprom *idprom)
 {
-	int vac_prop_len;
-	int vacsize = 0;
+	unsigned char cksum, i, *ptr = (unsigned char *)idprom;
 
-	vac_prop_len = prom_getproplen(prom_root_node, "vac-size");
-	if(vac_prop_len != -1) {
-		vacsize = prom_getint(prom_root_node, "vac-size");
-		return vacsize;
-	} else {
-		switch(idprom->id_machtype) {
-		case (SM_SUN4C | SM_4C_SS1):     /* SparcStation1 */
-		case (SM_SUN4C | SM_4C_IPC):     /* SparcStation IPX */
-		case (SM_SUN4C | SM_4C_SS1PLUS): /* SparcStation1+ */
-		case (SM_SUN4C | SM_4C_SLC):     /* SparcStation SLC */
-		case (SM_SUN4C | SM_4C_SS2):     /* SparcStation2 Cache-Chip BUG! */
-		case (SM_SUN4C | SM_4C_ELC):     /* SparcStation ELC */
-		case (SM_SUN4C | SM_4C_IPX):     /* SparcStation IPX */
-			return 65536;
-		default:
-			printk("find_vac_size: Can't determine size of VAC, bailing out...\n");
-			halt();
-			break;
-		};
-	};
-	return -1;
-}
+	for (i = cksum = 0; i <= 0x0E; i++)
+		cksum ^= *ptr++;
 
-/* find_vac_linesize() returns the size in bytes of the VAC linesize */ 
+	return cksum;
+}
 
-int
-find_vac_linesize(void)
+/* Create a local IDPROM copy, verify integrity, and display information. */
+void idprom_init(void)
 {
-	int vac_prop_len;
+	prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer));
 
-	vac_prop_len = prom_getproplen(prom_root_node, "vac-linesize");
+	idprom = &idprom_buffer;
 
-	if(vac_prop_len != -1)
-		return prom_getint(prom_root_node, "vac-linesize");
-	else {
-		switch(idprom->id_machtype) {
-		case (SM_SUN4C | SM_4C_SS1):     /* SparcStation1 */
-		case (SM_SUN4C | SM_4C_IPC):     /* SparcStation IPC */
-		case (SM_SUN4C | SM_4C_SS1PLUS): /* SparcStation1+ */
-		case (SM_SUN4C | SM_4C_SLC):     /* SparcStation SLC */
-			return 16;
-		case (SM_SUN4C | SM_4C_SS2):     /* SparcStation2 Cache-Chip BUG! */
-		case (SM_SUN4C | SM_4C_ELC):     /* SparcStation ELC */
-		case (SM_SUN4C | SM_4C_IPX):     /* SparcStation IPX */
-			return 32;
-		default:
-			printk("find_vac_linesize: Can't determine VAC linesize, bailing out...\n");
-			halt();
-			break;
-		};
-	};
-	return -1;
-}
+	if (idprom->id_format != 0x01)  {
+		prom_printf("IDPROM: Unknown format type!\n");
+		prom_halt();
+	}
 
-int
-find_vac_hwflushes(void)
-{
-	register int len;
-	int tmp1, tmp2;
+	if (idprom->id_cksum != calc_idprom_cksum(idprom)) {
+		prom_printf("IDPROM: Checksum failure (nvram=%x, calc=%x)!\n",
+			    idprom->id_cksum, calc_idprom_cksum(idprom));
+		prom_halt();
+	}
 
-	/* Sun 4/75 has typo in prom_node, it's a dash instead of an underscore
-	 * in the property name. :-(
-	 */
-	len = prom_getproperty(prom_root_node, "vac_hwflush",
-			       (char *) &tmp1, sizeof(int));
-	if(len != 4) tmp1=0;
-
-	len = prom_getproperty(prom_root_node, "vac-hwflush",
-			       (char *) &tmp2, sizeof(int));
-	if(len != 4) tmp2=0;
+	display_system_type(idprom->id_machtype);
 
-	return (tmp1|tmp2);
+	printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
+		    idprom->id_ethaddr[0], idprom->id_ethaddr[1],
+		    idprom->id_ethaddr[2], idprom->id_ethaddr[3],
+		    idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
 }
-
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/ioport.c linux/arch/sparc/kernel/ioport.c
--- v2.1.8/linux/arch/sparc/kernel/ioport.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/ioport.c	Sat Nov  9 10:11:39 1996
@@ -1,4 +1,4 @@
-/* $Id: ioport.c,v 1.18 1996/04/25 06:08:44 davem Exp $
+/* $Id: ioport.c,v 1.22 1996/10/11 00:59:46 davem Exp $
  * ioport.c:  Simple io mapping allocator.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -9,8 +9,6 @@
  * are administered by a general purpose allocator, and then you call
  * that allocator with your handle and the block size instead of this
  * weak stuff.
- *
- * XXX No joke, this needs to be rewritten badly. XXX
  */
 
 #include <linux/sched.h>
@@ -27,8 +25,8 @@
 #include <asm/pgtable.h>
 
 /* This points to the next to use virtual memory for io mappings */
-static long next_free_region = IOBASE_VADDR;
-static long dvma_next_free   = DVMA_VADDR;
+static unsigned long dvma_next_free   = DVMA_VADDR;
+unsigned long sparc_iobase_vaddr = IOBASE_VADDR;
 
 /*
  * sparc_alloc_io:
@@ -54,23 +52,30 @@
 	unsigned long addr = (unsigned long) address;
 	unsigned long offset = (addr & (~PAGE_MASK));
 
-	if (virtual)
+	if (virtual) {
 		vaddr = (unsigned long) virtual;
-	else
-		vaddr = next_free_region;
-		
-	len += offset;
-	if(((unsigned long) virtual + len) > (IOBASE_VADDR + IOBASE_LEN)) {
-		prom_printf("alloc_io: Mapping outside IOBASE area\n");
-		prom_halt();
-	}
-	if(check_region ((vaddr | offset), len)) {
-		prom_printf("alloc_io: 0x%lx is already in use\n", vaddr);
-		prom_halt();
-	}
 
-	/* Tell Linux resource manager about the mapping */
-	request_region ((vaddr | offset), len, name);
+		len += offset;
+		if(((unsigned long) virtual + len) > (IOBASE_VADDR + IOBASE_LEN)) {
+			prom_printf("alloc_io: Mapping outside IOBASE area\n");
+			prom_halt();
+		}
+		if(check_region ((vaddr | offset), len)) {
+			prom_printf("alloc_io: 0x%lx is already in use\n", vaddr);
+			prom_halt();
+		}
+
+		/* Tell Linux resource manager about the mapping */
+		request_region ((vaddr | offset), len, name);
+	} else {
+		vaddr = occupy_region(sparc_iobase_vaddr, IOBASE_END,
+				(offset + len + PAGE_SIZE-1) & PAGE_MASK, PAGE_SIZE, name);
+		if (vaddr == 0) {
+			/* Usually we cannot see printks in this case. */
+			prom_printf("alloc_io: cannot occupy %d region\n", len);
+			prom_halt();
+		}
+	}
 
 	base_address = vaddr;
 	/* Do the actual mapping */
@@ -78,12 +83,24 @@
 		mapioaddr(addr, vaddr, bus_type, rdonly);
 		vaddr += PAGE_SIZE;
 		addr += PAGE_SIZE;
-		if (!virtual)
-			next_free_region += PAGE_SIZE;
 	}
+
 	return (void *) (base_address | offset);
 }
 
+void sparc_free_io (void *virtual, int len)
+{
+	unsigned long vaddr = (unsigned long) virtual & PAGE_MASK;
+	unsigned long plen = (((unsigned long)virtual & ~PAGE_MASK) + len + PAGE_SIZE-1) & PAGE_MASK;
+
+	release_region(vaddr, plen);
+
+	for (; plen != 0;) {
+		plen -= PAGE_SIZE;
+		unmapioaddr(vaddr + plen);
+	}
+}
+
 /* Does DVMA allocations with PAGE_SIZE granularity.  How this basically
  * works is that the ESP chip can do DVMA transfers at ANY address with
  * certain size and boundary restrictions.  But other devices that are
@@ -109,9 +126,10 @@
 
 	/* Basically these can be mapped just like any old
 	 * IO pages, cacheable bit off, etc.  The physical
-	 * pages are pre-mapped in paging_init()
+	 * pages are now mapped dynamically to save space.
 	 */
 	base_address = vaddr;
+	mmu_map_dma_area(base_address, len);
 	/* Assign the memory area. */
 	dvma_next_free = PAGE_ALIGN(dvma_next_free+len);
 
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/irq.c linux/arch/sparc/kernel/irq.c
--- v2.1.8/linux/arch/sparc/kernel/irq.c	Mon May  6 12:26:03 1996
+++ linux/arch/sparc/kernel/irq.c	Sat Nov  9 10:11:40 1996
@@ -1,4 +1,4 @@
-/*  $Id: irq.c,v 1.44 1996/04/25 06:08:46 davem Exp $
+/*  $Id: irq.c,v 1.53 1996/10/16 12:30:18 zaitcev Exp $
  *  arch/sparc/kernel/irq.c:  Interrupt request handling routines. On the
  *                            Sparc the IRQ's are basically 'cast in stone'
  *                            and you are supposed to probe the prom's device
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/malloc.h>
+#include <linux/random.h>
 
 #include <asm/ptrace.h>
 #include <asm/processor.h>
@@ -32,6 +33,7 @@
 #include <asm/traps.h>
 #include <asm/irq.h>
 #include <asm/io.h>
+#include <asm/pgtable.h>
 
 /*
  * Dave Redman (djhr@tadpole.co.uk)
@@ -158,7 +160,7 @@
 	    return;
 	}
 	
-        save_flags(flags); cli();
+        save_and_cli(flags);
 	if (action && tmp)
 		tmp->next = action->next;
 	else
@@ -184,11 +186,13 @@
         printk("IO device interrupt, irq = %d\n", irq);
         printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc, 
 		    regs->npc, regs->u_regs[14]);
-        printk("Expecting: ");
-        for (i = 0; i < 16; i++)
-                if (action->handler)
-                        prom_printf("[%s:%d:0x%x] ", action->name, (int) i,
-				    (unsigned int) action->handler);
+	if (action) {
+		printk("Expecting: ");
+        	for (i = 0; i < 16; i++)
+                	if (action->handler)
+                        	prom_printf("[%s:%d:0x%x] ", action->name,
+				    (int) i, (unsigned int) action->handler);
+	}
         printk("AIEEE\n");
 	panic("bogus interrupt received");
 }
@@ -204,46 +208,12 @@
 #if 0
 	printk("I<%d,%d,%d>", smp_processor_id(), irq, smp_proc_in_lock[smp_processor_id()]);
 #endif
-	while (action) {
-		if (!action->handler)
-			unexpected_irq(irq, action->dev_id, regs);
-		else
-			action->handler(irq, action->dev_id, regs);
-		action = action->next;
-	}
-}
-
-/*
- * do_IRQ handles IRQ's that have been installed without the
- * SA_INTERRUPT flag: it uses the full signal-handling return
- * and runs with other interrupts enabled. All relatively slow
- * IRQ's should use this format: notably the keyboard/timer
- * routines.
- */
-asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
-{
-	struct irqaction * action;
-	unsigned int cpu_irq;
-	
-	cpu_irq = irq & NR_IRQS;
-	action = *(cpu_irq + irq_action);
-	kstat.interrupts[cpu_irq]++;
-	while (action) {
+	do {
+		if (!action || !action->handler)
+			unexpected_irq(irq, 0, regs);
 		action->handler(irq, action->dev_id, regs);
 		action = action->next;
-	}
-}
-
-/*
- * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return
- * stuff - the handler is also running with interrupts disabled unless
- * it explicitly enables them later.
- */
-asmlinkage void do_fast_IRQ(int irq)
-{
-	kstat.interrupts[irq&NR_IRQS]++;
-	printk("Got FAST_IRQ number %04lx\n", (long unsigned int) irq);
-	return;
+	} while (action);
 }
 
 /* Fast IRQ's on the Sparc can only have one routine attached to them,
@@ -256,6 +226,10 @@
 	struct irqaction *action;
 	unsigned long flags;
 	unsigned int cpu_irq;
+#ifdef __SMP__
+	struct tt_entry *trap_table;
+	extern struct tt_entry trapbase_cpu1, trapbase_cpu2, trapbase_cpu3;
+#endif
 	
 	cpu_irq = irq & NR_IRQS;
 	if(cpu_irq > 14)
@@ -273,7 +247,7 @@
 		return -EBUSY;
 	}
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 
 	/* If this is flagged as statically allocated then we use our
 	 * private struct which is never freed.
@@ -295,12 +269,27 @@
 	}
 
 	/* Dork with trap table if we get this far. */
-	sparc_ttable[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one =
-		SPARC_BRANCH((unsigned long) handler,
-			     (unsigned long) &sparc_ttable[SP_TRAP_IRQ1+(irq-1)].inst_one);
-	sparc_ttable[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = SPARC_RD_PSR_L0;
-	sparc_ttable[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_NOP;
-	sparc_ttable[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
+#define INSTANTIATE(table) \
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \
+		SPARC_BRANCH((unsigned long) handler, \
+			     (unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
+
+	INSTANTIATE(sparc_ttable)
+#ifdef __SMP__
+	trap_table = &trapbase_cpu1; INSTANTIATE(trap_table)
+	trap_table = &trapbase_cpu2; INSTANTIATE(trap_table)
+	trap_table = &trapbase_cpu3; INSTANTIATE(trap_table)
+#endif
+#undef INSTANTIATE
+	/*
+	 * XXX Correct thing whould be to flush only I- and D-cache lines
+	 * which contain the handler in question. But as of time of the
+	 * writing we have no CPU-neutral interface to fine-grained flushes.
+	 */
+	flush_cache_all();
 
 	action->handler = handler;
 	action->flags = irqflags;
@@ -342,7 +331,7 @@
 		}   
 	}
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 
 	/* If this is flagged as statically allocated then we use our
 	 * private struct which is never freed.
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
--- v2.1.8/linux/arch/sparc/kernel/process.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/process.c	Sat Nov  9 10:11:41 1996
@@ -1,7 +1,8 @@
-/*  $Id: process.c,v 1.51 1996/04/25 06:08:49 davem Exp $
+/*  $Id: process.c,v 1.77 1996/11/03 08:25:43 davem Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
  */
 
 /*
@@ -19,13 +20,13 @@
 #include <linux/unistd.h>
 #include <linux/ptrace.h>
 #include <linux/malloc.h>
-#include <linux/ldt.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/config.h>
 
 #include <asm/auxio.h>
 #include <asm/oplib.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -33,11 +34,10 @@
 #include <asm/processor.h>
 #include <asm/psr.h>
 #include <asm/system.h>
+#include <asm/elf.h>
 
 extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
 
-int active_ds = USER_DS;
-
 #ifndef __SMP__
 
 /*
@@ -79,20 +79,21 @@
 	volatile int cval;
 
 	while(1) {
-                if(0==read_smp_counter(spap))
-                	continue;
+		if(0==*spap)
+			continue;
 		cli();
 		/* Acquire exclusive access. */
 		while((cval = smp_swap(spap, -1)) == -1)
-			;
+			while(*spap == -1)
+				;
                 if (0==cval) {
 			/* ho hum, release it. */
-			smp_process_available = 0;
+			*spap = 0;
 			sti();
                         continue;
                 }
 		/* Something interesting happened, whee... */
-		smp_swap(spap, (cval - 1));
+		*spap = (cval - 1);
 		sti();
 		idle();
 	}
@@ -100,43 +101,143 @@
 
 #endif
 
-extern char saved_command_line[];
+extern char reboot_command [];
+
+#ifdef CONFIG_SUN_CONSOLE
+extern void console_restore_palette (void);
+extern int serial_console;
+#endif
+
+void halt_now(void)
+{
+	sti();
+	udelay(8000);
+	cli();
+#ifdef CONFIG_SUN_CONSOLE
+	if (!serial_console)
+		console_restore_palette ();
+#endif
+	prom_halt();
+	panic("Halt failed!");
+}
 
 void hard_reset_now(void)
 {
+	char *p;
+	
 	sti();
 	udelay(8000);
 	cli();
-	prom_feval("reset");
+
+	p = strchr (reboot_command, '\n');
+	if (p) *p = 0;
+#ifdef CONFIG_SUN_CONSOLE
+	if (!serial_console)
+		console_restore_palette ();
+#endif
+	if (*reboot_command)
+		prom_reboot (reboot_command);
+	prom_feval ("reset");
 	panic("Reboot failed!");
 }
 
 void show_regwindow(struct reg_window *rw)
 {
-	printk("l0:%08lx l1:%08lx l2:%08lx l3:%08lx l4:%08lx l5:%08lx l6:%08lx l7:%08lx\n",
+	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
+	       "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
 	       rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
 	       rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
-	printk("i0:%08lx i1:%08lx i2:%08lx i3:%08lx i4:%08lx i5:%08lx i6:%08lx i7:%08lx\n",
+	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
+	       "i4: %08lx i5: %08lx i6: %08lx i7: %08lx\n",
 	       rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
 	       rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
 }
 
+void show_stackframe(struct sparc_stackf *sf)
+{
+	unsigned long size;
+	unsigned long *stk;
+	int i;
+
+	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
+	       "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
+	       sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3],
+	       sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]);
+	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
+	       "i4: %08lx i5: %08lx fp: %08lx ret_pc: %08lx\n",
+	       sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3],
+	       sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc);
+	printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx\n"
+	       "x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n",
+	       (unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1],
+	       sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5],
+	       sf->xxargs[0]);
+	size = ((unsigned long)sf->fp) - ((unsigned long)sf);
+	size -= STACKFRAME_SZ;
+	stk = (unsigned long *)((unsigned long)sf + STACKFRAME_SZ);
+	i = 0;
+	do {
+		printk("s%d: %08lx\n", i++, *stk++);
+	} while ((size -= sizeof(unsigned long)));
+}
+
 void show_regs(struct pt_regs * regs)
 {
         printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx\n", regs->psr,
 	       regs->pc, regs->npc, regs->y);
-	printk("%%g0: %08lx %%g1: %08lx %%g2: %08lx %%g3: %08lx\n",
+	printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx\n",
 	       regs->u_regs[0], regs->u_regs[1], regs->u_regs[2],
 	       regs->u_regs[3]);
-	printk("%%g4: %08lx %%g5: %08lx %%g6: %08lx %%g7: %08lx\n",
+	printk("g4: %08lx g5: %08lx g6: %08lx g7: %08lx\n",
 	       regs->u_regs[4], regs->u_regs[5], regs->u_regs[6],
 	       regs->u_regs[7]);
-	printk("%%o0: %08lx %%o1: %08lx %%o2: %08lx %%o3: %08lx\n",
+	printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx\n",
 	       regs->u_regs[8], regs->u_regs[9], regs->u_regs[10],
 	       regs->u_regs[11]);
-	printk("%%o4: %08lx %%o5: %08lx %%sp: %08lx %%ret_pc: %08lx\n",
+	printk("o4: %08lx o5: %08lx sp: %08lx ret_pc: %08lx\n",
 	       regs->u_regs[12], regs->u_regs[13], regs->u_regs[14],
 	       regs->u_regs[15]);
+	show_regwindow((struct reg_window *)regs->u_regs[14]);
+}
+
+void show_thread(struct thread_struct *tss)
+{
+	int i;
+
+	printk("uwinmask:          0x%08lx\n", tss->uwinmask);
+	printk("kregs:             0x%08lx\n", (unsigned long)tss->kregs);
+	show_regs(tss->kregs);
+	printk("sig_address:       0x%08lx\n", tss->sig_address);
+	printk("sig_desc:          0x%08lx\n", tss->sig_desc);
+	printk("ksp:               0x%08lx\n", tss->ksp);
+	printk("kpc:               0x%08lx\n", tss->kpc);
+	printk("kpsr:              0x%08lx\n", tss->kpsr);
+	printk("kwim:              0x%08lx\n", tss->kwim);
+	printk("fork_kpsr:         0x%08lx\n", tss->fork_kpsr);
+	printk("fork_kwim:         0x%08lx\n", tss->fork_kwim);
+
+	for (i = 0; i < NSWINS; i++) {
+		if (!tss->rwbuf_stkptrs[i])
+			continue;
+		printk("reg_window[%d]:\n", i);
+		printk("stack ptr:         0x%08lx\n", tss->rwbuf_stkptrs[i]);
+		show_regwindow(&tss->reg_window[i]);
+	}
+	printk("w_saved:           0x%08lx\n", tss->w_saved);
+
+	/* XXX missing: float_regs */
+	printk("fsr:               0x%08lx\n", tss->fsr);
+	printk("fpqdepth:          0x%08lx\n", tss->fpqdepth);
+	/* XXX missing: fpqueue */
+
+	printk("sstk_info.stack:   0x%08lx\n",
+	        (unsigned long)tss->sstk_info.the_stack);
+	printk("sstk_info.status:  0x%08lx\n",
+	        (unsigned long)tss->sstk_info.cur_status);
+	printk("flags:             0x%08lx\n", tss->flags);
+	printk("current_ds:        0x%08x\n", tss->current_ds);
+
+	/* XXX missing: core_exec */
 }
 
 /*
@@ -163,21 +264,12 @@
 	mmu_exit_hook();
 }
 
-/*
- * Free old dead task when we know it can never be on the cpu again.
- */
-void release_thread(struct task_struct *dead_task)
-{
-}
-
 void flush_thread(void)
 {
 	/* Make sure old user windows don't get in the way. */
 	flush_user_windows();
 	current->tss.w_saved = 0;
 	current->tss.uwinmask = 0;
-	current->tss.sig_address = 0;
-	current->tss.sig_desc = 0;
 	current->tss.sstk_info.cur_status = 0;
 	current->tss.sstk_info.the_stack = 0;
 
@@ -197,17 +289,77 @@
 #endif
 	}
 
-	memset(&current->tss.reg_window[0], 0,
-	       (sizeof(struct reg_window) * NSWINS));
-	memset(&current->tss.rwbuf_stkptrs[0], 0,
-	       (sizeof(unsigned long) * NSWINS));
 	mmu_flush_hook();
 	/* Now, this task is no longer a kernel thread. */
 	current->tss.flags &= ~SPARC_FLAG_KTHREAD;
 }
 
-/*
- * Copy a Sparc thread.  The fork() return value conventions
+static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src)
+{
+	__asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t"
+			     "ldd\t[%1 + 0x08], %%g4\n\t"
+			     "ldd\t[%1 + 0x10], %%o4\n\t"
+			     "std\t%%g2, [%0 + 0x00]\n\t"
+			     "std\t%%g4, [%0 + 0x08]\n\t"
+			     "std\t%%o4, [%0 + 0x10]\n\t"
+			     "ldd\t[%1 + 0x18], %%g2\n\t"
+			     "ldd\t[%1 + 0x20], %%g4\n\t"
+			     "ldd\t[%1 + 0x28], %%o4\n\t"
+			     "std\t%%g2, [%0 + 0x18]\n\t"
+			     "std\t%%g4, [%0 + 0x20]\n\t"
+			     "std\t%%o4, [%0 + 0x28]\n\t"
+			     "ldd\t[%1 + 0x30], %%g2\n\t"
+			     "ldd\t[%1 + 0x38], %%g4\n\t"
+			     "ldd\t[%1 + 0x40], %%o4\n\t"
+			     "std\t%%g2, [%0 + 0x30]\n\t"
+			     "std\t%%g4, [%0 + 0x38]\n\t"
+			     "ldd\t[%1 + 0x48], %%g2\n\t"
+			     "std\t%%o4, [%0 + 0x40]\n\t"
+			     "std\t%%g2, [%0 + 0x48]\n\t" : :
+			     "r" (dst), "r" (src) :
+			     "g2", "g3", "g4", "g5", "o4", "o5");
+}
+
+static __inline__ void copy_regwin(struct reg_window *dst, struct reg_window *src)
+{
+	__asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t"
+			     "ldd\t[%1 + 0x08], %%g4\n\t"
+			     "ldd\t[%1 + 0x10], %%o4\n\t"
+			     "std\t%%g2, [%0 + 0x00]\n\t"
+			     "std\t%%g4, [%0 + 0x08]\n\t"
+			     "std\t%%o4, [%0 + 0x10]\n\t"
+			     "ldd\t[%1 + 0x18], %%g2\n\t"
+			     "ldd\t[%1 + 0x20], %%g4\n\t"
+			     "ldd\t[%1 + 0x28], %%o4\n\t"
+			     "std\t%%g2, [%0 + 0x18]\n\t"
+			     "std\t%%g4, [%0 + 0x20]\n\t"
+			     "std\t%%o4, [%0 + 0x28]\n\t"
+			     "ldd\t[%1 + 0x30], %%g2\n\t"
+			     "ldd\t[%1 + 0x38], %%g4\n\t"
+			     "std\t%%g2, [%0 + 0x30]\n\t"
+			     "std\t%%g4, [%0 + 0x38]\n\t" : :
+			     "r" (dst), "r" (src) :
+			     "g2", "g3", "g4", "g5", "o4", "o5");
+}
+
+static __inline__ struct sparc_stackf *
+clone_stackframe(struct sparc_stackf *dst, struct sparc_stackf *src)
+{
+	unsigned long size;
+	struct sparc_stackf *sp;
+
+	size = ((unsigned long)src->fp) - ((unsigned long)src);
+	sp = (struct sparc_stackf *)(((unsigned long)dst) - size); 
+
+	if (copy_to_user(sp, src, size))
+		return 0;
+	if (put_user(dst, &sp->fp))
+		return 0;
+	return sp;
+}
+
+
+/* Copy a Sparc thread.  The fork() return value conventions
  * under SunOS are nothing short of bletcherous:
  * Parent -->  %o0 == childs  pid, %o1 == 0
  * Child  -->  %o0 == parents pid, %o1 == 1
@@ -221,11 +373,11 @@
  */
 extern void ret_sys_call(void);
 
-void copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
-		 struct task_struct *p, struct pt_regs *regs)
+int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
+		struct task_struct *p, struct pt_regs *regs)
 {
 	struct pt_regs *childregs;
-	struct reg_window *old_stack, *new_stack;
+	struct reg_window *new_stack;
 	unsigned long stack_offset;
 
 #ifndef __SMP__
@@ -242,31 +394,60 @@
 	}
 
 	/* Calculate offset to stack_frame & pt_regs */
-	if(sparc_cpu_model == sun4c)
-		stack_offset = ((PAGE_SIZE*3) - TRACEREG_SZ);
-	else
-		stack_offset = ((PAGE_SIZE<<2) - TRACEREG_SZ);
+	stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ);
 
 	if(regs->psr & PSR_PS)
 		stack_offset -= REGWIN_SZ;
 	childregs = ((struct pt_regs *) (p->kernel_stack_page + stack_offset));
-	*childregs = *regs;
+	copy_regs(childregs, regs);
 	new_stack = (((struct reg_window *) childregs) - 1);
-	old_stack = (((struct reg_window *) regs) - 1);
-	*new_stack = *old_stack;
+	copy_regwin(new_stack, (((struct reg_window *) regs) - 1));
+
 	p->tss.ksp = p->saved_kernel_stack = (unsigned long) new_stack;
 	p->tss.kpc = (((unsigned long) ret_sys_call) - 0x8);
 	p->tss.kpsr = current->tss.fork_kpsr;
 	p->tss.kwim = current->tss.fork_kwim;
 	p->tss.kregs = childregs;
-	childregs->u_regs[UREG_FP] = sp;
 
 	if(regs->psr & PSR_PS) {
-		stack_offset += TRACEREG_SZ;
-		childregs->u_regs[UREG_FP] = p->kernel_stack_page + stack_offset;
+		childregs->u_regs[UREG_FP] = p->tss.ksp;
 		p->tss.flags |= SPARC_FLAG_KTHREAD;
-	} else
+		p->tss.current_ds = KERNEL_DS;
+		childregs->u_regs[UREG_G6] = (unsigned long) p;
+	} else {
+		childregs->u_regs[UREG_FP] = sp;
 		p->tss.flags &= ~SPARC_FLAG_KTHREAD;
+		p->tss.current_ds = USER_DS;
+
+		if (sp != current->tss.kregs->u_regs[UREG_FP]) {
+			struct sparc_stackf *childstack;
+			struct sparc_stackf *parentstack;
+
+			/*
+			 * This is a clone() call with supplied user stack.
+			 * Set some valid stack frames to give to the child.
+			 */
+			childstack = (struct sparc_stackf *)sp;
+			parentstack = (struct sparc_stackf *)
+					current->tss.kregs->u_regs[UREG_FP];
+
+#if 0
+			printk("clone: parent stack:\n");
+			show_stackframe(parentstack);
+#endif
+
+			childstack = clone_stackframe(childstack, parentstack);
+			if (!childstack)
+				return -EFAULT;
+
+#if 0
+			printk("clone: child stack:\n");
+			show_stackframe(childstack);
+#endif
+
+			childregs->u_regs[UREG_FP] = (unsigned long)childstack;
+		}
+	}
 
 	/* Set the return value for the child. */
 	childregs->u_regs[UREG_I0] = current->pid;
@@ -274,6 +455,8 @@
 
 	/* Set the return value for the parent. */
 	regs->u_regs[UREG_I1] = 0;
+
+	return 0;
 }
 
 /*
@@ -311,7 +494,7 @@
 /*
  * fill in the fpu structure for a core dump.
  */
-int dump_fpu (void *fpu_structure)
+int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs)
 {
 	/* Currently we report that we couldn't dump the fpu structure */
 	return 0;
@@ -323,15 +506,18 @@
  */
 asmlinkage int sparc_execve(struct pt_regs *regs)
 {
-	int error;
+	int error, base = 0;
 	char *filename;
 
-	flush_user_windows();
-	error = getname((char *) regs->u_regs[UREG_I0], &filename);
+	/* Check for indirect call. */
+	if(regs->u_regs[UREG_G1] == 0)
+		base = 1;
+
+	error = getname((char *) regs->u_regs[base + UREG_I0], &filename);
 	if(error)
 		return error;
-	error = do_execve(filename, (char **) regs->u_regs[UREG_I1],
-			  (char **) regs->u_regs[UREG_I2], regs);
+	error = do_execve(filename, (char **) regs->u_regs[base + UREG_I1],
+			  (char **) regs->u_regs[base + UREG_I2], regs);
 	putname(filename);
 	return error;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.1.8/linux/arch/sparc/kernel/ptrace.c	Sat May  4 19:39:23 1996
+++ linux/arch/sparc/kernel/ptrace.c	Sat Nov  9 10:11:42 1996
@@ -4,6 +4,9 @@
  *
  * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
  * and David Mosberger.
+ *
+ * Added Linux support -miguel (wierd, eh?, the orignal code was meant
+ * to emulate SunOS).
  */
 
 #include <linux/kernel.h>
@@ -15,6 +18,9 @@
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define MAGIC_CONSTANT 0x80000000
 
 /* change a pid into a task struct. */
 static inline struct task_struct * get_task(int pid)
@@ -70,7 +76,7 @@
 	}
 	page = pte_page(*pgtable);
 /* this is a hack for non-kernel-mapped video buffers and similar */
-	if (page >= high_memory)
+	if (MAP_NR(page) >= max_mapnr)
 		return 0;
 	page += addr & ~PAGE_MASK;
 	retval = *(unsigned long *) page;
@@ -127,15 +133,15 @@
 		goto repeat;
 	}
 /* this is a hack for non-kernel-mapped video buffers and similar */
-	flush_cache_page(vma, page);
-	if (page < high_memory) {
+	flush_cache_page(vma, addr);
+	if (MAP_NR(page) < max_mapnr) {
 		*(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
 		flush_page_to_ram(page);
 	}
 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
 /* this should also re-instate whatever read-only mode there was before */
 	set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
-	flush_tlb_page(vma, page);
+	flush_tlb_page(vma, addr);
 }
 
 static struct vm_area_struct * find_extend_vma(struct task_struct * tsk,
@@ -144,7 +150,7 @@
 	struct vm_area_struct * vma;
 
 	addr &= PAGE_MASK;
-	vma = find_vma(tsk,addr);
+	vma = find_vma(tsk->mm,addr);
 	if (!vma)
 		return NULL;
 	if (vma->vm_start <= addr)
@@ -250,7 +256,8 @@
  * is a valid errno will mean setting the condition codes to indicate
  * an error return.  This doesn't work, so we have this hook.
  */
-static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
+static inline void
+pt_error_return(struct pt_regs *regs, unsigned long error)
 {
 	regs->u_regs[UREG_I0] = error;
 	regs->psr |= PSR_C;
@@ -258,7 +265,8 @@
 	regs->npc += 4;
 }
 
-static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
+static inline void
+pt_succ_return(struct pt_regs *regs, unsigned long value)
 {
 	regs->u_regs[UREG_I0] = value;
 	regs->psr &= ~PSR_C;
@@ -266,13 +274,34 @@
 	regs->npc += 4;
 }
 
+static void
+pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr)
+{
+	if(put_user(value, addr))
+		return pt_error_return(regs, EFAULT);
+	regs->u_regs[UREG_I0] = 0;
+	regs->psr &= ~PSR_C;
+	regs->pc = regs->npc;
+	regs->npc += 4;
+}
+
+static void
+pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr)
+{
+	if (current->personality & PER_BSD)
+		pt_succ_return (regs, val);
+	else
+		pt_succ_return_linux (regs, val, addr);
+}
+
 /* Fuck me gently with a chainsaw... */
 static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
-				   struct task_struct *tsk)
+				   struct task_struct *tsk, long *addr)
 {
 	struct pt_regs *cregs = tsk->tss.kregs;
 	struct thread_struct *t = &tsk->tss;
-
+	int v;
+	
 	if(offset >= 1024)
 		offset -= 1024; /* whee... */
 	if(offset & ((sizeof(unsigned long) - 1))) {
@@ -281,82 +310,83 @@
 	}
 	if(offset >= 16 && offset < 784) {
 		offset -= 16; offset >>= 2;
-		pt_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset));	
+		pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
 		return;
 	}
 	if(offset >= 784 && offset < 832) {
 		offset -= 784; offset >>= 2;
-		pt_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset));
+		pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
 		return;
 	}
 	switch(offset) {
 	case 0:
-		regs->u_regs[UREG_I0] = t->ksp;
+		v = t->ksp;
 		break;
 	case 4:
-		regs->u_regs[UREG_I0] = t->kpc;
+		v = t->kpc;
 		break;
 	case 8:
-		regs->u_regs[UREG_I0] = t->kpsr;
+		v = t->kpsr;
 		break;
 	case 12:
-		regs->u_regs[UREG_I0] = t->uwinmask;
+		v = t->uwinmask;
 		break;
 	case 832:
-		regs->u_regs[UREG_I0] = t->w_saved;
+		v = t->w_saved;
 		break;
 	case 896:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I0];
+		v = cregs->u_regs[UREG_I0];
 		break;
 	case 900:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I1];
+		v = cregs->u_regs[UREG_I1];
 		break;
 	case 904:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I2];
+		v = cregs->u_regs[UREG_I2];
 		break;
 	case 908:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I3];
+		v = cregs->u_regs[UREG_I3];
 		break;
 	case 912:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I4];
+		v = cregs->u_regs[UREG_I4];
 		break;
 	case 916:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I5];
+		v = cregs->u_regs[UREG_I5];
 		break;
 	case 920:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I6];
+		v = cregs->u_regs[UREG_I6];
 		break;
 	case 924:
-		if(tsk->tss.flags & 0x80000000)
-			regs->u_regs[UREG_I0] = cregs->u_regs[UREG_G1];
+		if(tsk->tss.flags & MAGIC_CONSTANT)
+			v = cregs->u_regs[UREG_G1];
 		else
-			regs->u_regs[UREG_I0] = 0;
+			v = 0;
 		break;
 	case 940:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I0];
+		v = cregs->u_regs[UREG_I0];
 		break;
 	case 944:
-		regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I1];
+		v = cregs->u_regs[UREG_I1];
 		break;
 
 	case 948:
 		/* Isn't binary compatibility _fun_??? */
 		if(cregs->psr & PSR_C)
-			regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I0] << 24;
+			v = cregs->u_regs[UREG_I0] << 24;
 		else
-			regs->u_regs[UREG_I0] = 0;
+			v = 0;
 		break;
 
 		/* Rest of them are completely unsupported. */
 	default:
-		printk("%s [%d]: Wants to read user offset %d\n",
+		printk("%s [%d]: Wants to read user offset %ld\n",
 		       current->comm, current->pid, offset);
 		pt_error_return(regs, EIO);
 		return;
 	}
-	regs->psr &= ~PSR_C;
-	regs->pc = regs->npc;
-	regs->npc += 4;
+	if (current->personality & PER_BSD)
+		pt_succ_return (regs, v);
+	else
+		pt_succ_return_linux (regs, v, addr);
 	return;
 }
 
@@ -415,7 +445,7 @@
 
 		/* Rest of them are completely unsupported or "no-touch". */
 	default:
-		printk("%s [%d]: Wants to write user offset %d\n",
+		printk("%s [%d]: Wants to write user offset %ld\n",
 		       current->comm, current->pid, offset);
 		goto failure;
 	}
@@ -430,6 +460,34 @@
 /* #define ALLOW_INIT_TRACING */
 /* #define DEBUG_PTRACE */
 
+#ifdef DEBUG_PTRACE
+char *pt_rq [] = {
+"TRACEME",
+"PEEKTEXT",
+"PEEKDATA",
+"PEEKUSR",
+"POKETEXT",
+"POKEDATA",
+"POKEUSR",
+"CONT",
+"KILL",
+"SINGLESTEP",
+"SUNATTACH",
+"SUNDETACH",
+"GETREGS",
+"SETREGS",
+"GETFPREGS",
+"SETFPREGS",
+"READDATA",
+"WRITEDATA",
+"READTEXT",
+"WRITETEXT",
+"GETFPAREGS",
+"SETFPAREGS",
+""
+};
+#endif
+
 asmlinkage void do_ptrace(struct pt_regs *regs)
 {
 	unsigned long request = regs->u_regs[UREG_I0];
@@ -440,8 +498,21 @@
 	struct task_struct *child;
 
 #ifdef DEBUG_PTRACE
-	printk("do_ptrace: rq=%d pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
-	       (int) request, (int) pid, addr, data, addr2);
+	{
+		char *s;
+
+		if ((request > 0) && (request < 21))
+			s = pt_rq [request];
+		else
+			s = "unknown";
+
+		if (request == PTRACE_POKEDATA && data == 0x91d02001){
+			printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n",
+				pid, addr, addr2);
+		} else 
+			printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
+			       s, (int) request, (int) pid, addr, data, addr2);
+	}
 #endif
 	if(request == PTRACE_TRACEME) {
 		/* are we already being traced? */
@@ -527,12 +598,12 @@
 			pt_error_return(regs, -res);
 			return;
 		}
-		pt_succ_return(regs, tmp);
+		pt_os_succ_return(regs, tmp, (long *) data);
 		return;
 	}
 
 	case PTRACE_PEEKUSR:
-		read_sunos_user(regs, addr, child);
+		read_sunos_user(regs, addr, child, (long *) data);
 		return;
 
 	case PTRACE_POKEUSR:
@@ -550,10 +621,6 @@
 			return;
 		}
 		vma = find_extend_vma(child, addr);
-		if(vma && request == PTRACE_POKEDATA && (vma->vm_flags & VM_EXEC)) {
-			pt_error_return(regs, EIO);
-			return;
-		}
 		res = write_long(child, addr, data);
 		if(res < 0)
 			pt_error_return(regs, -res);
@@ -567,45 +634,48 @@
 		struct pt_regs *cregs = child->tss.kregs;
 		int rval;
 
-		rval = verify_area(VERIFY_WRITE, pregs, sizeof(struct pt_regs) - 4);
-		if(rval) {
-			pt_error_return(regs, rval);
-			return;
-		}
-		pregs->psr = cregs->psr;
-		pregs->pc = cregs->pc;
-		pregs->npc = cregs->npc;
-		pregs->y = cregs->y;
+		rval = verify_area(VERIFY_WRITE, pregs, sizeof(struct pt_regs));
+		if(rval)
+			return pt_error_return(regs, -rval);
+		__put_user(cregs->psr, (&pregs->psr));
+		__put_user(cregs->pc, (&pregs->pc));
+		__put_user(cregs->npc, (&pregs->npc));
+		__put_user(cregs->y, (&pregs->y));
 		for(rval = 1; rval < 16; rval++)
-			pregs->u_regs[rval - 1] = cregs->u_regs[rval];
+			__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
 		pt_succ_return(regs, 0);
+#ifdef DEBUG_PTRACE
+		printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]);
+#endif
 		return;
 	}
 
 	case PTRACE_SETREGS: {
 		struct pt_regs *pregs = (struct pt_regs *) addr;
 		struct pt_regs *cregs = child->tss.kregs;
-		unsigned long psr;
-		int rval, i;
+		unsigned long psr, pc, npc, y;
+		int i;
 
-		rval = verify_area(VERIFY_READ, pregs, sizeof(struct pt_regs) - 4);
-		if(rval) {
-			pt_error_return(regs, rval);
-			return;
-		}
 		/* Must be careful, tracing process can only set certain
 		 * bits in the psr.
 		 */
-		psr = (pregs->psr) & PSR_ICC;
+		i = verify_area(VERIFY_READ, pregs, sizeof(struct pt_regs));
+		if(i)
+			return pt_error_return(regs, -i);
+		__get_user(psr, (&pregs->psr));
+		__get_user(pc, (&pregs->pc));
+		__get_user(npc, (&pregs->npc));
+		__get_user(y, (&pregs->y));
+		psr &= PSR_ICC;
 		cregs->psr &= ~PSR_ICC;
 		cregs->psr |= psr;
-		if(!((pregs->pc | pregs->npc) & 3)) {
-			cregs->pc = pregs->pc;
-			cregs->npc = pregs->npc;
+		if(!((pc | npc) & 3)) {
+			cregs->pc = pc;
+			cregs->npc =npc;
 		}
-		cregs->y = pregs->y;
+		cregs->y = y;
 		for(i = 1; i < 16; i++)
-			cregs->u_regs[i] = pregs->u_regs[i-1];
+			__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
 		pt_succ_return(regs, 0);
 		return;
 	}
@@ -622,18 +692,21 @@
 				unsigned long insn;
 			} fpq[16];
 		} *fps = (struct fps *) addr;
-		int rval, i;
+		int i;
 
-		rval = verify_area(VERIFY_WRITE, fps, sizeof(struct fps));
-		if(rval) { pt_error_return(regs, rval); return; }
+		i = verify_area(VERIFY_WRITE, fps, sizeof(struct fps));
+		if(i)
+			return pt_error_return(regs, -i);
 		for(i = 0; i < 32; i++)
-			fps->regs[i] = child->tss.float_regs[i];
-		fps->fsr = child->tss.fsr;
-		fps->fpqd = child->tss.fpqdepth;
-		fps->flags = fps->extra = 0;
+			__put_user(child->tss.float_regs[i], (&fps->regs[i]));
+		__put_user(child->tss.fsr, (&fps->fsr));
+		__put_user(child->tss.fpqdepth, (&fps->fpqd));
+		__put_user(0, (&fps->flags));
+		__put_user(0, (&fps->extra));
 		for(i = 0; i < 16; i++) {
-			fps->fpq[i].insnaddr = child->tss.fpqueue[i].insn_addr;
-			fps->fpq[i].insn = child->tss.fpqueue[i].insn;
+			__put_user(child->tss.fpqueue[i].insn_addr,
+				   (&fps->fpq[i].insnaddr));
+			__put_user(child->tss.fpqueue[i].insn, (&fps->fpq[i].insn));
 		}
 		pt_succ_return(regs, 0);
 		return;
@@ -651,17 +724,18 @@
 				unsigned long insn;
 			} fpq[16];
 		} *fps = (struct fps *) addr;
-		int rval, i;
+		int i;
 
-		rval = verify_area(VERIFY_READ, fps, sizeof(struct fps));
-		if(rval) { pt_error_return(regs, rval); return; }
-		for(i = 0; i < 32; i++)
-			child->tss.float_regs[i] = fps->regs[i];
-		child->tss.fsr = fps->fsr;
-		child->tss.fpqdepth = fps->fpqd;
+		i = verify_area(VERIFY_READ, fps, sizeof(struct fps));
+		if(i)
+			return pt_error_return(regs, -i);
+		copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (32 * 4));
+		__get_user(child->tss.fsr, (&fps->fsr));
+		__get_user(child->tss.fpqdepth, (&fps->fpqd));
 		for(i = 0; i < 16; i++) {
-			child->tss.fpqueue[i].insn_addr = fps->fpq[i].insnaddr;
-			child->tss.fpqueue[i].insn = fps->fpq[i].insn;
+			__get_user(child->tss.fpqueue[i].insn_addr,
+				   (&fps->fpq[i].insnaddr));
+			__get_user(child->tss.fpqueue[i].insn, (&fps->fpq[i].insn));
 		}
 		pt_succ_return(regs, 0);
 		return;
@@ -674,18 +748,14 @@
 		unsigned char tmp;
 		int res, len = data;
 
-		res = verify_area(VERIFY_WRITE, (void *) dest, len);
-		if(res) {
-			pt_error_return(regs, -res);
-			return;
-		}
+		res = verify_area(VERIFY_WRITE, dest, len);
+		if(res)
+			return pt_error_return(regs, -res);
 		while(len) {
 			res = read_byte(child, src, &tmp);
-			if(res < 0) {
-				pt_error_return(regs, -res);
-				return;
-			}
-			*dest = tmp;
+			if(res < 0)
+				return pt_error_return(regs, -res);
+			__put_user(tmp, dest);
 			src++; dest++; len--;
 		}
 		pt_succ_return(regs, 0);
@@ -698,17 +768,16 @@
 		unsigned long dest = addr;
 		int res, len = data;
 
-		res = verify_area(VERIFY_READ, (void *) src, len);
-		if(res) {
-			pt_error_return(regs, -res);
-			return;
-		}
+		res = verify_area(VERIFY_READ, src, len);
+		if(res)
+			return pt_error_return(regs, -res);
 		while(len) {
-			res = write_byte(child, dest, *src);
-			if(res < 0) {
-				pt_error_return(regs, -res);
-				return;
-			}
+			unsigned long tmp;
+
+			__get_user(tmp, src);
+			res = write_byte(child, dest, tmp);
+			if(res < 0)
+				return pt_error_return(regs, -res);
 			src++; dest++; len--;
 		}
 		pt_succ_return(regs, 0);
@@ -716,7 +785,6 @@
 	}
 
 	case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
-		data = 0;
 		addr = 1;
 
 	case PTRACE_CONT: { /* restart after signal. */
@@ -724,15 +792,32 @@
 			pt_error_return(regs, EIO);
 			return;
 		}
+		if (addr != 1) {
+			if (addr & 3) {
+				pt_error_return(regs, EINVAL);
+				return;
+			}
+#ifdef DEBUG_PTRACE
+			printk ("Original: %08lx %08lx\n", child->tss.kregs->pc, child->tss.kregs->npc);
+			printk ("Continuing with %08lx %08lx\n", addr, addr+4);
+#endif
+			child->tss.kregs->pc = addr;
+			child->tss.kregs->npc = addr + 4;
+		}
+
 		if (request == PTRACE_SYSCALL)
 			child->flags |= PF_TRACESYS;
 		else
 			child->flags &= ~PF_TRACESYS;
+
 		child->exit_code = data;
-		if((addr != 1) & !(addr & 3)) {
-			child->tss.kregs->pc = addr;
-			child->tss.kregs->npc = addr + 4;
-		}
+#ifdef DEBUG_PTRACE
+		printk("CONT: %s [%d]: set exit_code = %x %x %x\n", child->comm,
+			child->pid, child->exit_code,
+			child->tss.kregs->pc,
+			child->tss.kregs->npc);
+		       
+#endif
 		wake_up_process(child);
 		pt_succ_return(regs, 0);
 		return;
@@ -787,7 +872,7 @@
 		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
-	current->tss.flags ^= 0x80000000;
+	current->tss.flags ^= MAGIC_CONSTANT;
 	notify_parent(current);
 	schedule();
 	/*
@@ -795,7 +880,12 @@
 	 * for normal use.  strace only continues with a signal if the
 	 * stopping signal is not SIGTRAP.  -brl
 	 */
-	if (current->exit_code)
+#ifdef DEBUG_PTRACE
+	printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
+		current->pid, current->exit_code);
+#endif
+	if (current->exit_code) {
 		current->signal |= (1 << (current->exit_code - 1));
+	}
 	current->exit_code = 0;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/rirq.S linux/arch/sparc/kernel/rirq.S
--- v2.1.8/linux/arch/sparc/kernel/rirq.S	Sun Apr 21 12:30:30 1996
+++ linux/arch/sparc/kernel/rirq.S	Sat Nov  9 10:11:42 1996
@@ -21,6 +21,7 @@
 #define twin_tmp1 l4
 #define twin_tmp2 l5
 #define twin_tmp3 l6
+#define curptr    g6
 
 	/* 7 WINDOW SPARC PATCH INSTRUCTIONS */
 	.globl	rirq_7win_patch1, rirq_7win_patch2, rirq_7win_patch3
@@ -44,8 +45,7 @@
 	wr	%t_psr, 0x0, %psr
 	WRITE_PAUSE
 
-	LOAD_CURRENT(twin_tmp2, twin_tmp1)
-	ld	[%twin_tmp2 + THREAD_W_SAVED], %twin_tmp1
+	ld	[%curptr + THREAD_W_SAVED], %twin_tmp1
 	orcc	%g0, %twin_tmp1, %g0
 	be	ret_irq_nobufwins
 	 nop
@@ -62,7 +62,7 @@
 
 	/* We have klock, so we must return just like a normal trap. */
 	b	ret_trap_entry
-	 nop
+	 clr	%l5
 
 ret_irq_nobufwins:
 	/* Load up the user's out registers so we can pull
@@ -73,7 +73,7 @@
 	/* If there are already live user windows in the
 	 * set we can return from trap safely.
 	 */
-	ld	[%twin_tmp2 + THREAD_UMASK], %twin_tmp1
+	ld	[%curptr + THREAD_UMASK], %twin_tmp1
 	orcc	%g0, %twin_tmp1, %g0
 	bne	ret_irq_userwins_ok
 	 nop
@@ -135,7 +135,7 @@
 
 	/* We have klock, so we must return just like a normal trap. */
 	b	ret_trap_entry
-	 nop
+	 clr	%l5
 
 ret_irq_kernel:
 	wr	%t_psr, 0x0, %psr
@@ -191,15 +191,14 @@
 
 	/* We have klock, so we must return just like a normal trap. */
 	b	ret_trap_entry
-	 nop
+	 clr	%l5
 
 	.globl	C_LABEL(sun4c_reti_stackchk)
 C_LABEL(sun4c_reti_stackchk):
 	be	1f
 	 and	%fp, 0xfff, %g1		! delay slot
 
-	b	ret_irq_user_stack_is_bolixed
-	 nop
+	b,a	ret_irq_user_stack_is_bolixed
 
 	/* See if we have to check the sanity of one page or two */
 1:
@@ -211,8 +210,7 @@
 	 andncc	%g1, 0xff8, %g0
 
 	/* %sp is in vma hole, yuck */
-	b	ret_irq_user_stack_is_bolixed
-	 nop
+	b,a	ret_irq_user_stack_is_bolixed
 
 1:
 	be	sun4c_reti_onepage	/* Only one page to check */
@@ -227,8 +225,7 @@
 	 lda	[%g1] ASI_PTE, %g2
 
 	/* Second page is in vma hole */
-	b	ret_irq_user_stack_is_bolixed
-	 nop
+	b,a	ret_irq_user_stack_is_bolixed
 
 1:
 	srl	%g2, 29, %g2
@@ -237,8 +234,7 @@
 	 lda	[%fp] ASI_PTE, %g2
 
 	/* Second page has bad perms */
-	b	ret_irq_user_stack_is_bolixed
-	 nop
+	b,a	ret_irq_user_stack_is_bolixed
 
 sun4c_reti_onepage:
 	srl	%g2, 29, %g2
@@ -247,8 +243,7 @@
 	 nop
 
 	/* A page had bad page permissions, losing... */
-	b	ret_irq_user_stack_is_bolixed
-	 nop
+	b,a	ret_irq_user_stack_is_bolixed
 
 	/* Whee, things are ok, load the window and continue. */
 1:
@@ -257,13 +252,13 @@
 	LOAD_WINDOW(sp)
 
 	save	%g0, %g0, %g0
-	b	ret_irq_userwins_ok
-	 nop
+	b,a	ret_irq_userwins_ok
 
 	.globl	C_LABEL(srmmu_reti_stackchk)
 C_LABEL(srmmu_reti_stackchk):
+	sethi	%hi(C_LABEL(page_offset)), %g1
 	bne	ret_irq_user_stack_is_bolixed
-	 sethi	%hi(KERNBASE), %g1
+	 ld	[%g1 + %lo(C_LABEL(page_offset))], %g1
 	cmp	%g1, %fp
 	bleu	ret_irq_user_stack_is_bolixed
 	 mov	AC_M_SFSR, %g1
@@ -291,5 +286,4 @@
 	bne	ret_irq_user_stack_is_bolixed
 	 nop
 
-	b	ret_irq_userwins_ok
-	 nop
+	b,a	ret_irq_userwins_ok
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/rtrap.S linux/arch/sparc/kernel/rtrap.S
--- v2.1.8/linux/arch/sparc/kernel/rtrap.S	Sun Apr 21 12:30:31 1996
+++ linux/arch/sparc/kernel/rtrap.S	Sat Nov  9 10:11:42 1996
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.27 1996/04/03 02:14:41 davem Exp $
+/* $Id: rtrap.S,v 1.39 1996/10/28 07:49:01 davem Exp $
  * rtrap.S: Return from Sparc trap low-level code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -19,14 +19,14 @@
 #define t_npc     l2
 #define t_wim     l3
 #define twin_tmp1 l4
-#define twin_tmp2 l5
-#define twin_tmp3 l6
+#define glob_tmp  g4
+#define curptr    g6
 
 	/* 7 WINDOW SPARC PATCH INSTRUCTIONS */
 	.globl	rtrap_7win_patch1, rtrap_7win_patch2, rtrap_7win_patch3
 	.globl	rtrap_7win_patch4, rtrap_7win_patch5
-rtrap_7win_patch1:	srl	%t_wim, 0x6, %twin_tmp2
-rtrap_7win_patch2:	and	%twin_tmp2, 0x7f, %twin_tmp2
+rtrap_7win_patch1:	srl	%t_wim, 0x6, %glob_tmp
+rtrap_7win_patch2:	and	%glob_tmp, 0x7f, %glob_tmp
 rtrap_7win_patch3:	srl	%g1, 7, %g2
 rtrap_7win_patch4:	srl	%g2, 6, %g2
 rtrap_7win_patch5:	and	%g1, 0x7f, %g1
@@ -47,53 +47,60 @@
 	.globl	ret_trap_entry, rtrap_patch1, rtrap_patch2
 	.globl	rtrap_patch3, rtrap_patch4, rtrap_patch5
 ret_trap_entry:
-	ld	[%sp + REGWIN_SZ + PT_PSR], %t_psr
+	sethi	%hi(C_LABEL(intr_count)), %g2
+	ld	[%g2 + %lo(C_LABEL(intr_count))], %g3
+	orcc	%g0, %g3, %g0
+	bne	0f
+	 sethi	%hi(C_LABEL(bh_active)), %l3
+	sethi	%hi(C_LABEL(bh_mask)), %l4
+9:
+	ld	[%l4 + %lo(C_LABEL(bh_mask))], %g5
+	ld	[%l3 + %lo(C_LABEL(bh_active))], %g4
+	sethi	%hi(C_LABEL(intr_count)), %l7
+	andcc	%g4, %g5, %g0
+	be	0f
+	 mov	1, %g7
+	call	C_LABEL(do_bottom_half)
+	 st	%g7, [%l7 + %lo(C_LABEL(intr_count))]
+	b	9b
+	 st	%g0, [%l7 + %lo(C_LABEL(intr_count))]
+0:
 	andcc	%t_psr, PSR_PS, %g0
-	bne	ret_trap_kernel
-	 nop
+	be	1f
+	 sethi	%hi(C_LABEL(need_resched)), %twin_tmp1
 
-	sethi	%hi(C_LABEL(need_resched)), %twin_tmp1
-	ld	[%twin_tmp1 + %lo(C_LABEL(need_resched))], %twin_tmp2
+	b	ret_trap_kernel
+	 wr	%t_psr, 0x0, %psr
 
-	cmp	%twin_tmp2, 0
+1:
+	ld	[%twin_tmp1 + %lo(C_LABEL(need_resched))], %g2
+
+	cmp	%g2, 0
 	be	signal_p
 	 nop
 
 	call	C_LABEL(schedule)
 	 nop
 
-	/* Try to return again... We are a different process,
-	 * most likely so load and then check if going back
-	 * to user or kernel this time.
-	 */
-	b	ret_trap_entry
-	 nop
-
 signal_p:
-	/* No signals for swapper. */
-	LOAD_CURRENT(twin_tmp1, twin_tmp3)
-	set	C_LABEL(init_task), %twin_tmp3
-	cmp	%twin_tmp3, %twin_tmp1
-	be	ret_trap_continue
-	 nop
-
-	ld	[%twin_tmp1 + TASK_SIGNAL], %twin_tmp2
-	ld	[%twin_tmp1 + TASK_BLOCKED], %o0
-	andncc	%twin_tmp2, %o0, %g0
-	be	ret_trap_continue
-	 nop
+	ld	[%curptr + TASK_SIGNAL], %g2
+	ld	[%curptr + TASK_BLOCKED], %o0
+	andncc	%g2, %o0, %g0
+	be,a	ret_trap_continue
+	 ld	[%sp + REGWIN_SZ + PT_PSR], %t_psr
 
+	mov	%l5, %o2
+	mov	%l6, %o3
 	call	C_LABEL(do_signal)
 	 add	%sp, REGWIN_SZ, %o1	! pt_regs ptr
 
-	/* Fall through... */
-ret_trap_continue:
+	/* Fall through. */
 	ld	[%sp + REGWIN_SZ + PT_PSR], %t_psr
+	clr	%l6
+ret_trap_continue:
 	wr	%t_psr, 0x0, %psr
-	WRITE_PAUSE
 
-	LOAD_CURRENT(twin_tmp2, twin_tmp1)
-	ld	[%twin_tmp2 + THREAD_W_SAVED], %twin_tmp1
+	ld	[%curptr + THREAD_W_SAVED], %twin_tmp1
 	orcc	%g0, %twin_tmp1, %g0
 	be	ret_trap_nobufwins
 	 nop
@@ -105,8 +112,7 @@
 	call	C_LABEL(try_to_clear_window_buffer)
 	 add	%sp, REGWIN_SZ, %o0
 
-	b	ret_trap_entry
-	 nop
+	b,a	signal_p
 
 ret_trap_nobufwins:
 	/* Load up the user's out registers so we can pull
@@ -117,7 +123,7 @@
 	/* If there are already live user windows in the
 	 * set we can return from trap safely.
 	 */
-	ld	[%twin_tmp2 + THREAD_UMASK], %twin_tmp1
+	ld	[%curptr + THREAD_UMASK], %twin_tmp1
 	orcc	%g0, %twin_tmp1, %g0
 	bne	ret_trap_userwins_ok
 	 nop
@@ -128,12 +134,11 @@
 ret_trap_pull_one_window:
 		rd	%wim, %t_wim
 		sll	%t_wim, 0x1, %twin_tmp1
-rtrap_patch1:	srl	%t_wim, 0x7, %twin_tmp2
-		or	%twin_tmp2, %twin_tmp1, %twin_tmp2
-rtrap_patch2:	and	%twin_tmp2, 0xff, %twin_tmp2
+rtrap_patch1:	srl	%t_wim, 0x7, %glob_tmp
+		or	%glob_tmp, %twin_tmp1, %glob_tmp
+rtrap_patch2:	and	%glob_tmp, 0xff, %glob_tmp
 
-		wr	%twin_tmp2, 0x0, %wim
-		WRITE_PAUSE
+		wr	%glob_tmp, 0x0, %wim
 
 				/* Here comes the architecture specific 
 				 * branch to the user stack checking routine
@@ -147,9 +152,13 @@
 	LOAD_PT_PRIV(sp, t_psr, t_pc, t_npc)
 	or	%t_pc, %t_npc, %g2
 	andcc	%g2, 0x3, %g0
-	bne	ret_trap_unaligned_pc
+	be	1f
 	 nop
 
+	b	ret_trap_unaligned_pc
+	 add	%sp, REGWIN_SZ, %o0
+
+1:
 	LOAD_PT_YREG(sp, g1)
 	LOAD_PT_GLOBALS(sp)
 
@@ -162,7 +171,6 @@
 	rett	%t_npc
 	
 ret_trap_unaligned_pc:
-	add	%sp, REGWIN_SZ, %o0
 	ld	[%sp + REGWIN_SZ + PT_PC], %o1
 	ld	[%sp + REGWIN_SZ + PT_NPC], %o2
 	ld	[%sp + REGWIN_SZ + PT_PSR], %o3
@@ -176,14 +184,9 @@
 	call	C_LABEL(do_memaccess_unaligned)
 	 nop
 
-	b	ret_trap_entry			! maybe signal posted
-	 nop
+	b,a	signal_p
 
 ret_trap_kernel:
-	ld	[%sp + REGWIN_SZ + PT_PSR], %t_psr
-	wr	%t_psr, 0x0, %psr
-	WRITE_PAUSE
-
 		/* Will the rett land us in the invalid window? */
 		mov	2, %g1
 		sll	%g1, %t_psr, %g1
@@ -192,10 +195,9 @@
 		rd	%wim, %g2
 		andcc	%g2, %g1, %g0
 		be	1f		! Nope, just return from the trap
-		 nop
+		 sll	%g2, 0x1, %g1
 
 		/* We have to grab a window before returning. */
-		sll	%g2, 0x1, %g1
 rtrap_patch4:	srl	%g2, 7,  %g2
 		or	%g1, %g2, %g1
 rtrap_patch5:	and	%g1, 0xff, %g1
@@ -203,16 +205,20 @@
 	wr	%g1, 0x0, %wim
 	WRITE_PAUSE
 
+	/* Grrr, make sure we load from the right %sp... */
+	LOAD_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
+
 	restore	%g0, %g0, %g0
 	LOAD_WINDOW(sp)
-	save	%g0, %g0, %g0
+	b	2f
+	 save	%g0, %g0, %g0
 
 	/* Reload the entire frame in case this is from a
 	 * kernel system call or whatever...
 	 */
 1:
 	LOAD_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
-
+2:
 	LEAVE_SYSCALL
 
 	wr	%t_psr, 0x0, %psr
@@ -231,16 +237,15 @@
 	call	C_LABEL(window_ret_fault)
 	 add	%sp, REGWIN_SZ, %o0
 
-	b	ret_trap_entry
-	 nop
+	b,a	signal_p
 
 	.globl	C_LABEL(sun4c_rett_stackchk)
 C_LABEL(sun4c_rett_stackchk):
 	be	1f
 	 and	%fp, 0xfff, %g1		! delay slot
 
-	b	ret_trap_user_stack_is_bolixed
-	 nop
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
 
 	/* See if we have to check the sanity of one page or two */
 1:
@@ -252,8 +257,8 @@
 	 andncc	%g1, 0xff8, %g0
 
 	/* %sp is in vma hole, yuck */
-	b	ret_trap_user_stack_is_bolixed
-	 nop
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
 
 1:
 	be	sun4c_rett_onepage	/* Only one page to check */
@@ -268,8 +273,8 @@
 	 lda	[%g1] ASI_PTE, %g2
 
 	/* Second page is in vma hole */
-	b	ret_trap_user_stack_is_bolixed
-	 nop
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
 
 1:
 	srl	%g2, 29, %g2
@@ -278,33 +283,31 @@
 	 lda	[%fp] ASI_PTE, %g2
 
 	/* Second page has bad perms */
-	b	ret_trap_user_stack_is_bolixed
-	 nop
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
 
 sun4c_rett_onepage:
 	srl	%g2, 29, %g2
 	andcc	%g2, 0x4, %g0
-	bne	1f
-	 nop
+	bne,a	1f
+	 restore %g0, %g0, %g0
 
 	/* A page had bad page permissions, losing... */
-	b	ret_trap_user_stack_is_bolixed
-	 nop
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
 
 	/* Whee, things are ok, load the window and continue. */
 1:
-	restore	%g0, %g0, %g0
-
 	LOAD_WINDOW(sp)
 
-	save	%g0, %g0, %g0
 	b	ret_trap_userwins_ok
-	 nop
+	 save	%g0, %g0, %g0
 
 	.globl	C_LABEL(srmmu_rett_stackchk)
 C_LABEL(srmmu_rett_stackchk):
+	sethi	%hi(C_LABEL(page_offset)), %g1
 	bne	ret_trap_user_stack_is_bolixed
-	 sethi	%hi(KERNBASE), %g1
+	 ld	[%g1 + %lo(C_LABEL(page_offset))], %g1
 	cmp	%g1, %fp
 	bleu	ret_trap_user_stack_is_bolixed
 	 mov	AC_M_SFSR, %g1
@@ -329,8 +332,7 @@
 	mov	AC_M_SFSR, %g1
 	lda	[%g1] ASI_M_MMUREGS, %g1
 	andcc	%g1, 0x2, %g0
-	bne	ret_trap_user_stack_is_bolixed
+	be	ret_trap_userwins_ok
 	 nop
 
-	b	ret_trap_userwins_ok
-	 nop
+	b,a	ret_trap_user_stack_is_bolixed
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sclow.S linux/arch/sparc/kernel/sclow.S
--- v2.1.8/linux/arch/sparc/kernel/sclow.S	Tue Apr 23 12:31:45 1996
+++ linux/arch/sparc/kernel/sclow.S	Sat Nov  9 10:11:43 1996
@@ -99,6 +99,7 @@
 	sth	%l4, [%l5 + 4]
 	CC_AND_RETT
 
+#if 0
 	.globl	LABEL(write)
 LABEL(write):
 	cmp	%i0, 255		/* fd >= NR_OPEN */
@@ -175,6 +176,7 @@
 
 write_error_return:
 	SC_AND_RETT
+#endif
 
 	/* XXX sys_nice() XXX */
 	/* XXX sys_setpriority() XXX */
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.1.8/linux/arch/sparc/kernel/setup.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/setup.c	Sat Nov  9 10:11:43 1996
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.62 1996/04/25 09:11:33 davem Exp $
+/*  $Id: setup.c,v 1.75 1996/10/12 12:37:27 davem Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -12,7 +12,6 @@
 #include <linux/unistd.h>
 #include <linux/ptrace.h>
 #include <linux/malloc.h>
-#include <linux/ldt.h>
 #include <linux/smp.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
@@ -22,6 +21,8 @@
 #include <linux/fs.h>
 #include <linux/kdev_t.h>
 #include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -35,6 +36,7 @@
 #include <asm/vaddrs.h>
 #include <asm/kdebug.h>
 #include <asm/mbus.h>
+#include <asm/idprom.h>
 
 struct screen_info screen_info = {
 	0, 0,			/* orig-x, orig-y */
@@ -73,14 +75,14 @@
 {
 	unsigned long prom_tbr, flags;
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	__asm__ __volatile__("rd %%tbr, %0\n\t" : "=r" (prom_tbr));
 	__asm__ __volatile__("wr %0, 0x0, %%tbr\n\t"
 			     "nop\n\t"
 			     "nop\n\t"
 			     "nop\n\t" : : "r" (&trapbase));
 
-#if CONFIG_SUN_CONSOLE
+#ifdef CONFIG_SUN_CONSOLE
         console_restore_palette ();
 #endif
 	prom_printf("PROM SYNC COMMAND...\n");
@@ -108,6 +110,9 @@
 #define BOOTME_SINGLE 0x2
 #define BOOTME_KGDB   0x4
 
+extern char *console_fb_path;
+static int console_fb = 0;
+
 void kernel_enter_debugger(void)
 {
 	if (boot_flags & BOOTME_KGDB) {
@@ -131,61 +136,86 @@
 	return 0;
 }
 
-/* This routine does no error checking, make sure your string is sane
- * before calling this!
- * XXX This is cheese, make generic and better.
+/* 
+ * Process kernel command line switches that are specific to the
+ * SPARC or that require special low-level processing.
  */
-void
-boot_flags_init(char *commands)
+static void process_switch(char c)
+{
+	switch (c) {
+	case 'd':
+		boot_flags |= BOOTME_DEBUG;
+		break;
+	case 's':
+		boot_flags |= BOOTME_SINGLE;
+		break;
+	case 'h':
+		prom_printf("boot_flags_init: Halt!\n");
+		halt();
+		break;
+	default:
+		printk("Unknown boot switch (-%c)\n", c);
+		break;
+	}
+}
+
+static void boot_flags_init(char *commands)
 {
-	int i;
-	for(i=0; i<strlen(commands); i++) {
-		if(commands[i]=='-') {
-			switch(commands[i+1]) {
-			case 'd':
-				boot_flags |= BOOTME_DEBUG;
+	while (*commands) {
+		/* Move to the start of the next "argument". */
+		while (*commands && *commands == ' ')
+			commands++;
+
+		/* Process any command switches, otherwise skip it. */
+		if (*commands == '\0')
+			break;
+		else if (*commands == '-') {
+			commands++;
+			while (*commands && *commands != ' ')
+				process_switch(*commands++);
+		} else if (strlen(commands) >= 9
+			   && !strncmp(commands, "kgdb=tty", 8)) {
+			boot_flags |= BOOTME_KGDB;
+			switch (commands[8]) {
+#ifdef CONFIG_SUN_SERIAL
+			case 'a':
+				rs_kgdb_hook(0);
+				printk("KGDB: Using serial line /dev/ttya.\n");
 				break;
-			case 's':
-				boot_flags |= BOOTME_SINGLE;
+			case 'b':
+				rs_kgdb_hook(1);
+				printk("KGDB: Using serial line /dev/ttyb.\n");
 				break;
-			case 'h':
-				prom_printf("boot_flags_init: Found halt flag, doing so now...\n");
-				halt();
+#endif
+#ifdef CONFIG_AP1000
+			case 'c':
+				printk("KGDB: AP1000+ debugging\n");
 				break;
+#endif
 			default:
-				printk("boot_flags_init: Unknown boot arg (-%c)\n",
-				       commands[i+1]);
+				printk("KGDB: Unknown tty line.\n");
+				boot_flags &= ~BOOTME_KGDB;
 				break;
-			};
+			}
+			commands += 9;
 		} else {
-			if(commands[i]=='k' && commands[i+1]=='g' &&
-			   commands[i+2]=='d' && commands[i+3]=='b' &&
-			   commands[i+4]=='=' && commands[i+5]=='t' &&
-			   commands[i+6]=='t' && commands[i+7]=='y') {
-				printk("KGDB: Using serial line /dev/tty%c for "
-				       "session\n", commands[i+8]);
-				boot_flags |= BOOTME_KGDB;
-#if CONFIG_SUN_SERIAL
-				if(commands[i+8]=='a')
-					rs_kgdb_hook(0);
-				else if(commands[i+8]=='b')
-					rs_kgdb_hook(1);
-				else
-#endif
-#if CONFIG_AP1000
-                                if(commands[i+8]=='c')
-				  printk("KGDB: ap1000+ debugging\n");
-				else
-#endif
-				{
-					printk("KGDB: whoops bogon tty line "
-					       "requested, disabling session\n");
-					boot_flags &= (~BOOTME_KGDB);
+			if (!strncmp(commands, "console=", 8)) {
+				commands += 8;
+				if (!strncmp (commands, "ttya", 4)) {
+					console_fb = 2;
+					prom_printf ("Using /dev/ttya as console.\n");
+				} else if (!strncmp (commands, "ttyb", 4)) {
+					console_fb = 3;
+					prom_printf ("Using /dev/ttyb as console.\n");
+				} else {
+					console_fb = 1;
+					console_fb_path = commands;
 				}
 			}
+			while (*commands && *commands != ' ')
+				commands++;
 		}
 	}
-	return;
 }
 
 /* This routine will in the future do all the nasty prom stuff
@@ -197,12 +227,24 @@
 extern void load_mmu(void);
 extern int prom_probe_memory(void);
 extern void sun4c_probe_vac(void);
-extern void get_idprom(void);
 extern char cputypval;
 extern unsigned long start, end;
 extern void panic_setup(char *, int *);
+extern unsigned long srmmu_endmem_fixup(unsigned long);
+
+extern unsigned short root_flags;
+extern unsigned short root_dev;
+extern unsigned short ram_flags;
+extern unsigned ramdisk_image;
+extern unsigned ramdisk_size;
+#define RAMDISK_IMAGE_START_MASK	0x07FF
+#define RAMDISK_PROMPT_FLAG		0x8000
+#define RAMDISK_LOAD_FLAG		0x4000
+
+extern int root_mountflags;
 
 char saved_command_line[256];
+char reboot_command[256];
 enum sparc_cpu sparc_cpu_model;
 
 struct tt_entry *sparc_ttable;
@@ -227,6 +269,7 @@
 
 	/* Set sparc_cpu_model */
 	sparc_cpu_model = sun_unknown;
+	if(!strcmp(&cputypval,"sun4 ")) { sparc_cpu_model=sun4; }
 	if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; }
 	if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; }
 	if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
@@ -234,32 +277,36 @@
 	if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
 	printk("ARCH: ");
 	packed = 0;
-	switch(sparc_cpu_model)
-	  {
-	  case sun4c:
-		  printk("SUN4C\n");
-		  sun4c_probe_vac();
-		  packed = 0;
-		  break;
-          case sun4m:
-		  printk("SUN4M\n");
-		  packed = 1;
-		  break;
-	  case sun4d:
-		  printk("SUN4D\n");
-		  packed = 1;
-		  break;
-	  case sun4e:
-		  printk("SUN4E\n");
-		  packed = 0;
-		  break;
-	  case sun4u:
-		  printk("SUN4U\n");
-		  break;
-	  default:
-		  printk("UNKNOWN!\n");
-		  break;
-	  };
+	switch(sparc_cpu_model) {
+	case sun4:
+		printk("SUN4\n");
+		sun4c_probe_vac();
+		packed = 0;
+		break;
+	case sun4c:
+		printk("SUN4C\n");
+		sun4c_probe_vac();
+		packed = 0;
+		break;
+	case sun4m:
+		printk("SUN4M\n");
+		packed = 1;
+		break;
+	case sun4d:
+		printk("SUN4D\n");
+		packed = 1;
+		break;
+	case sun4e:
+		printk("SUN4E\n");
+		packed = 0;
+		break;
+	case sun4u:
+		printk("SUN4U\n");
+		break;
+	default:
+		printk("UNKNOWN!\n");
+		break;
+	};
 
 	boot_flags_init(*cmdline_p);
 	if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) && 
@@ -272,7 +319,7 @@
 		breakpoint();
 	}
 
-	get_idprom();
+	idprom_init();
 	load_mmu();
 	total = prom_probe_memory();
 	*memory_start_p = (((unsigned long) &end));
@@ -292,9 +339,44 @@
 
 	prom_setsync(prom_sync_me);
 
-	*memory_end_p = (end_of_phys_memory + PAGE_OFFSET);
-	if(*memory_end_p > IOBASE_VADDR)
-		*memory_end_p = IOBASE_VADDR;
+	*memory_end_p = (end_of_phys_memory + KERNBASE);
+	if((sparc_cpu_model == sun4c) ||
+	   (sparc_cpu_model == sun4))
+		goto not_relevant;
+	if(end_of_phys_memory >= 0x0d000000) {
+		*memory_end_p = 0xfd000000;
+	} else {
+		if((sparc_cpu_model == sun4m) ||
+		   (sparc_cpu_model == sun4d))
+			*memory_end_p = srmmu_endmem_fixup(*memory_end_p);
+	}
+not_relevant:
+		
+	if (!root_flags)
+		root_mountflags &= ~MS_RDONLY;
+	ROOT_DEV = to_kdev_t(root_dev);
+#ifdef CONFIG_BLK_DEV_RAM
+	rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
+	rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
+	rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);	
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (ramdisk_image) {
+		initrd_start = ramdisk_image;
+		if (initrd_start < KERNBASE) initrd_start += KERNBASE;
+		initrd_end = initrd_start + ramdisk_size;
+		if (initrd_end > *memory_end_p) {
+			printk(KERN_CRIT "initrd extends beyond end of memory "
+		                 	 "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+		       			 initrd_end,*memory_end_p);
+			initrd_start = 0;
+		}
+		if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
+			initrd_below_start_ok = 1;
+			*memory_start_p = PAGE_ALIGN (initrd_end);
+		}
+	}
+#endif	
 
 	/* Due to stack alignment restrictions and assumptions... */
 	init_task.mm->mmap->vm_page_prot = PAGE_SHARED;
@@ -307,30 +389,29 @@
 #if !CONFIG_SUN_SERIAL
 		serial_console = 0;
 #else
-		int idev = prom_query_input_device();
-		int odev = prom_query_output_device();
-		if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) {
-			serial_console = 0;
-		} else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) {
-			serial_console = 1;
-		} else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) {
-			prom_printf("Console on ttyb is not supported\n");
-			prom_halt();
-		} else {
-			prom_printf("Inconsistent console\n");
-			prom_halt();
+		switch (console_fb) {
+		case 0: /* Let get our io devices from prom */
+			{
+				int idev = prom_query_input_device();
+				int odev = prom_query_output_device();
+				if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) {
+					serial_console = 0;
+				} else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) {
+					serial_console = 1;
+				} else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) {
+					serial_console = 2;
+				} else {
+					prom_printf("Inconsistent console\n");
+					prom_halt();
+				}
+			}
+			break;
+		case 1: serial_console = 0; break; /* Force one of the framebuffers as console */
+		case 2: serial_console = 1; break; /* Force ttya as console */
+		case 3: serial_console = 2; break; /* Force ttyb as console */
 		}
 #endif
 	}
-#if 1
-	/* XXX ROOT_DEV hack for kgdb - davem XXX */
-#if 1
-	ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255); /* NFS */
-#else
-	ROOT_DEV = 0x801; /* SCSI DISK */
-#endif
-
-#endif
 }
 
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
@@ -345,6 +426,8 @@
 
 extern char *smp_info(void);
 
+extern int linux_num_cpus;
+
 int get_cpuinfo(char *buffer)
 {
 	int cpuid=get_cpuid();
@@ -353,7 +436,8 @@
             "fpu\t\t: %s\n"
             "promlib\t\t: Version %d Revision %d\n"
             "type\t\t: %s\n"
-            "Elf Support\t: %s\n"   /* I can't remember when I do --ralp */
+	    "ncpus probed\t: %d\n"
+	    "ncpus active\t: %d\n"
 #ifndef __SMP__
             "BogoMips\t: %lu.%02lu\n"
 #else
@@ -375,11 +459,7 @@
             romvec->pv_romvers, prom_rev,
 #endif
             &cputypval,
-#if CONFIG_BINFMT_ELF
-            "yes",
-#else
-            "no",
-#endif
+	    linux_num_cpus, smp_num_cpus,
 #ifndef __SMP__
             loops_per_sec/500000, (loops_per_sec/5000) % 100,
 #else
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c
--- v2.1.8/linux/arch/sparc/kernel/signal.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/signal.c	Sat Nov  9 10:11:44 1996
@@ -1,7 +1,9 @@
-/*  $Id: signal.c,v 1.32 1996/04/22 19:37:48 davem Exp $
+/*  $Id: signal.c,v 1.57 1996/10/31 00:59:01 davem Exp $
  *  linux/arch/sparc/kernel/signal.c
  *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  */
 
 #include <linux/sched.h>
@@ -13,19 +15,27 @@
 #include <linux/unistd.h>
 #include <linux/mm.h>
 
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/ptrace.h>
+#include <asm/svr4.h>
 
 #define _S(nr) (1<<((nr)-1))
 
 #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
 
 asmlinkage int sys_waitpid(pid_t pid, unsigned long *stat_addr, int options);
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs);
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
+			 unsigned long orig_o0, int ret_from_syscall);
 
-/*
- * atomically swap in the new signal mask, and wait for a signal.
+/* This turned off for production... */
+/* #define DEBUG_FATAL_SIGNAL 1 */
+
+#ifdef DEBUG_FATAL_SIGNAL
+extern void instruction_dump (unsigned long *pc);
+#endif
+
+/* atomically swap in the new signal mask, and wait for a signal.
  * This is really tricky on the Sparc, watch out...
  */
 asmlinkage inline void _sigpause_common(unsigned int set, struct pt_regs *regs)
@@ -34,21 +44,24 @@
 
 	mask = current->blocked;
 	current->blocked = set & _BLOCKABLE;
-
-	/* Advance over the syscall instruction for when
-	 * we return.  We want setup_frame to save the proper
-	 * state, including the error return number & condition
-	 * codes.
-	 */
 	regs->pc = regs->npc;
 	regs->npc += 4;
-	regs->psr |= PSR_C;
-	regs->u_regs[UREG_I0] = EINTR;
 
+	/* Condition codes and return value where set here for sigpause,
+	 * and so got used by setup_frame, which again causes sigreturn()
+	 * to return -EINTR.
+	 */
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(mask, regs))
+		/*
+		 * Return -EINTR and set condition code here,
+		 * so the interrupted system call actually returns
+		 * these.
+		 */
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EINTR;
+		if (do_signal(mask, regs, 0, 0))
 			return;
 	}
 }
@@ -60,58 +73,47 @@
 
 asmlinkage void do_sigsuspend (struct pt_regs *regs)
 {
-	unsigned long mask;
-	unsigned long set;
-
-	set = regs->u_regs [UREG_I0];
-	mask = current->blocked;
-	current->blocked = set & _BLOCKABLE;
-	regs->pc = regs->npc;
-	regs->npc += 4;
-	while (1) {
-		current->state = TASK_INTERRUPTIBLE;
-		schedule();
-		if (do_signal(mask,regs)){
-			regs->psr |= PSR_C;
-			regs->u_regs [UREG_I0] = EINTR;
-			return;
-		}
-	}
+	_sigpause_common(regs->u_regs[UREG_I0], regs);
 }
 
 asmlinkage void do_sigreturn(struct pt_regs *regs)
 {
-	struct sigcontext_struct *scptr =
-		(struct sigcontext_struct *) regs->u_regs[UREG_I0];
+	struct sigcontext *scptr =
+		(struct sigcontext *) regs->u_regs[UREG_I0];
+	unsigned long pc, npc, psr;
 
 	synchronize_user_stack();
 
 	/* Check sanity of the user arg. */
-	if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext_struct)) ||
-	   ((((unsigned long) scptr)) & 0x3)) {
-		printk("%s [%d]: do_sigreturn, scptr is invalid at pc<%08lx> scptr<%p>\n",
+	if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext)) ||
+	   (((unsigned long) scptr) & 3)) {
+		printk("%s [%d]: do_sigreturn, scptr is invalid at "
+		       "pc<%08lx> scptr<%p>\n",
 		       current->comm, current->pid, regs->pc, scptr);
 		do_exit(SIGSEGV);
 	}
-
-	if((scptr->sigc_pc | scptr->sigc_npc) & 3)
-		return; /* Nice try. */
-
-	current->blocked = scptr->sigc_mask & _BLOCKABLE;
-	current->tss.sstk_info.cur_status = (scptr->sigc_onstack & 1);
-	regs->pc = scptr->sigc_pc;
-	regs->npc = scptr->sigc_npc;
-	regs->u_regs[UREG_FP] = scptr->sigc_sp;
-	regs->u_regs[UREG_I0] = scptr->sigc_o0;
-	regs->u_regs[UREG_G1] = scptr->sigc_g1;
+	__get_user(pc, &scptr->sigc_pc);
+	__get_user(npc, &scptr->sigc_npc);
+	if((pc | npc) & 3)
+		do_exit(SIGSEGV); /* Nice try. */
+
+	__get_user(current->blocked, &scptr->sigc_mask);
+	current->blocked &= _BLOCKABLE;
+	__get_user(current->tss.sstk_info.cur_status, &scptr->sigc_onstack);
+	current->tss.sstk_info.cur_status &= 1;
+	regs->pc = pc;
+	regs->npc = npc;
+	__get_user(regs->u_regs[UREG_FP], &scptr->sigc_sp);
+	__get_user(regs->u_regs[UREG_I0], &scptr->sigc_o0);
+	__get_user(regs->u_regs[UREG_G1], &scptr->sigc_g1);
 
 	/* User can only change condition codes in %psr. */
-	regs->psr &= (~PSR_ICC);
-	regs->psr |= (scptr->sigc_psr & PSR_ICC);
+	__get_user(psr, &scptr->sigc_psr);
+	regs->psr &= ~(PSR_ICC);
+	regs->psr |= (psr & PSR_ICC);
 }
 
-/*
- * Set up a signal frame... Make the stack look the way SunOS
+/* Set up a signal frame... Make the stack look the way SunOS
  * expects it to look which is basically:
  *
  * ---------------------------------- <-- %sp at signal time
@@ -127,33 +129,39 @@
 	struct reg_window sig_window;
 	int sig_num;
 	int sig_code;
-	struct sigcontext_struct *sig_scptr;
+	struct sigcontext *sig_scptr;
 	int sig_address;
-	struct sigcontext_struct sig_context;
+	struct sigcontext sig_context;
 };
 /* To align the structure properly. */
 #define SF_ALIGNEDSZ  (((sizeof(struct signal_sframe) + 7) & (~7)))
 
+/* Checks if the fp is valid */
+int invalid_frame_pointer (void *fp, int fplen)
+{
+	if ((((unsigned long) fp) & 7) ||
+	    !__access_ok((unsigned long)fp, fplen) ||
+	    ((sparc_cpu_model == sun4 || sparc_cpu_model == sun4c) &&
+	     ((unsigned long) fp < 0xe0000000 && (unsigned long) fp >= 0x20000000)))
+		return 1;
+	
+	return 0;
+}
+
 static inline void
-setup_frame(struct sigaction *sa, struct sigcontext_struct **fp,
-	    unsigned long pc, unsigned long npc, struct pt_regs *regs,
-	    int signr, unsigned long oldmask)
+setup_frame(struct sigaction *sa, unsigned long pc, unsigned long npc,
+	    struct pt_regs *regs, int signr, unsigned long oldmask)
 {
 	struct signal_sframe *sframep;
-	struct sigcontext_struct *sc;
+	struct sigcontext *sc;
 	int window = 0;
 	int old_status = current->tss.sstk_info.cur_status;
 
 	synchronize_user_stack();
-	sframep = (struct signal_sframe *) *fp;
+	sframep = (struct signal_sframe *) regs->u_regs[UREG_FP];
 	sframep = (struct signal_sframe *) (((unsigned long) sframep)-SF_ALIGNEDSZ);
-	sc = &sframep->sig_context;
-	if(verify_area(VERIFY_WRITE, sframep, sizeof(*sframep)) ||
-	   (((unsigned long) sframep) & 7) ||
-	   (((unsigned long) sframep) >= KERNBASE) ||
-	   ((sparc_cpu_model == sun4 || sparc_cpu_model == sun4c) &&
-	    ((unsigned long) sframep < 0xe0000000 && (unsigned long) sframep >= 0x20000000))) {
-#if 0 /* fills up the console logs... */
+	if (invalid_frame_pointer (sframep, sizeof(*sframep))){
+#if 0 /* fills up the console logs during crashme runs, yuck... */
 		printk("%s [%d]: User has trashed signal stack\n",
 		       current->comm, current->pid);
 		printk("Sigstack ptr %p handler at pc<%08lx> for sig<%d>\n",
@@ -162,71 +170,306 @@
 		/* Don't change signal code and address, so that
 		 * post mortem debuggers can have a look.
 		 */
-		current->sig->action[SIGILL-1].sa_handler = SIG_DFL;
-		current->blocked &= ~(1<<(SIGILL-1));
-		send_sig(SIGILL,current,1);
+		do_exit(SIGILL);
 		return;
 	}
-	*fp = (struct sigcontext_struct *) sframep;
 
-	sc->sigc_onstack = old_status;
-	sc->sigc_mask = oldmask;
-	sc->sigc_sp = regs->u_regs[UREG_FP];
-	sc->sigc_pc = pc;
-	sc->sigc_npc = npc;
-	sc->sigc_psr = regs->psr;
-	sc->sigc_g1 = regs->u_regs[UREG_G1];
-	sc->sigc_o0 = regs->u_regs[UREG_I0];
-	sc->sigc_oswins = current->tss.w_saved;
+	sc = &sframep->sig_context;
+
+	/* We've already made sure frame pointer isn't in kernel space... */
+	__put_user(old_status, &sc->sigc_onstack);
+	__put_user(oldmask, &sc->sigc_mask);
+	__put_user(regs->u_regs[UREG_FP], &sc->sigc_sp);
+	__put_user(pc, &sc->sigc_pc);
+	__put_user(npc, &sc->sigc_npc);
+	__put_user(regs->psr, &sc->sigc_psr);
+	__put_user(regs->u_regs[UREG_G1], &sc->sigc_g1);
+	__put_user(regs->u_regs[UREG_I0], &sc->sigc_o0);
+	__put_user(current->tss.w_saved, &sc->sigc_oswins);
 	if(current->tss.w_saved)
 		for(window = 0; window < current->tss.w_saved; window++) {
 			sc->sigc_spbuf[window] =
 				(char *)current->tss.rwbuf_stkptrs[window];
-			memcpy(&sc->sigc_wbuf[window],
+			copy_to_user(&sc->sigc_wbuf[window],
 			       &current->tss.reg_window[window],
 			       sizeof(struct reg_window));
 		}
 	else
-		memcpy(sframep, (char *)regs->u_regs[UREG_FP],
+		copy_to_user(sframep, (char *)regs->u_regs[UREG_FP],
 		       sizeof(struct reg_window));
 
 	current->tss.w_saved = 0; /* So process is allowed to execute. */
-	sframep->sig_num = signr;
+	__put_user(signr, &sframep->sig_num);
 	if(signr == SIGSEGV ||
 	   signr == SIGILL ||
 	   signr == SIGFPE ||
 	   signr == SIGBUS ||
 	   signr == SIGEMT) {
-		sframep->sig_code = current->tss.sig_desc;
-		sframep->sig_address = current->tss.sig_address;
+		__put_user(current->tss.sig_desc, &sframep->sig_code);
+		__put_user(current->tss.sig_address, &sframep->sig_address);
 	} else {
-		sframep->sig_code = 0;
-		sframep->sig_address = 0;
+		__put_user(0, &sframep->sig_code);
+		__put_user(0, &sframep->sig_address);
+	}
+	__put_user(sc, &sframep->sig_scptr);
+	regs->u_regs[UREG_FP] = (unsigned long) sframep;
+	regs->pc = (unsigned long) sa->sa_handler;
+	regs->npc = (regs->pc + 4);
+}
+
+/* Setup a Solaris stack frame */
+static inline void
+setup_svr4_frame(struct sigaction *sa, unsigned long pc, unsigned long npc,
+		 struct pt_regs *regs, int signr, unsigned long oldmask)
+{
+	svr4_signal_frame_t *sfp;
+	svr4_gregset_t  *gr;
+	svr4_siginfo_t  *si;
+	svr4_mcontext_t *mc;
+	svr4_gwindows_t *gw;
+	svr4_ucontext_t *uc;
+	int window = 0;
+
+	synchronize_user_stack();
+	sfp = (svr4_signal_frame_t *) regs->u_regs[UREG_FP] - REGWIN_SZ;
+	sfp = (svr4_signal_frame_t *) (((unsigned long) sfp)-SVR4_SF_ALIGNED);
+
+	if (invalid_frame_pointer (sfp, sizeof (*sfp))){
+#if 0
+		printk ("Invalid stack frame\n");
+#endif
+		do_exit(SIGILL);
+		return;
+	}
+
+	/* Start with a clean frame pointer and fill it */
+	clear_user(sfp, sizeof (*sfp));
+
+	/* Setup convenience variables */
+	si = &sfp->si;
+	uc = &sfp->uc;
+	gw = &sfp->gw;
+	mc = &uc->mcontext;
+	gr = &mc->greg;
+	
+	/* FIXME: where am I supposed to put this?
+	 * sc->sigc_onstack = old_status;
+	 * anyways, it does not look like it is used for anything at all.
+	 */
+	__put_user(oldmask, &uc->sigmask.sigbits [0]);
+
+	/* Store registers */
+	__put_user(regs->pc, &((*gr) [SVR4_PC]));
+	__put_user(regs->npc, &((*gr) [SVR4_NPC]));
+	__put_user(regs->psr, &((*gr) [SVR4_PSR]));
+	__put_user(regs->y, &((*gr) [SVR4_Y]));
+	
+	/* Copy g [1..7] and o [0..7] registers */
+	copy_to_user(&(*gr)[SVR4_G1], &regs->u_regs [UREG_G1], sizeof (uint) * 7);
+	copy_to_user(&(*gr)[SVR4_O0], &regs->u_regs [UREG_I0], sizeof (uint) * 8);
+
+	/* Setup sigaltstack, FIXME */
+	__put_user(0xdeadbeef, &uc->stack.sp);
+	__put_user(0, &uc->stack.size);
+	__put_user(0, &uc->stack.flags);	/* Possible: ONSTACK, DISABLE */
+
+	/* Save the currently window file: */
+
+	/* 1. Link sfp->uc->gwins to our windows */
+	__put_user(gw, &mc->gwin);
+	    
+	/* 2. Number of windows to restore at setcontext (): */
+	__put_user(current->tss.w_saved, &gw->count);
+
+	/* 3. Save each valid window
+	 *    Currently, it makes a copy of the windows from the kernel copy.
+	 *    David's code for SunOS, makes the copy but keeps the pointer to
+	 *    the kernel.  My version makes the pointer point to a userland 
+	 *    copy of those.  Mhm, I wonder if I shouldn't just ignore those
+	 *    on setcontext and use those that are on the kernel, the signal
+	 *    handler should not be modyfing those, mhm.
+	 *
+	 *    These windows are just used in case synchronize_user_stack failed
+	 *    to flush the user windows.
+	 */
+	for(window = 0; window < current->tss.w_saved; window++) {
+		__put_user((int *) &(gw->win [window]), &gw->winptr [window]);
+		copy_to_user(&gw->win [window], &current->tss.reg_window [window], sizeof (svr4_rwindow_t));
+		__put_user(0, gw->winptr [window]);
+	}
+
+	/* 4. We just pay attention to the gw->count field on setcontext */
+	current->tss.w_saved = 0; /* So process is allowed to execute. */
+
+	/* Setup the signal information.  Solaris expects a bunch of
+	 * information to be passed to the signal handler, we don't provide
+	 * that much currently, should use those that David already
+	 * is providing with tss.sig_desc
+	 */
+	__put_user(signr, &si->siginfo.signo);
+	__put_user(SVR4_SINOINFO, &si->siginfo.code);
+
+	regs->u_regs[UREG_FP] = (unsigned long) sfp;
+	regs->pc = (unsigned long) sa->sa_handler;
+	regs->npc = (regs->pc + 4);
+
+	/* Arguments passed to signal handler */
+	if (regs->u_regs [14]){
+		struct reg_window *rw = (struct reg_window *) regs->u_regs [14];
+
+		__put_user(signr, &rw->ins [0]);
+		__put_user(si, &rw->ins [1]);
+		__put_user(uc, &rw->ins [2]);
+		__put_user(sfp, &rw->ins [6]);	/* frame pointer */
+#if 0
+		regs->u_regs[UREG_I0] = signr;
+		regs->u_regs[UREG_I1] = (uint) si;
+		regs->u_regs[UREG_I2] = (uint) uc;
+#endif
+	}
+}
+
+asmlinkage int
+svr4_getcontext (svr4_ucontext_t *uc, struct pt_regs *regs)
+{
+	svr4_gregset_t  *gr;
+	svr4_mcontext_t *mc;
+
+	synchronize_user_stack();
+	if (current->tss.w_saved){
+		printk ("Uh oh, w_saved is not zero (%ld)\n", current->tss.w_saved);
+		do_exit (SIGSEGV);
+	}
+	if(clear_user(uc, sizeof (*uc)))
+		return -EFAULT;
+
+	/* Setup convenience variables */
+	mc = &uc->mcontext;
+	gr = &mc->greg;
+	
+	/* We only have < 32 signals, fill the first slot only */
+	__put_user(current->sig->action->sa_mask, &uc->sigmask.sigbits [0]);
+
+	/* Store registers */
+	__put_user(regs->pc, &uc->mcontext.greg [SVR4_PC]);
+	__put_user(regs->npc, &uc->mcontext.greg [SVR4_NPC]);
+	__put_user(regs->psr, &uc->mcontext.greg [SVR4_PSR]);
+        __put_user(regs->y, &uc->mcontext.greg [SVR4_Y]);
+	
+	/* Copy g [1..7] and o [0..7] registers */
+	copy_to_user(&(*gr)[SVR4_G1], &regs->u_regs [UREG_G1], sizeof (uint) * 7);
+	copy_to_user(&(*gr)[SVR4_O0], &regs->u_regs [UREG_I0], sizeof (uint) * 8);
+
+	/* Setup sigaltstack, FIXME */
+	__put_user(0xdeadbeef, &uc->stack.sp);
+	__put_user(0, &uc->stack.size);
+	__put_user(0, &uc->stack.flags);	/* Possible: ONSTACK, DISABLE */
+
+	/* The register file is not saved
+	 * we have already stuffed all of it with sync_user_stack
+	 */
+	return 0;
+}
+
+
+/* Set the context for a svr4 application, this is Solaris way to sigreturn */
+asmlinkage int svr4_setcontext (svr4_ucontext_t *c, struct pt_regs *regs)
+{
+	struct thread_struct *tp = &current->tss;
+	svr4_gregset_t  *gr;
+	unsigned long pc, npc, psr;
+	
+	/* Fixme: restore windows, or is this already taken care of in
+	 * svr4_setup_frame when sync_user_windows is done?
+	 */
+	flush_user_windows();
+	
+	if (tp->w_saved){
+		printk ("Uh oh, w_saved is: 0x%lx\n", tp->w_saved);
+		do_exit(SIGSEGV);
+	}
+	if (((uint) c) & 3){
+		printk ("Unaligned structure passed\n");
+		do_exit (SIGSEGV);
+	}
+
+	if(!__access_ok((unsigned long)c, sizeof(*c))) {
+		/* Miguel, add nice debugging msg _here_. ;-) */
+		do_exit(SIGSEGV);
 	}
-	sframep->sig_scptr = sc;
-	regs->u_regs[UREG_FP] = (unsigned long) *fp;
+
+	/* Check for valid PC and nPC */
+	gr = &c->mcontext.greg;
+	__get_user(pc, &((*gr)[SVR4_PC]));
+	__get_user(npc, &((*gr)[SVR4_NPC]));
+	if((pc | npc) & 3) {
+	        printk ("setcontext, PC or nPC were bogus\n");
+		do_exit (SIGSEGV);
+	}
+	/* Retrieve information from passed ucontext */
+	__get_user(current->blocked, &c->sigmask.sigbits [0]);
+	current->blocked &= _BLOCKABLE;
+	regs->pc = pc;
+	regs->npc = npc;
+	__get_user(regs->y, &((*gr) [SVR4_Y]));
+	__get_user(psr, &((*gr) [SVR4_PSR]));
+	regs->psr &= ~(PSR_ICC);
+	regs->psr |= (psr & PSR_ICC);
+
+	/* Restore g[1..7] and o[0..7] registers */
+	copy_from_user(&regs->u_regs [UREG_G1], &(*gr)[SVR4_G1], sizeof (uint) * 7);
+	copy_from_user(&regs->u_regs [UREG_I0], &(*gr)[SVR4_O0], sizeof (uint) * 8);
+
+	printk ("Setting PC=%lx nPC=%lx\n", regs->pc, regs->npc);
+	return -EINTR;
 }
 
-/*
- * Note that 'init' is a special process: it doesn't get signals it doesn't
+static inline void handle_signal(unsigned long signr, struct sigaction *sa,
+				 unsigned long oldmask, struct pt_regs *regs,
+				 int svr4_signal)
+{
+	if(svr4_signal)
+		setup_svr4_frame(sa, regs->pc, regs->npc, regs, signr, oldmask);
+	else
+		setup_frame(sa, regs->pc, regs->npc, regs, signr, oldmask);
+
+	if(sa->sa_flags & SA_ONESHOT)
+		sa->sa_handler = NULL;
+	if(!(sa->sa_flags & SA_NOMASK))
+		current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+}
+
+static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
+				   struct sigaction *sa)
+{
+	switch(regs->u_regs[UREG_I0]) {
+		case ERESTARTNOHAND:
+		no_system_call_restart:
+			regs->u_regs[UREG_I0] = EINTR;
+			regs->psr |= PSR_C;
+			break;
+		case ERESTARTSYS:
+			if(!(sa->sa_flags & SA_RESTART))
+				goto no_system_call_restart;
+		/* fallthrough */
+		case ERESTARTNOINTR:
+			regs->u_regs[UREG_I0] = orig_i0;
+			regs->pc -= 4;
+			regs->npc -= 4;
+	}
+}
+
+/* Note that 'init' is a special process: it doesn't get signals it doesn't
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
  * mistake.
- *
- * Note that we go through the signals twice: once to check the signals that
- * the kernel can handle, and then we build all the user-level signal handling
- * stack-frames in one go after that.
  */
-
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
+			 unsigned long orig_i0, int restart_syscall)
 {
-	unsigned long mask = ~current->blocked;
-	unsigned long handler_signal = 0;
-	struct sigcontext_struct *frame = NULL;
-	unsigned long pc = 0;
-	unsigned long npc = 0;
-	unsigned long signr;
+	unsigned long signr, mask = ~current->blocked;
 	struct sigaction *sa;
-
+	int svr4_signal = current->personality == PER_SVR4;
+	
 	while ((signr = current->signal & mask) != 0) {
 		signr = ffz(~signr);
 		clear_bit(signr, &current->signal);
@@ -261,7 +504,13 @@
 			case SIGCONT: case SIGCHLD: case SIGWINCH:
 				continue;
 
-			case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+			case SIGTSTP: case SIGTTIN: case SIGTTOU:
+				if (is_orphaned_pgrp(current->pgrp))
+					continue;
+
+			case SIGSTOP:
+				if (current->flags & PF_PTRACED)
+					continue;
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
 				if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
@@ -271,7 +520,7 @@
 				continue;
 
 			case SIGQUIT: case SIGILL: case SIGTRAP:
-			case SIGABRT: case SIGFPE: case SIGSEGV:
+			case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS:
 				if(current->binfmt && current->binfmt->core_dump) {
 					if(current->binfmt->core_dump(signr, regs))
 						signr |= 0x80;
@@ -283,47 +532,21 @@
 				do_exit(signr);
 			}
 		}
-		/* OK, we're invoking a handler. */
-		if(regs->psr & PSR_C) {
-			if(regs->u_regs[UREG_I0] == ERESTARTNOHAND ||
-			   (regs->u_regs[UREG_I0] == ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
-				regs->u_regs[UREG_I0] = EINTR;
-		}
-		handler_signal |= 1 << (signr - 1);
-		mask &= ~sa->sa_mask;
+		if(restart_syscall)
+			syscall_restart(orig_i0, regs, sa);
+		handle_signal(signr, sa, oldmask, regs, svr4_signal);
+		return 1;
 	}
-	if((regs->psr & PSR_C) &&
+	if(restart_syscall &&
 	   (regs->u_regs[UREG_I0] == ERESTARTNOHAND ||
 	    regs->u_regs[UREG_I0] == ERESTARTSYS ||
 	    regs->u_regs[UREG_I0] == ERESTARTNOINTR)) {
 		/* replay the system call when we are done */
-		regs->u_regs[UREG_I0] = regs->u_regs[UREG_G0];
+		regs->u_regs[UREG_I0] = orig_i0;
 		regs->pc -= 4;
 		regs->npc -= 4;
 	}
-	if(!handler_signal)
-		return 0;
-	pc = regs->pc;
-	npc = regs->npc;
-	frame = (struct sigcontext_struct *) regs->u_regs[UREG_FP];
-	signr = 1;
-	sa = current->sig->action;
-	for(mask = 1; mask; sa++, signr++, mask += mask) {
-		if(mask > handler_signal)
-			break;
-		if(!(mask & handler_signal))
-			continue;
-		setup_frame(sa, &frame, pc, npc, regs, signr, oldmask);
-		pc = (unsigned long) sa->sa_handler;
-		npc = pc + 4;
-		if(sa->sa_flags & SA_ONESHOT)
-			sa->sa_handler = NULL;
-		current->blocked |= sa->sa_mask;
-		oldmask |= sa->sa_mask;
-	}
-	regs->pc = pc;
-	regs->npc = npc;
-	return 1;
+	return 0;
 }
 
 asmlinkage int
@@ -331,16 +554,14 @@
 {
 	/* First see if old state is wanted. */
 	if(ossptr) {
-		if(verify_area(VERIFY_WRITE, ossptr, sizeof(struct sigstack)))
+		if(copy_to_user(ossptr, &current->tss.sstk_info, sizeof(struct sigstack)))
 			return -EFAULT;
-		memcpy(ossptr, &current->tss.sstk_info, sizeof(struct sigstack));
 	}
 
 	/* Now see if we want to update the new state. */
 	if(ssptr) {
-		if(verify_area(VERIFY_READ, ssptr, sizeof(struct sigstack)))
+		if(copy_from_user(&current->tss.sstk_info, ssptr, sizeof(struct sigstack)))
 			return -EFAULT;
-		memcpy(&current->tss.sstk_info, ssptr, sizeof(struct sigstack));
 	}
 	return 0;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c
--- v2.1.8/linux/arch/sparc/kernel/smp.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/smp.c	Sat Nov  9 10:11:45 1996
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/tasks.h>
 #include <linux/smp.h>
+#include <linux/interrupt.h>
 
 #include <asm/delay.h>
 #include <asm/irq.h>
@@ -91,18 +92,12 @@
 char *smp_info(void)
 {
 	sprintf(smp_buf,
-"\n        CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
-"State: %s\t\t%s\t\t%s\t\t%s\n"
-"Lock:  %08lx\t\t%08lx\t%08lx\t%08lx\n"
-"\n"
-"klock: %x\n",
-		(cpu_present_map & 1) ? ((active_kernel_processor == 0) ? "akp" : "online") : "offline",
-		(cpu_present_map & 2) ? ((active_kernel_processor == 1) ? "akp" : "online") : "offline",
-		(cpu_present_map & 4) ? ((active_kernel_processor == 2) ? "akp" : "online") : "offline",
-		(cpu_present_map & 8) ? ((active_kernel_processor == 3) ? "akp" : "online") : "offline",
-		smp_proc_in_lock[0], smp_proc_in_lock[1], smp_proc_in_lock[2],
-		smp_proc_in_lock[3], 
-		kernel_flag);
+"        CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
+"State:  %s\t\t%s\t\t%s\t\t%s\n",
+(cpu_present_map & 1) ? ((active_kernel_processor == 0) ? "akp" : "online") : "offline",
+(cpu_present_map & 2) ? ((active_kernel_processor == 1) ? "akp" : "online") : "offline",
+(cpu_present_map & 4) ? ((active_kernel_processor == 2) ? "akp" : "online") : "offline",
+(cpu_present_map & 8) ? ((active_kernel_processor == 3) ? "akp" : "online") : "offline");
 	return smp_buf;
 }
 
@@ -169,8 +164,11 @@
 	local_flush_tlb_all();
 
 	/* Fix idle thread fields. */
+	__asm__ __volatile__("ld [%0], %%g6\n\t"
+			     : : "r" (&current_set[smp_processor_id()])
+			     : "memory" /* paranoid */);
 	current->mm->mmap->vm_page_prot = PAGE_SHARED;
-	current->mm->mmap->vm_start = KERNBASE;
+	current->mm->mmap->vm_start = PAGE_OFFSET;
 	current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
 
 	local_flush_cache_all();
@@ -197,7 +195,7 @@
 	int cpucount = 0;
 	int i = 0;
 
-	printk("Entering SparclinuxMultiPenguin(SMP) Mode...\n");
+	printk("Entering SMP Mode...\n");
 
 	penguin_ctable.which_io = 0;
 	penguin_ctable.phys_addr = (char *) srmmu_ctx_table_phys;
@@ -252,7 +250,7 @@
 				cpu_number_map[i] = i;
 				cpu_logical_map[i] = i;
 			} else {
-				printk("Penguin %d is stuck in the bottle.\n", i);
+				printk("Processor %d is stuck.\n", i);
 			}
 		}
 		if(!(cpu_callin_map[i])) {
@@ -262,7 +260,7 @@
 	}
 	local_flush_cache_all();
 	if(cpucount == 0) {
-		printk("Error: only one Penguin found.\n");
+		printk("Error: only one Processor found.\n");
 		cpu_present_map = (1 << smp_processor_id());
 	} else {
 		unsigned long bogosum = 0;
@@ -270,7 +268,7 @@
 			if(cpu_present_map & (1 << i))
 				bogosum += cpu_data[i].udelay_val;
 		}
-		printk("Total of %d Penguins activated (%lu.%02lu PenguinMIPS).\n",
+		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
 		       cpucount + 1,
 		       (bogosum + 2500)/500000,
 		       ((bogosum + 2500)/5000)%100);
@@ -299,10 +297,7 @@
  * A processor may get stuck with irq's off waiting to send a message and
  * thus not replying to the person spinning for a reply....
  *
- * In the end invalidate ought to be the NMI and a very very short
- * function (to avoid the old IDE disk problems), and other messages sent
- * with IRQ's enabled in a civilised fashion. That will also boost
- * performance.
+ * On the Sparc we use NMI's for all messages except reschedule.
  */
 
 static volatile int message_cpu = NO_PROC_ID;
@@ -404,7 +399,7 @@
 		return;
 	}
 	smp_cpu_in_msg[p]--;
-	smp_swap(&message_cpu, NO_PROC_ID);
+	message_cpu = NO_PROC_ID;
 }
 
 struct smp_funcall {
@@ -444,7 +439,7 @@
 	printk("xc%d<", me);
 #endif
 	if(smp_processors_ready) {
-		save_flags(flags); cli();
+		save_and_cli(flags);
 		if(me != active_kernel_processor)
 			goto cross_call_not_master;
 
@@ -497,7 +492,7 @@
 #endif
 		/* See wait case 3 in smp_message_pass()... */
 		smp_cpu_in_msg[me]--;
-		smp_swap(&message_cpu, NO_PROC_ID); /* store buffers... */
+		message_cpu = NO_PROC_ID;
 		restore_flags(flags);
 		return; /* made it... */
 
@@ -589,7 +584,7 @@
 #ifdef DEBUG_CAPTURE
 	printk("C<%d>", smp_processor_id());
 #endif
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	if(!capture_level) {
 		release = 0;
 		smp_message_pass(MSG_ALL_BUT_SELF, MSG_CAPTURE, 0, 1);
@@ -608,7 +603,7 @@
 #ifdef DEBUG_CAPTURE
 	printk("R<%d>", smp_processor_id());
 #endif
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	if(!(capture_level - 1)) {
 		release = 1;
 		for(i = 0; i < smp_num_cpus; i++)
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sparc-stub.c linux/arch/sparc/kernel/sparc-stub.c
--- v2.1.8/linux/arch/sparc/kernel/sparc-stub.c	Mon May  6 12:26:03 1996
+++ linux/arch/sparc/kernel/sparc-stub.c	Sat Nov  9 10:11:45 1996
@@ -1,4 +1,4 @@
-/* $Id: sparc-stub.c,v 1.16 1996/04/25 06:09:01 davem Exp $
+/* $Id: sparc-stub.c,v 1.19 1996/09/30 02:21:48 davem Exp $
  * sparc-stub.c:  KGDB support for the Linux kernel.
  *
  * Modifications to run under Linux
@@ -185,7 +185,7 @@
 {
 	int i, flags;
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	for(i=0; i < 256; i++)
 		copy_ttentry(&sparc_ttable[i], &kgdb_savettable[i]);
 	restore_flags(flags);
@@ -198,7 +198,7 @@
 	int flags;
 
 	/* We are dorking with a live trap table, all irqs off */
-	save_flags(flags); cli();
+	save_and_cli(flags);
 
 	/* Make new vector */
 	sparc_ttable[tnum].inst_one =
@@ -370,7 +370,7 @@
 	unsigned long flags;
 	unsigned char c;
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	flush_cache_all = flush_cache_all_nop;
 
 	/* Initialize our copy of the Linux Sparc trap table */
@@ -673,16 +673,16 @@
 	if (!initialized)
 		return;
 
-	/* Again, watch those c-prefixes for solaris/elf kernels */
-#ifndef __svr4__
-	asm("	.globl _breakinst
+	/* Again, watch those c-prefixes for ELF kernels */
+#if defined(__svr4__) || defined(__ELF__)
+	asm("	.globl breakinst
 
-	     _breakinst: ta 1
+	     breakinst: ta 1
             ");
 #else
-	asm("	.globl breakinst
+	asm("	.globl _breakinst
 
-	     breakinst: ta 1
+	     _breakinst: ta 1
             ");
 #endif
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.1.8/linux/arch/sparc/kernel/sparc_ksyms.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Sat Nov  9 10:11:46 1996
@@ -0,0 +1,187 @@
+/* $Id: sparc_ksyms.c,v 1.24 1996/10/27 08:36:08 davem Exp $
+ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/oplib.h>
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/auxio.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/idprom.h>
+#include <asm/svr4.h>
+#include <asm/head.h>
+#include <asm/smp.h>
+#include <asm/mostek.h>
+#include <asm/ptrace.h>
+#include <asm/user.h>
+#ifdef CONFIG_SBUS
+#include <asm/sbus.h>
+#endif
+
+struct poll {
+	int fd;
+	short events;
+	short revents;
+};
+
+extern int svr4_getcontext (svr4_ucontext_t *, struct pt_regs *);
+extern int svr4_setcontext (svr4_ucontext_t *, struct pt_regs *);
+extern int sunos_poll(struct poll * ufds, size_t nfds, int timeout);
+extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long,
+				unsigned long, unsigned long, unsigned long);
+void _sigpause_common (unsigned int set, struct pt_regs *);
+extern void __copy_1page(void *, const void *);
+extern void *__memcpy(void *, const void *, __kernel_size_t);
+extern void *__memset(void *, int, __kernel_size_t);
+extern void *bzero_1page(void *);
+extern void *__bzero(void *, size_t);
+extern void *__memscan_zero(void *, size_t);
+extern void *__memscan_generic(void *, int, size_t);
+extern int __memcmp(const void *, const void *, __kernel_size_t);
+extern int __strncmp(const char *, const char *, __kernel_size_t);
+
+extern int __copy_to_user(unsigned long to, unsigned long from, int size);
+extern int __copy_from_user(unsigned long to, unsigned long from, int size);
+extern int __clear_user(unsigned long addr, int size);
+extern int __strncpy_from_user(unsigned long dest, unsigned long src, int count);
+
+extern void bcopy (const char *, char *, int);
+extern int __ashrdi3(int, int);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+/* One thing to note is that the way the symbols of the mul/div
+ * support routines are named is a mess, they all start with
+ * a '.' which makes it a bitch to export, here is the trick:
+ */
+#define DD(sym) extern int __sparc_dot_ ## sym (int) __asm__("." ## #sym)
+#define XD(sym) { (void *) & __sparc_dot_ ## sym, "." ## #sym }
+
+DD(rem);
+DD(urem);
+DD(div);
+DD(udiv);
+DD(mul);
+DD(umul);
+
+static struct symbol_table arch_symbol_table = {
+#include <linux/symtab_begin.h>
+
+	/* used by various drivers */
+	X(sparc_cpu_model),
+#ifdef __SMP__
+	X(kernel_flag),
+	X(kernel_counter),
+	X(active_kernel_processor),
+	X(syscall_count),
+#endif
+	X(page_offset),
+
+	X(udelay),
+	X(mstk48t02_regs),
+#if CONFIG_SUN_AUXIO
+	X(auxio_register),
+#endif
+	X(request_fast_irq),
+	X(sparc_alloc_io),
+	X(sparc_free_io),
+	X(mmu_unlockarea),
+	X(mmu_lockarea),
+	X(SBus_chain),
+
+	/* Solaris/SunOS binary compatibility */
+	X(svr4_setcontext),
+	X(svr4_getcontext),
+	X(_sigpause_common),
+	X(sunos_mmap),
+	X(sunos_poll),
+
+	/* Should really be in linux/kernel/ksyms.c */
+	X(dump_thread),
+
+	/* prom symbols */
+	X(idprom),
+	X(prom_root_node),
+	X(prom_getchild),
+        X(prom_getsibling),
+	X(prom_searchsiblings),
+	X(prom_firstprop),
+        X(prom_nextprop),
+	X(prom_getproplen),
+	X(prom_getproperty),
+        X(prom_setprop),
+        X(prom_nodeops),
+	X(prom_getbootargs),
+	X(prom_apply_obio_ranges),
+	X(prom_getname),
+	X(prom_feval),
+	X(romvec),
+
+	/* sparc library symbols */
+	X(bcopy),
+	X(memmove),
+	X(memscan),
+	X(strlen),
+	X(strnlen),
+	X(strcpy),
+	X(strncpy),
+	X(strcat),
+	X(strncat),
+	X(strcmp),
+	X(strncmp),
+	X(strchr),
+	X(strrchr),
+	X(strpbrk),
+	X(strtok),
+	X(strstr),
+	X(strspn),
+
+	/* Special internal versions of library functions. */
+	X(__copy_1page),
+	X(__memcpy),
+	X(__memset),
+	X(bzero_1page),
+	X(__bzero),
+	X(__memscan_zero),
+	X(__memscan_generic),
+	X(__memcmp),
+	X(__strncmp),
+
+	/* Moving data to/from userspace. */
+	X(__copy_to_user),
+	X(__copy_from_user),
+	X(__clear_user),
+	X(__strncpy_from_user),
+
+	/* No version information on these, as gcc produces such symbols. */
+	XNOVERS(memcmp),
+	XNOVERS(memcpy),
+	XNOVERS(memset),
+	XNOVERS(__ashrdi3),
+
+	XD(rem),
+	XD(urem),
+	XD(mul),
+	XD(umul),
+	XD(div),
+	XD(udiv),
+#include <linux/symtab_end.h>
+};
+
+void arch_syms_export(void)
+{
+	register_symtab(&arch_symbol_table);
+#if CONFIG_AP1000
+	ap_register_ksyms();
+#endif
+}
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sun4c_irq.c linux/arch/sparc/kernel/sun4c_irq.c
--- v2.1.8/linux/arch/sparc/kernel/sun4c_irq.c	Tue Apr 23 12:31:45 1996
+++ linux/arch/sparc/kernel/sun4c_irq.c	Sat Nov  9 10:11:46 1996
@@ -46,7 +46,7 @@
 	unsigned long flags;
 	unsigned char current_mask, new_mask;
     
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	irq_nr &= NR_IRQS;
 	current_mask = *interrupt_enable;
 	switch(irq_nr) {
@@ -75,7 +75,7 @@
 	unsigned long flags;
 	unsigned char current_mask, new_mask;
     
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	irq_nr &= NR_IRQS;
 	current_mask = *interrupt_enable;
 	switch(irq_nr) {
@@ -110,7 +110,7 @@
 	clear_intr = sun4c_timers->timer_limit10;
 }
 
-static void sun4c_clear_profile_irq(void )
+static void sun4c_clear_profile_irq(void)
 {
 	/* Errm.. not sure how to do this.. */
 }
@@ -136,6 +136,8 @@
 	 * them until we have a real console driver so L1-A works.
 	 */
 	sun4c_timers->timer_limit10 = (((1000000/HZ) + 1) << 10);
+	master_l10_counter = &sun4c_timers->cur_count10;
+	master_l10_limit = &sun4c_timers->timer_limit10;
 
 	irq = request_irq(TIMER_IRQ,
 			  counter_fn,
@@ -149,9 +151,9 @@
 	claim_ticker14(NULL, PROFILE_IRQ, 0);
 }
 
-static void sun4c_nop(void)
-{
-}
+#ifdef __SMP__
+static void sun4c_nop(void) {}
+#endif
 
 void sun4c_init_IRQ(void)
 {
@@ -176,9 +178,9 @@
 	load_profile_irq = sun4c_load_profile_irq;
 	init_timers = sun4c_init_timers;
 #ifdef __SMP__
-	set_cpu_int = sun4c_nop;
-	clear_cpu_int = sun4c_nop;
-	set_irq_udt = sun4c_nop;
+	set_cpu_int = (void (*) (int, int))sun4c_nop;
+	clear_cpu_int = (void (*) (int, int))sun4c_nop;
+	set_irq_udt = (void (*) (int))sun4c_nop;
 #endif
 	*interrupt_enable = (SUN4C_INT_ENABLE);
 	sti();
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sun4m_irq.c linux/arch/sparc/kernel/sun4m_irq.c
--- v2.1.8/linux/arch/sparc/kernel/sun4m_irq.c	Mon May  6 12:26:03 1996
+++ linux/arch/sparc/kernel/sun4m_irq.c	Sat Nov  9 10:11:47 1996
@@ -62,13 +62,13 @@
 	SUN4M_INT_FLOPPY,				  /*  5 irq 11 */
 	(SUN4M_INT_SERIAL | SUN4M_INT_KBDMS),	  	  /*  6 irq 12 */
 	SUN4M_INT_MODULE_ERR,			  	  /*  7 irq 15 */
-	SUN4M_INT_SBUS(1),				  /*  8 irq 2 */
-	SUN4M_INT_SBUS(2),				  /*  9 irq 3 */
-	SUN4M_INT_SBUS(3),				  /* 10 irq 5 */
-	SUN4M_INT_SBUS(4),				  /* 11 irq 7 */
-	SUN4M_INT_SBUS(5),				  /* 12 irq 9 */
-	SUN4M_INT_SBUS(6),				  /* 13 irq 11 */
-	SUN4M_INT_SBUS(7)				  /* 14 irq 13 */
+	SUN4M_INT_SBUS(0),				  /*  8 irq 2 */
+	SUN4M_INT_SBUS(1),				  /*  9 irq 3 */
+	SUN4M_INT_SBUS(2),				  /* 10 irq 5 */
+	SUN4M_INT_SBUS(3),				  /* 11 irq 7 */
+	SUN4M_INT_SBUS(4),				  /* 12 irq 9 */
+	SUN4M_INT_SBUS(5),				  /* 13 irq 11 */
+	SUN4M_INT_SBUS(6)				  /* 14 irq 13 */
 };
 
 inline unsigned long sun4m_get_irqmask(unsigned int irq)
@@ -98,7 +98,7 @@
 	int cpu = smp_processor_id();
 
 	mask = sun4m_get_irqmask(irq_nr);
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	if (irq_nr > 15)
 		sun4m_interrupts->set = mask;
 	else
@@ -117,14 +117,14 @@
          */
         if (irq_nr != 0x0b) {
 		mask = sun4m_get_irqmask(irq_nr);
-		save_flags(flags); cli();
+		save_and_cli(flags);
 		if (irq_nr > 15)
 			sun4m_interrupts->clear = mask;
 		else
 			sun4m_interrupts->cpu_intregs[cpu].clear = mask;
 		restore_flags(flags);    
 	} else {
-		save_flags(flags); cli();
+		save_and_cli(flags);
 		sun4m_interrupts->clear = SUN4M_INT_FLOPPY;
 		restore_flags(flags);
 	}
@@ -176,6 +176,7 @@
 	sun4m_timers->cpu_timers[0].l14_timer_limit = limit;
 }
 
+#if HANDLE_LVL14_IRQ
 static void sun4m_lvl14_handler(int irq, void *dev_id, struct pt_regs * regs)
 {
 	volatile unsigned int clear;
@@ -192,6 +193,7 @@
 	 */
 	sun4m_timers->cpu_timers[0].l14_timer_limit = lvl14_resolution;
 }
+#endif /* HANDLE_LVL14_IRQ */
 
 static void sun4m_init_timers(void (*counter_fn)(int, void *, struct pt_regs *))
 {
@@ -236,6 +238,8 @@
 		       cnt_regs[4].which_io, 0x0);
     
 	sun4m_timers->l10_timer_limit =  (((1000000/HZ) + 1) << 10);
+	master_l10_counter = &sun4m_timers->l10_cur_count;
+	master_l10_limit = &sun4m_timers->l10_timer_limit;
 
 	irq = request_irq(TIMER_IRQ,
 			  counter_fn,
@@ -247,12 +251,12 @@
 	}
     
 	/* Can't cope with multiple CPUS yet so no level14 tick events */
-#if 0
+#if HANDLE_LVL14_IRQ
 	if (linux_num_cpus > 1)
 		claim_ticker14(NULL, PROFILE_IRQ, 0);
 	else
 		claim_ticker14(sun4m_lvl14_handler, PROFILE_IRQ, lvl14_resolution);
-#endif
+#endif /* HANDLE_LVL14_IRQ */
 	if(linux_num_cpus > 1) {
 		for(cpu = 0; cpu < 4; cpu++)
 			sun4m_timers->cpu_timers[cpu].l14_timer_limit = 0;
@@ -317,7 +321,8 @@
 		printk("Warning:"
 		       "sun4m multiple CPU interrupt code requires work\n");
 #endif
-		irq_rcvreg = &sun4m_interrupts->undirected_target;
+		irq_rcvreg = (unsigned long *)
+				&sun4m_interrupts->undirected_target;
 		sun4m_interrupts->undirected_target = 0;
 	}
 	enable_irq = sun4m_enable_irq;
@@ -327,9 +332,9 @@
 	load_profile_irq = sun4m_load_profile_irq;
 	init_timers = sun4m_init_timers;
 #ifdef __SMP__
-	set_cpu_int = sun4m_send_ipi;
-	clear_cpu_int = sun4m_clear_ipi;
-	set_irq_udt = sun4m_set_udt;
+	set_cpu_int = (void (*) (int, int))sun4m_send_ipi;
+	clear_cpu_int = (void (*) (int, int))sun4m_clear_ipi;
+	set_irq_udt = (void (*) (int))sun4m_set_udt;
 #endif
 	sti();
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sunos_ioctl.c linux/arch/sparc/kernel/sunos_ioctl.c
--- v2.1.8/linux/arch/sparc/kernel/sunos_ioctl.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/sunos_ioctl.c	Sat Nov  9 10:11:48 1996
@@ -1,11 +1,11 @@
-/* $Id: sunos_ioctl.c,v 1.20 1996/04/25 06:09:08 davem Exp $
+/* $Id: sunos_ioctl.c,v 1.26 1996/10/31 00:59:06 davem Exp $
  * sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility.
  * 
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 
 #include <linux/sched.h>
 #include <linux/errno.h>
@@ -32,24 +32,25 @@
 asmlinkage int sunos_ioctl (int fd, unsigned long cmd, unsigned long arg)
 {
 	struct file *filp;
-	int foo;
+	int ret;
 
 	if (fd >= NR_OPEN || !(filp = current->files->fd [fd]))
 		return -EBADF;
 
 	/* First handle an easy compat. case for tty ldisc. */
 	if(cmd == TIOCSETD) {
-		int *p, ntty = N_TTY, old_fs;
+		int *p, ntty = N_TTY;
+		int tmp, oldfs;
 
 		p = (int *) arg;
-		foo = verify_area(VERIFY_WRITE, p, sizeof(int));
-		if(foo) return foo;
-		if(*p == 2) {
-			old_fs = get_fs();
+		if(get_user(tmp, p))
+			return -EFAULT;
+		if(tmp == 2) {
+			oldfs = get_fs();
 			set_fs(KERNEL_DS);
-			foo = sys_ioctl(fd, cmd, (int) &ntty);
-			set_fs(old_fs);
-			return (foo == -EINVAL ? -EOPNOTSUPP : foo);
+			ret = sys_ioctl(fd, cmd, (int) &ntty);
+			set_fs(oldfs);
+			return (ret == -EINVAL ? -EOPNOTSUPP : ret);
 		}
 	}
 
@@ -65,46 +66,46 @@
 		return sys_ioctl(fd, SIOCDELRT, arg);
 	case _IOW('i', 12, struct ifreq):
 		return sys_ioctl(fd, SIOCSIFADDR, arg);
-	case _IORW('i', 13, struct ifreq):
+	case _IOWR('i', 13, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFADDR, arg);
 	case _IOW('i', 14, struct ifreq):
 		return sys_ioctl(fd, SIOCSIFDSTADDR, arg);
-	case _IORW('i', 15, struct ifreq):
+	case _IOWR('i', 15, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFDSTADDR, arg);
 	case _IOW('i', 16, struct ifreq):
 		return sys_ioctl(fd, SIOCSIFFLAGS, arg);
-	case _IORW('i', 17, struct ifreq):
+	case _IOWR('i', 17, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFFLAGS, arg);
 	case _IOW('i', 18, struct ifreq):
 		return sys_ioctl(fd, SIOCSIFMEM, arg);
-	case _IORW('i', 19, struct ifreq):
+	case _IOWR('i', 19, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFMEM, arg);
-	case _IORW('i', 20, struct ifconf):
+	case _IOWR('i', 20, struct ifconf):
 		return sys_ioctl(fd, SIOCGIFCONF, arg);
 	case _IOW('i', 21, struct ifreq): /* SIOCSIFMTU */
 		return sys_ioctl(fd, SIOCSIFMTU, arg);
-	case _IORW('i', 22, struct ifreq): /* SIOCGIFMTU */
+	case _IOWR('i', 22, struct ifreq): /* SIOCGIFMTU */
 		return sys_ioctl(fd, SIOCGIFMTU, arg);
 
-	case _IORW('i', 23, struct ifreq):
+	case _IOWR('i', 23, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFBRDADDR, arg);
 	case _IOW('i', 24, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFBRDADDR, arg);
-	case _IORW('i', 25, struct ifreq):
+	case _IOWR('i', 25, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFNETMASK, arg);
 	case _IOW('i', 26, struct ifreq):
 		return sys_ioctl(fd, SIOCSIFNETMASK, arg);
-	case _IORW('i', 27, struct ifreq):
+	case _IOWR('i', 27, struct ifreq):
 		return sys_ioctl(fd, SIOCGIFMETRIC, arg);
 	case _IOW('i', 28, struct ifreq):
 		return sys_ioctl(fd, SIOCSIFMETRIC, arg);
 
 	case _IOW('i', 30, struct arpreq):
 		return sys_ioctl(fd, SIOCSARP, arg);
-	case _IOW('i', 31, struct arpreq):
+	case _IOWR('i', 31, struct arpreq):
 		return sys_ioctl(fd, SIOCGARP, arg);
 	case _IOW('i', 32, struct arpreq):
-		return sys_ioctl(fd, SIOCGARP, arg);
+		return sys_ioctl(fd, SIOCDARP, arg);
 
 	case _IOW('i', 40, struct ifreq): /* SIOCUPPER */
 	case _IOW('i', 41, struct ifreq): /* SIOCLOWER */
@@ -140,41 +141,39 @@
 		return 0;
 	/* Non posix grp */
 	case _IOW('t', 118, int): {
-		int oldval, *ptr;
+		int oldval, newval, *ptr;
 
 		cmd = TIOCSPGRP;
 		ptr = (int *) arg;
-		oldval = verify_area(VERIFY_WRITE, ptr, sizeof(int));
-		if(oldval)
-			return oldval;
-		oldval = *ptr;
-		foo = sys_ioctl(fd, cmd, arg);
-		if(*ptr == -1) {
-			*ptr = oldval;
-			foo = -EIO;
+		if(get_user(oldval, ptr))
+			return -EFAULT;
+		ret = sys_ioctl(fd, cmd, arg);
+		__get_user(newval, ptr);
+		if(newval == -1) {
+			__put_user(oldval, ptr);
+			ret = -EIO;
 		}
-		if(foo == -ENOTTY)
-			foo = -EIO;
-		return foo;
+		if(ret == -ENOTTY)
+			ret = -EIO;
+		return ret;
 	}
 
 	case _IOR('t', 119, int): {
-		int oldval, *ptr;
+		int oldval, newval, *ptr;
 
 		cmd = TIOCGPGRP;
 		ptr = (int *) arg;
-		oldval = verify_area(VERIFY_WRITE, ptr, sizeof(int));
-		if(oldval)
-			return oldval;
-		oldval = *ptr;
-		foo = sys_ioctl(fd, cmd, arg);
-		if(*ptr == -1) {
-			*ptr = oldval;
-			foo = -EIO;
+		if(get_user(oldval, ptr))
+			return -EFAULT;
+		ret = sys_ioctl(fd, cmd, arg);
+		__get_user(newval, ptr);
+		if(newval == -1) {
+			__put_user(oldval, ptr);
+			ret = -EIO;
 		}
-		if(foo == -ENOTTY)
-			foo = -EIO;
-		return foo;
+		if(ret == -ENOTTY)
+			ret = -EIO;
+		return ret;
 	}
 	}
 
@@ -184,9 +183,9 @@
 	}
 #endif
 
-	foo = sys_ioctl(fd, cmd, arg);
+	ret = sys_ioctl(fd, cmd, arg);
 	/* so stupid... */
-	return (foo == -EINVAL ? -EOPNOTSUPP : foo);
+	return (ret == -EINVAL ? -EOPNOTSUPP : ret);
 }
 
 
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sys_solaris.c linux/arch/sparc/kernel/sys_solaris.c
--- v2.1.8/linux/arch/sparc/kernel/sys_solaris.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/kernel/sys_solaris.c	Sat Nov  9 10:11:48 1996
@@ -0,0 +1,29 @@
+/*
+ * linux/arch/sparc/sys_solaris.c
+ *
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/personality.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+
+asmlinkage int
+do_solaris_syscall (struct pt_regs *regs)
+{
+	current->personality = PER_SVR4;
+	current->exec_domain = lookup_exec_domain(PER_SVR4);
+
+	if (current->exec_domain && current->exec_domain->handler){
+		current->exec_domain->handler (regs);
+		current->exec_domain->use_count = 0;
+		return regs->u_regs [UREG_I0];
+	}
+	printk ("No solaris handler\n");
+	send_sig (SIGSEGV, current, 1);
+	return 0;
+}
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c
--- v2.1.8/linux/arch/sparc/kernel/sys_sparc.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/sys_sparc.c	Sat Nov  9 10:11:48 1996
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.11 1996/04/25 06:09:10 davem Exp $
+/* $Id: sys_sparc.c,v 1.25 1996/11/03 20:58:07 davem Exp $
  * linux/arch/sparc/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -7,7 +7,9 @@
  */
 
 #include <linux/errno.h>
+#include <linux/types.h>
 #include <linux/sched.h>
+#include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/sem.h>
 #include <linux/msg.h>
@@ -15,7 +17,7 @@
 #include <linux/stat.h>
 #include <linux/mman.h>
 
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 
 /* XXX Make this per-binary type, this way we can detect the type of
  * XXX a binary.  Every Sparc executable calls this very early on.
@@ -25,6 +27,17 @@
 	return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */
 }
 
+extern asmlinkage unsigned long sys_brk(unsigned long brk);
+
+asmlinkage unsigned long sparc_brk(unsigned long brk)
+{
+	if(sparc_cpu_model == sun4c) {
+		if(brk >= 0x20000000 && brk < 0xe0000000)
+			return current->mm->brk;
+	}
+	return sys_brk(brk);
+}
+
 /*
  * sys_pipe() is the normal C calling standard for creating
  * a pipe. It's not the way unix traditionally does this, though.
@@ -43,43 +56,29 @@
 	}
 }
 
-/* Note most sanity checking already done in sclow.S code. */
-asmlinkage int quick_sys_write(unsigned int fd, char *buf, unsigned int count)
-{
-	struct file *file = current->files->fd[fd];
-	struct inode *inode = file->f_inode;
-	int error;
-
-	error = verify_area(VERIFY_READ, buf, count);
-	if(error)
-		return error;
-	/*
-	 * If data has been written to the file, remove the setuid and
-	 * the setgid bits. We do it anyway otherwise there is an
-	 * extremely exploitable race - does your OS get it right |->
-	 *
-	 * Set ATTR_FORCE so it will always be changed.
-	 */
-	if (!suser() && (inode->i_mode & (S_ISUID | S_ISGID))) {
-		struct iattr newattrs;
-		newattrs.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
-		newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_FORCE;
-		notify_change(inode, &newattrs);
-	}
-
-	down(&inode->i_sem);
-	error = file->f_op->write(inode,file,buf,count);
-	up(&inode->i_sem);
-	return error;
-}
-
-/* XXX do we need this crap? XXX */
-
 /*
  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
  *
  * This is really horribly ugly.
  */
+
+struct ipc_kludge {
+    struct msgbuf *msgp;
+    long msgtyp;
+};
+
+#define SEMOP	 	1
+#define SEMGET 		2
+#define SEMCTL 		3
+#define MSGSND 		11
+#define MSGRCV 		12
+#define MSGGET 		13
+#define MSGCTL 		14
+#define SHMAT 		21
+#define SHMDT 		22
+#define SHMGET 		23
+#define SHMCTL 		24
+
 asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
 {
 	int version;
@@ -95,12 +94,10 @@
 			return sys_semget (first, second, third);
 		case SEMCTL: {
 			union semun fourth;
-			int err;
 			if (!ptr)
 				return -EINVAL;
-			if ((err = verify_area (VERIFY_READ, ptr, sizeof(long))))
-				return err;
-			fourth.__pad = (void *) get_fs_long(ptr);
+			if(get_user(fourth.__pad, (void **)ptr))
+				return -EFAULT;
 			return sys_semctl (first, second, third, fourth);
 			}
 		default:
@@ -115,13 +112,10 @@
 			switch (version) {
 			case 0: {
 				struct ipc_kludge tmp;
-				int err;
 				if (!ptr)
 					return -EINVAL;
-				if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
-					return err;
-				memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
-					       sizeof (tmp));
+				if(copy_from_user(&tmp,(struct ipc_kludge *) ptr, sizeof (tmp)))
+					return -EFAULT;
 				return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
 				}
 			case 1: default:
@@ -141,17 +135,15 @@
 			case 0: default: {
 				ulong raddr;
 				int err;
-				if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong))))
-					return err;
+
 				err = sys_shmat (first, (char *) ptr, second, &raddr);
 				if (err)
 					return err;
-				put_fs_long (raddr, (ulong *) third);
+				if(put_user (raddr, (ulong *) third))
+					return -EFAULT;
 				return 0;
 				}
 			case 1:	/* iBCS2 emulator entry point */
-				if (get_fs() != get_ds())
-					return -EINVAL;
 				return sys_shmat (first, (char *) ptr, second, (ulong *) third);
 			}
 		case SHMDT: 
@@ -166,22 +158,7 @@
 	return -EINVAL;
 }
 
-unsigned long get_sparc_unmapped_area(unsigned long len)
-{
-	unsigned long addr = 0xE8000000UL;
-	struct vm_area_struct * vmm;
-
-	if (len > TASK_SIZE)
-		return 0;
-	for (vmm = find_vma(current, addr); ; vmm = vmm->vm_next) {
-		/* At this point:  (!vmm || addr < vmm->vm_end). */
-		if (TASK_SIZE - len < addr)
-			return 0;
-		if (!vmm || addr + len <= vmm->vm_start)
-			return addr;
-		addr = vmm->vm_end;
-	}
-}
+extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len);
 
 /* Linux version of mmap */
 asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
@@ -197,7 +174,7 @@
 	    }
 	}
 	if(!(flags & MAP_FIXED) && !addr) {
-		addr = get_sparc_unmapped_area(len);
+		addr = get_unmapped_area(addr, len);
 		if(!addr){
 			return -ENOMEM;
 		}
@@ -205,3 +182,56 @@
 	retval = do_mmap(file, addr, len, prot, flags, off);
 	return retval;
 }
+
+extern int do_open_namei(const char * pathname, int flag, int mode,
+               struct inode ** res_inode, struct inode * base);
+
+#define BSD_EMUL "/usr/gnemul/sunos"
+#define SOL_EMUL "/usr/gnemul/solaris"
+
+int
+open_namei(const char * pathname, int flag, int mode,
+               struct inode ** res_inode, struct inode * base)
+{
+	if (!base && (current->personality & (PER_BSD|PER_SVR4)) && *pathname == '/'){
+		struct inode *emul_ino;
+		const char   *p = pathname;
+		char *emul_path = current->personality & PER_BSD ? BSD_EMUL : SOL_EMUL;
+		int v;
+
+		while (*p == '/')
+			p++;
+		
+		if (do_open_namei (emul_path, flag, mode, &emul_ino, NULL) >= 0 && emul_ino){
+			v = do_open_namei (p, flag, mode, res_inode, emul_ino);
+			if (v >= 0)
+				return v;
+		}
+	}
+	return do_open_namei (pathname, flag, mode, res_inode, base);
+}
+
+
+/* we come to here via sys_nis_syscall so it can setup the regs argument */
+asmlinkage unsigned long
+c_sys_nis_syscall (struct pt_regs *regs)
+{
+	printk ("Unimplemented SPARC system call %d\n",(int)regs->u_regs[1]);
+	show_regs (regs);
+	return -ENOSYS;
+}
+
+/* #define DEBUG_SPARC_BREAKPOINT */
+
+asmlinkage void
+sparc_breakpoint (struct pt_regs *regs)
+{
+#ifdef DEBUG_SPARC_BREAKPOINT
+        printk ("TRAP: Entering kernel PC=%x, nPC=%x\n", regs->pc, regs->npc);
+#endif
+	force_sig(SIGTRAP, current);
+#ifdef DEBUG_SPARC_BREAKPOINT
+	printk ("TRAP: Returning to space: PC=%x nPC=%x\n", regs->pc, regs->npc);
+#endif
+}
+
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.1.8/linux/arch/sparc/kernel/sys_sunos.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/sys_sunos.c	Sat Nov  9 10:11:49 1996
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.40 1996/04/25 09:11:36 davem Exp $
+/* $Id: sys_sunos.c,v 1.61 1996/11/03 20:58:11 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -37,6 +37,7 @@
 #include <linux/resource.h>
 #include <linux/ipc.h>
 #include <linux/shm.h>
+#include <linux/sem.h>
 #include <linux/signal.h>
 #include <linux/uio.h>
 #include <linux/utsname.h>
@@ -45,8 +46,9 @@
 #include <linux/stat.h>
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
+#include <linux/errno.h>
 
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 #ifndef KERNEL_DS
 #include <linux/segment.h>
 #endif
@@ -68,7 +70,7 @@
 #include <linux/time.h>
 #include <linux/personality.h>
 
-extern unsigned long get_sparc_unmapped_area(unsigned long len);
+extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len);
 
 /* We use the SunOS mmap() semantics. */
 asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len,
@@ -78,6 +80,7 @@
 	struct file * file = NULL;
 	unsigned long retval, ret_type;
 
+	current->personality |= PER_BSD;
 	if(flags & MAP_NORESERVE) {
 		printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n",
 		       current->comm);
@@ -87,7 +90,7 @@
 		if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 			return -EBADF;
 	if(!(flags & MAP_FIXED) && !addr) {
-		addr = get_sparc_unmapped_area(len);
+		addr = get_unmapped_area(addr, len);
 		if(!addr)
 			return -ENOMEM;
 	}
@@ -100,13 +103,15 @@
 		flags |= MAP_ANONYMOUS;
 		file = 0;
 	}
+	if(!(flags & MAP_FIXED))
+		addr = 0;
 	ret_type = flags & _MAP_NEW;
 	flags &= ~_MAP_NEW;
 	retval = do_mmap(file, addr, len, prot, flags, off);
 	if(ret_type)
 		return retval;
 	else
-		return ((retval < KERNBASE) ? 0 : retval);
+		return ((retval < PAGE_OFFSET) ? 0 : retval);
 }
 
 /* lmbench calls this, just say "yeah, ok" */
@@ -125,6 +130,11 @@
 	unsigned long rlim;
 	unsigned long newbrk, oldbrk;
 
+	if(sparc_cpu_model == sun4c) {
+		if(brk >= 0x20000000 && brk < 0xe0000000)
+			return current->mm->brk;
+	}
+
 	if (brk < current->mm->end_code)
 		return -ENOMEM;
 
@@ -155,7 +165,7 @@
 	/*
 	 * Check against existing mmap mappings.
 	 */
-	if (find_vma_intersection(current, oldbrk, newbrk+PAGE_SIZE))
+	if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE))
 		return -ENOMEM;
 
 	/*
@@ -168,7 +178,7 @@
 	freepages >>= 1;
 	freepages += nr_free_pages;
 	freepages += nr_swap_pages;
-	freepages -= MAP_NR(high_memory) >> 4;
+	freepages -= max_mapnr >> 4;
 	freepages -= (newbrk-oldbrk) >> PAGE_SHIFT;
 	if (freepages < 0)
 		return -ENOMEM;
@@ -275,13 +285,13 @@
 	unsigned long limit;
 	int num_pages, pnum;
 
-	if(addr & (PAGE_SIZE - 1))
+	if(addr & ~(PAGE_MASK))
 		return -EINVAL;
 
 	num_pages = (len / PAGE_SIZE);
 	if(verify_area(VERIFY_WRITE, array, num_pages))
-		return -EFAULT; /* bum array, you lose... */
-	if((addr >= KERNBASE) || ((addr + len) > KERNBASE))
+		return -EFAULT;
+	if((addr >= PAGE_OFFSET) || ((addr + len) > PAGE_OFFSET))
 		return -ENOMEM; /* I'm sure you're curious about kernel mappings.. */
 
 	/* Wheee, go through pte's */
@@ -297,7 +307,7 @@
 		if(pte_none(*ptep))
 			return -ENOMEM; /* As per SunOS manpage */
 		/* Page in core or Swapped page? */
-		array[pnum] = pte_present(*ptep) ? 1 : 0;
+		__put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
 	}
 	return 0; /* Success... I think... */
 }
@@ -319,7 +329,7 @@
 	unsigned long flags;
 	unsigned long old;
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	old = current->blocked;
 	current->blocked |= (blk_mask & _BLOCKABLE);
 	restore_flags(flags);
@@ -331,7 +341,7 @@
 	unsigned long flags;
 	unsigned long retval;
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	retval = current->blocked;
 	current->blocked = newmask & _BLOCKABLE;
 	restore_flags(flags);
@@ -377,7 +387,7 @@
 	put_user(ino, &dirent->d_ino);
 	put_user(namlen, &dirent->d_namlen);
 	put_user(reclen, &dirent->d_reclen);
-	memcpy_tofs(dirent->d_name, name, namlen);
+	copy_to_user(dirent->d_name, name, namlen);
 	put_user(0, dirent->d_name + namlen);
 	((char *) dirent) += reclen;
 	buf->curr = dirent;
@@ -396,8 +406,6 @@
 		return -EBADF;
 	if (!file->f_op || !file->f_op->readdir)
 		return -ENOTDIR;
-	if(verify_area(VERIFY_WRITE, dirent, cnt))
-		return -EFAULT;
 	if(cnt < (sizeof(struct sunos_dirent) + 255))
 		return -EINVAL;
 
@@ -446,7 +454,7 @@
 	put_user(ino, &dirent->d_ino);
 	put_user(namlen, &dirent->d_namlen);
 	put_user(reclen, &dirent->d_reclen);
-	memcpy_tofs(dirent->d_name, name, namlen);
+	copy_to_user(dirent->d_name, name, namlen);
 	put_user(0, dirent->d_name + namlen);
 	((char *) dirent) += reclen;
 	buf->curr = dirent;
@@ -465,9 +473,6 @@
 		return -EBADF;
 	if (!file->f_op || !file->f_op->readdir)
 		return -ENOTDIR;
-	if(verify_area(VERIFY_WRITE, dirent, cnt) ||
-	   verify_area(VERIFY_WRITE, basep, sizeof(unsigned int)))
-		return -EFAULT;
 	if(cnt < (sizeof(struct sunos_direntry) + 255))
 		return -EINVAL;
 
@@ -487,14 +492,15 @@
 
 asmlinkage int sunos_getdomainname(char *name, int len)
 {
-	int error;
+        int nlen = strlen(system_utsname.domainname);
+
+        if (nlen < len)
+                len = nlen;
 
 	if(len > __NEW_UTS_LEN)
 		return -EFAULT;
-	error = verify_area(VERIFY_WRITE, name, len);
-	if(error)
+	if(copy_to_user(name, system_utsname.domainname, len))
 		return -EFAULT;
-	memcpy_tofs(name, system_utsname.domainname, len);
 	return 0;
 }
 
@@ -509,23 +515,15 @@
 
 asmlinkage int sunos_uname(struct sunos_utsname *name)
 {
-	int error;
 	if(!name)
 		return -EFAULT;
-	error = verify_area(VERIFY_WRITE, name, sizeof *name);
-	if(error)
-		return error;
-	memcpy_tofs(&name->sname[0], &system_utsname.sysname[0],
-		    sizeof(name->sname) - 1);
-	memcpy_tofs(&name->nname[0], &system_utsname.nodename[0],
-		    sizeof(name->nname) - 1);
-	name->nname[8] = '\0';
-	memcpy_tofs(&name->rel[0], &system_utsname.release[0],
-		    sizeof(name->rel) - 1);
-	memcpy_tofs(&name->ver[0], &system_utsname.version[0],
-		    sizeof(name->ver) - 1);
-	memcpy_tofs(&name->mach[0], &system_utsname.machine[0],
-		    sizeof(name->mach) - 1);
+	if(copy_to_user(&name->sname[0], &system_utsname.sysname[0], sizeof(name->sname) - 1))
+		return -EFAULT;
+	copy_to_user(&name->nname[0], &system_utsname.nodename[0], sizeof(name->nname) - 1);
+	put_user('\0', &name->nname[8]);
+	copy_to_user(&name->rel[0], &system_utsname.release[0], sizeof(name->rel) - 1);
+	copy_to_user(&name->ver[0], &system_utsname.version[0], sizeof(name->ver) - 1);
+	copy_to_user(&name->mach[0], &system_utsname.machine[0], sizeof(name->mach) - 1);
 	return 0;
 }
 
@@ -533,8 +531,7 @@
 {
 	struct pt_regs *regs;
 
-	regs = (struct pt_regs *) (current->saved_kernel_stack +
-				   sizeof(struct reg_window));
+	regs = current->tss.kregs;
 	current->tss.sig_address = regs->pc;
 	current->tss.sig_desc = regs->u_regs[UREG_G1];
 	send_sig(SIGSYS, current, 1);
@@ -691,16 +688,13 @@
 
 asmlinkage int sunos_nfs_mount(char *dir_name, int linux_flags, void *data)
 {
-	int  ret = -ENODEV, error;
+	int  ret = -ENODEV;
 	int  server_fd;
 	char *the_name;
 	struct nfs_mount_data linux_nfs_mount;
 	struct sunos_nfs_mount_args *sunos_mount = data;
 	dev_t dev;
 
-	error = verify_area(VERIFY_READ, data, sizeof (struct sunos_nfs_mount_args));
-	if (error)
-		return error;
 	/* Ok, here comes the fun part: Linux's nfs mount needs a
 	 * socket connection to the server, but SunOS mount does not
 	 * require this, so we use the information on the destination
@@ -753,7 +747,6 @@
 sunos_mount(char *type, char *dir, int flags, void *data)
 {
 	int linux_flags = MS_MGC_MSK; /* new semantics */
-	int error;
 	char *dev_fname = 0;
 
 	/* We don't handle the integer fs type */
@@ -770,9 +763,6 @@
 		linux_flags |= MS_RDONLY;
 	if(flags & SMNT_NOSUID)
 		linux_flags |= MS_NOSUID;
-	error = verify_area(VERIFY_READ, type, 16);
-	if(error)
-		return error;
 	if(strcmp(type, "ext2") == 0) {
 		dev_fname = (char *) data;
 	} else if(strcmp(type, "iso9660") == 0) {
@@ -784,18 +774,14 @@
 	} else if(strcmp(type, "xiafs") == 0) {
 		dev_fname = (char *) data;
 	} else if(strcmp(type, "nfs") == 0) {
-		error = sunos_nfs_mount (dir, flags, data);
-		return error;
+		return sunos_nfs_mount (dir, flags, data);
         } else if(strcmp(type, "ufs") == 0) {
 		printk("Warning: UFS filesystem mounts unsupported.\n");
 		return -ENODEV;
 	} else if(strcmp(type, "proc")) {
 		return -ENODEV;
 	}
-	if(error)
-		return error;
-	error = sys_mount(dev_fname, dir, type, linux_flags, NULL);
-	return error;
+	return sys_mount(dev_fname, dir, type, linux_flags, NULL);
 }
 
 extern asmlinkage int sys_setsid(void);
@@ -834,7 +820,12 @@
 
 extern asmlinkage unsigned long sunos_gethostid(void)
 {
-	return (unsigned long) idprom->id_sernum;
+#if CONFIG_AP1000
+	return mpp_cid();
+#else
+	return ((unsigned long)idprom->id_machtype << 24) |
+	       (unsigned long)idprom->id_sernum;
+#endif
 }
 
 extern asmlinkage long sunos_sysconf (int name)
@@ -916,15 +907,12 @@
 
 int sunos_poll(struct poll * ufds, size_t nfds, int timeout)
 {
-        int i,j, count, fdcount, error, retflag;
+        int i,j, count, fdcount, retflag;
 	struct poll * fdpnt;
 	struct poll * fds, *fds1;
 	select_table wait_table, *wait;
 	struct select_table_entry *entry;
 
-	if ((error = verify_area(VERIFY_READ, ufds, nfds*sizeof(struct poll))))
-		return error;
-
 	if (nfds > NR_OPEN)
 		return -EINVAL;
 
@@ -932,7 +920,11 @@
 	|| !(fds = (struct poll *)kmalloc(nfds*sizeof(struct poll), GFP_KERNEL)))
 		return -ENOMEM;
 
-	memcpy_fromfs(fds, ufds, nfds*sizeof(struct poll));
+	if(copy_from_user(fds, ufds, nfds*sizeof(struct poll))) {
+		free_page((unsigned long)entry);
+		kfree(fds);
+		return -EFAULT;
+	}
 
 	if (timeout < 0)
 		current->timeout = 0x7fffffff;
@@ -963,6 +955,7 @@
 
 		if ((fdpnt->events & LINUX_POLLIN)
 		&& check(SEL_IN, wait, current->files->fd[i])) {
+			retflag = 0;
 			if (fdpnt->events & POLLIN)
 				retflag = POLLIN;
 			if (fdpnt->events & POLLRDNORM)
@@ -1005,7 +998,7 @@
 		if (fds->revents) {
 			fdcount++;
 		}
-		put_fs_word(fds->revents, &ufds->revents);
+		__put_user(fds->revents, &ufds->revents);
 	}
 	kfree(fds1);
 	current->timeout = 0;
@@ -1013,6 +1006,48 @@
 	return fdcount;
 }
 
+extern asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg);
+extern asmlinkage int sys_semget (key_t key, int nsems, int semflg);
+extern asmlinkage int sys_semop  (int semid, struct sembuf *tsops, unsigned nsops);
+
+asmlinkage int sunos_semsys(int op, unsigned long arg1, unsigned long arg2,
+			    unsigned long arg3, void *ptr)
+{
+	union semun arg4;
+
+	switch (op) {
+	case 0:
+		/* Most arguments match on a 1:1 basis but cmd doesn't */
+		switch(arg3) {
+		case 4:
+			arg3=GETPID; break;
+		case 5:
+			arg3=GETVAL; break;
+		case 6:
+			arg3=GETALL; break;
+		case 3:
+			arg3=GETNCNT; break;
+		case 7:
+			arg3=GETZCNT; break;
+		case 8:
+			arg3=SETVAL; break;
+		case 9:
+			arg3=SETALL; break;
+		}
+		/* sys_semctl(): */
+		arg4.__pad=ptr; /* value to modify semaphore to */
+		return sys_semctl((int)arg1, (int)arg2, (int)arg3, arg4 );
+	case 1:
+		/* sys_semget(): */
+		return sys_semget((key_t)arg1, (int)arg2, (int)arg3);
+	case 2:
+		/* sys_semop(): */
+		return sys_semop((int)arg1, (struct sembuf *)arg2, (unsigned)arg3);
+	default:
+		return -EINVAL;
+	}
+}
+    
 extern asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr);
 extern asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
 extern asmlinkage int sys_shmdt (char *shmaddr);
@@ -1033,7 +1068,8 @@
 		return (int) raddr;
 	case 1:
 		/* sys_shmctl(): modify shared memory area attr. */
-		return sys_shmctl((int)arg1,(int)arg2,(struct shmid_ds *)arg3);
+		rval = sys_shmctl((int)arg1,(int)arg2,(struct shmid_ds *)arg3);
+		return (rval);
 	case 2:
 		/* sys_shmdt(): detach a shared memory area */
 		return sys_shmdt((char *)arg1);
@@ -1043,4 +1079,100 @@
 	default:
 		return -EINVAL;
 	}
+}
+
+asmlinkage int sunos_open(const char *filename, int flags, int mode)
+{
+	current->personality |= PER_BSD;
+	return sys_open (filename, flags, mode);
+}
+
+
+#define SUNOS_EWOULDBLOCK 35
+
+/* see the sunos man page read(2v) for an explanation
+   of this garbage. We use O_NDELAY to mark
+   file descriptors that have been set non-blocking 
+   using 4.2BSD style calls. (tridge) */
+
+static inline int check_nonblock(int ret,int fd)
+{
+	if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY))
+		return -SUNOS_EWOULDBLOCK;
+	return ret;
+}
+
+extern asmlinkage int sys_read(unsigned int fd,char *buf,int count);
+extern asmlinkage int sys_write(unsigned int fd,char *buf,int count);
+extern asmlinkage int sys_recv(int fd, void * ubuf, int size, unsigned flags);
+extern asmlinkage int sys_send(int fd, void * buff, int len, unsigned flags);
+extern asmlinkage int sys_accept(int fd, struct sockaddr *sa, int *addrlen);
+extern asmlinkage int sys_readv(unsigned long fd, const struct iovec * vector, long count);
+extern asmlinkage int sys_writev(unsigned long fd, const struct iovec * vector, long count);
+
+
+asmlinkage int sunos_read(unsigned int fd,char *buf,int count)
+{
+	return check_nonblock(sys_read(fd,buf,count),fd);
+}
+
+asmlinkage int sunos_readv(unsigned long fd, const struct iovec * vector, long count)
+{
+	return check_nonblock(sys_readv(fd,vector,count),fd);
+}
+
+asmlinkage int sunos_write(unsigned int fd,char *buf,int count)
+{
+	return check_nonblock(sys_write(fd,buf,count),fd);
+}
+
+asmlinkage int sunos_writev(unsigned long fd, const struct iovec * vector, long count)
+{
+	return check_nonblock(sys_writev(fd,vector,count),fd);
+}
+
+asmlinkage int sunos_recv(int fd, void * ubuf, int size, unsigned flags)
+{
+	return check_nonblock(sys_recv(fd,ubuf,size,flags),fd);
+}
+
+asmlinkage int sunos_send(int fd, void * buff, int len, unsigned flags)
+{
+	return check_nonblock(sys_send(fd,buff,len,flags),fd);
+}
+
+asmlinkage int sunos_accept(int fd, struct sockaddr *sa, int *addrlen)
+{
+	return check_nonblock(sys_accept(fd,sa,addrlen),fd);	
+}
+
+#define SUNOS_SV_INTERRUPT 2
+
+extern asmlinkage int sys_sigaction(int, const struct sigaction *, struct sigaction *);
+
+asmlinkage int sunos_sigaction(int signum, const struct sigaction *action,
+	struct sigaction *oldaction)
+{
+	struct sigaction tmp_sa;
+	const  int sigaction_size = sizeof (struct sigaction) - sizeof (void *);
+	int err;
+	int old_fs;
+	
+	if(copy_from_user(&tmp_sa, action, sigaction_size))
+		return -EFAULT;
+	if (tmp_sa.sa_flags & SUNOS_SV_INTERRUPT)
+		tmp_sa.sa_flags &= ~SUNOS_SV_INTERRUPT;
+	else
+		tmp_sa.sa_flags |= SA_RESTART;
+	old_fs = get_fs ();
+	set_fs (get_ds ());
+	err = sys_sigaction (signum, &tmp_sa, oldaction);
+	if (err == 0 && oldaction){
+		if (oldaction->sa_flags & SA_RESTART)
+			oldaction->sa_flags &= ~SA_RESTART;
+		else
+			oldaction->sa_flags |= SUNOS_SV_INTERRUPT;
+	}
+	set_fs (old_fs);
+	return err;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S
--- v2.1.8/linux/arch/sparc/kernel/systbls.S	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/systbls.S	Sat Nov  9 10:11:50 1996
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.41 1996/04/25 09:11:39 davem Exp $
+/* $Id: systbls.S,v 1.51 1996/11/03 20:58:04 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -47,92 +47,94 @@
 /*5*/	.long C_LABEL(sys_open), C_LABEL(sys_close), C_LABEL(sys_wait4)
 	.long C_LABEL(sys_creat), C_LABEL(sys_link)
 /*10*/  .long C_LABEL(sys_unlink), C_LABEL(sunos_execv), C_LABEL(sys_chdir)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_mknod)
-/*15*/	.long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sys_brk)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_lseek)
-/*20*/	.long C_LABEL(sys_getpid), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_mknod)
+/*15*/	.long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sparc_brk)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_lseek)
+/*20*/	.long C_LABEL(sys_getpid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_setuid), C_LABEL(sys_getuid)
-/*25*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_alarm)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_pause)
+/*25*/	.long C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_pause)
 /*30*/	.long C_LABEL(sys_utime), C_LABEL(sys_stty), C_LABEL(sys_gtty)
 	.long C_LABEL(sys_access), C_LABEL(sys_nice), C_LABEL(sys_ftime)
 	.long C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_newlstat), C_LABEL(sys_dup)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_newlstat), C_LABEL(sys_dup)
 	.long C_LABEL(sys_pipe), C_LABEL(sys_times), C_LABEL(sys_profil)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_setgid), C_LABEL(sys_getgid)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_setgid), C_LABEL(sys_getgid)
 	.long C_LABEL(sys_signal), C_LABEL(sys_geteuid)
-/*50*/	.long C_LABEL(sys_getegid), C_LABEL(sys_acct), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ioctl), C_LABEL(sys_reboot)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_symlink), C_LABEL(sys_readlink)
+/*50*/	.long C_LABEL(sys_getegid), C_LABEL(sys_acct), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_ioctl), C_LABEL(sys_reboot)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_symlink), C_LABEL(sys_readlink)
 	.long C_LABEL(sys_execve), C_LABEL(sys_umask), C_LABEL(sys_chroot)
-	.long C_LABEL(sys_newfstat), C_LABEL(sys_ni_syscall), C_LABEL(sys_getpagesize)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_vfork), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_mmap), C_LABEL(sys_ni_syscall), C_LABEL(sys_munmap)
-	.long C_LABEL(sys_mprotect), C_LABEL(sys_ni_syscall), C_LABEL(sys_vhangup)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_getgroups)
-	.long C_LABEL(sys_setgroups), C_LABEL(sys_getpgrp), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_setitimer), C_LABEL(sys_ni_syscall), C_LABEL(sys_swapon)
-	.long C_LABEL(sys_getitimer), C_LABEL(sys_ni_syscall), C_LABEL(sys_sethostname)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_dup2), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_fcntl), C_LABEL(sys_select), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*100*/	.long C_LABEL(sys_getpriority), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_gettimeofday), C_LABEL(sys_getrusage)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_settimeofday), C_LABEL(sys_fchown)
-	.long C_LABEL(sys_fchmod), C_LABEL(sys_ni_syscall), C_LABEL(sys_setreuid)
+	.long C_LABEL(sys_newfstat), C_LABEL(sys_nis_syscall), C_LABEL(sys_getpagesize)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_vfork), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_mmap), C_LABEL(sys_nis_syscall), C_LABEL(sys_munmap)
+	.long C_LABEL(sys_mprotect), C_LABEL(sys_nis_syscall), C_LABEL(sys_vhangup)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getgroups)
+	.long C_LABEL(sys_setgroups), C_LABEL(sys_getpgrp), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_setitimer), C_LABEL(sys_nis_syscall), C_LABEL(sys_swapon)
+	.long C_LABEL(sys_getitimer), C_LABEL(sys_nis_syscall), C_LABEL(sys_sethostname)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_dup2), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_fcntl), C_LABEL(sys_select), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*100*/	.long C_LABEL(sys_getpriority), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_gettimeofday), C_LABEL(sys_getrusage)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_readv)
+	.long C_LABEL(sys_writev), C_LABEL(sys_settimeofday), C_LABEL(sys_fchown)
+	.long C_LABEL(sys_fchmod), C_LABEL(sys_nis_syscall), C_LABEL(sys_setreuid)
 	.long C_LABEL(sys_setregid), C_LABEL(sys_rename), C_LABEL(sys_truncate)
-	.long C_LABEL(sys_ftruncate), C_LABEL(sys_flock), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_mkdir), C_LABEL(sys_rmdir), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_getrlimit)
-	.long C_LABEL(sys_setrlimit), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*150*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_statfs), C_LABEL(sys_fstatfs)
-	.long C_LABEL(sys_umount), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_setdomainname)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_quotactl), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_mount), C_LABEL(sys_ustat), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_getdents), C_LABEL(sys_setsid)
-	.long C_LABEL(sys_fchdir), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_sigpending), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_setpgid), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_newuname), C_LABEL(sys_init_module)
+	.long C_LABEL(sys_ftruncate), C_LABEL(sys_flock), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_mkdir), C_LABEL(sys_rmdir), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getrlimit)
+	.long C_LABEL(sys_setrlimit), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*150*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_statfs), C_LABEL(sys_fstatfs)
+	.long C_LABEL(sys_umount), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_setdomainname)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_quotactl), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_mount), C_LABEL(sys_ustat), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_getdents), C_LABEL(sys_setsid)
+	.long C_LABEL(sys_fchdir), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_sigpending), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_setpgid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_newuname), C_LABEL(sys_init_module)
 	.long C_LABEL(sys_personality), C_LABEL(sys_prof), C_LABEL(sys_break)
 	.long C_LABEL(sys_lock), C_LABEL(sys_mpx), C_LABEL(sys_ulimit)
 	.long C_LABEL(sys_getppid), C_LABEL(sys_sigaction), C_LABEL(sys_sgetmask)
 /*200*/	.long C_LABEL(sys_ssetmask), C_LABEL(sys_sigsuspend), C_LABEL(sys_newlstat)
-	.long C_LABEL(sys_uselib), C_LABEL(old_readdir), C_LABEL(sys_ni_syscall)
+	.long C_LABEL(sys_uselib), C_LABEL(old_readdir), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_socketcall), C_LABEL(sys_syslog), C_LABEL(sys_olduname)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_idle), C_LABEL(sys_ni_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_idle), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_waitpid), C_LABEL(sys_swapoff), C_LABEL(sys_sysinfo)
 	.long C_LABEL(sys_ipc), C_LABEL(sys_sigreturn), C_LABEL(sys_clone)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_adjtimex), C_LABEL(sys_sigprocmask)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_adjtimex), C_LABEL(sys_sigprocmask)
 	.long C_LABEL(sys_create_module), C_LABEL(sys_delete_module)
 	.long C_LABEL(sys_get_kernel_syms), C_LABEL(sys_getpgid), C_LABEL(sys_bdflush)
-	.long C_LABEL(sys_sysfs), C_LABEL(sys_ni_syscall), C_LABEL(sys_setfsuid)
+	.long C_LABEL(sys_sysfs), C_LABEL(sys_nis_syscall), C_LABEL(sys_setfsuid)
 	.long C_LABEL(sys_setfsgid), C_LABEL(sys_llseek), C_LABEL(sys_time)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_stime), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_llseek)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_stime), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_llseek)
 	/* "We are the Knights of the Forest of Ni!!" */
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall)
-/*250*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+	.long C_LABEL(sys_mlock), C_LABEL(sys_munlock), C_LABEL(sys_mlockall)
+	.long C_LABEL(sys_munlockall), C_LABEL(sys_sched_setparam)
+	.long C_LABEL(sys_sched_getparam), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_sched_get_priority_max), C_LABEL(sys_sched_get_priority_min)
+	.long C_LABEL(sys_sched_rr_get_interval), C_LABEL(sys_nanosleep)
+/*250*/	.long C_LABEL(sys_mremap)
+	.long C_LABEL(sys_sysctl)
+	.long C_LABEL(sys_getsid), C_LABEL(sys_fdatasync), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 
 	/* Now the SunOS syscall table. */
 
@@ -140,7 +142,7 @@
 	.globl C_LABEL(sunos_sys_table)
 C_LABEL(sunos_sys_table):
 /*0*/	.long C_LABEL(sunos_indir), C_LABEL(sys_exit), C_LABEL(sys_fork)
-	.long C_LABEL(sys_read), C_LABEL(sys_write), C_LABEL(sys_open)
+	.long C_LABEL(sunos_read), C_LABEL(sunos_write), C_LABEL(sunos_open)
 	.long C_LABEL(sys_close), C_LABEL(sunos_wait4), C_LABEL(sys_creat)
 	.long C_LABEL(sys_link), C_LABEL(sys_unlink), C_LABEL(sunos_execv)
 	.long C_LABEL(sys_chdir), C_LABEL(sunos_nosys), C_LABEL(sys_mknod)
@@ -172,15 +174,15 @@
 	.long C_LABEL(sunos_getdtablesize), C_LABEL(sys_dup2), C_LABEL(sunos_nop)
 	.long C_LABEL(sys_fcntl), C_LABEL(sunos_select), C_LABEL(sunos_nop)
 	.long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_socket)
-	.long C_LABEL(sys_connect), C_LABEL(sys_accept)
-/*100*/	.long C_LABEL(sys_getpriority), C_LABEL(sys_send), C_LABEL(sys_recv)
+	.long C_LABEL(sys_connect), C_LABEL(sunos_accept)
+/*100*/	.long C_LABEL(sys_getpriority), C_LABEL(sunos_send), C_LABEL(sunos_recv)
 	.long C_LABEL(sunos_nosys), C_LABEL(sys_bind), C_LABEL(sys_setsockopt)
-	.long C_LABEL(sys_listen), C_LABEL(sunos_nosys), C_LABEL(sys_sigaction)
+	.long C_LABEL(sys_listen), C_LABEL(sunos_nosys), C_LABEL(sunos_sigaction)
 	.long C_LABEL(sunos_sigblock), C_LABEL(sunos_sigsetmask), C_LABEL(sys_sigpause)
 	.long C_LABEL(sys_sigstack), C_LABEL(sys_recvmsg), C_LABEL(sys_sendmsg)
 	.long C_LABEL(sunos_nosys), C_LABEL(sys_gettimeofday), C_LABEL(sys_getrusage)
-	.long C_LABEL(sys_getsockopt), C_LABEL(sunos_nosys), C_LABEL(sys_readv)
-	.long C_LABEL(sys_writev), C_LABEL(sys_settimeofday), C_LABEL(sys_fchown)
+	.long C_LABEL(sys_getsockopt), C_LABEL(sunos_nosys), C_LABEL(sunos_readv)
+	.long C_LABEL(sunos_writev), C_LABEL(sys_settimeofday), C_LABEL(sys_fchown)
 	.long C_LABEL(sys_fchmod), C_LABEL(sys_recvfrom), C_LABEL(sys_setreuid)
 	.long C_LABEL(sys_setregid), C_LABEL(sys_rename), C_LABEL(sys_truncate)
 	.long C_LABEL(sys_ftruncate), C_LABEL(sys_flock), C_LABEL(sunos_nosys)
@@ -196,7 +198,7 @@
 	.long C_LABEL(sys_umount), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
 	.long C_LABEL(sunos_getdomainname), C_LABEL(sys_setdomainname)
 	.long C_LABEL(sunos_nosys), C_LABEL(sys_quotactl), C_LABEL(sunos_nosys)
-	.long C_LABEL(sunos_mount), C_LABEL(sys_ustat), C_LABEL(sunos_nosys)
+	.long C_LABEL(sunos_mount), C_LABEL(sys_ustat), C_LABEL(sunos_semsys)
 	.long C_LABEL(sunos_nosys), C_LABEL(sunos_shmsys), C_LABEL(sunos_audit)
 	.long C_LABEL(sunos_nosys), C_LABEL(sunos_getdents), C_LABEL(sys_setsid)
 	.long C_LABEL(sys_fchdir), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
@@ -370,107 +372,107 @@
 	/* One thing left, Solaris syscall table, TODO */
 	.globl	C_LABEL(solaris_sys_table)
 C_LABEL(solaris_sys_table):
-/*0*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_exit), C_LABEL(sys_fork)
+/*0*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_exit), C_LABEL(sys_fork)
 	.long C_LABEL(sys_read), C_LABEL(sys_write)
 /*5*/	.long C_LABEL(solaris_open), C_LABEL(sys_close), C_LABEL(sys_wait4)
 	.long C_LABEL(sys_creat), C_LABEL(sys_link)
-/*10*/	.long C_LABEL(sys_unlink), C_LABEL(sys_ni_syscall), C_LABEL(sys_chdir)
+/*10*/	.long C_LABEL(sys_unlink), C_LABEL(sys_nis_syscall), C_LABEL(sys_chdir)
 	.long C_LABEL(sys_time), C_LABEL(sys_mknod)
 /*15*/	.long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sys_brk)
 	.long C_LABEL(sys_stat), C_LABEL(sys_lseek)
-/*20*/	.long C_LABEL(sunos_getpid), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_setuid), C_LABEL(sunos_getuid)
-/*25*/	.long C_LABEL(sys_stime), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_alarm), C_LABEL(sys_ni_syscall), C_LABEL(sys_pause)
-/*30*/	.long C_LABEL(sys_utime), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+/*20*/	.long C_LABEL(sunos_getpid), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_setuid), C_LABEL(sunos_getuid)
+/*25*/	.long C_LABEL(sys_stime), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_alarm), C_LABEL(sys_nis_syscall), C_LABEL(sys_pause)
+/*30*/	.long C_LABEL(sys_utime), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_access), C_LABEL(sys_nice)
-/*35*/	.long C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall)
-/*40*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*45*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*50*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*55*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*60*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*65*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*70*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*75*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*80*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*85*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*90*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*95*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*100*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*105*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*110*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*115*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*120*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*125*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*130*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*135*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*140*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*145*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*150*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*155*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*160*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*165*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*170*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*175*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*180*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*185*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*190*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*195*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*200*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*205*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*210*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*215*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*220*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*225*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*230*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*235*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*240*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*245*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*250*/	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-	.long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
-/*255*/	.long C_LABEL(sys_ni_syscall)
+/*35*/	.long C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall)
+/*40*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*45*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*50*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*55*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*60*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*65*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*70*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*75*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*80*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*85*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*90*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*95*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*100*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*105*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*110*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*115*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*120*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*125*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*130*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*135*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*140*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*145*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*150*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*155*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*160*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*165*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*170*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*175*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*180*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*185*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*190*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*195*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*200*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*205*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*210*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*215*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*220*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*225*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*230*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*235*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*240*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*245*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*250*/	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
+/*255*/	.long C_LABEL(sys_nis_syscall)
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/tadpole.c linux/arch/sparc/kernel/tadpole.c
--- v2.1.8/linux/arch/sparc/kernel/tadpole.c	Mon Apr 22 10:59:39 1996
+++ linux/arch/sparc/kernel/tadpole.c	Sat Nov  9 10:11:50 1996
@@ -61,7 +61,7 @@
 	if (!clk_ctrl)
 		return;
 	if (!(clk_state & CLOCK_INIT_DONE)) {
-		save_flags(flags); cli();
+		save_and_cli(flags);
 		clk_init();
 		clk_state |= CLOCK_INIT_DONE;       /* all done */
 		restore_flags(flags);
@@ -70,7 +70,7 @@
 	if (!(clk_ctrl[2] & 1))
 		return;               /* no speed up yet */
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 
 	/* if SCSI DMA in progress, don't slow clock */
 	mcsr = ldphys(MACIO_SCSI_CSR_ADDR);
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/tick14.c linux/arch/sparc/kernel/tick14.c
--- v2.1.8/linux/arch/sparc/kernel/tick14.c	Sun Apr 21 12:30:31 1996
+++ linux/arch/sparc/kernel/tick14.c	Sat Nov  9 10:11:50 1996
@@ -35,7 +35,7 @@
 
 	if (!linux_lvl14)
 		return;
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	linux_lvl14[0] =  lvl14_save[0];
 	linux_lvl14[1] =  lvl14_save[1];
 	linux_lvl14[2] =  lvl14_save[2];
@@ -49,7 +49,7 @@
     
 	if (!linux_lvl14)
 		return;
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	linux_lvl14[0] =  obp_lvl14[0];
 	linux_lvl14[1] =  obp_lvl14[1];
 	linux_lvl14[2] =  obp_lvl14[2];
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/time.c linux/arch/sparc/kernel/time.c
--- v2.1.8/linux/arch/sparc/kernel/time.c	Sun Apr 21 12:30:31 1996
+++ linux/arch/sparc/kernel/time.c	Sat Nov  9 10:11:52 1996
@@ -1,7 +1,8 @@
-/* $Id: time.c,v 1.12 1996/04/04 16:30:30 tridge Exp $
+/* $Id: time.c,v 1.19 1996/10/31 06:28:26 davem Exp $
  * linux/arch/sparc/kernel/time.c
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
  *
  * This file handles the Sparc specific time handling details.
  */
@@ -12,6 +13,7 @@
 #include <linux/param.h>
 #include <linux/string.h>
 #include <linux/mm.h>
+#include <linux/interrupt.h>
 #include <linux/timex.h>
 
 #include <asm/oplib.h>
@@ -22,11 +24,19 @@
 #include <asm/irq.h>
 #include <asm/io.h>
 
+#ifdef CONFIG_AP1000
+#include <asm/ap1000/apservice.h>
+#endif
+
+
 enum sparc_clock_type sp_clock_typ;
 struct mostek48t02 *mstk48t02_regs = 0;
 struct mostek48t08 *mstk48t08_regs = 0;
 static int set_rtc_mmss(unsigned long);
 
+__volatile__ unsigned int *master_l10_counter;
+__volatile__ unsigned int *master_l10_limit;
+
 /*
  * timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
@@ -40,7 +50,7 @@
 
 	do_timer(regs);
 
-	/* XXX I don't know if this is right for the Sparc yet. XXX */
+	/* Determine when to update the Mostek clock. */
 	if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
 	    xtime.tv_usec > 500000 - (tick >> 1) &&
 	    xtime.tv_usec < 500000 + (tick >> 1))
@@ -81,81 +91,150 @@
 	  )*60 + sec; /* finally seconds */
 }
 
-/* Clock probing, we probe the timers here also. */
-volatile unsigned int foo_limit;
+/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
+static void kick_start_clock(void)
+{
+	register struct mostek48t02 *regs = mstk48t02_regs;
+	unsigned char sec;
+	int i, count;
+
+	prom_printf("CLOCK: Clock was stopped. Kick start ");
+
+	/* Turn on the kick start bit to start the oscillator. */
+	regs->creg |= MSTK_CREG_WRITE;
+	regs->sec &= ~MSTK_STOP;
+	regs->hour |= MSTK_KICK_START;
+	regs->creg &= ~MSTK_CREG_WRITE;
+
+	/* Delay to allow the clock oscillator to start. */
+	sec = MSTK_REG_SEC(regs);
+	for (i = 0; i < 3; i++) {
+		while (sec == MSTK_REG_SEC(regs))
+			for (count = 0; count < 100000; count++)
+				/* nothing */ ;
+		prom_printf(".");
+		sec = regs->sec;
+	}
+	prom_printf("\n");
+
+	/* Turn off kick start and set a "valid" time and date. */
+	regs->creg |= MSTK_CREG_WRITE;
+	regs->hour &= ~MSTK_KICK_START;
+	MSTK_SET_REG_SEC(regs,0);
+	MSTK_SET_REG_MIN(regs,0);
+	MSTK_SET_REG_HOUR(regs,0);
+	MSTK_SET_REG_DOW(regs,5);
+	MSTK_SET_REG_DOM(regs,1);
+	MSTK_SET_REG_MONTH(regs,8);
+	MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO);
+	regs->creg &= ~MSTK_CREG_WRITE;
+
+	/* Ensure the kick start bit is off. If it isn't, turn it off. */
+	while (regs->hour & MSTK_KICK_START) {
+		prom_printf("CLOCK: Kick start still on!\n");
+		regs->creg |= MSTK_CREG_WRITE;
+		regs->hour &= ~MSTK_KICK_START;
+		regs->creg &= ~MSTK_CREG_WRITE;
+	}
+
+	prom_printf("CLOCK: Kick start procedure successful.\n");
+}
+
+/* Return nonzero if the clock chip battery is low. */
+static int has_low_battery(void)
+{
+	register struct mostek48t02 *regs = mstk48t02_regs;
+	unsigned char data1, data2;
+
+	data1 = regs->eeprom[0];	/* Read some data. */
+	regs->eeprom[0] = ~data1;	/* Write back the complement. */
+	data2 = regs->eeprom[0];	/* Read back the complement. */
+	regs->eeprom[0] = data1;	/* Restore the original value. */
 
+	return (data1 == data2);	/* Was the write blocked? */
+}
+
+/* Probe for the real time clock chip. */
 static void clock_probe(void)
 {
-	char node_str[128];
-	register int node, type;
 	struct linux_prom_registers clk_reg[2];
+	char model[128];
+	register int node, cpuunit, bootbus;
 
-	/* This will basically traverse the node-tree of the prom to see
-	 * which timer chip is on this machine.
-	 */
-	node = 0;
-	if(sparc_cpu_model == sun4) {
-		printk("clock_probe: No SUN4 Clock/Timer support yet...\n");
-		return;
-	}
-	if(sparc_cpu_model == sun4c)
-		node = prom_getchild(prom_root_node);
-	else
-		if(sparc_cpu_model == sun4m)
-			node=prom_getchild(prom_searchsiblings(prom_getchild(prom_root_node), "obio"));
-	type = 0;
-	sp_clock_typ = MSTK_INVALID;
-	for(;;) {
-		prom_getstring(node, "model", node_str, sizeof(node_str));
-		if(strcmp(node_str, "mk48t02") == 0) {
-			sp_clock_typ = MSTK48T02;
-			if(prom_getproperty(node, "reg", (char *) clk_reg, sizeof(clk_reg)) == -1) {
-				printk("clock_probe: FAILED!\n");
-				halt();
-			}
-			prom_apply_obio_ranges(clk_reg, 1);
-			/* Map the clock register io area read-only */
-			mstk48t02_regs = (struct mostek48t02 *) 
-				sparc_alloc_io((void *) clk_reg[0].phys_addr,
-					       (void *) 0, sizeof(*mstk48t02_regs),
-					       "clock", clk_reg[0].which_io, 0x0);
-			mstk48t08_regs = 0;  /* To catch weirdness */
-			break;
-		}
+	/* Determine the correct starting PROM node for the probe. */
+	node = prom_getchild(prom_root_node);
+	switch (sparc_cpu_model) {
+	case sun4c:
+		break;
+	case sun4m:
+		node = prom_getchild(prom_searchsiblings(node, "obio"));
+		break;
+	case sun4d:
+		node = prom_getchild(bootbus = prom_searchsiblings(prom_getchild(cpuunit = prom_searchsiblings(node, "cpu-unit")), "bootbus"));
+		break;
+	default:
+		prom_printf("CLOCK: Unsupported architecture!\n");
+		prom_halt();
+	}
 
-		if(strcmp(node_str, "mk48t08") == 0) {
-			sp_clock_typ = MSTK48T08;
-			if(prom_getproperty(node, "reg", (char *) clk_reg,
-					    sizeof(clk_reg)) == -1) {
-				printk("clock_probe: FAILED!\n");
-				halt();
-			}
-			prom_apply_obio_ranges(clk_reg, 1);
-			/* Map the clock register io area read-only */
-			mstk48t08_regs = (struct mostek48t08 *)
-				sparc_alloc_io((void *) clk_reg[0].phys_addr,
-					       (void *) 0, sizeof(*mstk48t08_regs),
-					       "clock", clk_reg[0].which_io, 0x0);
+	/* Find the PROM node describing the real time clock. */
+	sp_clock_typ = MSTK_INVALID;
+	node = prom_searchsiblings(node,"eeprom");
+	if (!node) {
+		prom_printf("CLOCK: No clock found!\n");
+		prom_halt();
+	}
 
-			mstk48t02_regs = &mstk48t08_regs->regs;
-			break;
+	/* Get the model name and setup everything up. */
+	model[0] = '\0';
+	prom_getstring(node, "model", model, sizeof(model));
+	if (strcmp(model, "mk48t02") == 0) {
+		sp_clock_typ = MSTK48T02;
+		if (prom_getproperty(node, "reg", (char *) clk_reg, sizeof(clk_reg)) == -1) {
+			prom_printf("clock_probe: FAILED!\n");
+			prom_halt();
 		}
-
-		node = prom_getsibling(node);
-		if(node == 0) {
-			printk("Aieee, could not find timer chip type\n");
-			return;
+		if (sparc_cpu_model == sun4d)
+			prom_apply_generic_ranges (bootbus, cpuunit, clk_reg, 1);
+		else
+			prom_apply_obio_ranges(clk_reg, 1);
+		/* Map the clock register io area read-only */
+		mstk48t02_regs = (struct mostek48t02 *) 
+			sparc_alloc_io((void *) clk_reg[0].phys_addr,
+				       (void *) 0, sizeof(*mstk48t02_regs),
+				       "clock", clk_reg[0].which_io, 0x0);
+		mstk48t08_regs = 0;  /* To catch weirdness */
+	} else if (strcmp(model, "mk48t08") == 0) {
+		sp_clock_typ = MSTK48T08;
+		if(prom_getproperty(node, "reg", (char *) clk_reg,
+				    sizeof(clk_reg)) == -1) {
+			prom_printf("clock_probe: FAILED!\n");
+			prom_halt();
 		}
+		if (sparc_cpu_model == sun4d)
+			prom_apply_generic_ranges (bootbus, cpuunit, clk_reg, 1);
+		else
+			prom_apply_obio_ranges(clk_reg, 1);
+		/* Map the clock register io area read-only */
+		mstk48t08_regs = (struct mostek48t08 *)
+			sparc_alloc_io((void *) clk_reg[0].phys_addr,
+				       (void *) 0, sizeof(*mstk48t08_regs),
+				       "clock", clk_reg[0].which_io, 0x0);
+
+		mstk48t02_regs = &mstk48t08_regs->regs;
+	} else {
+		prom_printf("CLOCK: Unknown model name '%s'\n",model);
+		prom_halt();
 	}
-}
-
-#ifndef BCD_TO_BIN
-#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
-#endif
 
-#ifndef BIN_TO_BCD
-#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
-#endif
+	/* Report a low battery voltage condition. */
+	if (has_low_battery())
+		printk(KERN_CRIT "NVRAM: Low battery voltage!\n");
+
+	/* Kick start the clock if it is completely stopped. */
+	if (mstk48t02_regs->sec & MSTK_STOP)
+		kick_start_clock();
+}
 
 void time_init(void)
 {
@@ -164,6 +243,11 @@
 
 #if CONFIG_AP1000
 	init_timers(timer_interrupt);
+	{
+	  extern struct cap_init cap_init;
+	  xtime.tv_sec = cap_init.init_time;
+	  xtime.tv_usec = 0;
+	}
         return;
 #endif
 
@@ -176,35 +260,62 @@
 		prom_halt();
 	}		
 	mregs->creg |= MSTK_CREG_READ;
-	sec = BCD_TO_BIN(mregs->sec);
-	min = BCD_TO_BIN(mregs->min);
-	hour = BCD_TO_BIN(mregs->hour);
-	day = BCD_TO_BIN(mregs->dom);
-	mon = BCD_TO_BIN(mregs->mnth);
-	year = (BCD_TO_BIN(mregs->yr) + MSTK_YR_ZERO);
+	sec = MSTK_REG_SEC(mregs);
+	min = MSTK_REG_MIN(mregs);
+	hour = MSTK_REG_HOUR(mregs);
+	day = MSTK_REG_DOM(mregs);
+	mon = MSTK_REG_MONTH(mregs);
+	year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) );
 	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
 	xtime.tv_usec = 0;
 	mregs->creg &= ~MSTK_CREG_READ;
 	return;
 }
 
-/* Nothing fancy on the Sparc yet. */
+#if !CONFIG_AP1000
+static __inline__ unsigned long do_gettimeoffset(void)
+{
+	unsigned long offset = 0;
+	unsigned int count;
+
+	count = (*master_l10_counter >> 10) & 0x1fffff;
+
+	if(test_bit(TIMER_BH, &bh_active))
+		offset = 1000000;
+
+	return offset + count;
+}
+#endif
+
 void do_gettimeofday(struct timeval *tv)
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 #if CONFIG_AP1000
 	ap_gettimeofday(&xtime);
 #endif
 	*tv = xtime;
+#if !CONFIG_AP1000
+	tv->tv_usec += do_gettimeoffset();
+	if(tv->tv_usec >= 1000000) {
+		tv->tv_usec -= 1000000;
+		tv->tv_sec++;
+	}
+#endif
 	restore_flags(flags);
 }
 
 void do_settimeofday(struct timeval *tv)
 {
 	cli();
+#if !CONFIG_AP1000
+	tv->tv_usec -= do_gettimeoffset();
+	if(tv->tv_usec < 0) {
+		tv->tv_usec += 1000000;
+		tv->tv_sec--;
+	}
+#endif
 	xtime = *tv;
 	time_state = TIME_BAD;
 	time_maxerror = 0x70000000;
@@ -214,30 +325,37 @@
 
 static int set_rtc_mmss(unsigned long nowtime)
 {
-	int retval = 0;
 	int real_seconds, real_minutes, mostek_minutes;
-	struct mostek48t02 *mregs = mstk48t02_regs;
+	struct mostek48t02 *regs = mstk48t02_regs;
 
-	if(!mregs)
-		retval = -1;
-	else {
-		mregs->creg |= MSTK_CREG_READ;
-		mostek_minutes = BCD_TO_BIN(mregs->min);
-		mregs->creg &= ~MSTK_CREG_READ;
-
-		real_seconds = nowtime % 60;
-		real_minutes = nowtime / 60;
-		if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1)
-			real_minutes += 30;
-		real_minutes %= 60;
-		if (abs(real_minutes - mostek_minutes) < 30) {
-			mregs->creg |= MSTK_CREG_WRITE;
-			mregs->sec = real_seconds;
-			mregs->min = real_minutes;
-			mregs->creg &= ~MSTK_CREG_WRITE;
-		} else
-			retval = -1;
-	}
+	/* Not having a register set can lead to trouble. */
+	if (!regs) 
+		return -1;
+
+	/* Read the current RTC minutes. */
+	regs->creg |= MSTK_CREG_READ;
+	mostek_minutes = MSTK_REG_MIN(regs);
+	regs->creg &= ~MSTK_CREG_READ;
+
+	/*
+	 * since we're only adjusting minutes and seconds,
+	 * don't interfere with hour overflow. This avoids
+	 * messing with unknown time zones but requires your
+	 * RTC not to be off by more than 15 minutes
+	 */
+	real_seconds = nowtime % 60;
+	real_minutes = nowtime / 60;
+	if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1)
+		real_minutes += 30;	/* correct for half hour time zone */
+	real_minutes %= 60;
+
+	if (abs(real_minutes - mostek_minutes) < 30) {
+		regs->creg |= MSTK_CREG_WRITE;
+		MSTK_SET_REG_SEC(regs,real_seconds);
+		MSTK_SET_REG_MIN(regs,real_minutes);
+		regs->creg &= ~MSTK_CREG_WRITE;
+	} else
+		return -1;
 
-	return retval;
+	return 0;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/trampoline.S linux/arch/sparc/kernel/trampoline.S
--- v2.1.8/linux/arch/sparc/kernel/trampoline.S	Sun Apr 21 12:30:31 1996
+++ linux/arch/sparc/kernel/trampoline.S	Sat Nov  9 10:11:52 1996
@@ -1,4 +1,4 @@
-/* $Id: trampoline.S,v 1.3 1996/04/03 02:15:05 davem Exp $
+/* $Id: trampoline.S,v 1.5 1996/09/22 06:43:10 davem Exp $
  * mp.S:  Multiprocessor low-level routines on the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -24,26 +24,26 @@
 	.globl C_LABEL(sparc_cpu_startup)
 C_LABEL(sparc_cpu_startup):
 cpu1_startup:
-	sethi	%hi(C_LABEL(trapbase_cpu1)), %g7
-	or	%g7, %lo(C_LABEL(trapbase_cpu1)), %g7
-	sethi	%hi(C_LABEL(cpu1_stack)), %g6
-	or	%g6, %lo(C_LABEL(cpu1_stack)), %g6
+	sethi	%hi(C_LABEL(trapbase_cpu1)), %g3
+	or	%g3, %lo(C_LABEL(trapbase_cpu1)), %g3
+	sethi	%hi(C_LABEL(cpu1_stack)), %g2
+	or	%g2, %lo(C_LABEL(cpu1_stack)), %g2
 	b	1f
 	 nop	
 
 cpu2_startup:
-	sethi	%hi(C_LABEL(trapbase_cpu2)), %g7
-	or	%g7, %lo(C_LABEL(trapbase_cpu2)), %g7
-	sethi	%hi(C_LABEL(cpu2_stack)), %g6
-	or	%g6, %lo(C_LABEL(cpu2_stack)), %g6
+	sethi	%hi(C_LABEL(trapbase_cpu2)), %g3
+	or	%g3, %lo(C_LABEL(trapbase_cpu2)), %g3
+	sethi	%hi(C_LABEL(cpu2_stack)), %g2
+	or	%g2, %lo(C_LABEL(cpu2_stack)), %g2
 	b	1f
 	 nop	
 
 cpu3_startup:
-	sethi	%hi(C_LABEL(trapbase_cpu3)), %g7
-	or	%g7, %lo(C_LABEL(trapbase_cpu3)), %g7
-	sethi	%hi(C_LABEL(cpu3_stack)), %g6
-	or	%g6, %lo(C_LABEL(cpu3_stack)), %g6
+	sethi	%hi(C_LABEL(trapbase_cpu3)), %g3
+	or	%g3, %lo(C_LABEL(trapbase_cpu3)), %g3
+	sethi	%hi(C_LABEL(cpu3_stack)), %g2
+	or	%g2, %lo(C_LABEL(cpu3_stack)), %g2
 	b	1f
 	 nop	
 
@@ -59,14 +59,17 @@
 	WRITE_PAUSE
 
 	/* This identifies "this cpu". */
-	wr	%g7, 0x0, %tbr
+	wr	%g3, 0x0, %tbr
 	WRITE_PAUSE
 
 	/* Give ourselves a stack. */
 	set	0x2000, %g5
-	add	%g6, %g5, %g6		! end of stack
-	sub	%g6, REGWIN_SZ, %sp
+	add	%g2, %g5, %g2		! end of stack
+	sub	%g2, REGWIN_SZ, %sp
 	mov	0, %fp
+
+	/* Set up curptr. */
+	set	C_LABEL(init_task), %g6
 
 	/* Turn on traps (PSR_ET). */
 	rd	%psr, %g1
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/traps.c linux/arch/sparc/kernel/traps.c
--- v2.1.8/linux/arch/sparc/kernel/traps.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/traps.c	Sat Nov  9 10:11:52 1996
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.43 1996/04/24 09:09:42 davem Exp $
+/* $Id: traps.c,v 1.47 1996/10/27 08:36:17 davem Exp $
  * arch/sparc/kernel/traps.c
  *
  * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -62,11 +62,20 @@
 	prom_halt();
 }
 
-void die_if_kernel(char *str, struct pt_regs *regs)
+void instruction_dump (unsigned long *pc)
 {
-	unsigned long i;
-	unsigned long *pc;
+	int i;
+	
+	if((((unsigned long) pc) & 3))
+                return;
+
+	for(i = -3; i < 6; i++)
+		printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
+	printk("\n");
+}
 
+void die_if_kernel(char *str, struct pt_regs *regs)
+{
 	/* Amuse the user. */
 	printk(
 "              \\|/ ____ \\|/\n"
@@ -80,10 +89,7 @@
 	ap_panic();
 #endif
 	printk("Instruction DUMP:");
-	pc = (unsigned long *) regs->pc;
-	for(i = -3; i < 6; i++)
-		printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
-	printk("\n");
+	instruction_dump ((unsigned long *) regs->pc);
 	if(regs->psr & PSR_PS)
 		do_exit(SIGKILL);
 	do_exit(SIGSEGV);
@@ -96,10 +102,15 @@
 		printk("Unimplemented Sparc TRAP, type = %02lx\n", type);
 		die_if_kernel("Whee... Hello Mr. Penguin", current->tss.kregs);
 	}	
+
 	if(type == SP_TRAP_SBPT) {
 		send_sig(SIGTRAP, current, 1);
 		return;
 	}
+
+	if(psr & PSR_PS)
+		die_if_kernel("Kernel bad trap", current->tss.kregs);
+
 	current->tss.sig_desc = SUBSIG_BADTRAP(type - 0x80);
 	current->tss.sig_address = pc;
 	send_sig(SIGILL, current, 1);
@@ -142,6 +153,11 @@
 	}
 	current->tss.sig_address = pc;
 	current->tss.sig_desc = SUBSIG_PRIVINST;
+#if 0
+	show_regs (regs);
+	instruction_dump ((unsigned long *) regs->pc);
+	printk ("do_MNA!\n");
+#endif
 	send_sig(SIGBUS, current, 1);
 }
 
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/unaligned.c linux/arch/sparc/kernel/unaligned.c
--- v2.1.8/linux/arch/sparc/kernel/unaligned.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/kernel/unaligned.c	Sun Nov 10 23:25:55 1996
@@ -0,0 +1,395 @@
+/* $Id: unaligned.c,v 1.10 1996/11/10 21:25:47 davem Exp $
+ * unaligned.c: Unaligned load/store trap handling with special
+ *              cases for the kernel to do them more quickly.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* #define DEBUG_MNA */
+
+extern void die_if_kernel(char *, struct pt_regs *);
+
+enum direction {
+	load,    /* ld, ldd, ldh, ldsh */
+	store,   /* st, std, sth, stsh */
+	both,    /* Swap, ldstub, etc. */
+	fpload,
+	fpstore,
+	invalid,
+};
+
+#ifdef DEBUG_MNA
+static char *dirstrings[] = {
+  "load", "store", "both", "fpload", "fpstore", "invalid"
+};
+#endif
+
+static inline enum direction decode_direction(unsigned int insn)
+{
+	unsigned long tmp = (insn >> 21) & 1;
+
+	if(!tmp)
+		return load;
+	else {
+		if(((insn>>19)&0x3f) == 15)
+			return both;
+		else
+			return store;
+	}
+}
+
+/* 8 = double-word, 4 = word, 2 = half-word */
+static inline int decode_access_size(unsigned int insn)
+{
+	insn = (insn >> 19) & 3;
+
+	if(!insn)
+		return 4;
+	else if(insn == 3)
+		return 8;
+	else if(insn == 2)
+		return 2;
+	else {
+		printk("Impossible unaligned trap. insn=%08x\n", insn);
+		die_if_kernel("Byte sized unaligned access?!?!", current->tss.kregs);
+		return 4; /* just to keep gcc happy. */
+	}
+}
+
+/* 1 = signed, 0 = unsigned */
+static inline int decode_signedness(unsigned int insn)
+{
+	return (insn >> 22) & 1;
+}
+
+static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
+				       unsigned int rd)
+{
+	int yep;
+
+	if(rs2 >= 16 || rs1 >= 16 || rd >= 16)
+		yep = 1;
+	else
+		yep = 0;
+	if(yep) {
+		/* Wheee... */
+		__asm__ __volatile__("save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "restore; restore; restore; restore;\n\t"
+				     "restore; restore; restore;\n\t");
+	}
+}
+
+static inline int sign_extend_halfword(int hword)
+{
+	return hword << 16 >> 16;
+}
+
+static inline int sign_extend_imm13(int imm)
+{
+	return imm << 19 >> 19;
+}
+
+static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
+{
+	struct reg_window *win;
+
+	if(reg < 16)
+		return (!reg ? 0 : regs->u_regs[reg]);
+
+	/* Ho hum, the slightly complicated case. */
+	win = (struct reg_window *) regs->u_regs[UREG_FP];
+	return win->locals[reg - 16]; /* yes, I know what this does... */
+}
+
+static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
+{
+	struct reg_window *win;
+
+	if(reg < 16)
+		return &regs->u_regs[reg];
+	win = (struct reg_window *) regs->u_regs[UREG_FP];
+	return &win->locals[reg - 16];
+}
+
+static inline unsigned long compute_effective_address(struct pt_regs *regs,
+						      unsigned int insn)
+{
+	unsigned int rs1 = (insn >> 14) & 0x1f;
+	unsigned int rs2 = insn & 0x1f;
+	unsigned int rd = (insn >> 25) & 0x1f;
+	unsigned int imm13 = (insn & 0x1fff);
+
+	if(insn & 0x2000) {
+		maybe_flush_windows(rs1, 0, rd);
+		return (fetch_reg(rs1, regs) + sign_extend_imm13(imm13));
+	} else {
+		maybe_flush_windows(rs1, rs2, rd);
+		return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs));
+	}
+}
+
+static inline void do_integer_load(unsigned long *dest_reg, int size,
+				   unsigned long *saddr, int is_signed)
+{
+	unsigned char bytes[4];
+
+	switch(size) {
+	case 2:
+		bytes[0] = *((unsigned char *)saddr + 1);
+		bytes[1] = *((unsigned char *)saddr + 0);
+		*dest_reg = (bytes[0] | (bytes[1] << 8));
+		if(is_signed)
+			*dest_reg = sign_extend_halfword(*dest_reg);
+		break;
+
+	case 4:
+		bytes[0] = *((unsigned char *)saddr + 3);
+		bytes[1] = *((unsigned char *)saddr + 2);
+		bytes[2] = *((unsigned char *)saddr + 1);
+		bytes[3] = *((unsigned char *)saddr + 0);
+		*dest_reg = (bytes[0] | (bytes[1] << 8) |
+			     (bytes[2] << 16) | (bytes[3] << 24));
+		break;
+
+	case 8:
+		bytes[0] = *((unsigned char *)saddr + 3);
+		bytes[1] = *((unsigned char *)saddr + 2);
+		bytes[2] = *((unsigned char *)saddr + 1);
+		bytes[3] = *((unsigned char *)saddr + 0);
+		*dest_reg++ = (bytes[0] | (bytes[1] << 8) |
+			     (bytes[2] << 16) | (bytes[3] << 24));
+		saddr++;
+		bytes[0] = *((unsigned char *)saddr + 3);
+		bytes[1] = *((unsigned char *)saddr + 2);
+		bytes[2] = *((unsigned char *)saddr + 1);
+		bytes[3] = *((unsigned char *)saddr + 0);
+		*dest_reg = (bytes[0] | (bytes[1] << 8) |
+			     (bytes[2] << 16) | (bytes[3] << 24));
+		break;
+
+	default:
+		panic("Impossible unaligned load.");
+	};
+}
+
+static inline void store_common(unsigned long *src_val,
+				int size, unsigned long *dst_addr)
+{
+	unsigned char *daddr = (unsigned char *) dst_addr;
+	switch(size) {
+	case 2:
+		daddr[0] = ((*src_val) >> 8) & 0xff;
+		daddr[1] = (*src_val & 0xff);
+		break;
+
+	case 4:
+		daddr[0] = ((*src_val) >> 24) & 0xff;
+		daddr[1] = ((*src_val) >> 16) & 0xff;
+		daddr[2] = ((*src_val) >> 8) & 0xff;
+		daddr[3] = (*src_val & 0xff);
+		break;
+
+	case 8:
+		daddr[0] = ((*src_val) >> 24) & 0xff;
+		daddr[1] = ((*src_val) >> 16) & 0xff;
+		daddr[2] = ((*src_val) >> 8) & 0xff;
+		daddr[3] = (*src_val & 0xff);
+		daddr += 4;
+		src_val++;
+		daddr[0] = ((*src_val) >> 24) & 0xff;
+		daddr[1] = ((*src_val) >> 16) & 0xff;
+		daddr[2] = ((*src_val) >> 8) & 0xff;
+		daddr[3] = (*src_val & 0xff);
+		break;
+
+	default:
+		panic("Impossible unaligned store.");
+	}
+}
+
+static inline void do_integer_store(int reg_num, int size,
+				    unsigned long *dst_addr,
+				    struct pt_regs *regs)
+{
+	unsigned long *src_val;
+	static unsigned long zero[2] = { 0, 0 };
+
+	if(reg_num)
+		src_val = fetch_reg_addr(reg_num, regs);
+	else
+		src_val = &zero[0];
+	store_common(src_val, size, dst_addr);
+}
+
+static inline void do_atomic(unsigned long *srcdest_reg, unsigned long *mem)
+{
+	unsigned long flags, tmp;
+
+#ifdef __SMP__
+	/* XXX Need to capture/release other cpu's around this. */
+#endif
+	save_and_cli(flags);
+	tmp = *srcdest_reg;
+	do_integer_load(srcdest_reg, 4, mem, 0);
+	store_common(&tmp, 4, mem);
+	restore_flags(flags);
+}
+
+static inline void advance(struct pt_regs *regs)
+{
+	regs->pc   = regs->npc;
+	regs->npc += 4;
+}
+
+static inline int floating_point_load_or_store_p(unsigned int insn)
+{
+	return (insn >> 24) & 1;
+}
+
+static inline int ok_for_kernel(unsigned int insn)
+{
+	return !floating_point_load_or_store_p(insn);
+}
+
+asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+{
+	enum direction dir = decode_direction(insn);
+	int size = decode_access_size(insn);
+
+	if(!ok_for_kernel(insn) || dir == both) {
+		printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n",
+		       regs->pc);
+		panic("Wheee. Kernel does fpu/atomic unaligned load/store.");
+		/* Not reached... */
+	} else {
+		unsigned long addr = compute_effective_address(regs, insn);
+
+#ifdef DEBUG_MNA
+		printk("KMNA: pc=%08lx [dir=%s addr=%08lx size=%d] retpc[%08lx]\n",
+		       regs->pc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]);
+#endif
+		switch(dir) {
+		case load:
+			do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
+					size, (unsigned long *) addr,
+					decode_signedness(insn));
+			break;
+
+		case store:
+			do_integer_store(((insn>>25)&0x1f), size,
+					 (unsigned long *) addr, regs);
+			break;
+		case both:
+#if 0 /* unsupported */
+			do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
+				  (unsigned long *) addr);
+			break;
+#endif
+		default:
+			panic("Impossible kernel unaligned trap.");
+			/* Not reached... */
+		}
+		advance(regs);
+	}
+}
+
+static inline int ok_for_user(struct pt_regs *regs, unsigned int insn,
+			      enum direction dir)
+{
+	unsigned int reg;
+	int retval, check = (dir == load) ? VERIFY_READ : VERIFY_WRITE;
+	int size = ((insn >> 19) & 3) == 3 ? 8 : 4;
+
+	if((regs->pc | regs->npc) & 3)
+		return 0;
+
+	/* Must verify_area() in all the necessary places. */
+#define WINREG_ADDR(regnum) ((void *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum)))
+	retval = 0;
+	reg = (insn >> 25) & 0x1f;
+	if(reg >= 16) {
+		retval = verify_area(check, WINREG_ADDR(reg - 16), size);
+		if(retval)
+			return retval;
+	}
+	reg = (insn >> 14) & 0x1f;
+	if(reg >= 16) {
+		retval = verify_area(check, WINREG_ADDR(reg - 16), size);
+		if(retval)
+			return retval;
+	}
+	if(!(insn & 0x2000)) {
+		reg = (insn & 0x1f);
+		if(reg >= 16) {
+			retval = verify_area(check, WINREG_ADDR(reg - 16), size);
+			if(retval)
+				return retval;
+		}
+	}
+	return retval;
+#undef WINREG_ADDR
+}
+
+asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+{
+	enum direction dir;
+
+	if(!(current->tss.flags & SPARC_FLAG_UNALIGNED) ||
+	   (((insn >> 30) & 3) != 3))
+		goto kill_user;
+	dir = decode_direction(insn);
+	if(!ok_for_user(regs, insn, dir)) {
+		goto kill_user;
+	} else {
+		int size = decode_access_size(insn);
+		unsigned long addr;
+
+		if(floating_point_load_or_store_p(insn)) {
+			printk("User FPU load/store unaligned unsupported.\n");
+			goto kill_user;
+		}
+
+		addr = compute_effective_address(regs, insn);
+		switch(dir) {
+		case load:
+			do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
+					size, (unsigned long *) addr,
+					decode_signedness(insn));
+			break;
+
+		case store:
+			do_integer_store(((insn>>25)&0x1f), size,
+					 (unsigned long *) addr, regs);
+			break;
+
+		case both:
+			do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
+				  (unsigned long *) addr);
+			break;
+
+		default:
+			panic("Impossible user unaligned trap.");
+		}
+		advance(regs);
+		return;
+	}
+
+kill_user:
+	current->tss.sig_address = regs->pc;
+	current->tss.sig_desc = SUBSIG_PRIVINST;
+	send_sig(SIGBUS, current, 1);
+}
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/windows.c linux/arch/sparc/kernel/windows.c
--- v2.1.8/linux/arch/sparc/kernel/windows.c	Mon Mar  4 08:49:57 1996
+++ linux/arch/sparc/kernel/windows.c	Sat Nov  9 10:11:53 1996
@@ -9,11 +9,29 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 
+#include <asm/uaccess.h>
+
 /* Do save's until all user register windows are out of the cpu. */
 void flush_user_windows(void)
 {
-	if(current->tss.uwinmask)
-		flush_user_windows();
+	register int ctr asm("g5");
+
+	ctr = 0;
+	__asm__ __volatile__("
+1:
+	ld	[%%g6 + %2], %%g4
+	orcc	%%g0, %%g4, %%g0
+	add	%0, 1, %0
+	bne	1b
+	 save	%%sp, -64, %%sp
+2:
+	subcc	%0, 1, %0
+	bne	2b
+	 restore %%g0, %%g0, %%g0"
+	: "=&r" (ctr)
+	: "0" (ctr),
+	  "i" ((const unsigned long)(&(((struct task_struct *)0)->tss.uwinmask)))
+	: "g4");
 }
 
 static inline void shift_window_buffer(int first_win, int last_win, struct thread_struct *tp)
@@ -37,11 +55,11 @@
  */
 void synchronize_user_stack(void)
 {
-	struct thread_struct *tp = &current->tss;
+	struct thread_struct *tp;
 	int window;
 
 	flush_user_windows();
-
+	tp = &current->tss;
 	if(!tp->w_saved)
 		return;
 
@@ -49,17 +67,17 @@
 	for(window = tp->w_saved - 1; window >= 0; window--) {
 		unsigned long sp = tp->rwbuf_stkptrs[window];
 
-		/* See if %sp is reasonable at all. */
-		if(verify_area(VERIFY_WRITE, (char *) sp, sizeof(struct reg_window)))
+		/* Ok, let it rip. */
+		if(copy_to_user((char *) sp, &tp->reg_window[window],
+				sizeof(struct reg_window)))
 			continue;
 
-		/* Ok, let it rip. */
-		memcpy((char *) sp, &tp->reg_window[window], sizeof(struct reg_window));
 		shift_window_buffer(window, tp->w_saved - 1, tp);
 		tp->w_saved--;
 	}
 }
 
+#if 0
 /* An optimization. */
 static inline void copy_aligned_window(void *dest, const void *src)
 {
@@ -82,27 +100,25 @@
 			     "r" (dest), "r" (src) :
 			     "g2", "g3", "g4", "g5");
 }
+#endif
 
 /* Try to push the windows in a threads window buffer to the
  * user stack.  Unaligned %sp's are not allowed here.
  */
 
-#define stack_is_bad(sp, rw) \
-  (((sp) & 7) || verify_area(rw, (char *) (sp), sizeof(struct reg_window)))
-
 void try_to_clear_window_buffer(struct pt_regs *regs, int who)
 {
-	struct thread_struct *tp = &current->tss;
+	struct thread_struct *tp;
 	int window;
 
 	flush_user_windows();
+	tp = &current->tss;
 	for(window = 0; window < tp->w_saved; window++) {
 		unsigned long sp = tp->rwbuf_stkptrs[window];
 
-		if(stack_is_bad(sp, VERIFY_WRITE))
+		if((sp & 7) ||
+		   copy_to_user((char *) sp, &tp->reg_window[window], REGWIN_SZ))
 			do_exit(SIGILL);
-		else
-			copy_aligned_window((char *) sp, &tp->reg_window[window]);
 	}
 	tp->w_saved = 0;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/wof.S linux/arch/sparc/kernel/wof.S
--- v2.1.8/linux/arch/sparc/kernel/wof.S	Mon May  6 12:26:03 1996
+++ linux/arch/sparc/kernel/wof.S	Sat Nov  9 10:11:54 1996
@@ -1,4 +1,4 @@
-/* $Id: wof.S,v 1.22 1996/04/03 02:15:10 davem Exp $
+/* $Id: wof.S,v 1.29 1996/10/11 01:00:04 davem Exp $
  * wof.S: Sparc window overflow handler.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -92,14 +92,14 @@
 	LOAD_CURRENT(curptr, twin_tmp)
 
 	andcc	%t_psr, PSR_PS, %g0
-	be	spwin_fromuser				! all user wins, branch
-	 nop
+	be,a	spwin_fromuser				! all user wins, branch
+	 save	%g0, %g0, %g0				! Go where saving will occur
 	
 	/* See if any user windows are active in the set. */
 	ld	[%curptr + THREAD_UMASK], %twin_tmp	! grab win mask
 	orcc	%g0, %twin_tmp, %g0			! check for set bits
 	bne	spwin_exist_uwins			! yep, there are some
-	 nop
+	 andn	%twin_tmp, %glob_tmp, %twin_tmp		! compute new umask
 
 	/* Save into the window which must be saved and do it.
 	 * Basically if we are here, this means that we trapped
@@ -108,7 +108,6 @@
 	 */
 	save	%g0, %g0, %g0		! save into the window to stash away
 	wr	%glob_tmp, 0x0, %wim	! set new %wim, this is safe now
-	WRITE_PAUSE			! burn cpu cycles due to bad engineering
 
 spwin_no_userwins_from_kernel:
 	/* LOCATION: Window to be saved */
@@ -140,16 +139,12 @@
 	 * But first, store the new user window mask calculated
 	 * above.
 	 */
-	andn	%twin_tmp, %glob_tmp, %twin_tmp		! compute new umask
 	st	%twin_tmp, [%curptr + THREAD_UMASK]
-
-spwin_fromuser:
-	/* LOCATION: Trap window */
 	save	%g0, %g0, %g0		! Go to where the saving will occur
 
+spwin_fromuser:
 	/* LOCATION: Window to be saved */
 	wr	%glob_tmp, 0x0, %wim	! Now it is safe to set new %wim
-	WRITE_PAUSE			! burn baby burn
 
 	/* LOCATION: Window to be saved */
 
@@ -192,6 +187,7 @@
 	/* Restore saved globals */
 	mov	%saved_g5, %g5
 	mov	%saved_g6, %g6
+
 	wr	%t_psr, 0x0, %psr
 	WRITE_PAUSE
 	jmp	%t_pc
@@ -243,8 +239,10 @@
 
 	/* Restore the saved globals and build a pt_regs frame. */
 	mov	%saved_g5, %g5
+	mov	%g6, %l4
 	mov	%saved_g6, %g6
 	STORE_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
+	mov	%l4, %g6
 
 	ENTER_SYSCALL
 
@@ -260,7 +258,7 @@
 	 * be given the look of death from Commander Peanut.
 	 */
 	b	ret_trap_entry
-	 nop
+	 clr	%l6
 
 spwin_bad_ustack_from_kernel:
 	/* LOCATION: Window to be saved */
@@ -277,7 +275,8 @@
 	/* LOCATION: Trap window */
 
 	/* Restore globals, condition codes in the %psr and
-	 * return from trap.
+	 * return from trap.  Note, restoring %g6 when returning
+	 * to kernel mode is not necessarily these days. ;-)
 	 */
 	mov	%saved_g5, %g5
 	mov	%saved_g6, %g6
@@ -315,8 +314,8 @@
 	be	1f
 	 sra	%sp, 29, %glob_tmp
 
-	b	spwin_user_stack_is_bolixed
-	 nop
+	b	spwin_user_stack_is_bolixed + 0x4
+	 rd	%psr, %glob_tmp
 
 1:
 	add	%glob_tmp, 0x1, %glob_tmp
@@ -324,8 +323,8 @@
 	be	1f
 	 and	%sp, 0xfff, %glob_tmp		! delay slot
 
-	b	spwin_user_stack_is_bolixed
-	 nop
+	b	spwin_user_stack_is_bolixed + 0x4
+	 rd	%psr, %glob_tmp
 
 	/* See if our dump area will be on more than one
 	 * page.
@@ -343,8 +342,8 @@
 	be	1f
 	 add	%sp, 0x38, %glob_tmp	/* Is second page in vma hole? */
 
-	b	spwin_user_stack_is_bolixed
-	 nop
+	b	spwin_user_stack_is_bolixed + 0x4
+	 rd	%psr, %glob_tmp
 
 1:
 	sra	%glob_tmp, 29, %glob_tmp
@@ -353,8 +352,8 @@
 	be	1f
 	 add	%sp, 0x38, %glob_tmp
 
-	b	spwin_user_stack_is_bolixed
-	 nop
+	b	spwin_user_stack_is_bolixed + 0x4
+	 rd	%psr, %glob_tmp
 
 1:
 	lda	[%glob_tmp] ASI_PTE, %glob_tmp
@@ -365,8 +364,8 @@
 	be	spwin_good_ustack			! success
 	 nop
 
-	b	spwin_user_stack_is_bolixed
-	 nop
+	b	spwin_user_stack_is_bolixed + 0x4
+	 rd	%psr, %glob_tmp
 
 	/* This is a generic SRMMU routine.  As far as I know this
 	 * works for all current v8/srmmu implementations, we'll
@@ -388,8 +387,9 @@
 	 * kernel is page aligned, which should always be the case.
 	 */
 	/* Check results of callers andcc %sp, 0x7, %g0 */
+	sethi	%hi(C_LABEL(page_offset)), %glob_tmp
 	bne	spwin_user_stack_is_bolixed
-	 sethi	%hi(KERNBASE), %glob_tmp
+	 ld	[%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp
 	cmp	%glob_tmp, %sp
 	bleu	spwin_user_stack_is_bolixed
 	 mov	AC_M_SFSR, %glob_tmp
@@ -414,8 +414,8 @@
 	mov	AC_M_SFSR, %glob_tmp
 	lda	[%glob_tmp] ASI_M_MMUREGS, %glob_tmp
 	andcc	%glob_tmp, 0x2, %g0			! did we fault?
-	be	spwin_finish_up				! cool beans, success
-	 nop
+	be,a	spwin_finish_up + 0x4			! cool beans, success
+	 restore %g0, %g0, %g0
 
-	b	spwin_user_stack_is_bolixed		! we faulted, ugh
-	 nop
+	b	spwin_user_stack_is_bolixed + 0x4	! we faulted, ugh
+	 rd	%psr, %glob_tmp
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/kernel/wuf.S linux/arch/sparc/kernel/wuf.S
--- v2.1.8/linux/arch/sparc/kernel/wuf.S	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/kernel/wuf.S	Sat Nov  9 10:11:54 1996
@@ -1,4 +1,4 @@
-/* $Id: wuf.S,v 1.23 1996/04/25 06:09:18 davem Exp $
+/* $Id: wuf.S,v 1.27 1996/10/11 01:00:06 davem Exp $
  * wuf.S: Window underflow trap handler for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller
@@ -27,6 +27,8 @@
 #define twin_tmp1    l4
 #define twin_tmp2    l5
 
+#define curptr       g6
+
 	.text
 	.align	4
 
@@ -88,7 +90,6 @@
 fnwin_patch2:	and	%twin_tmp1, 0xff, %twin_tmp1
 
 	wr	%twin_tmp1, 0x0, %wim	/* Make window 'I' invalid */
-	WRITE_PAUSE
 
 	andcc	%t_psr, PSR_PS, %g0
 	be	fwin_from_user
@@ -135,7 +136,7 @@
 	 */
 				.globl	C_LABEL(fwin_mmu_patchme)
 C_LABEL(fwin_mmu_patchme):	b	C_LABEL(sun4c_fwin_stackchk)
-				andcc	%sp, 0x7, %g0
+				 andcc	%sp, 0x7, %g0
 
 fwin_user_stack_is_bolixed:
 	/* LOCATION: Window 'W' */
@@ -152,13 +153,14 @@
 
 	/* Save kernel %sp in global while we change windows. */
 	mov	%l5, %g2
+	mov	%l4, %curptr
 
 	save	%g0, %g0, %g0
 
 	/* LOCATION: Window 'O' */
 
 	rd	%psr, %g3		/* Read %psr in live user window */
-	mov	%fp, %g6		/* Save bogus frame pointer. */
+	mov	%fp, %g4		/* Save bogus frame pointer. */
 
 	save	%g0, %g0, %g0
 
@@ -176,9 +178,8 @@
 	/* Fix users window mask and buffer save count. */
 	mov	0x1, %g5
 	sll	%g5, %g3, %g5
-	LOAD_CURRENT(twin_tmp1, g1)
-	st	%g5, [%twin_tmp1 + THREAD_UMASK]	! one live user window still
-	st	%g0, [%twin_tmp1 + THREAD_W_SAVED]	! no windows in the buffer
+	st	%g5, [%curptr + THREAD_UMASK]		! one live user window still
+	st	%g0, [%curptr + THREAD_W_SAVED]		! no windows in the buffer
 
 	ENTER_SYSCALL
 
@@ -186,10 +187,10 @@
 	WRITE_PAUSE
 
 	call	C_LABEL(window_underflow_fault)
-	 mov	%g6, %o0
+	 mov	%g4, %o0
 
 	b	ret_trap_entry
-	 nop
+	 clr	%l6
 
 fwin_user_stack_is_ok:
 	/* LOCATION: Window 'W' */
@@ -247,8 +248,7 @@
 	be	1f
 	 and	%sp, 0xfff, %l0		! delay slot
 
-	b	fwin_user_stack_is_bolixed
-	 nop
+	b,a	fwin_user_stack_is_bolixed
 
 	/* See if we have to check the sanity of one page or two */
 1:
@@ -259,8 +259,7 @@
 	be	1f
 	 andncc	%l0, 0xff8, %g0
 
-	b	fwin_user_stack_is_bolixed	/* %sp is in vma hole, yuck */
-	 nop
+	b,a	fwin_user_stack_is_bolixed	/* %sp is in vma hole, yuck */
 
 1:
 	be	sun4c_fwin_onepage	/* Only one page to check */
@@ -273,8 +272,7 @@
 	be	1f
 	 lda	[%l0] ASI_PTE, %l1
 
-	b	fwin_user_stack_is_bolixed	/* Second page in vma hole */
-	 nop
+	b,a	fwin_user_stack_is_bolixed	/* Second page in vma hole */
 
 1:
 	srl	%l1, 29, %l1
@@ -282,8 +280,7 @@
 	bne	sun4c_fwin_onepage
 	 lda	[%sp] ASI_PTE, %l1	
 
-	b	fwin_user_stack_is_bolixed	/* Second page has bad perms */
-	 nop
+	b,a	fwin_user_stack_is_bolixed	/* Second page has bad perms */
 
 sun4c_fwin_onepage:
 	srl	%l1, 29, %l1
@@ -292,22 +289,21 @@
 	 nop
 
 	/* A page had bad page permissions, losing... */
-	b	fwin_user_stack_is_bolixed
-	 nop
+	b,a	fwin_user_stack_is_bolixed
 
 	.globl	C_LABEL(srmmu_fwin_stackchk)
 C_LABEL(srmmu_fwin_stackchk):
 	/* LOCATION: Window 'W' */
 
 	/* Caller did 'andcc %sp, 0x7, %g0' */
+	sethi	%hi(C_LABEL(page_offset)), %l5
 	bne	fwin_user_stack_is_bolixed
-	 nop
+	 ld	[%l5 + %lo(C_LABEL(page_offset))], %l5
 
 	/* Check if the users stack is in kernel vma, then our
 	 * trial and error technique below would succeed for
 	 * the 'wrong' reason.
 	 */
-	sethi	%hi(KERNBASE), %l5		! delay slot for above
 	mov	AC_M_SFSR, %l4
 	cmp	%l5, %sp
 	bleu	fwin_user_stack_is_bolixed
@@ -342,8 +338,8 @@
 	mov	AC_M_SFSR, %twin_tmp2
 	lda	[%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2	! read fault status
 	andcc	%twin_tmp2, 0x2, %g0			! did fault occur?
-	be	fwin_user_finish_up
-	 nop
+	be,a	fwin_user_finish_up + 0x4
+	 wr	%t_psr, 0x0, %psr
 
 	/* Did I ever tell you about my window lobotomy?
 	 * anyways... fwin_user_stack_is_bolixed expects
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile
--- v2.1.8/linux/arch/sparc/lib/Makefile	Sun Apr 21 12:30:31 1996
+++ linux/arch/sparc/lib/Makefile	Sat Nov  9 10:11:59 1996
@@ -1,18 +1,40 @@
-# $Id: Makefile,v 1.9 1996/03/23 01:37:15 davem Exp $
+# $Id: Makefile,v 1.12 1996/10/27 08:36:26 davem Exp $
 # Makefile for Sparc library files..
 #
 
 CFLAGS := $(CFLAGS) -ansi
 
 OBJS  = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
-        strlen.o
+        strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
+	strncpy_from_user.o
 
 lib.a: $(OBJS)
 	$(AR) rcs lib.a $(OBJS)
 	sync
 
+checksum.o: checksum.S
+	$(CC) -ansi -c -o checksum.o checksum.S
+
 memcpy.o: memcpy.S
-	$(CC) -ansi -c -o memcpy.o memcpy.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S
+
+memcmp.o: memcmp.S
+	$(CC) -ansi -c -o memcmp.o memcmp.S
+
+memscan.o: memscan.S
+	$(CC) -ansi -c -o memscan.o memscan.S
+
+strncmp.o: strncmp.S
+	$(CC) -ansi -c -o strncmp.o strncmp.S
+
+strncpy_from_user.o: strncpy_from_user.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S
+
+blockops.o: blockops.S
+	$(CC) -ansi -c -o blockops.o blockops.S
+
+memset.o: memset.S
+	$(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S
 
 strlen.o: strlen.S
 	$(CC) -ansi -c -o strlen.o strlen.S
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/ashrdi3.S linux/arch/sparc/lib/ashrdi3.S
--- v2.1.8/linux/arch/sparc/lib/ashrdi3.S	Sat Nov 25 02:59:00 1995
+++ linux/arch/sparc/lib/ashrdi3.S	Sat Nov  9 10:12:00 1996
@@ -1,4 +1,4 @@
-/* $Id: ashrdi3.S,v 1.2 1995/11/25 00:58:59 davem Exp $
+/* $Id: ashrdi3.S,v 1.3 1996/09/07 23:18:10 davem Exp $
  * ashrdi3.S:	The filesystem code creates all kinds of references to
  *              this little routine on the sparc with gcc.
  *
@@ -11,19 +11,26 @@
 C_LABEL(__ashrdi3):
 	tst	%o2
 	be	3f
-	or	%g0, 32, %g2
+	 or	%g0, 32, %g2
+
 	sub	%g2, %o2, %g2
+
 	tst	%g2
 	bg	1f
-	sra	%o0, %o2, %o4
+	 sra	%o0, %o2, %o4
+
 	sra	%o0, 31, %o4
 	sub	%g0, %g2, %g2
 	ba	2f
-	sra	%o0, %g2, %o5
-1:	sll	%o0, %g2, %g3
+	 sra	%o0, %g2, %o5
+
+1:
+	sll	%o0, %g2, %g3
 	srl	%o1, %o2, %g2
 	or	%g2, %g3, %o5
-2:	or	%g0, %o4, %o0
+2:
+	or	%g0, %o4, %o0
 	or	%g0, %o5, %o1
-3:	jmpl	%o7 + 8, %g0
-	nop
+3:
+	jmpl	%o7 + 8, %g0
+	 nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/blockops.S linux/arch/sparc/lib/blockops.S
--- v2.1.8/linux/arch/sparc/lib/blockops.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/lib/blockops.S	Sat Nov  9 10:12:00 1996
@@ -0,0 +1,103 @@
+/* $Id: blockops.S,v 1.5 1996/09/24 05:22:56 davem Exp $
+ * blockops.S: Common block zero optimized routines.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/cprefix.h>
+
+	/* Zero out 64 bytes of memory at (buf + offset).
+	 * Assumes %g1 contains zero.
+	 */
+#define BLAST_BLOCK(buf, offset) \
+	std	%g0, [buf + offset + 0x38]; \
+	std	%g0, [buf + offset + 0x30]; \
+	std	%g0, [buf + offset + 0x28]; \
+	std	%g0, [buf + offset + 0x20]; \
+	std	%g0, [buf + offset + 0x18]; \
+	std	%g0, [buf + offset + 0x10]; \
+	std	%g0, [buf + offset + 0x08]; \
+	std	%g0, [buf + offset + 0x00];
+
+	/* Copy 32 bytes of memory at (src + offset) to
+	 * (dst + offset).
+	 */
+#define MIRROR_BLOCK(dst, src, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[src + offset + 0x18], t0; \
+	ldd	[src + offset + 0x10], t2; \
+	ldd	[src + offset + 0x08], t4; \
+	ldd	[src + offset + 0x00], t6; \
+	std	t0, [dst + offset + 0x18]; \
+	std	t2, [dst + offset + 0x10]; \
+	std	t4, [dst + offset + 0x08]; \
+	std	t6, [dst + offset + 0x00];
+
+	/* Profiling evidence indicates that memset() is
+	 * commonly called for blocks of size PAGE_SIZE,
+	 * and (2 * PAGE_SIZE) (for kernel stacks)
+	 * and with a second arg of zero.  We assume in
+	 * all of these cases that the buffer is aligned
+	 * on at least an 8 byte boundry.
+	 *
+	 * Therefore we special case them to make them
+	 * as fast as possible.
+	 */
+
+	.text
+	.align	4
+
+	.globl	C_LABEL(bzero_2page), C_LABEL(bzero_1page)
+C_LABEL(bzero_2page):
+	/* %o0 = buf */
+	or	%g0, %g0, %g1
+	or	%o0, %g0, %o1
+	or	%g0, 0x20, %g2
+1:
+	BLAST_BLOCK(%o0, 0x00)
+	BLAST_BLOCK(%o0, 0x40)
+	BLAST_BLOCK(%o0, 0x80)
+	BLAST_BLOCK(%o0, 0xc0)
+	subcc	%g2, 1, %g2
+	bne	1b
+	 add	%o0, 0x100, %o0
+
+	retl
+	 mov	%o1, %o0
+
+C_LABEL(bzero_1page):
+	/* %o0 = buf */
+	or	%g0, %g0, %g1
+	or	%o0, %g0, %o1
+	or	%g0, 0x10, %g2
+1:
+	BLAST_BLOCK(%o0, 0x00)
+	BLAST_BLOCK(%o0, 0x40)
+	BLAST_BLOCK(%o0, 0x80)
+	BLAST_BLOCK(%o0, 0xc0)
+	subcc	%g2, 1, %g2
+	bne	1b
+	 add	%o0, 0x100, %o0
+
+	retl
+	 mov	%o1, %o0
+
+	.globl	C_LABEL(__copy_1page)
+C_LABEL(__copy_1page):
+	/* %o0 = dst, %o1 = src */
+	or	%g0, 0x10, %g1
+1:
+	MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x40, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x60, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x80, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0xa0, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0xc0, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0xe0, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	subcc	%g1, 1, %g1
+	add	%o0, 0x100, %o0
+	bne	1b
+	 add	%o1, 0x100, %o1
+
+	retl
+	 nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/checksum.S linux/arch/sparc/lib/checksum.S
--- v2.1.8/linux/arch/sparc/lib/checksum.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/lib/checksum.S	Sat Nov  9 10:12:01 1996
@@ -0,0 +1,439 @@
+/* checksum.S: Sparc optimized checksum code.
+ *
+ *  Copyright(C) 1995 Linus Torvalds
+ *  Copyright(C) 1995 Miguel de Icaza
+ *  Copyright(C) 1996 David S. Miller
+ *
+ * derived from:
+ *	Linux/Alpha checksum c-code
+ *      Linux/ix86 inline checksum assembly
+ *      RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code)
+ *	David Mosberger-Tang for optimized reference c-code
+ *	BSD4.4 portable checksum routine
+ */
+
+#include <asm-sparc/cprefix.h>
+
+#define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5)	\
+	ldd	[buf + offset + 0x00], t0;			\
+	ldd	[buf + offset + 0x08], t2;			\
+	addxcc	t0, sum, sum;					\
+	addxcc	t1, sum, sum;					\
+	ldd	[buf + offset + 0x10], t4;			\
+	addxcc	t2, sum, sum;					\
+	addxcc	t3, sum, sum;					\
+	ldd	[buf + offset + 0x18], t0;			\
+	addxcc	t4, sum, sum;					\
+	addxcc	t5, sum, sum;					\
+	addxcc	t0, sum, sum;					\
+	addxcc	t1, sum, sum;
+
+#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1, t2, t3)	\
+	ldd	[buf - offset - 0x08], t0;			\
+	ldd	[buf - offset - 0x00], t2;			\
+	addxcc	t0, sum, sum;					\
+	addxcc	t1, sum, sum;					\
+	addxcc	t2, sum, sum;					\
+	addxcc	t3, sum, sum;
+
+	/* Do end cruft out of band to get better cache patterns. */
+csum_partial_end_cruft:
+	be	1f				! caller asks %o1 & 0x8
+	 andcc	%o1, 4, %g0			! nope, check for word remaining
+	ldd	[%o0], %g2			! load two
+	addcc	%g2, %o2, %o2			! add first word to sum
+	addxcc	%g3, %o2, %o2			! add second word as well
+	add	%o0, 8, %o0			! advance buf ptr
+	addx	%g0, %o2, %o2			! add in final carry
+	andcc	%o1, 4, %g0			! check again for word remaining
+1:	be	1f				! nope, skip this code
+	 andcc	%o1, 3, %o1			! check for trailing bytes
+	ld	[%o0], %g2			! load it
+	addcc	%g2, %o2, %o2			! add to sum
+	add	%o0, 4, %o0			! advance buf ptr
+	addx	%g0, %o2, %o2			! add in final carry
+	andcc	%o1, 3, %g0			! check again for trailing bytes
+1:	be	1f				! no trailing bytes, return
+	 addcc	%o1, -1, %g0			! only one byte remains?
+	bne	2f				! at least two bytes more
+	 subcc	%o1, 2, %o1			! only two bytes more?
+	b	4f				! only one byte remains
+	 or	%g0, %g0, %o4			! clear fake hword value
+2:	lduh	[%o0], %o4			! get hword
+	be	6f				! jmp if only hword remains
+	 add	%o0, 2, %o0			! advance buf ptr either way
+	sll	%o4, 16, %o4			! create upper hword
+4:	ldub	[%o0], %o5			! get final byte
+	sll	%o5, 8, %o5			! put into place
+	or	%o5, %o4, %o4			! coalese with hword (if any)
+6:	addcc	%o4, %o2, %o2			! add to sum
+1:	retl					! get outta here
+	 addx	%g0, %o2, %o0			! add final carry into retval
+
+	/* Also do alignment out of band to get better cache patterns. */
+csum_partial_fix_alignment:
+	cmp	%o1, 6
+	bl	cpte - 0x4
+	 andcc	%o0, 0x2, %g0
+	be	1f
+	 andcc	%o0, 0x4, %g0
+	lduh	[%o0 + 0x00], %g2
+	sub	%o1, 2, %o1
+	add	%o0, 2, %o0
+	sll	%g2, 16, %g2
+	addcc	%g2, %o2, %o2
+	srl	%o2, 16, %g3
+	addx	%g0, %g3, %g2
+	sll	%o2, 16, %o2
+	sll	%g2, 16, %g3
+	srl	%o2, 16, %o2
+	andcc	%o0, 0x4, %g0
+	or	%g3, %o2, %o2
+1:	be	cpa
+	 andcc	%o1, 0xffffff80, %o3
+	ld	[%o0 + 0x00], %g2
+	sub	%o1, 4, %o1
+	addcc	%g2, %o2, %o2
+	add	%o0, 4, %o0
+	addx	%g0, %o2, %o2
+	b	cpa
+	 andcc	%o1, 0xffffff80, %o3
+
+	/* The common case is to get called with a nicely aligned
+	 * buffer of size 0x20.  Follow the code path for that case.
+	 */
+	.globl	C_LABEL(csum_partial)
+C_LABEL(csum_partial):			/* %o0=buf, %o1=len, %o2=sum */
+	andcc	%o0, 0x7, %g0				! alignment problems?
+	bne	csum_partial_fix_alignment		! yep, handle it
+	 sethi	%hi(cpte - 8), %g7			! prepare table jmp ptr
+	andcc	%o1, 0xffffff80, %o3			! num loop iterations
+cpa:	be	3f					! none to do
+	 andcc	%o1, 0x70, %g1				! clears carry flag too
+5:	CSUM_BIGCHUNK(%o0, 0x00, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x20, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x40, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x60, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	addx	%g0, %o2, %o2				! sink in final carry
+	subcc	%o3, 128, %o3				! detract from loop iters
+	bne	5b					! more to do
+	 add	%o0, 128, %o0				! advance buf ptr
+	andcc	%o1, 0x70, %g1				! clears carry flag too
+3:	be	cpte					! nope
+	 andcc	%o1, 0xf, %g0				! anything left at all?
+	srl	%g1, 1, %o4				! compute offset
+	sub	%g7, %g1, %g7				! adjust jmp ptr
+	sub	%g7, %o4, %g7				! final jmp ptr adjust
+	jmp	%g7 + %lo(cpte - 8)			! enter the table
+	 add	%o0, %g1, %o0				! advance buf ptr
+cptbl:	CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3, %g4, %g5)
+	addx	%g0, %o2, %o2				! fetch final carry
+	andcc	%o1, 0xf, %g0				! anything left at all?
+cpte:	bne	csum_partial_end_cruft			! yep, handle it
+	 andcc	%o1, 8, %g0				! check how much
+cpout:	retl						! get outta here
+	 mov	%o2, %o0				! return computed csum
+
+	/* This aligned version executes typically in 8.5 superscalar cycles, this
+	 * is the best I can do.  I say 8.5 because the final add will pair with
+	 * the next ldd in the main unrolled loop.  Thus the pipe is always full.
+	 */
+#define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7)	\
+	ldd	[src + off + 0x00], t0;							\
+	ldd	[src + off + 0x08], t2;							\
+	addxcc	t0, sum, sum;								\
+	ldd	[src + off + 0x10], t4;							\
+	addxcc	t1, sum, sum;								\
+	ldd	[src + off + 0x18], t6;							\
+	addxcc	t2, sum, sum;								\
+	std	t0, [dst + off + 0x00];							\
+	addxcc	t3, sum, sum;								\
+	std	t2, [dst + off + 0x08];							\
+	addxcc	t4, sum, sum;								\
+	std	t4, [dst + off + 0x10];							\
+	addxcc	t5, sum, sum;								\
+	std	t6, [dst + off + 0x18];							\
+	addxcc	t6, sum, sum;								\
+	addxcc	t7, sum, sum;
+
+	/* 12 superscalar cycles seems to be the limit for this case,
+	 * because of this we thus do all the ldd's together to get
+	 * Viking MXCC into streaming mode.  Ho hum...
+	 */
+#define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7)	\
+	ldd	[src + off + 0x00], t0;						\
+	ldd	[src + off + 0x08], t2;						\
+	ldd	[src + off + 0x10], t4;						\
+	ldd	[src + off + 0x18], t6;						\
+	st	t0, [dst + off + 0x00];						\
+	addxcc	t0, sum, sum;							\
+	st	t1, [dst + off + 0x04];						\
+	addxcc	t1, sum, sum;							\
+	st	t2, [dst + off + 0x08];						\
+	addxcc	t2, sum, sum;							\
+	st	t3, [dst + off + 0x0c];						\
+	addxcc	t3, sum, sum;							\
+	st	t4, [dst + off + 0x10];						\
+	addxcc	t4, sum, sum;							\
+	st	t5, [dst + off + 0x14];						\
+	addxcc	t5, sum, sum;							\
+	st	t6, [dst + off + 0x18];						\
+	addxcc	t6, sum, sum;							\
+	st	t7, [dst + off + 0x1c];						\
+	addxcc	t7, sum, sum;
+
+	/* Yuck, 6 superscalar cycles... */
+#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3)	\
+	ldd	[src - off - 0x08], t0;				\
+	ldd	[src - off - 0x00], t2;				\
+	addxcc	t0, sum, sum;					\
+	st	t0, [dst - off - 0x08];				\
+	addxcc	t1, sum, sum;					\
+	st	t1, [dst - off - 0x04];				\
+	addxcc	t2, sum, sum;					\
+	st	t2, [dst - off - 0x00];				\
+	addxcc	t3, sum, sum;					\
+	st	t3, [dst - off + 0x04];
+
+	/* Handle the end cruft code out of band for better cache patterns. */
+cc_end_cruft:
+	be	1f
+	 andcc	%o3, 4, %g0
+	ldd	[%o0 + 0x00], %g2
+	add	%o1, 8, %o1
+	addcc	%g2, %g7, %g7
+	add	%o0, 8, %o0
+	addxcc	%g3, %g7, %g7
+	st	%g2, [%o1 - 0x08]
+	addx	%g0, %g7, %g7
+	andcc	%o3, 4, %g0
+	st	%g3, [%o1 - 0x04]
+1:	be	1f
+	 andcc	%o3, 3, %o3
+	ld	[%o0 + 0x00], %g2
+	add	%o1, 4, %o1
+	addcc	%g2, %g7, %g7
+	st	%g2, [%o1 - 0x04]
+	addx	%g0, %g7, %g7
+	add	%o0, 4, %o0
+	andcc	%o3, 3, %g0
+1:	be	1f
+	 addcc	%o3, -1, %g0
+	bne	2f
+	 subcc	%o3, 2, %o3
+	b	4f
+	 or	%g0, %g0, %o4
+2:	lduh	[%o0 + 0x00], %o4
+	add	%o0, 2, %o0
+	sth	%o4, [%o1 + 0x00]
+	be	6f
+	 add	%o1, 2, %o1
+	sll	%o4, 16, %o4
+4:	ldub	[%o0 + 0x00], %o5
+	stb	%o5, [%o1 + 0x00]
+	sll	%o5, 8, %o5
+	or	%o5, %o4, %o4
+6:	addcc	%o4, %g7, %g7
+1:	retl
+	 addx	%g0, %g7, %o0
+
+	/* Also, handle the alignment code out of band. */
+cc_dword_align:
+	cmp	%g1, 6
+	bl,a	ccte
+	 andcc	%g1, 0xf, %o3
+	andcc	%o0, 0x1, %g0
+	bne	ccslow
+	 andcc	%o0, 0x2, %g0
+	be	1f
+	 andcc	%o0, 0x4, %g0
+	lduh	[%o0 + 0x00], %g2
+	sub	%g1, 2, %g1
+	sth	%g2, [%o1 + 0x00]
+	add	%o0, 2, %o0
+	sll	%g2, 16, %g2
+	addcc	%g2, %g7, %g7
+	add	%o1, 2, %o1
+	srl	%g7, 16, %g3
+	addx	%g0, %g3, %g2
+	sll	%g7, 16, %g7
+	sll	%g2, 16, %g3
+	srl	%g7, 16, %g7
+	andcc	%o0, 0x4, %g0
+	or	%g3, %g7, %g7
+1:	be	3f
+	 andcc	%g1, 0xffffff80, %g0
+	ld	[%o0 + 0x00], %g2
+	sub	%g1, 4, %g1
+	st	%g2, [%o1 + 0x00]
+	add	%o0, 4, %o0
+	addcc	%g2, %g7, %g7
+	add	%o1, 4, %o1
+	addx	%g0, %g7, %g7
+	b	3f
+	 andcc	%g1, 0xffffff80, %g0
+
+	/* Sun, you just can't beat me, you just can't.  Stop trying,
+	 * give up.  I'm serious, I am going to kick the living shit
+	 * out of you, game over, lights out.
+	 */
+	.align	8
+	.globl	C_LABEL(csum_partial_copy)
+C_LABEL(csum_partial_copy):		/* %o0=src, %o1=dest, %o2=len, %o3=sum */
+	xor	%o0, %o1, %o4		! get changing bits
+	mov	%o2, %g1		! free up %o2
+	andcc	%o4, 3, %g0		! check for mismatched alignment
+	bne	ccslow			! better this than unaligned/fixups
+	 andcc	%o0, 7, %g0		! need to align things?
+	mov	%o3, %g7		! free up %o3
+	bne	cc_dword_align		! yes, we check for short lengths there
+	 andcc	%g1, 0xffffff80, %g0	! can we use unrolled loop?
+3:	be	3f			! nope, less than one loop remains
+	 andcc	%o1, 4, %g0		! dest aligned on 4 or 8 byte boundry?
+	be	ccdbl + 4		! 8 byte aligned, kick ass
+5:	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	sub	%g1, 128, %g1		! detract from length
+	addx	%g0, %g7, %g7		! add in last carry bit
+	andcc	%g1, 0xffffff80, %g0	! more to csum?
+	add	%o0, 128, %o0		! advance src ptr
+	bne	5b			! we did not go negative, continue looping
+	 add	%o1, 128, %o1		! advance dest ptr
+3:	andcc	%g1, 0x70, %o2		! can use table?
+ccmerge:be	ccte			! nope, go and check for end cruft
+	 andcc	%g1, 0xf, %o3		! get low bits of length (clears carry btw)
+	srl	%o2, 1, %o4		! begin negative offset computation
+	sethi	%hi(ccte - 8), %o5	! set up table ptr end
+	add	%o0, %o2, %o0		! advance src ptr
+	sub	%o5, %o4, %o5		! continue table calculation
+	sll	%o2, 1, %g2		! constant multiplies are fun...
+	sub	%o5, %g2, %o5		! some more adjustments
+	jmp	%o5 + %lo(ccte - 8)	! jump into it, duff style, wheee...
+	 add	%o1, %o2, %o1		! advance dest ptr (carry is clear btw)
+cctbl:	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5)
+	addx	%g0, %g7, %g7
+	andcc	%o3, 0xf, %g0		! check for low bits set
+ccte:	bne	cc_end_cruft		! something left, handle it out of band
+	 andcc	%o3, 8, %g0		! begin checks for that code
+	retl				! return
+	 mov	%g7, %o0		! give em the computed checksum
+ccdbl:	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	sub	%g1, 128, %g1		! detract from length
+	addx	%g0, %g7, %g7		! add in last carry bit
+	andcc	%g1, 0xffffff80, %g0	! more to csum?
+	add	%o0, 128, %o0		! advance src ptr
+	bne	ccdbl			! we did not go negative, continue looping
+	 add	%o1, 128, %o1		! advance dest ptr
+	b	ccmerge			! finish it off, above
+	 andcc	%g1, 0x70, %o2		! can use table? (clears carry btw)
+
+ccslow:
+	save	%sp, -104, %sp
+	mov	%i0, %g2
+	mov	%g2, %o4
+	orcc	%i2, %g0, %o5
+	ble	.LL37
+	 mov	0, %o3
+	andcc	%g2, 1, %g3
+	be	.LL50
+	 sra	%o5, 1, %o1
+	ldub	[%g2], %o3
+	add	%i2, -1, %o5
+	add	%g2, 1, %o4
+	sra	%o5, 1, %o1
+.LL50:
+	cmp	%o1, 0
+	be	.LL39
+	 andcc	%o4, 2, %g0
+	be,a	.LL51
+	 sra	%o1, 1, %o1
+	add	%o1, -1, %o1
+	lduh	[%o4], %o0
+	add	%o5, -2, %o5
+	add	%o3, %o0, %o3
+	add	%o4, 2, %o4
+	sra	%o1, 1, %o1
+.LL51:
+	cmp	%o1, 0
+	be	.LL41
+	 mov	0, %o2
+.LL42:
+	ld	[%o4], %o0
+	add	%o3, %o2, %o3
+	add	%o3, %o0, %o3
+	cmp	%o3, %o0
+	 addx	%g0, 0, %o2
+	addcc	%o1, -1, %o1
+	bne	.LL42
+	 add	%o4, 4, %o4
+	add	%o3, %o2, %o3
+	sethi	%hi(65535), %o0
+	or	%o0, %lo(65535), %o0
+	and	%o3, %o0, %o0
+	srl	%o3, 16, %o1
+	add	%o0, %o1, %o3
+.LL41:
+	andcc	%o5, 2, %g0
+	be	.LL52
+	 andcc	%o5, 1, %g0
+	lduh	[%o4], %o0
+	add	%o3, %o0, %o3
+	add	%o4, 2, %o4
+.LL39:
+	andcc	%o5, 1, %g0
+.LL52:
+	be	.LL53
+	 sethi	%hi(65535), %o0
+	ldub	[%o4], %o0
+	sll	%o0, 8, %o0
+	add	%o3, %o0, %o3
+	sethi	%hi(65535), %o0
+.LL53:
+	or	%o0, %lo(65535), %o0
+	and	%o3, %o0, %o2
+	srl	%o3, 16, %o1
+	add	%o2, %o1, %o1
+	and	%o1, %o0, %o2
+	srl	%o1, 16, %o1
+	add	%o2, %o1, %o1
+	and	%o1, %o0, %o0
+	srl	%o1, 16, %o1
+	add	%o0, %o1, %o1
+	sll	%o1, 16, %o0
+	cmp	%g3, 0
+	be	.LL37
+	 srl	%o0, 16, %o3
+	srl	%o0, 24, %o1
+	and	%o3, 255, %o0
+	sll	%o0, 8, %o0
+	or	%o1, %o0, %o3
+.LL37:
+	add	%o3, %i3, %o1
+	sethi	%hi(65535), %o0
+	or	%o0, %lo(65535), %o0
+	and	%o1, %o0, %o0
+	srl	%o1, 16, %o1
+	add	%o0, %o1, %i0
+	mov	%i1, %o0
+	mov	%g2, %o1
+	call	C_LABEL(__memcpy)
+	 mov	%i2, %o2
+	ret
+	 restore
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/memcmp.S linux/arch/sparc/lib/memcmp.S
--- v2.1.8/linux/arch/sparc/lib/memcmp.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/lib/memcmp.S	Sat Nov  9 10:12:01 1996
@@ -0,0 +1,314 @@
+#include <asm/cprefix.h>
+
+	.text
+	.align 4
+	.global C_LABEL(__memcmp), C_LABEL(memcmp)
+C_LABEL(__memcmp):
+C_LABEL(memcmp):
+#if 1
+	cmp	%o2, 0
+	ble	L3
+	 mov	0, %g3
+L5:
+	ldub	[%o0], %g2
+	ldub	[%o1], %g3
+	sub	%g2, %g3, %g2
+	mov	%g2, %g3
+	sll	%g2, 24, %g2
+
+	cmp	%g2, 0
+	bne	L3
+	 add	%o0, 1, %o0
+
+	add	%o2, -1, %o2
+
+	cmp	%o2, 0
+	bg	L5
+	 add	%o1, 1, %o1
+L3:
+	sll	%g3, 24, %o0
+	sra	%o0, 24, %o0
+
+	retl
+	 nop
+#else
+	save	%sp, -104, %sp
+	mov	%i2, %o4
+	mov	%i0, %o0
+
+	cmp	%o4, 15
+	ble	L72
+	 mov	%i1, %i2
+
+	andcc	%i2, 3, %g0
+	be	L161
+	 andcc	%o0, 3, %g2
+L75:
+	ldub	[%o0], %g3
+	ldub	[%i2], %g2
+	add	%o0,1, %o0
+
+	subcc	%g3, %g2, %i0
+	bne	L156
+	 add	%i2, 1, %i2
+
+	andcc	%i2, 3, %g0
+	bne	L75
+	 add	%o4, -1, %o4
+
+	andcc	%o0, 3, %g2
+L161:
+	bne,a	L78
+	 mov	%i2, %i1
+
+	mov	%o0, %i5
+	mov	%i2, %i3
+	srl	%o4, 2, %i4
+
+	cmp	%i4, 0
+	bge	L93
+	 mov	%i4, %g2
+
+	add %i4, 3, %g2
+L93:
+	sra	%g2, 2, %g2
+	sll	%g2, 2, %g2
+	sub	%i4, %g2, %g2
+
+	cmp	%g2, 1
+	be,a	L88
+	 add	%o0, 4, %i5
+
+	bg	L94
+	 cmp	%g2, 2
+
+	cmp	%g2, 0
+	be,a	L86
+	 ld	[%o0], %g3
+
+	b	L162
+	 ld	[%i5], %g3
+L94:
+	be	L81
+	 cmp	%g2, 3
+
+	be,a	L83
+	 add	%o0, -4, %i5
+
+	b	L162
+	 ld	[%i5], %g3
+L81:
+	add	%o0, -8, %i5
+	ld	[%o0], %g3
+	add	%i2, -8, %i3
+	ld	[%i2], %g2
+
+	b	L82
+	 add	%i4, 2, %i4
+L83:
+	ld	[%o0], %g4
+	add	%i2, -4, %i3
+	ld	[%i2], %g1
+
+	b	L84
+	 add	%i4, 1, %i4
+L86:
+	b	L87
+	 ld	[%i2], %g2
+L88:
+	add	%i2, 4, %i3
+	ld	[%o0], %g4
+	add	%i4, -1, %i4
+	ld	[%i2], %g1
+L95:
+	ld	[%i5], %g3
+L162:
+	cmp	%g4, %g1
+	be	L87
+	 ld	[%i3], %g2
+
+	cmp	%g4, %g1
+L163:
+	bleu	L114
+	 mov	-1, %i0
+
+	b	L114
+	 mov	1, %i0
+L87:
+	ld	[%i5 + 4], %g4
+	cmp	%g3, %g2
+	bne	L163
+	 ld	[%i3 + 4], %g1
+L84:
+	ld	[%i5 + 8], %g3
+
+	cmp	%g4, %g1
+	bne	L163
+	 ld	[%i3 + 8], %g2
+L82:
+	ld	[%i5 + 12], %g4
+	cmp	%g3, %g2
+	bne	L163
+	 ld	[%i3 + 12], %g1
+
+	add	%i5, 16, %i5
+
+	addcc	%i4, -4, %i4
+	bne	L95
+	 add	%i3, 16, %i3
+
+	cmp	%g4, %g1
+	bne	L163
+	 nop
+
+	b	L114
+	 mov	0, %i0
+L78:
+	srl	%o4, 2, %i0
+	and	%o0, -4, %i3
+	orcc	%i0, %g0, %g3
+	sll	%g2, 3, %o7
+	mov	32, %g2
+
+	bge	L129
+	 sub	%g2, %o7, %o1
+
+	add	%i0, 3, %g3
+L129:
+	sra	%g3, 2, %g2
+	sll	%g2, 2, %g2
+	sub	%i0, %g2, %g2
+
+	cmp	%g2, 1
+	be,a	L124
+	 ld	[%i3], %o3
+
+	bg	L130
+	 cmp	%g2, 2
+
+	cmp	%g2, 0
+	be,a	L122
+	 ld	[%i3], %o2
+
+	b	L164
+	sll	%o3, %o7, %g3
+L130:
+	be	L117
+	 cmp	%g2, 3
+
+	be,a	L119
+	 ld	[%i3], %g1
+
+	b	L164
+	 sll	%o3, %o7, %g3
+L117:
+	ld	[%i3], %g4
+	add	%i2, -8, %i1
+	ld	[%i3 + 4], %o3
+	add	%i0, 2, %i0
+	ld	[%i2], %i4
+
+	b	L118
+	 add	%i3, -4, %i3
+L119:
+	ld	[%i3 + 4], %g4
+	add	%i2, -4, %i1
+	ld	[%i2], %i5
+
+	b	L120
+	 add	%i0, 1, %i0
+L122:
+	ld	[%i3 + 4], %g1
+	ld	[%i2], %i4
+
+	b	L123
+	 add	%i3, 4, %i3
+L124:
+	add	%i2, 4, %i1
+	ld	[%i3 + 4], %o2
+	add	%i0, -1, %i0
+	ld	[%i2], %i5
+	add	%i3, 8, %i3
+L131:
+	sll	%o3, %o7, %g3
+L164:
+	srl	%o2, %o1, %g2
+	ld	[%i3], %g1
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i5
+	bne	L163
+	 ld	[%i1], %i4
+L123:
+	sll	%o2, %o7, %g3
+	srl	%g1, %o1, %g2
+	ld	[%i3 + 4], %g4
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i4
+	bne	L163
+	 ld	[%i1 + 4], %i5
+L120:
+	sll	%g1, %o7, %g3
+	srl	%g4, %o1, %g2
+	ld	[%i3 + 8], %o3
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i5
+	bne	L163
+	 ld	[%i1 + 8], %i4
+L118:
+	sll	%g4, %o7, %g3
+	srl	%o3, %o1, %g2
+	ld	[%i3 + 12], %o2
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i4
+	bne	L163
+	 ld	[%i1 + 12], %i5
+
+	add	%i3, 16, %i3
+	addcc	%i0, -4, %i0
+	bne	L131
+	 add	%i1, 16, %i1
+
+	sll	%o3, %o7, %g3
+	srl	%o2, %o1, %g2
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i5
+	be,a	L114
+	 mov	0, %i0
+
+	b,a L163
+L114:
+	cmp	%i0, 0
+	bne	L156
+	 and	%o4, -4, %g2
+
+	add	%o0, %g2, %o0
+	add	%i2, %g2, %i2
+	and	%o4, 3, %o4
+L72:
+	cmp	%o4, 0
+	be	L156
+	 mov	0, %i0
+
+	ldub	[%o0], %g3
+L165:
+	ldub	[%i2], %g2
+	add	%o0, 1, %o0
+
+	subcc	%g3, %g2, %i0
+	bne	L156
+	 add	%i2, 1, %i2
+
+	addcc	%o4, -1, %o4
+	bne,a	L165
+	 ldub	[%o0], %g3
+
+	mov	0, %i0
+L156:
+	ret
+	restore
+#endif
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/memcpy.S linux/arch/sparc/lib/memcpy.S
--- v2.1.8/linux/arch/sparc/lib/memcpy.S	Mon Mar  4 08:49:57 1996
+++ linux/arch/sparc/lib/memcpy.S	Sat Nov  9 10:12:03 1996
@@ -1,520 +1,364 @@
-! Fast memmove/memcpy/bcopy
-! Copyright Australian National University, 1995
-! This file may be used under the terms of the GNU Public License
-! Author: Paul Mackerras, September 95
-! Minor beautifications David S. Miller
+/* memcpy.S: Sparc optimized memcpy code.
+ *
+ *  Copyright(C) 1995 Linus Torvalds
+ *  Copyright(C) 1996 David S. Miller
+ *  Copyright(C) 1996 Eddie C. Dost
+ *  Copyright(C) 1996 Jakub Jelinek
+ *
+ * derived from:
+ *	e-mail between David and Eddie.
+ */
 
 #include <asm/cprefix.h>
+#include <asm/ptrace.h>
 
-	.globl	C_LABEL(bcopy)
-C_LABEL(bcopy):
-	mov	%o0,%o3
-	mov	%o1,%o0
-	mov	%o3,%o1
+/* Both these macros have to start with exactly the same insn */
+#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src + offset + 0x00], %t0; \
+	ldd	[%src + offset + 0x08], %t2; \
+	ldd	[%src + offset + 0x10], %t4; \
+	ldd	[%src + offset + 0x18], %t6; \
+	st	%t0, [%dst + offset + 0x00]; \
+	st	%t1, [%dst + offset + 0x04]; \
+	st	%t2, [%dst + offset + 0x08]; \
+	st	%t3, [%dst + offset + 0x0c]; \
+	st	%t4, [%dst + offset + 0x10]; \
+	st	%t5, [%dst + offset + 0x14]; \
+	st	%t6, [%dst + offset + 0x18]; \
+	st	%t7, [%dst + offset + 0x1c];
+
+#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src + offset + 0x00], %t0; \
+	ldd	[%src + offset + 0x08], %t2; \
+	ldd	[%src + offset + 0x10], %t4; \
+	ldd	[%src + offset + 0x18], %t6; \
+	std	%t0, [%dst + offset + 0x00]; \
+	std	%t2, [%dst + offset + 0x08]; \
+	std	%t4, [%dst + offset + 0x10]; \
+	std	%t6, [%dst + offset + 0x18];
+
+#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+	ldd	[%src - offset - 0x10], %t0; \
+	ldd	[%src - offset - 0x08], %t2; \
+	st	%t0, [%dst - offset - 0x10]; \
+	st	%t1, [%dst - offset - 0x0c]; \
+	st	%t2, [%dst - offset - 0x08]; \
+	st	%t3, [%dst - offset - 0x04];
+
+#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \
+	lduh	[%src + offset + 0x00], %t0; \
+	lduh	[%src + offset + 0x02], %t1; \
+	lduh	[%src + offset + 0x04], %t2; \
+	lduh	[%src + offset + 0x06], %t3; \
+	sth	%t0, [%dst + offset + 0x00]; \
+	sth	%t1, [%dst + offset + 0x02]; \
+	sth	%t2, [%dst + offset + 0x04]; \
+	sth	%t3, [%dst + offset + 0x06];
+
+#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
+	ldub	[%src - offset - 0x02], %t0; \
+	ldub	[%src - offset - 0x01], %t1; \
+	stb	%t0, [%dst - offset - 0x02]; \
+	stb	%t1, [%dst - offset - 0x01];
+
+	.text
+	.align	4
 
-	.globl	C_LABEL(amemmove)
+	.globl	C_LABEL(__memcpy), C_LABEL(memcpy), C_LABEL(bcopy)
+	.globl	C_LABEL(amemmove), C_LABEL(memmove)
+C_LABEL(bcopy):
+	mov	%o0, %o3
+	mov	%o1, %o0
+	mov	%o3, %o1
 C_LABEL(amemmove):
-	.globl	C_LABEL(memmove)
-	.globl	C_LABEL(memcpy)
 C_LABEL(memmove):
-C_LABEL(memcpy):
-	save	%sp,-96,%sp
-	mov	%i0,%l7
-
-	cmp	%i0,%i1		! check for dest within source area
-	bleu,a	1f
-	andcc	%i0,3,%l1
-	add	%i1,%i2,%l0
-	cmp	%i0,%l0
-	blu,a	Lback
-	mov	%l0,%i1
-
-	! copying forwards
-	! first get dest to be word-aligned
-	andcc	%i0,3,%l1
-1:
-	be,a	Lwalign		! if dest already word-aligned
-	cmp	%i2,4
-	mov	4,%l2
-	sub	%l2,%l1,%l2	! #bytes until word-aligned
-	subcc	%i2,%l2,%i2
-	ble,a	Lend		! not copying enough to get past word bdry
-	addcc	%i2,%l2,%i2
-
-1:
-	ldub	[%i1],%o0	! copy single bytes until word-aligned
-	add	%i1,1,%i1
-	subcc	%l2,1,%l2
-	stb	%o0,[%i0]
-	bgt	1b
-	add	%i0,1,%i0
-	cmp	%i2,4
-
-Lwalign:			! dest now word aligned
-	blt,a	Lend
-	orcc	%i2,%g0,%g0
-
-	andcc	%i1,3,%l0
-	be,a	Ldoword		! if dest word aligned wrt src
-	andcc	%i0,4,%g0
-
-	! yucky cases where we have to shift
-
-	mov	4,%l2
-	sub	%l2,%l0,%l2	! address adjustment, used at Lendn
-	sll	%l0,3,%l0	! bit offset = shift left count
-	sll	%l2,3,%l1	! shift right count
-	add	%i1,%l2,%i1	! round up to next word
-	ld	[%i1-4],%o0	! get first word
-
-	andcc	%i0,4,%g0	! get destination double-word aligned
-	be,a	1f
-	andcc	%i1,4,%g0
-	ld	[%i1],%o1	! by constructing and storing one word
-	add	%i0,4,%i0
-	add	%i1,4,%i1
-	sub	%i2,4,%i2
-	sll	%o0,%l0,%o0
-	srl	%o1,%l1,%l6
-	or	%o0,%l6,%o0
-	st	%o0,[%i0-4]
-	mov	%o1,%o0
-
-	andcc	%i1,4,%g0	! now construct & store pairs of double-words
-1:
-	bne,a	3f		! if source now not double-word aligned
-	subcc	%i2,4,%i2
-	subcc	%i2,16,%i2
-	blt	2f
-	mov	%o0,%o1
+/* This should be kept as optimized as possible */
+	cmp	%o0, %o1
+	bleu	1f
+	 xor	%o0, %o1, %o4
+
+	add	%o1, %o2, %o3
+	cmp	%o3, %o0
+	bleu	2f
+	 andcc	%o4, 3, %g0
+
+/* But I think from now on, we can hold on. Or tell me, is memmoving
+ * overlapping regions such a nice game? */
+
+	mov	%o0, %g1
+	add	%o1, %o2, %o1
+	add	%o0, %o2, %o0
+	sub	%o1, 1, %o1
+	sub	%o0, 1, %o0
+	
+reverse_bytes:
+	ldub	[%o1], %o4
+	subcc	%o2, 1, %o2
+	stb	%o4, [%o0]
+	sub	%o1, 1, %o1
+	bne	reverse_bytes
+	 sub	%o0, 1, %o0
+
+	retl
+	 mov	%g1, %o0
+
+/* And here start optimizing again... */
+
+dword_align:
+	andcc	%o1, 1, %g0
+	be	4f
+	 andcc	%o1, 2, %g0
+
+	ldub	[%o1], %g2
+	add	%o1, 1, %o1
+	stb	%g2, [%o0]
+	sub	%o2, 1, %o2
+	bne	3f
+	 add	%o0, 1, %o0
+
+	lduh	[%o1], %g2
+	add	%o1, 2, %o1
+	sth	%g2, [%o0]
+	sub	%o2, 2, %o2
+	b	3f
+	 add	%o0, 2, %o0
 4:
-	ldd	[%i1],%o2
-	sll	%o1,%l0,%o4
-	ldd	[%i1+8],%o0
-	add	%i0,16,%i0
-	add	%i1,16,%i1
-	subcc	%i2,16,%i2
-	srl	%o2,%l1,%l6
-	or	%l6,%o4,%o4
-	sll	%o2,%l0,%o5
-	srl	%o3,%l1,%l6
-	or	%l6,%o5,%o5
-	std	%o4,[%i0-16]
-	sll	%o3,%l0,%o4
-	srl	%o0,%l1,%l6
-	or	%l6,%o4,%o4
-	sll	%o0,%l0,%o5
-	srl	%o1,%l1,%l6
-	or	%l6,%o5,%o5
-	bge	4b
-	std	%o4,[%i0-8]
+	lduh	[%o1], %g2
+	add	%o1, 2, %o1
+	sth	%g2, [%o0]
+	sub	%o2, 2, %o2
+	b	3f
+	 add	%o0, 2, %o0
+
+C_LABEL(__memcpy):
+C_LABEL(memcpy):	/* %o0=dst %o1=src %o2=len */
+	xor	%o0, %o1, %o4
+1:
+	andcc	%o4, 3, %o5
 2:
-	addcc	%i2,12,%i2
-	blt,a	Lendn
-	addcc	%i2,4,%i2
-5:
-	ld	[%i1],%o2
-	add	%i0,4,%i0
-	add	%i1,4,%i1
-	subcc	%i2,4,%i2
-	sll	%o1,%l0,%o0
-	srl	%o2,%l1,%o1
-	or	%o1,%o0,%o0
-	st	%o0,[%i0-4]
-	bge	5b
-	mov	%o2,%o1
-	ba	Lendn
-	addcc	%i2,4,%i2
+	bne	cannot_optimize
+	 cmp	%o2, 15
 
-3:
-	blt,a	Lendn
-	addcc	%i2,4,%i2
-	ld	[%i1],%o1
-	add	%i1,4,%i1
-	subcc	%i2,16,%i2
-	blt,a	8f
-	addcc	%i2,16,%i2
-7:
-	ldd	[%i1],%o2
-	sll	%o0,%l0,%o4
-	srl	%o1,%l1,%l6
-	or	%l6,%o4,%o4
-	sll	%o1,%l0,%o5
-	ldd	[%i1+8],%o0
-	add	%i0,16,%i0
-	add	%i1,16,%i1
-	subcc	%i2,16,%i2
-	srl	%o2,%l1,%l6
-	or	%l6,%o5,%o5
-	std	%o4,[%i0-16]
-	sll	%o2,%l0,%o4
-	srl	%o3,%l1,%l6
-	or	%l6,%o4,%o4
-	sll	%o3,%l0,%o5
-	srl	%o0,%l1,%l6
-	or	%l6,%o5,%o5
-	bge	7b
-	std	%o4,[%i0-8]
-	addcc	%i2,16,%i2
-8:
-	sll	%o0,%l0,%o4
-	srl	%o1,%l1,%l6
-	or	%l6,%o4,%o4
-	st	%o4,[%i0]
-	add	%i0,4,%i0
-	subcc	%i2,4,%i2
-	blt,a	Lendn
-	addcc	%i2,4,%i2
-	mov	%o1,%o0
-	ld	[%i1],%o1
-	ba	8b
-	add	%i1,4,%i1
-
-
-Ldoword:
-	! here both dest and src are word-aligned
-	! make dest double-word aligned
-	be,a	1f
-	andcc	%i1,4,%g0
-	ld	[%i1],%o0
-	add	%i0,4,%i0
-	add	%i1,4,%i1
-	sub	%i2,4,%i2
-	st	%o0,[%i0-4]
-	cmp	%i2,4
-	blt,a	Lend
-	orcc	%i2,%g0,%g0
-	andcc	%i1,4,%g0
+	bleu	short_aligned_end
+	 andcc	%o1, 3, %g0
 
-1:
-	be,a	Ldodble		! if source double-word aligned now
-	subcc	%i2,32,%i2
-	ld	[%i1],%o5
-	add	%i1,4,%i1
-	subcc	%i2,36,%i2
-	blt,a	3f
-	add	%i2,32,%i2
-2:
-	ldd	[%i1],%o2
-	add	%i1,32,%i1
-	subcc	%i2,32,%i2
-	mov	%o5,%o0
-	ldd	[%i1-24],%o4
-	mov	%o2,%o1
-	std	%o0,[%i0]
-	mov	%o3,%o2
-	ldd	[%i1-16],%o0
-	mov	%o4,%o3
-	std	%o2,[%i0+8]
-	mov	%o5,%o2
-	ldd	[%i1-8],%o4
-	mov	%o0,%o3
-	std	%o2,[%i0+16]
-	mov	%o1,%o0
-	mov	%o4,%o1
-	std	%o0,[%i0+24]
-	bge	2b
-	add	%i0,32,%i0
-	add	%i2,32,%i2
+	bne	dword_align
 3:
-	st	%o5,[%i0]
-	add	%i0,4,%i0
-	subcc	%i2,4,%i2
-	blt,a	Lend
-	addcc	%i2,4,%i2
-	ld	[%i1],%o5
-	ba	3b
-	add	%i1,4,%i1
-
-Ldodble:
-	! dest and source are both double-word aligned
-	blt,a	2f
-	addcc	%i2,28,%i2
-1:
-	ldd	[%i1],%o0	! copy sets of 4 double-words
-	subcc	%i2,32,%i2
-	ldd	[%i1+8],%o2
-	add	%i1,32,%i1
-	ldd	[%i1-16],%o4
-	add	%i0,32,%i0
-	std	%o0,[%i0-32]
-	ldd	[%i1-8],%o0
-	std	%o2,[%i0-24]
-	std	%o4,[%i0-16]
-	bge	1b
-	std	%o0,[%i0-8]
-	addcc	%i2,28,%i2
-2:
-	blt,a	Lend
-	addcc	%i2,4,%i2
-3:
-	ld	[%i1],%o0	! copy words
-	add	%i1,4,%i1
-	add	%i0,4,%i0
-	subcc	%i2,4,%i2
-	bge	3b
-	st	%o0,[%i0-4]
-	ba	Lend
-	addcc	%i2,4,%i2
-
-Lendn:
-	sub	%i1,%l2,%i1
-Lend:
-	ble	Lout
-	nop
-1:
-	ldub	[%i1],%o0
-	add	%i1,1,%i1
-	subcc	%i2,1,%i2
-	stb	%o0,[%i0]
-	bgt	1b
-	add	%i0,1,%i0
-
-	ba	Lout
-	nop
-
-Lback:	! Here we have to copy backwards
-	add	%i0,%i2,%i0
-	! first get dest to be word-aligned
-	andcc	%i0,3,%l2	! #bytes until word-aligned
-	be,a	Lbwal		! if dest already word-aligned
-	cmp	%i2,4
-	subcc	%i2,%l2,%i2
-	ble,a	Lbend		! not copying enough to get past word bdry
-	addcc	%i2,%l2,%i2
+	 andcc	%o1, 4, %g0
 
-1:
-	ldub	[%i1-1],%o0	! copy single bytes until word-aligned
-	sub	%i1,1,%i1
-	subcc	%l2,1,%l2
-	stb	%o0,[%i0-1]
-	bgt	1b
-	sub	%i0,1,%i0
-	cmp	%i2,4
-
-Lbwal:				! dest now word aligned
-	blt,a	Lbend
-	orcc	%i2,%g0,%g0
-
-	andcc	%i1,3,%l2
-	be,a	Lbword		! if dest word aligned wrt src
-	andcc	%i0,4,%g0
-
-	! yucky cases where we have to shift
-	! note %l2 used below at Lbendn
-
-	mov	4,%l0
-	sub	%l0,%l2,%l0	! # bytes to right of src in word
-	sll	%l0,3,%l0	! bit offset = shift right count
-	sll	%l2,3,%l1	! shift left count
-	sub	%i1,%l2,%i1	! round down to word boundary
-	ld	[%i1],%o1	! get first word
-
-	andcc	%i0,4,%g0	! get destination double-word aligned
-	be,a	1f
-	andcc	%i1,4,%g0
-	ld	[%i1-4],%o0	! by constructing and storing one word
-	sub	%i0,4,%i0
-	sub	%i1,4,%i1
-	sub	%i2,4,%i2
-	srl	%o1,%l0,%o1
-	sll	%o0,%l1,%l6
-	or	%o1,%l6,%o1
-	st	%o1,[%i0]
-	mov	%o0,%o1
+	be	2f
+	 mov	%o2, %g1
 
-	andcc	%i1,4,%g0	! now construct & store pairs of double-words
-1:
-	bne,a	3f		! if source now not double-word aligned
-	subcc	%i2,4,%i2
-	subcc	%i2,16,%i2
-	blt	2f
-	mov	%o1,%o0
-4:
-	ldd	[%i1-8],%o2
-	srl	%o0,%l0,%o5
-	ldd	[%i1-16],%o0
-	sub	%i0,16,%i0
-	sub	%i1,16,%i1
-	subcc	%i2,16,%i2
-	sll	%o3,%l1,%l6
-	or	%l6,%o5,%o5
-	srl	%o3,%l0,%o4
-	sll	%o2,%l1,%l6
-	or	%l6,%o4,%o4
-	std	%o4,[%i0+8]
-	srl	%o2,%l0,%o5
-	sll	%o1,%l1,%l6
-	or	%l6,%o5,%o5
-	srl	%o1,%l0,%o4
-	sll	%o0,%l1,%l6
-	or	%l6,%o4,%o4
-	bge	4b
-	std	%o4,[%i0]
+	ld	[%o1], %o4
+	sub	%g1, 4, %g1
+	st	%o4, [%o0]
+	add	%o1, 4, %o1
+	add	%o0, 4, %o0
 2:
-	addcc	%i2,12,%i2
-	blt,a	Lbendn
-	addcc	%i2,4,%i2
-5:
-	ld	[%i1-4],%o2
-	sub	%i0,4,%i0
-	sub	%i1,4,%i1
-	subcc	%i2,4,%i2
-	srl	%o0,%l0,%o0
-	sll	%o2,%l1,%o1
-	or	%o1,%o0,%o0
-	st	%o0,[%i0]
-	bge	5b
-	mov	%o2,%o0
-	ba	Lbendn
-	addcc	%i2,4,%i2
+	andcc	%g1, 0xffffff80, %g7
+	be	3f
+	 andcc	%o0, 4, %g0
 
+	be	ldd_std + 4
+5:
+	MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc	%g7, 128, %g7
+	add	%o1, 128, %o1
+	bne	5b
+	 add	%o0, 128, %o0
 3:
-	blt,a	Lbendn
-	addcc	%i2,4,%i2
-	ld	[%i1-4],%o0
-	sub	%i1,4,%i1
-	subcc	%i2,16,%i2
-	blt,a	8f
-	addcc	%i2,16,%i2
-7:
-	ldd	[%i1-8],%o2
-	srl	%o1,%l0,%o5
-	sll	%o0,%l1,%l6
-	or	%l6,%o5,%o5
-	srl	%o0,%l0,%o4
-	ldd	[%i1-16],%o0
-	sub	%i0,16,%i0
-	sub	%i1,16,%i1
-	subcc	%i2,16,%i2
-	sll	%o3,%l1,%l6
-	or	%l6,%o4,%o4
-	std	%o4,[%i0+8]
-	srl	%o3,%l0,%o5
-	sll	%o2,%l1,%l6
-	or	%l6,%o5,%o5
-	srl	%o2,%l0,%o4
-	sll	%o1,%l1,%l6
-	or	%l6,%o4,%o4
-	bge	7b
-	std	%o4,[%i0]
-	addcc	%i2,16,%i2
-8:
-	srl	%o1,%l0,%o5
-	sll	%o0,%l1,%l6
-	or	%l6,%o5,%o5
-	st	%o5,[%i0-4]
-	sub	%i0,4,%i0
-	subcc	%i2,4,%i2
-	blt,a	Lbendn
-	addcc	%i2,4,%i2
-	mov	%o0,%o1
-	ld	[%i1-4],%o0
-	ba	8b
-	sub	%i1,4,%i1
-
-
-Lbword:
-	! here both dest and src are word-aligned
-	! make dest double-word aligned
-	be,a	1f
-	andcc	%i1,4,%g0
-	ld	[%i1-4],%o0
-	sub	%i0,4,%i0
-	sub	%i1,4,%i1
-	sub	%i2,4,%i2
-	st	%o0,[%i0]
-	cmp	%i2,4
-	blt,a	Lbend
-	orcc	%i2,%g0,%g0
-	andcc	%i1,4,%g0
+	andcc	%g1, 0x70, %g7
+	be	memcpy_table_end
+	 andcc	%g1, 8, %g0
+
+	sethi	%hi(memcpy_table_end), %o5
+	srl	%g7, 1, %o4
+	add	%g7, %o4, %o4
+	add	%o1, %g7, %o1
+	sub	%o5, %o4, %o5
+	jmpl	%o5 + %lo(memcpy_table_end), %g0
+	 add	%o0, %g7, %o0
+
+memcpy_table:
+	MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+
+memcpy_table_end:
+	be	memcpy_last7
+	 andcc	%g1, 4, %g0
+
+	ldd	[%o1], %g2
+	add	%o0, 8, %o0
+	add	%o1, 8, %o1
+	st	%g2, [%o0 - 0x08]
+	st	%g3, [%o0 - 0x04]
+memcpy_last7:
+	be	1f
+	 andcc	%g1, 2, %g0
+
+	ld	[%o1], %g2
+	add	%o1, 4, %o1
+	st	%g2, [%o0]
+	add	%o0, 4, %o0
+1:
+	be	1f
+	 andcc	%g1, 1, %g0
+
+	lduh	[%o1], %g2
+	add	%o1, 2, %o1
+	sth	%g2, [%o0]
+	add	%o0, 2, %o0
+1:
+	be	1f
+	 nop
+
+	ldub	[%o1], %g2
+	stb	%g2, [%o0]
+1:
+	retl
+ 	 nop
+
+	/* Placed here for cache reasons. */
+	.globl	C_LABEL(__copy_to_user), C_LABEL(__copy_from_user)
+C_LABEL(__copy_to_user):
+	b	copy_user_common
+	 st	%o0, [%g6 + THREAD_EX_ADDR]
+
+C_LABEL(__copy_from_user):
+	st	%o1, [%g6 + THREAD_EX_ADDR]
+
+copy_user_common:
+	ld	[%g6 + THREAD_EX_COUNT], %g1
+	set	copy_user_failure, %g2
+	add	%g1, 1, %g1
+	st	%o7, [%g6 + THREAD_EX_PC]
+	st	%g1, [%g6 + THREAD_EX_COUNT]
+	call	C_LABEL(__memcpy)
+	 st	%g2, [%g6 + THREAD_EX_EXPC]
+
+copy_user_success:
+	ldd	[%g6 + THREAD_EX_COUNT], %g2
+	mov	0, %o0
+	sub	%g2, 1, %g1
+	jmpl	%g3 + 0x8, %g0
+	 st	%g1, [%g6 + THREAD_EX_COUNT]
+
+copy_user_failure:
+	jmpl	%g3 + 0x8, %g0
+	 mov	%g2, %o0
+
+ldd_std:
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc	%g7, 128, %g7
+	add	%o1, 128, %o1
+	bne	ldd_std
+	 add	%o0, 128, %o0
+
+	andcc	%g1, 0x70, %g7
+	be	memcpy_table_end
+	 andcc	%g1, 8, %g0
+
+	sethi	%hi(memcpy_table_end), %o5
+	srl	%g7, 1, %o4
+	add	%g7, %o4, %o4
+	add	%o1, %g7, %o1
+	sub	%o5, %o4, %o5
+	jmpl	%o5 + %lo(memcpy_table_end), %g0
+	 add	%o0, %g7, %o0
+
+cannot_optimize:
+	bleu	short_end
+	 cmp	%o5, 2
+
+	bne	byte_chunk
+	 and	%o2, 0xfffffff0, %o3
+	 
+	andcc	%o1, 1, %g0
+	be	1f
+	 nop
+
+	ldub	[%o1], %g2
+	add	%o1, 1, %o1
+	sub	%o2, 1, %o2
+	stb	%g2, [%o0]
+	andcc	%o2, 0xfffffff0, %o3
+	be	short_end
+	 add	%o0, 1, %o0
+1:
+	MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+	MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5)
+	subcc	%o3, 0x10, %o3
+	add	%o1, 0x10, %o1
+	bne	1b
+	 add	%o0, 0x10, %o0
+	b	2f
+	 and	%o2, 0xe, %o3
+	
+byte_chunk:
+	MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3)
+	subcc	%o3, 0x10, %o3
+	add	%o1, 0x10, %o1
+	bne	byte_chunk
+	 add	%o0, 0x10, %o0
 
-1:
-	be,a	Lbdble		! if source double-word aligned now
-	subcc	%i2,32,%i2
-	ld	[%i1-4],%o4
-	sub	%i1,4,%i1
-	subcc	%i2,36,%i2
-	blt,a	3f
-	add	%i2,32,%i2
+short_end:
+	and	%o2, 0xe, %o3
 2:
-	ldd	[%i1-8],%o2
-	sub	%i1,32,%i1
-	subcc	%i2,32,%i2
-	mov	%o4,%o1
-	ldd	[%i1+16],%o4
-	mov	%o3,%o0
-	std	%o0,[%i0-8]
-	mov	%o2,%o3
-	ldd	[%i1+8],%o0
-	mov	%o5,%o2
-	std	%o2,[%i0-16]
-	mov	%o4,%o3
-	ldd	[%i1],%o4
-	mov	%o1,%o2
-	std	%o2,[%i0-24]
-	mov	%o0,%o1
-	mov	%o5,%o0
-	std	%o0,[%i0-32]
-	bge	2b
-	sub	%i0,32,%i0
-	add	%i2,32,%i2
-3:
-	st	%o4,[%i0-4]
-	sub	%i0,4,%i0
-	subcc	%i2,4,%i2
-	blt,a	Lbend
-	addcc	%i2,4,%i2
-	ld	[%i1-4],%o4
-	ba	3b
-	sub	%i1,4,%i1
-
-Lbdble:
-	! dest and source are both double-word aligned
-	blt,a	2f
-	addcc	%i2,28,%i2
+	sethi	%hi(short_table_end), %o5
+	sll	%o3, 3, %o4
+	add	%o0, %o3, %o0
+	sub	%o5, %o4, %o5
+	add	%o1, %o3, %o1
+	jmpl	%o5 + %lo(short_table_end), %g0
+	 andcc	%o2, 1, %g0
+
+	MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
+short_table_end:
+	be	1f
+	 nop
+	ldub	[%o1], %g2
+	stb	%g2, [%o0]
+1:
+	retl
+ 	 nop
+
+short_aligned_end:
+	bne	short_end
+	 andcc	%o2, 8, %g0
+
+	be	1f
+	 andcc	%o2, 4, %g0
+
+	ld	[%o1 + 0x00], %g2
+	ld	[%o1 + 0x04], %g3
+	add	%o1, 8, %o1
+	st	%g2, [%o0 + 0x00]
+	st	%g3, [%o0 + 0x04]
+	add	%o0, 8, %o0
 1:
-	ldd	[%i1-8],%o0	! copy sets of 4 double-words
-	subcc	%i2,32,%i2
-	ldd	[%i1-16],%o2
-	sub	%i1,32,%i1
-	ldd	[%i1+8],%o4
-	sub	%i0,32,%i0
-	std	%o0,[%i0+24]
-	ldd	[%i1],%o0
-	std	%o2,[%i0+16]
-	std	%o4,[%i0+8]
-	bge	1b
-	std	%o0,[%i0]
-	addcc	%i2,28,%i2
-2:
-	blt,a	Lbend
-	addcc	%i2,4,%i2
-3:
-	ld	[%i1-4],%o0	! copy words
-	sub	%i1,4,%i1
-	sub	%i0,4,%i0
-	subcc	%i2,4,%i2
-	bge	3b
-	st	%o0,[%i0]
-	ba	Lbend
-	addcc	%i2,4,%i2
-
-Lbendn:
-	add	%i1,%l2,%i1
-Lbend:
-	ble	Lout
-	nop
-1:
-	ldub	[%i1-1],%o0
-	sub	%i1,1,%i1
-	subcc	%i2,1,%i2
-	stb	%o0,[%i0-1]
-	bgt	1b
-	sub	%i0,1,%i0
-
-Lout:
-	ret
-	restore	%l7,0,%o0
-
-
+	b	memcpy_last7
+	 mov	%o2, %g1
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/memscan.S linux/arch/sparc/lib/memscan.S
--- v2.1.8/linux/arch/sparc/lib/memscan.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/lib/memscan.S	Sat Nov  9 10:12:04 1996
@@ -0,0 +1,135 @@
+/* $Id: memscan.S,v 1.4 1996/09/08 02:01:20 davem Exp $
+ * memscan.S: Optimized memscan for the Sparc.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/cprefix.h>
+
+/* In essence, this is just a fancy strlen. */
+
+#define LO_MAGIC 0x01010101
+#define HI_MAGIC 0x80808080
+
+	.text
+	.align	4
+	.globl	C_LABEL(__memscan_zero), C_LABEL(__memscan_generic)
+	.globl	C_LABEL(memscan)
+C_LABEL(__memscan_zero):
+	/* %o0 = addr, %o1 = size */
+	cmp	%o1, 0
+	bne,a	1f
+	 andcc	%o0, 3, %g0
+
+	retl
+	 nop
+
+1:
+	be	mzero_scan_word
+	 sethi	%hi(HI_MAGIC), %g2
+
+	ldsb	[%o0], %g3
+mzero_still_not_word_aligned:
+	cmp	%g3, 0
+	bne	1f
+	 add	%o0, 1, %o0
+
+	retl
+	 sub	%o0, 1, %o0
+
+1:
+	subcc	%o1, 1, %o1
+	bne,a	1f
+	 andcc	%o0, 3, %g0
+
+	retl
+	 nop
+
+1:
+	bne,a	mzero_still_not_word_aligned
+	 ldsb	[%o0], %g3
+
+	sethi	%hi(HI_MAGIC), %g2
+mzero_scan_word:
+	or	%g2, %lo(HI_MAGIC), %o3
+	sethi	%hi(LO_MAGIC), %g3
+	or	%g3, %lo(LO_MAGIC), %o2
+mzero_next_word:
+	ld	[%o0], %g2
+mzero_next_word_preloaded:
+	sub	%g2, %o2, %g2
+mzero_next_word_preloaded_next:
+	andcc	%g2, %o3, %g0
+	bne	mzero_byte_zero
+	 add	%o0, 4, %o0
+
+mzero_check_out_of_fuel:
+	subcc	%o1, 4, %o1
+	bg,a	1f
+	 ld	[%o0], %g2
+
+	retl
+	 nop
+
+1:
+	b	mzero_next_word_preloaded_next
+	 sub	%g2, %o2, %g2
+
+	/* Check every byte. */
+mzero_byte_zero:
+	ldsb	[%o0 - 4], %g2
+	cmp	%g2, 0
+	bne	mzero_byte_one
+	 sub	%o0, 4, %g3
+
+	retl
+	 mov	%g3, %o0
+
+mzero_byte_one:
+	ldsb	[%o0 - 3], %g2
+	cmp	%g2, 0
+	bne,a	mzero_byte_two_and_three
+	 ldsb	[%o0 - 2], %g2
+
+	retl
+	 sub	%o0, 3, %o0
+
+mzero_byte_two_and_three:
+	cmp	%g2, 0
+	bne,a	1f
+	 ldsb	[%o0 - 1], %g2
+
+	retl
+	 sub	%o0, 2, %o0
+
+1:
+	cmp	%g2, 0
+	bne,a	mzero_next_word_preloaded
+	 ld	[%o0], %g2
+
+	retl
+	 sub	%o0, 1, %o0
+
+mzero_found_it:
+	retl
+	 sub	%o0, 2, %o0
+
+C_LABEL(memscan):
+C_LABEL(__memscan_generic):
+	/* %o0 = addr, %o1 = c, %o2 = size */
+	cmp	%o2, 0
+	bne,a	0f
+	 ldub	[%o0], %g2
+
+	b,a	2f
+1:
+	ldub	[%o0], %g2
+0:
+	cmp	%g2, %o1
+	be	2f
+	 addcc	%o2, -1, %o2
+	bne	1b
+	 add	%o0, 1, %o0
+2:
+	retl
+	 nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/memset.S linux/arch/sparc/lib/memset.S
--- v2.1.8/linux/arch/sparc/lib/memset.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/lib/memset.S	Sat Nov  9 10:12:05 1996
@@ -0,0 +1,166 @@
+/* linux/arch/sparc/lib/memset.S: Sparc optimized memset and bzero code
+ * Hand optimized from GNU libc's memset
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/cprefix.h>
+#include <asm/ptrace.h>
+
+#define HANDLE_UNALIGNED 1
+
+	/* Store 64 bytes at (BASE + OFFSET) using value SOURCE. */
+#define ZERO_BIG_BLOCK(base, offset, source)    \
+	std	source, [base + offset + 0x00]; \
+	std	source, [base + offset + 0x08]; \
+	std	source, [base + offset + 0x10]; \
+	std	source, [base + offset + 0x18]; \
+	std	source, [base + offset + 0x20]; \
+	std	source, [base + offset + 0x28]; \
+	std	source, [base + offset + 0x30]; \
+	std	source, [base + offset + 0x38];
+
+#define ZERO_LAST_BLOCKS(base, offset, source)	\
+	std	source, [base - offset - 0x38]; \
+	std	source, [base - offset - 0x30]; \
+	std	source, [base - offset - 0x28]; \
+	std	source, [base - offset - 0x20]; \
+	std	source, [base - offset - 0x18]; \
+	std	source, [base - offset - 0x10]; \
+	std	source, [base - offset - 0x08]; \
+	std	source, [base - offset - 0x00];
+
+	.text
+	.align 4
+
+	.globl	C_LABEL(__bzero), C_LABEL(__memset), C_LABEL(memset)
+C_LABEL(__memset):
+C_LABEL(memset):
+	and	%o1, 0xff, %g3
+	sll	%g3, 8, %g2
+	or	%g3, %g2, %g3
+	sll	%g3, 16, %g2
+	or	%g3, %g2, %g3
+	b	1f
+	 mov	%o2, %o1
+
+#if HANDLE_UNALIGNED
+/* As this is highly unprobable, we optimize the other case (4 aligned)
+ * Define HANDLE_UNALIGNED to 0, if all the alignment work is done by
+ * the trap. Then we have to hope nobody will memset something unaligned
+ * with large counts, as this would lead to a lot of traps...
+ */
+3:
+	cmp	%o2, 3
+	be	2f
+	 stb	%g3, [%o0]
+
+	cmp	%o2, 2
+	be	2f
+	 stb	%g3, [%o0 + 0x01]
+
+	stb	%g3, [%o0 + 0x02]
+2:
+	sub	%o2, 4, %o2
+	add	%o1, %o2, %o1
+	b	4f
+	 sub	%o0, %o2, %o0
+#endif /* HANDLE_UNALIGNED */
+
+	.globl	C_LABEL(__clear_user)
+C_LABEL(__clear_user):
+	st	%o0, [%g6 + THREAD_EX_ADDR]
+	ld	[%g6 + THREAD_EX_COUNT], %g1
+	set	clear_user_failure, %g2
+	add	%g1, 1, %g1
+	st	%o7, [%g6 + THREAD_EX_PC]
+	st	%g1, [%g6 + THREAD_EX_COUNT]
+	call	C_LABEL(__bzero)
+	 st	%g2, [%g6 + THREAD_EX_EXPC]
+
+clear_user_success:
+	ldd	[%g6 + THREAD_EX_COUNT], %g2
+	mov	0, %o0
+	sub	%g2, 1, %g1
+	jmpl	%g3 + 0x8, %g0
+	 st	%g1, [%g6 + THREAD_EX_COUNT]
+
+clear_user_failure:
+	jmpl	%g3 + 0x8, %g0
+	 mov	%g2, %o0
+
+C_LABEL(__bzero):
+	mov	%g0, %g3
+1:
+	cmp	%o1, 7
+	bleu	7f
+	 mov	%o0, %g1
+
+#if HANDLE_UNALIGNED
+	andcc	%o0, 3, %o2
+	bne	3b
+#endif /* HANDLE_UNALIGNED */
+4:
+	 andcc	%o0, 4, %g0
+
+	be	2f
+	 mov	%g3, %g2
+
+	st	%g3, [%o0]
+	sub	%o1, 4, %o1
+	add	%o0, 4, %o0
+2:
+	andcc	%o1, 0xffffff80, %o3	! Now everything is 8 aligned and o1 is len to run
+	be	9f
+	 andcc	%o1, 0x78, %o2
+4:
+	ZERO_BIG_BLOCK(%o0, 0x00, %g2)
+	subcc	%o3, 128, %o3
+	ZERO_BIG_BLOCK(%o0, 0x40, %g2)
+	bne	4b
+	 add	%o0, 128, %o0
+
+	orcc	%o2, %g0, %g0
+9:
+	be	6f
+	 andcc	%o1, 7, %o1
+
+	srl	%o2, 1, %o3
+	set	bzero_table + 64, %o4
+	sub	%o4, %o3, %o4
+	jmp	%o4
+	 add	%o0, %o2, %o0
+
+bzero_table:
+	ZERO_LAST_BLOCKS(%o0, 0x48, %g2)
+	ZERO_LAST_BLOCKS(%o0, 0x08, %g2)
+
+6:
+	be	8f
+	 andcc	%o1, 4, %g0
+
+	be	1f
+	 andcc	%o1, 2, %g0
+
+	st	%g3, [%o0]
+	add	%o0, 4, %o0
+1:
+	be	1f
+	 andcc	%o1, 1, %g0
+
+	sth	%g3, [%o0]
+	add	%o0, 2, %o0
+1:
+	bne,a	8f
+	 stb	%g3, [%o0]
+8:
+	retl
+	 mov	%g1,%o0
+
+/* Don't care about alignment here. It is highly 
+ * unprobable and at most two traps may happen
+ */
+7:
+	b	6b
+	 orcc	%o1, 0, %g0
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/mul.S linux/arch/sparc/lib/mul.S
--- v2.1.8/linux/arch/sparc/lib/mul.S	Sat Nov 25 02:59:02 1995
+++ linux/arch/sparc/lib/mul.S	Sat Nov  9 10:12:05 1996
@@ -1,4 +1,4 @@
-/* $Id: mul.S,v 1.2 1995/11/25 00:59:00 davem Exp $
+/* $Id: mul.S,v 1.4 1996/09/30 02:22:32 davem Exp $
  * mul.S:       This routine was taken from glibc-1.09 and is covered
  *              by the GNU Library General Public License Version 2.
  */
@@ -20,7 +20,7 @@
 	mov	%o0, %y		! multiplier -> Y
 	andncc	%o0, 0xfff, %g0	! test bits 12..31
 	be	Lmul_shortway	! if zero, can do it the short way
-	andcc	%g0, %g0, %o4	! zero the partial product and clear N and V
+	 andcc	%g0, %g0, %o4	! zero the partial product and clear N and V
 
 	/*
 	 * Long multiply.  32 steps, followed by a final shift step.
@@ -66,23 +66,23 @@
 #if 0
 	tst	%o0
 	bge	1f
-	rd	%y, %o0
+	 rd	%y, %o0
 
 	! %o0 was indeed negative; fix upper 32 bits of result by subtracting 
 	! %o1 (i.e., return %o4 - %o1 in %o1).
 	retl
-	sub	%o4, %o1, %o1
+	 sub	%o4, %o1, %o1
 
 1:
 	retl
-	mov	%o4, %o1
+	 mov	%o4, %o1
 #else
 	/* Faster code adapted from tege@sics.se's code for umul.S.  */
 	sra	%o0, 31, %o2	! make mask from sign bit
 	and	%o1, %o2, %o2	! %o2 = 0 or %o1, depending on sign of %o0
 	rd	%y, %o0		! get lower half of product
 	retl
-	sub	%o4, %o2, %o1	! subtract compensation 
+	 sub	%o4, %o2, %o1	! subtract compensation 
 				!  and put upper half in place
 #endif
 
@@ -125,4 +125,11 @@
 	srl	%o5, 20, %o5	! shift low bits right 20, zero fill at left
 	or	%o5, %o0, %o0	! construct low part of result
 	retl
-	sra	%o4, 20, %o1	! ... and extract high part of result
+	 sra	%o4, 20, %o1	! ... and extract high part of result
+
+	.globl	.mul_patch
+.mul_patch:
+	smul	%o0, %o1, %o0
+	retl
+	 rd	%y, %o1
+	nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/rem.S linux/arch/sparc/lib/rem.S
--- v2.1.8/linux/arch/sparc/lib/rem.S	Sat Nov 25 02:59:04 1995
+++ linux/arch/sparc/lib/rem.S	Sat Nov  9 10:12:07 1996
@@ -1,4 +1,4 @@
-/* $Id: rem.S,v 1.2 1995/11/25 00:59:02 davem Exp $
+/* $Id: rem.S,v 1.7 1996/09/30 02:22:34 davem Exp $
  * rem.S:       This routine was taken from glibc-1.09 and is covered
  *              by the GNU Library General Public License Version 2.
  */
@@ -47,13 +47,14 @@
 	! compute sign of result; if neither is negative, no problem
 	orcc	%o1, %o0, %g0	! either negative?
 	bge	2f			! no, go do the divide
-	xor	%o1, %o0, %g6	! compute sign in any case
+	 mov	%o0, %g2	! compute sign in any case
+
 	tst	%o1
 	bge	1f
-	tst	%o0
+	 tst	%o0
 	! %o1 is definitely negative; %o0 might also be negative
 	bge	2f			! if %o0 not negative...
-	sub	%g0, %o1, %o1	! in any case, make %o1 nonneg
+	 sub	%g0, %o1, %o1	! in any case, make %o1 nonneg
 1:	! %o0 is negative, %o1 is nonnegative
 	sub	%g0, %o0, %o0	! make %o0 nonnegative
 2:
@@ -61,22 +62,24 @@
 	! Ready to divide.  Compute size of quotient; scale comparand.
 	orcc	%o1, %g0, %o5
 	bne	1f
-	mov	%o0, %o3
+	 mov	%o0, %o3
 
 		! Divide by zero trap.  If it returns, return 0 (about as
 		! wrong as possible, but that is what SunOS does...).
 		ta	ST_DIV0
 		retl
-		clr	%o0
+		 clr	%o0
 
 1:
 	cmp	%o3, %o5			! if %o1 exceeds %o0, done
 	blu	Lgot_result		! (and algorithm fails otherwise)
-	clr	%o2
+	 clr	%o2
+
 	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
 	cmp	%o3, %g1
 	blu	Lnot_really_big
-	clr	%o4
+	 clr	%o4
 
 	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
 	! as our usual N-at-a-shot divide step will cause overflow and havoc.
@@ -86,15 +89,19 @@
 	1:
 		cmp	%o5, %g1
 		bgeu	3f
-		mov	1, %g7
+		 mov	1, %g7
+
 		sll	%o5, 4, %o5
+
 		b	1b
-		add	%o4, 1, %o4
+		 add	%o4, 1, %o4
 
 	! Now compute %g7.
-	2:	addcc	%o5, %o5, %o5
+	2:
+		addcc	%o5, %o5, %o5
+
 		bcc	Lnot_too_big
-		add	%g7, 1, %g7
+		 add	%g7, 1, %g7
 
 		! We get here if the %o1 overflowed while shifting.
 		! This means that %o3 has the high-order bit set.
@@ -102,15 +109,18 @@
 		sll	%g1, 4, %g1	! high order bit
 		srl	%o5, 1, %o5		! rest of %o5
 		add	%o5, %g1, %o5
+
 		b	Ldo_single_div
-		sub	%g7, 1, %g7
+		 sub	%g7, 1, %g7
 
 	Lnot_too_big:
-	3:	cmp	%o5, %o3
+	3:
+		cmp	%o5, %o3
 		blu	2b
-		nop
+		 nop
+
 		be	Ldo_single_div
-		nop
+		 nop
 	/* NB: these are commented out in the V8-Sparc manual as well */
 	/* (I do not understand this) */
 	! %o5 > %o3: went too far: back up 1 step
@@ -127,19 +137,23 @@
 	Ldo_single_div:
 		subcc	%g7, 1, %g7
 		bl	Lend_regular_divide
-		nop
+		 nop
+
 		sub	%o3, %o5, %o3
 		mov	1, %o2
+
 		b	Lend_single_divloop
-		nop
+		 nop
 	Lsingle_divloop:
 		sll	%o2, 1, %o2
+
 		bl	1f
-		srl	%o5, 1, %o5
+		 srl	%o5, 1, %o5
 		! %o3 >= 0
 		sub	%o3, %o5, %o3
+
 		b	2f
-		add	%o2, 1, %o2
+		 add	%o2, 1, %o2
 	1:	! %o3 < 0
 		add	%o3, %o5, %o3
 		sub	%o2, 1, %o2
@@ -147,7 +161,8 @@
 	Lend_single_divloop:
 		subcc	%g7, 1, %g7
 		bge	Lsingle_divloop
-		tst	%o3
+		 tst	%o3
+
 		b,a	Lend_regular_divide
 
 Lnot_really_big:
@@ -155,206 +170,213 @@
 	sll	%o5, 4, %o5
 	cmp	%o5, %o3
 	bleu	1b
-	addcc	%o4, 1, %o4
+	 addcc	%o4, 1, %o4
 	be	Lgot_result
-	sub	%o4, 1, %o4
+	 sub	%o4, 1, %o4
 
 	tst	%o3	! set up for initial iteration
 Ldivloop:
 	sll	%o2, 4, %o2
 		! depth 1, accumulated bits 0
 	bl	L.1.16
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 2, accumulated bits 1
 	bl	L.2.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 3
 	bl	L.3.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 7
 	bl	L.4.23
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2+1), %o2
+
+	b	9f
+	 add	%o2, (7*2+1), %o2
 	
 L.4.23:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2-1), %o2
-	
+	b	9f
+	 add	%o2, (7*2-1), %o2
 	
 L.3.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 5
 	bl	L.4.21
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2+1), %o2
+	b	9f
+	 add	%o2, (5*2+1), %o2
 	
 L.4.21:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (5*2-1), %o2
 	
 L.2.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 1
 	bl	L.3.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 3
 	bl	L.4.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
 L.4.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (3*2-1), %o2
+
 L.3.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 1
 	bl	L.4.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2+1), %o2
-	
+	b	9f
+	 add	%o2, (1*2+1), %o2
+
 L.4.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
 L.1.16:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 2, accumulated bits -1
 	bl	L.2.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -1
 	bl	L.3.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -1
 	bl	L.4.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
 L.4.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
 L.3.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -3
 	bl	L.4.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
 L.4.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2-1), %o2
-	
-	
-	
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
 L.2.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -3
 	bl	L.3.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -5
 	bl	L.4.11
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-5*2+1), %o2
+
 L.4.11:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
+
 L.3.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -7
 	bl	L.4.9
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
 L.4.9:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
 	9:
 Lend_regular_divide:
 	subcc	%o4, 1, %o4
 	bge	Ldivloop
-	tst	%o3
+	 tst	%o3
+
 	bl,a	Lgot_result
 	! non-restoring fixup here (one instruction only!)
 	add	%o3, %o1, %o3
 
-
 Lgot_result:
+	! check to see if answer should be < 0
+	tst	%g2
+	bl,a	1f
+	 sub %g0, %o3, %o3
+1:
+	retl
+	 mov %o3, %o0
 
+	.globl	.rem_patch
+.rem_patch:
+	sra	%o0, 0x1f, %o4
+	wr	%o4, 0x0, %y
+	nop
+	nop
+	nop
+	sdivcc	%o0, %o1, %o2
+	bvs,a	1f
+	 xnor	%o2, %g0, %o2
+1:	smul	%o2, %o1, %o2
 	retl
-	mov %o3, %o0
+	 sub	%o0, %o2, %o0
+	nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/sdiv.S linux/arch/sparc/lib/sdiv.S
--- v2.1.8/linux/arch/sparc/lib/sdiv.S	Sat Nov 25 02:59:06 1995
+++ linux/arch/sparc/lib/sdiv.S	Sat Nov  9 10:12:08 1996
@@ -1,4 +1,4 @@
-/* $Id: sdiv.S,v 1.2 1995/11/25 00:59:04 davem Exp $
+/* $Id: sdiv.S,v 1.6 1996/10/02 17:37:00 davem Exp $
  * sdiv.S:      This routine was taken from glibc-1.09 and is covered
  *              by the GNU Library General Public License Version 2.
  */
@@ -47,13 +47,14 @@
 	! compute sign of result; if neither is negative, no problem
 	orcc	%o1, %o0, %g0	! either negative?
 	bge	2f			! no, go do the divide
-	xor	%o1, %o0, %g6	! compute sign in any case
+	 xor	%o1, %o0, %g2	! compute sign in any case
+
 	tst	%o1
 	bge	1f
-	tst	%o0
+	 tst	%o0
 	! %o1 is definitely negative; %o0 might also be negative
 	bge	2f			! if %o0 not negative...
-	sub	%g0, %o1, %o1	! in any case, make %o1 nonneg
+	 sub	%g0, %o1, %o1	! in any case, make %o1 nonneg
 1:	! %o0 is negative, %o1 is nonnegative
 	sub	%g0, %o0, %o0	! make %o0 nonnegative
 2:
@@ -61,22 +62,24 @@
 	! Ready to divide.  Compute size of quotient; scale comparand.
 	orcc	%o1, %g0, %o5
 	bne	1f
-	mov	%o0, %o3
+	 mov	%o0, %o3
 
 		! Divide by zero trap.  If it returns, return 0 (about as
 		! wrong as possible, but that is what SunOS does...).
 		ta	ST_DIV0
 		retl
-		clr	%o0
+		 clr	%o0
 
 1:
 	cmp	%o3, %o5			! if %o1 exceeds %o0, done
 	blu	Lgot_result		! (and algorithm fails otherwise)
-	clr	%o2
+	 clr	%o2
+
 	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
 	cmp	%o3, %g1
 	blu	Lnot_really_big
-	clr	%o4
+	 clr	%o4
 
 	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
 	! as our usual N-at-a-shot divide step will cause overflow and havoc.
@@ -86,15 +89,18 @@
 	1:
 		cmp	%o5, %g1
 		bgeu	3f
-		mov	1, %g7
+		 mov	1, %g7
+
 		sll	%o5, 4, %o5
+
 		b	1b
-		add	%o4, 1, %o4
+		 add	%o4, 1, %o4
 
 	! Now compute %g7.
-	2:	addcc	%o5, %o5, %o5
+	2:
+		addcc	%o5, %o5, %o5
 		bcc	Lnot_too_big
-		add	%g7, 1, %g7
+		 add	%g7, 1, %g7
 
 		! We get here if the %o1 overflowed while shifting.
 		! This means that %o3 has the high-order bit set.
@@ -102,15 +108,18 @@
 		sll	%g1, 4, %g1	! high order bit
 		srl	%o5, 1, %o5		! rest of %o5
 		add	%o5, %g1, %o5
+
 		b	Ldo_single_div
-		sub	%g7, 1, %g7
+		 sub	%g7, 1, %g7
 
 	Lnot_too_big:
-	3:	cmp	%o5, %o3
+	3:
+		cmp	%o5, %o3
 		blu	2b
-		nop
+		 nop
+
 		be	Ldo_single_div
-		nop
+		 nop
 	/* NB: these are commented out in the V8-Sparc manual as well */
 	/* (I do not understand this) */
 	! %o5 > %o3: went too far: back up 1 step
@@ -127,19 +136,23 @@
 	Ldo_single_div:
 		subcc	%g7, 1, %g7
 		bl	Lend_regular_divide
-		nop
+		 nop
+
 		sub	%o3, %o5, %o3
 		mov	1, %o2
+
 		b	Lend_single_divloop
-		nop
+		 nop
 	Lsingle_divloop:
 		sll	%o2, 1, %o2
+
 		bl	1f
-		srl	%o5, 1, %o5
+		 srl	%o5, 1, %o5
 		! %o3 >= 0
 		sub	%o3, %o5, %o3
+
 		b	2f
-		add	%o2, 1, %o2
+		 add	%o2, 1, %o2
 	1:	! %o3 < 0
 		add	%o3, %o5, %o3
 		sub	%o2, 1, %o2
@@ -147,7 +160,8 @@
 	Lend_single_divloop:
 		subcc	%g7, 1, %g7
 		bge	Lsingle_divloop
-		tst	%o3
+		 tst	%o3
+
 		b,a	Lend_regular_divide
 
 Lnot_really_big:
@@ -155,83 +169,81 @@
 	sll	%o5, 4, %o5
 	cmp	%o5, %o3
 	bleu	1b
-	addcc	%o4, 1, %o4
+	 addcc	%o4, 1, %o4
+
 	be	Lgot_result
-	sub	%o4, 1, %o4
+	 sub	%o4, 1, %o4
 
 	tst	%o3	! set up for initial iteration
 Ldivloop:
 	sll	%o2, 4, %o2
 		! depth 1, accumulated bits 0
 	bl	L.1.16
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 2, accumulated bits 1
 	bl	L.2.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 3
 	bl	L.3.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 7
 	bl	L.4.23
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2+1), %o2
-	
+	b	9f
+	 add	%o2, (7*2+1), %o2
+
 L.4.23:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (7*2-1), %o2
+
 L.3.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 5
 	bl	L.4.21
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2+1), %o2
-	
+	b	9f
+	 add	%o2, (5*2+1), %o2
+
 L.4.21:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2-1), %o2
-	
-	
-	
+	b	9f
+	 add	%o2, (5*2-1), %o2
+
 L.2.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 1
 	bl	L.3.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 3
 	bl	L.4.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
 L.4.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2-1), %o2
+	b	9f
+	 add	%o2, (3*2-1), %o2
 	
 	
 L.3.17:
@@ -239,126 +251,129 @@
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 1
 	bl	L.4.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2+1), %o2
-	
+	b	9f
+	 add	%o2, (1*2+1), %o2
+
 L.4.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
 L.1.16:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 2, accumulated bits -1
 	bl	L.2.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -1
 	bl	L.3.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -1
 	bl	L.4.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
 L.4.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
 L.3.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -3
 	bl	L.4.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
 L.4.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2-1), %o2
-	
-	
-	
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
 L.2.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -3
 	bl	L.3.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -5
 	bl	L.4.11
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-5*2+1), %o2
+
 L.4.11:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
 L.3.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -7
 	bl	L.4.9
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
 L.4.9:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
 	9:
 Lend_regular_divide:
 	subcc	%o4, 1, %o4
 	bge	Ldivloop
-	tst	%o3
+	 tst	%o3
+
 	bl,a	Lgot_result
 	! non-restoring fixup here (one instruction only!)
 	sub	%o2, 1, %o2
 
-
 Lgot_result:
 	! check to see if answer should be < 0
-	tst	%g6
+	tst	%g2
 	bl,a	1f
-	sub %g0, %o2, %o2
+	 sub %g0, %o2, %o2
 1:
 	retl
-	mov %o2, %o0
+	 mov %o2, %o0
+
+	.globl	.div_patch
+.div_patch:
+	sra	%o0, 0x1f, %o2
+	wr	%o2, 0x0, %y
+	nop
+	nop
+	nop
+	sdivcc	%o0, %o1, %o0
+	bvs,a	1f
+	 xnor	%o0, %g0, %o0
+1:	retl
+	 nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/strlen.S linux/arch/sparc/lib/strlen.S
--- v2.1.8/linux/arch/sparc/lib/strlen.S	Tue Apr 23 12:31:45 1996
+++ linux/arch/sparc/lib/strlen.S	Sat Nov  9 10:12:08 1996
@@ -12,77 +12,77 @@
 	.align 4
 	.global C_LABEL(strlen)
 C_LABEL(strlen):
-	mov	%o0,%o1
-	andcc	%o0,3,%g0		! and with %o0 so no dependency problems
+	mov	%o0, %o1
+	andcc	%o0, 3, %g0		! and with %o0 so no dependency problems
 	be	scan_words
-	 sethi	%hi(HI_MAGIC),%g2	! common case and most Sparcs predict taken
+	 sethi	%hi(HI_MAGIC), %g2	! common case and most Sparcs predict taken
 
-	ldsb	[%o0],%g2
+	ldsb	[%o0], %g2
 still_not_word_aligned:
-	cmp	%g2,0
+	cmp	%g2, 0
 	bne,a	1f
-	 add	%o0,1,%o0
+	 add	%o0, 1, %o0
 
 	/* Ok, so there are tons of quick interlocks above for the
 	 * < 4 length string unaligned... not too common so I'm not
 	 * very concerned.
 	 */
 	retl
-	 sub	%o0,%o1,%o0
+	 sub	%o0, %o1, %o0
 
 1:
-	andcc	%o0,3,%g0
+	andcc	%o0, 3, %g0
 	bne,a	still_not_word_aligned
-	 ldsb	[%o0],%g2
+	 ldsb	[%o0], %g2
 
 	/* HyperSparc executes each sethi/or pair in 1 cycle. */
-	sethi	%hi(HI_MAGIC),%g2
+	sethi	%hi(HI_MAGIC), %g2
 scan_words:
-	or	%g2,%lo(HI_MAGIC),%o3
-	sethi	%hi(LO_MAGIC),%g3
-	or	%g3,%lo(LO_MAGIC),%o2
+	or	%g2, %lo(HI_MAGIC), %o3
+	sethi	%hi(LO_MAGIC), %g3
+	or	%g3, %lo(LO_MAGIC), %o2
 next_word:
-	ld	[%o0],%g2		! no dependencies
+	ld	[%o0], %g2		! no dependencies
 next_word_preloaded:
-	sub	%g2,%o2,%g2		! lots of locks here
-	andcc	%g2,%o3,%g0		! and I dont like it...
+	sub	%g2, %o2, %g2		! lots of locks here
+	andcc	%g2, %o3, %g0		! and I dont like it...
 	be	next_word
-	 add	%o0,4,%o0
+	 add	%o0, 4, %o0
 
 	/* Check every byte. */
 byte_zero:
-	ldsb	[%o0-4],%g2
-	cmp	%g2,0
+	ldsb	[%o0 - 0x4], %g2
+	cmp	%g2, 0
 	bne	byte_one
-	 add	%o0,-4,%g3
+	 add	%o0, -4, %g3
 
 	retl
-	 sub	%g3,%o1,%o0
+	 sub	%g3, %o1, %o0
 
 byte_one:
-	ldsb	[%o0-3],%g2
-	cmp	%g2,0
+	ldsb	[%o0 - 0x3], %g2
+	cmp	%g2, 0
 	bne,a	byte_two_and_three
-	 ldsb	[%o0-2],%g2
+	 ldsb	[%o0 - 0x2], %g2
 
-	sub	%g3,%o1,%o0
+	sub	%g3, %o1, %o0
 	retl
-	 add	%o0,1,%o0
+	 add	%o0, 1, %o0
 
 byte_two_and_three:
-	cmp	%g2,0
+	cmp	%g2, 0
 	be,a	found_it
-	 sub	%g3,%o1,%o0
+	 sub	%g3, %o1, %o0
 
-	ldsb	[%o0-1],%g2
-	cmp	%g2,0
+	ldsb	[%o0 - 0x1], %g2
+	cmp	%g2, 0
 	bne,a	next_word_preloaded
-	 ld	[%o0],%g2
+	 ld	[%o0], %g2
 
-	sub	%g3,%o1,%o0
+	sub	%g3, %o1, %o0
 	retl
-	 add	%o0,3,%o0
+	 add	%o0, 3, %o0
 
 found_it:
 	retl
-	 add	%o0,2,%o0
+	 add	%o0, 2, %o0
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/strncmp.S linux/arch/sparc/lib/strncmp.S
--- v2.1.8/linux/arch/sparc/lib/strncmp.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/lib/strncmp.S	Sat Nov  9 10:12:09 1996
@@ -0,0 +1,120 @@
+/* $Id: strncmp.S,v 1.2 1996/09/09 02:47:20 davem Exp $
+ * strncmp.S: Hand optimized Sparc assembly of GCC output from GNU libc
+ *            generic strncmp routine.
+ */
+
+#include <asm/cprefix.h>
+
+	.text
+	.align 4
+	.global C_LABEL(__strncmp), C_LABEL(strncmp)
+C_LABEL(__strncmp):
+C_LABEL(strncmp):
+	mov	%o0, %g3
+	mov	0, %o3
+
+	cmp	%o2, 3
+	ble	7f
+	 mov	0, %g2
+
+	sra	%o2, 2, %o4
+	ldub	[%g3], %o3
+
+0:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be,a	1f
+	 ldub	[%g3], %o3
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+	ldub	[%o1], %g2
+	add	%g3,1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be,a	1f
+	 ldub	[%g3], %o3
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be,a	1f
+	 ldub	[%g3], %o3
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be	1f
+	 add	%o4, -1, %o4
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+
+	cmp	%o4, 0
+	bg,a	0b
+	 ldub	[%g3], %o3
+
+	b	7f
+	 and	%o2, 3, %o2
+
+9:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be	7f
+	 add	%o2, -1, %o2
+
+8:
+	retl
+	 sub	%o0, %g2, %o0
+
+7:
+	cmp	%o2, 0
+	bg,a	9b
+	 ldub	[%g3], %o3
+
+	and	%g2, 0xff, %o0
+	retl
+	 sub	%o3, %o0, %o0
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/strncpy_from_user.S linux/arch/sparc/lib/strncpy_from_user.S
--- v2.1.8/linux/arch/sparc/lib/strncpy_from_user.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/lib/strncpy_from_user.S	Sat Nov  9 10:12:09 1996
@@ -0,0 +1,49 @@
+/* strncpy_from_user.S: Sparc strncpy from userspace.
+ *
+ *  Copyright(C) 1996 David S. Miller
+ */
+
+#include <asm/cprefix.h>
+#include <asm/ptrace.h>
+
+	.text
+	.align	4
+
+	/* Must return:
+	 *
+	 * -EFAULT		for an exception
+	 * count		if we hit the buffer limit
+	 * bytes copied		if we hit a null byte
+	 */
+
+	.globl	C_LABEL(__strncpy_from_user)
+C_LABEL(__strncpy_from_user):
+	/* %o0=dest, %o1=src, %o2=count */
+	ld	[%g6 + THREAD_EX_COUNT], %g1
+	set	strncpy_user_failure, %g2
+	add	%g1, 1, %g3
+	st	%o7, [%g6 + THREAD_EX_PC]
+	st	%g3, [%g6 + THREAD_EX_COUNT]
+	st	%g2, [%g6 + THREAD_EX_EXPC]
+
+	mov	%o2, %o3
+1:
+	subcc	%o2, 1, %o2
+	bneg	2f
+	 nop
+
+	ldub	[%o1], %o4
+	add	%o0, 1, %o0
+	cmp	%o4, 0
+	add	%o1, 1, %o1
+	bne	1b
+	 stb	%o4, [%o0 - 1]
+2:
+	add	%o2, 1, %o0
+	st	%g1, [%g6 + THREAD_EX_COUNT]
+	retl
+	 sub	%o3, %o0, %o0
+
+strncpy_user_failure:
+	jmpl	%g3 + 0x8, %g0
+	 mov	%g1, %o0
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/udiv.S linux/arch/sparc/lib/udiv.S
--- v2.1.8/linux/arch/sparc/lib/udiv.S	Sat Nov 25 02:59:08 1995
+++ linux/arch/sparc/lib/udiv.S	Sat Nov  9 10:12:09 1996
@@ -1,4 +1,4 @@
-/* $Id: udiv.S,v 1.2 1995/11/25 00:59:06 davem Exp $
+/* $Id: udiv.S,v 1.4 1996/09/30 02:22:38 davem Exp $
  * udiv.S:      This routine was taken from glibc-1.09 and is covered
  *              by the GNU Library General Public License Version 2.
  */
@@ -48,22 +48,24 @@
 	! Ready to divide.  Compute size of quotient; scale comparand.
 	orcc	%o1, %g0, %o5
 	bne	1f
-	mov	%o0, %o3
+	 mov	%o0, %o3
 
 		! Divide by zero trap.  If it returns, return 0 (about as
 		! wrong as possible, but that is what SunOS does...).
 		ta	ST_DIV0
 		retl
-		clr	%o0
+		 clr	%o0
 
 1:
 	cmp	%o3, %o5			! if %o1 exceeds %o0, done
 	blu	Lgot_result		! (and algorithm fails otherwise)
-	clr	%o2
+	 clr	%o2
+
 	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
 	cmp	%o3, %g1
 	blu	Lnot_really_big
-	clr	%o4
+	 clr	%o4
 
 	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
 	! as our usual N-at-a-shot divide step will cause overflow and havoc.
@@ -73,15 +75,18 @@
 	1:
 		cmp	%o5, %g1
 		bgeu	3f
-		mov	1, %g7
+		 mov	1, %g7
+
 		sll	%o5, 4, %o5
+
 		b	1b
-		add	%o4, 1, %o4
+		 add	%o4, 1, %o4
 
 	! Now compute %g7.
-	2:	addcc	%o5, %o5, %o5
+	2:
+		addcc	%o5, %o5, %o5
 		bcc	Lnot_too_big
-		add	%g7, 1, %g7
+		 add	%g7, 1, %g7
 
 		! We get here if the %o1 overflowed while shifting.
 		! This means that %o3 has the high-order bit set.
@@ -89,15 +94,18 @@
 		sll	%g1, 4, %g1	! high order bit
 		srl	%o5, 1, %o5		! rest of %o5
 		add	%o5, %g1, %o5
+
 		b	Ldo_single_div
-		sub	%g7, 1, %g7
+		 sub	%g7, 1, %g7
 
 	Lnot_too_big:
-	3:	cmp	%o5, %o3
+	3:
+		cmp	%o5, %o3
 		blu	2b
-		nop
+		 nop
+
 		be	Ldo_single_div
-		nop
+		 nop
 	/* NB: these are commented out in the V8-Sparc manual as well */
 	/* (I do not understand this) */
 	! %o5 > %o3: went too far: back up 1 step
@@ -114,19 +122,21 @@
 	Ldo_single_div:
 		subcc	%g7, 1, %g7
 		bl	Lend_regular_divide
-		nop
+		 nop
+
 		sub	%o3, %o5, %o3
 		mov	1, %o2
+
 		b	Lend_single_divloop
-		nop
+		 nop
 	Lsingle_divloop:
 		sll	%o2, 1, %o2
 		bl	1f
-		srl	%o5, 1, %o5
+		 srl	%o5, 1, %o5
 		! %o3 >= 0
 		sub	%o3, %o5, %o3
 		b	2f
-		add	%o2, 1, %o2
+		 add	%o2, 1, %o2
 	1:	! %o3 < 0
 		add	%o3, %o5, %o3
 		sub	%o2, 1, %o2
@@ -134,214 +144,212 @@
 	Lend_single_divloop:
 		subcc	%g7, 1, %g7
 		bge	Lsingle_divloop
-		tst	%o3
+		 tst	%o3
+
 		b,a	Lend_regular_divide
 
 Lnot_really_big:
 1:
 	sll	%o5, 4, %o5
+
 	cmp	%o5, %o3
 	bleu	1b
-	addcc	%o4, 1, %o4
+	 addcc	%o4, 1, %o4
+
 	be	Lgot_result
-	sub	%o4, 1, %o4
+	 sub	%o4, 1, %o4
 
 	tst	%o3	! set up for initial iteration
 Ldivloop:
 	sll	%o2, 4, %o2
 		! depth 1, accumulated bits 0
 	bl	L.1.16
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 2, accumulated bits 1
 	bl	L.2.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 3
 	bl	L.3.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 7
 	bl	L.4.23
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2+1), %o2
-	
+	b	9f
+	 add	%o2, (7*2+1), %o2
+
 L.4.23:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (7*2-1), %o2
+
 L.3.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 5
 	bl	L.4.21
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2+1), %o2
-	
+	b	9f
+	 add	%o2, (5*2+1), %o2
+
 L.4.21:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2-1), %o2
-	
-	
-	
+	b	9f
+	 add	%o2, (5*2-1), %o2
+
 L.2.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 1
 	bl	L.3.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 3
 	bl	L.4.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
 L.4.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (3*2-1), %o2
+
 L.3.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 1
 	bl	L.4.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2+1), %o2
-	
+	b	9f
+	 add	%o2, (1*2+1), %o2
+
 L.4.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
 L.1.16:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 2, accumulated bits -1
 	bl	L.2.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -1
 	bl	L.3.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -1
 	bl	L.4.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
 L.4.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
 L.3.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -3
 	bl	L.4.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
 L.4.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2-1), %o2
-	
-	
-	
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
 L.2.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -3
 	bl	L.3.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -5
 	bl	L.4.11
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-5*2+1), %o2
+
 L.4.11:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
 L.3.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -7
 	bl	L.4.9
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
 L.4.9:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
 	9:
 Lend_regular_divide:
 	subcc	%o4, 1, %o4
 	bge	Ldivloop
-	tst	%o3
+	 tst	%o3
+
 	bl,a	Lgot_result
 	! non-restoring fixup here (one instruction only!)
 	sub	%o2, 1, %o2
 
-
 Lgot_result:
 
 	retl
-	mov %o2, %o0
+	 mov %o2, %o0
+
+	.globl	.udiv_patch
+.udiv_patch:
+	wr	%g0, 0x0, %y
+	nop
+	nop
+	retl
+	 udiv	%o0, %o1, %o0
+	nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/umul.S linux/arch/sparc/lib/umul.S
--- v2.1.8/linux/arch/sparc/lib/umul.S	Sat Nov 25 02:59:11 1995
+++ linux/arch/sparc/lib/umul.S	Sat Nov  9 10:12:10 1996
@@ -1,4 +1,4 @@
-/* $Id: umul.S,v 1.2 1995/11/25 00:59:08 davem Exp $
+/* $Id: umul.S,v 1.4 1996/09/30 02:22:39 davem Exp $
  * umul.S:      This routine was taken from glibc-1.09 and is covered
  *              by the GNU Library General Public License Version 2.
  */
@@ -24,9 +24,10 @@
 .umul:
 	or	%o0, %o1, %o4
 	mov	%o0, %y		! multiplier -> Y
+
 	andncc	%o4, 0xfff, %g0	! test bits 12..31 of *both* args
 	be	Lmul_shortway	! if zero, can do it the short way
-	andcc	%g0, %g0, %o4	! zero the partial product and clear N and V
+	 andcc	%g0, %g0, %o4	! zero the partial product and clear N and V
 
 	/*
 	 * Long multiply.  32 steps, followed by a final shift step.
@@ -103,17 +104,19 @@
 #if 0
 	tst	%o1
 	bl,a	1f		! if %o1 < 0 (high order bit = 1),
-	add	%o4, %o0, %o4	! %o4 += %o0 (add y to upper half)
-1:	rd	%y, %o0		! get lower half of product
+	 add	%o4, %o0, %o4	! %o4 += %o0 (add y to upper half)
+
+1:
+	rd	%y, %o0		! get lower half of product
 	retl
-	addcc	%o4, %g0, %o1	! put upper half in place and set Z for %o1==0
+	 addcc	%o4, %g0, %o1	! put upper half in place and set Z for %o1==0
 #else
 	/* Faster code from tege@sics.se.  */
 	sra	%o1, 31, %o2	! make mask from sign bit
 	and	%o0, %o2, %o2	! %o2 = 0 or %o0, depending on sign of %o1
 	rd	%y, %o0		! get lower half of product
 	retl
-	addcc	%o4, %o2, %o1	! add compensation and put upper half in place
+	 addcc	%o4, %o2, %o1	! add compensation and put upper half in place
 #endif
 
 Lmul_shortway:
@@ -156,4 +159,11 @@
 	srl	%o5, 20, %o5	! shift low bits right 20
 	or	%o5, %o0, %o0
 	retl
-	addcc	%g0, %g0, %o1	! %o1 = zero, and set Z
+	 addcc	%g0, %g0, %o1	! %o1 = zero, and set Z
+
+	.globl	.umul_patch
+.umul_patch:
+	umul	%o0, %o1, %o0
+	retl
+	 rd	%y, %o1
+	nop
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/lib/urem.S linux/arch/sparc/lib/urem.S
--- v2.1.8/linux/arch/sparc/lib/urem.S	Sat Nov 25 02:59:13 1995
+++ linux/arch/sparc/lib/urem.S	Sat Nov  9 10:12:10 1996
@@ -1,4 +1,4 @@
-/* $Id: urem.S,v 1.2 1995/11/25 00:59:11 davem Exp $
+/* $Id: urem.S,v 1.4 1996/09/30 02:22:42 davem Exp $
  * urem.S:      This routine was taken from glibc-1.09 and is covered
  *              by the GNU Library General Public License Version 2.
  */
@@ -46,22 +46,24 @@
 	! Ready to divide.  Compute size of quotient; scale comparand.
 	orcc	%o1, %g0, %o5
 	bne	1f
-	mov	%o0, %o3
+	 mov	%o0, %o3
 
 		! Divide by zero trap.  If it returns, return 0 (about as
 		! wrong as possible, but that is what SunOS does...).
 		ta	ST_DIV0
 		retl
-		clr	%o0
+		 clr	%o0
 
 1:
 	cmp	%o3, %o5			! if %o1 exceeds %o0, done
 	blu	Lgot_result		! (and algorithm fails otherwise)
-	clr	%o2
+	 clr	%o2
+
 	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
 	cmp	%o3, %g1
 	blu	Lnot_really_big
-	clr	%o4
+	 clr	%o4
 
 	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
 	! as our usual N-at-a-shot divide step will cause overflow and havoc.
@@ -71,15 +73,18 @@
 	1:
 		cmp	%o5, %g1
 		bgeu	3f
-		mov	1, %g7
+		 mov	1, %g7
+
 		sll	%o5, 4, %o5
+
 		b	1b
-		add	%o4, 1, %o4
+		 add	%o4, 1, %o4
 
 	! Now compute %g7.
-	2:	addcc	%o5, %o5, %o5
+	2:
+		addcc	%o5, %o5, %o5
 		bcc	Lnot_too_big
-		add	%g7, 1, %g7
+		 add	%g7, 1, %g7
 
 		! We get here if the %o1 overflowed while shifting.
 		! This means that %o3 has the high-order bit set.
@@ -87,15 +92,18 @@
 		sll	%g1, 4, %g1	! high order bit
 		srl	%o5, 1, %o5		! rest of %o5
 		add	%o5, %g1, %o5
+
 		b	Ldo_single_div
-		sub	%g7, 1, %g7
+		 sub	%g7, 1, %g7
 
 	Lnot_too_big:
-	3:	cmp	%o5, %o3
+	3:
+		cmp	%o5, %o3
 		blu	2b
-		nop
+		 nop
+
 		be	Ldo_single_div
-		nop
+		 nop
 	/* NB: these are commented out in the V8-Sparc manual as well */
 	/* (I do not understand this) */
 	! %o5 > %o3: went too far: back up 1 step
@@ -112,19 +120,21 @@
 	Ldo_single_div:
 		subcc	%g7, 1, %g7
 		bl	Lend_regular_divide
-		nop
+		 nop
+
 		sub	%o3, %o5, %o3
 		mov	1, %o2
+
 		b	Lend_single_divloop
-		nop
+		 nop
 	Lsingle_divloop:
 		sll	%o2, 1, %o2
 		bl	1f
-		srl	%o5, 1, %o5
+		 srl	%o5, 1, %o5
 		! %o3 >= 0
 		sub	%o3, %o5, %o3
 		b	2f
-		add	%o2, 1, %o2
+		 add	%o2, 1, %o2
 	1:	! %o3 < 0
 		add	%o3, %o5, %o3
 		sub	%o2, 1, %o2
@@ -132,214 +142,214 @@
 	Lend_single_divloop:
 		subcc	%g7, 1, %g7
 		bge	Lsingle_divloop
-		tst	%o3
+		 tst	%o3
+
 		b,a	Lend_regular_divide
 
 Lnot_really_big:
 1:
 	sll	%o5, 4, %o5
+
 	cmp	%o5, %o3
 	bleu	1b
-	addcc	%o4, 1, %o4
+	 addcc	%o4, 1, %o4
+
 	be	Lgot_result
-	sub	%o4, 1, %o4
+	 sub	%o4, 1, %o4
 
 	tst	%o3	! set up for initial iteration
 Ldivloop:
 	sll	%o2, 4, %o2
 		! depth 1, accumulated bits 0
 	bl	L.1.16
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 2, accumulated bits 1
 	bl	L.2.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 3
 	bl	L.3.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 7
 	bl	L.4.23
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2+1), %o2
-	
+	b	9f
+	 add	%o2, (7*2+1), %o2
+
 L.4.23:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (7*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (7*2-1), %o2
+
 L.3.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 5
 	bl	L.4.21
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2+1), %o2
-	
+	b	9f
+	 add	%o2, (5*2+1), %o2
+
 L.4.21:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (5*2-1), %o2
-	
-	
-	
+	b	9f
+	 add	%o2, (5*2-1), %o2
+
 L.2.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits 1
 	bl	L.3.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 3
 	bl	L.4.19
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
 L.4.19:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (3*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (3*2-1), %o2
+
 L.3.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits 1
 	bl	L.4.17
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2+1), %o2
+	b	9f
+	 add	%o2, (1*2+1), %o2
 	
 L.4.17:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (1*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
 L.1.16:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 2, accumulated bits -1
 	bl	L.2.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -1
 	bl	L.3.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -1
 	bl	L.4.15
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
 L.4.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-1*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
 L.3.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -3
 	bl	L.4.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
 L.4.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-3*2-1), %o2
-	
-	
-	
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
 L.2.15:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 3, accumulated bits -3
 	bl	L.3.13
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -5
 	bl	L.4.11
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2+1), %o2
+	b	9f
+	 add	%o2, (-5*2+1), %o2
 	
 L.4.11:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-5*2-1), %o2
-	
-	
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
 L.3.13:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
 			! depth 4, accumulated bits -7
 	bl	L.4.9
-	srl	%o5,1,%o5
+	 srl	%o5,1,%o5
 	! remainder is positive
 	subcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2+1), %o2
-	
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
 L.4.9:
 	! remainder is negative
 	addcc	%o3,%o5,%o3
-		b	9f
-		add	%o2, (-7*2-1), %o2
-	
-	
-	
-	
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
 	9:
 Lend_regular_divide:
 	subcc	%o4, 1, %o4
 	bge	Ldivloop
-	tst	%o3
+	 tst	%o3
+
 	bl,a	Lgot_result
 	! non-restoring fixup here (one instruction only!)
 	add	%o3, %o1, %o3
 
-
 Lgot_result:
 
 	retl
-	mov %o3, %o0
+	 mov %o3, %o0
+
+	.globl	.urem_patch
+.urem_patch:
+	wr	%g0, 0x0, %y
+	nop
+	nop
+	nop
+	udiv	%o0, %o1, %o2
+	umul	%o2, %o1, %o2
+	retl
+	 sub	%o0, %o2, %o0
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/Makefile linux/arch/sparc/mm/Makefile
--- v2.1.8/linux/arch/sparc/mm/Makefile	Sun Apr 21 17:32:01 1996
+++ linux/arch/sparc/mm/Makefile	Sat Nov  9 10:12:13 1996
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.20 1996/04/21 10:30:43 davem Exp $
+# $Id: Makefile,v 1.21 1996/04/26 10:45:53 tridge Exp $
 # Makefile for the linux Sparc-specific parts of the memory manager.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -8,6 +8,6 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
-O_OBJS   := fault.o init.o sun4c.o srmmu.o loadmmu.o generic.o
+O_OBJS   := fault.o init.o sun4c.o srmmu.o loadmmu.o generic.o asyncd.o
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/asyncd.c linux/arch/sparc/mm/asyncd.c
--- v2.1.8/linux/arch/sparc/mm/asyncd.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/mm/asyncd.c	Sat Nov  9 10:12:15 1996
@@ -0,0 +1,189 @@
+/*  $Id: asyncd.c,v 1.8 1996/09/21 04:30:12 davem Exp $
+ *  The asyncd kernel daemon. This handles paging on behalf of 
+ *  processes that receive page faults due to remote (async) memory
+ *  accesses. 
+ *
+ *  Idea and skeleton code courtesy of David Miller (bless his cotton socks)
+ *
+ *  Implemented by tridge
+ */
+
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/segment.h> /* for memcpy_to/fromfs */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+/* 
+ * The wait queue for waking up the async daemon:
+ */
+static struct wait_queue * asyncd_wait = NULL;
+
+struct async_job {
+	volatile struct async_job *next;
+	int taskid;
+	struct mm_struct *mm;
+	unsigned long address;
+	int write;
+	void (*callback)(int,unsigned long,int,int);
+};
+
+static volatile struct async_job *async_queue = NULL;
+static volatile struct async_job *async_queue_end = NULL;
+
+static void add_to_async_queue(int taskid,
+			       struct mm_struct *mm,
+			       unsigned long address,
+			       int write,
+			       void (*callback)(int,unsigned long,int,int))
+{
+	struct async_job *a = kmalloc(sizeof(*a),GFP_ATOMIC);
+
+	if (!a)
+		panic("out of memory in asyncd\n");
+
+	a->next = NULL;
+	a->taskid = taskid;
+	a->mm = mm;
+	a->address = address;
+	a->write = write;
+	a->callback = callback;
+
+	if (!async_queue) {
+		async_queue = a;
+	} else {
+		async_queue_end->next = a;
+	}
+	async_queue_end = a;
+}
+
+
+void async_fault(unsigned long address, int write, int taskid,
+		 void (*callback)(int,unsigned long,int,int))
+{
+	struct task_struct *tsk = task[taskid];
+	struct mm_struct *mm = tsk->mm;
+
+#if 0
+	printk("paging in %x for task=%d\n",address,taskid);
+#endif
+	add_to_async_queue(taskid, mm, address, write, callback);
+	wake_up(&asyncd_wait);  
+}
+
+static int fault_in_page(int taskid,
+			 struct vm_area_struct *vma,
+			 unsigned address,int write)
+{
+	struct task_struct *tsk = task[taskid];
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	if (!tsk || !tsk->mm)
+		return 1;
+
+	if (!vma || (write && !(vma->vm_flags & VM_WRITE)))
+	  goto bad_area;
+	if (vma->vm_start > address)
+	  goto bad_area;
+	
+	pgd = pgd_offset(vma->vm_mm, address);
+	pmd = pmd_alloc(pgd,address);
+	if(!pmd)
+		goto no_memory;
+	pte = pte_alloc(pmd, address);
+	if(!pte)
+		goto no_memory;
+	if(!pte_present(*pte)) {
+		do_no_page(tsk, vma, address, write);
+		goto finish_up;
+	}
+	set_pte(pte, pte_mkyoung(*pte));
+	flush_tlb_page(vma, address);
+	if(!write)
+		goto finish_up;
+	if(pte_write(*pte)) {
+		set_pte(pte, pte_mkdirty(*pte));
+		flush_tlb_page(vma, address);
+		goto finish_up;
+	}
+	do_wp_page(tsk, vma, address, write);
+
+	/* Fall through for do_wp_page */
+finish_up:
+	update_mmu_cache(vma, address, *pte);
+	return 0;
+
+no_memory:
+	oom(tsk);
+	return 1;
+	
+bad_area:	  
+	tsk->tss.sig_address = address;
+	tsk->tss.sig_desc = SUBSIG_NOMAPPING;
+	send_sig(SIGSEGV, tsk, 1);
+	return 1;
+}
+
+/* Note the semaphore operations must be done here, and _not_
+ * in async_fault().
+ */
+static void run_async_queue(void)
+{
+	int ret;
+	while (async_queue) {
+		volatile struct async_job *a = async_queue;
+		struct mm_struct *mm = a->mm;
+		struct vm_area_struct *vma;
+		async_queue = async_queue->next;
+		down(&mm->mmap_sem);
+		vma = find_vma(mm, a->address);
+		ret = fault_in_page(a->taskid,vma,a->address,a->write);
+		a->callback(a->taskid,a->address,a->write,ret);
+		up(&mm->mmap_sem);
+		kfree_s((void *)a,sizeof(*a));
+	}
+}
+
+
+
+
+/*
+ * The background async daemon.
+ * Started as a kernel thread from the init process.
+ */
+int asyncd(void *unused)
+{
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "asyncd");
+	current->blocked = ~0UL; /* block all signals */
+  
+	/* Give kswapd a realtime priority. */
+	current->policy = SCHED_FIFO;
+	current->priority = 32;  /* Fixme --- we need to standardise our
+				    namings for POSIX.4 realtime scheduling
+				    priorities.  */
+  
+	printk("Started asyncd\n");
+  
+	while (1) {
+		current->signal = 0;
+		interruptible_sleep_on(&asyncd_wait);
+		run_async_queue();
+	}
+}
+
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
--- v2.1.8/linux/arch/sparc/mm/fault.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/mm/fault.c	Sat Nov  9 10:12:16 1996
@@ -1,7 +1,8 @@
-/* $Id: fault.c,v 1.62 1996/04/25 06:09:26 davem Exp $
+/* $Id: fault.c,v 1.77 1996/10/28 00:56:02 davem Exp $
  * fault.c:  Page fault handlers for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
  */
 
 #include <asm/head.h>
@@ -17,8 +18,6 @@
 
 #include <asm/system.h>
 #include <asm/segment.h>
-#include <asm/openprom.h>
-#include <asm/idprom.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/memreg.h>
@@ -122,6 +121,8 @@
 	printk("       Synchronous Vaddr %08lx\n", svaddr);
 	printk("      Asynchronous Error %08lx\n", aerr);
 	printk("      Asynchronous Vaddr %08lx\n", avaddr);
+	if (sun4c_memerr_reg)
+		printk("     Memory Parity Error %08lx\n", *sun4c_memerr_reg);
 	printk("REGISTER DUMP:\n");
 	show_regs(regs);
 	prom_halt();
@@ -131,36 +132,47 @@
 			       unsigned long address)
 {
 	struct vm_area_struct *vma;
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
 	int from_user = !(regs->psr & PSR_PS);
-
 #if 0
-	printk("CPU[%d]: f<pid=%d,tf=%d,wr=%d,addr=%08lx",
-	       smp_processor_id(), current->pid, text_fault,
-	       write, address);
-	printk(",pc=%08lx> ", regs->pc);
+	static unsigned long last_one;
 #endif
 
+	down(&mm->mmap_sem);
 	if(text_fault)
 		address = regs->pc;
 
+#if 0
+	if(current->tss.ex.count) {
+		printk("f<pid=%d,tf=%d,wr=%d,addr=%08lx,pc=%08lx>\n",
+		       tsk->pid, text_fault, write, address, regs->pc);
+		printk("EX: count<%d> pc<%08lx> expc<%08lx> address<%08lx>\n",
+		       (int) current->tss.ex.count, current->tss.ex.pc,
+		       current->tss.ex.expc, current->tss.ex.address);
+#if 0
+		if(last_one == address) {
+			printk("Twice in a row, AIEEE.  Spinning so you can see the dump.\n");
+			show_regs(regs);
+			sti();
+			while(1)
+				barrier();
+		}
+		last_one = address;
+#endif
+	}
+#endif
 	/* Now actually handle the fault.  Do kernel faults special,
 	 * because on the sun4c we could have faulted trying to read
 	 * the vma area of the task and without the following code
 	 * we'd fault recursively until all our stack is gone. ;-(
 	 */
-	if(!from_user && address >= KERNBASE) {
-#ifdef __SMP__
-		printk("CPU[%d]: Kernel faults at addr=%08lx\n",
-		       smp_processor_id(), address);
-		while(1)
-			;
-#else
+	if(!from_user && address >= PAGE_OFFSET) {
 		quick_kernel_fault(address);
 		return;
-#endif
 	}
 
-	vma = find_vma(current, address);
+	vma = find_vma(mm, address);
 	if(!vma)
 		goto bad_area;
 	if(vma->vm_start <= address)
@@ -183,40 +195,98 @@
 			goto bad_area;
 	}
 	handle_mm_fault(vma, address, write);
+	up(&mm->mmap_sem);
 	return;
 	/*
 	 * Something tried to access memory that isn't in our memory map..
 	 * Fix it, but check if it's kernel or user first..
 	 */
 bad_area:
+	up(&mm->mmap_sem);
+	/* Did we have an exception handler installed? */
+	if(current->tss.ex.count == 1) {
+		if(from_user) {
+			printk("Yieee, exception signalled from user mode.\n");
+		} else {
+			/* Set pc to %g1, set %g1 to -EFAULT and %g2 to
+			 * the faulting address so we can cleanup.
+			 */
+			printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
+			printk("EX: count<%d> pc<%08lx> expc<%08lx> address<%08lx>\n",
+			       (int) current->tss.ex.count, current->tss.ex.pc,
+			       current->tss.ex.expc, current->tss.ex.address);
+			current->tss.ex.count = 0;
+			regs->pc = current->tss.ex.expc;
+			regs->npc = regs->pc + 4;
+			regs->u_regs[UREG_G1] = -EFAULT;
+			regs->u_regs[UREG_G2] = address - current->tss.ex.address;
+			regs->u_regs[UREG_G3] = current->tss.ex.pc;
+			return;
+		}
+	}
 	if(from_user) {
 #if 0
-		printk("%s [%d]: segfaults at %08lx pc=%08lx\n",
-		       current->comm, current->pid, address, regs->pc);
+		printk("Fault whee %s [%d]: segfaults at %08lx pc=%08lx\n",
+		       tsk->comm, tsk->pid, address, regs->pc);
 #endif
-		current->tss.sig_address = address;
-		current->tss.sig_desc = SUBSIG_NOMAPPING;
-		send_sig(SIGSEGV, current, 1);
+		tsk->tss.sig_address = address;
+		tsk->tss.sig_desc = SUBSIG_NOMAPPING;
+		send_sig(SIGSEGV, tsk, 1);
 		return;
 	}
 	if((unsigned long) address < PAGE_SIZE) {
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
 	} else
 		printk(KERN_ALERT "Unable to handle kernel paging request");
-	printk(" at virtual address %08lx\n",address);
-	printk(KERN_ALERT "current->mm->context = %08lx\n",
-	       (unsigned long) current->mm->context);
-	printk(KERN_ALERT "current->mm->pgd = %08lx\n",
-	       (unsigned long) current->mm->pgd);
+	printk(KERN_ALERT " at virtual address %08lx\n",address);
+	printk(KERN_ALERT "tsk->mm->context = %08lx\n",
+	       (unsigned long) tsk->mm->context);
+	printk(KERN_ALERT "tsk->mm->pgd = %08lx\n",
+	       (unsigned long) tsk->mm->pgd);
 	die_if_kernel("Oops", regs);
 }
 
+asmlinkage void do_sun4c_fault(struct pt_regs *regs, int text_fault, int write,
+			       unsigned long address)
+{
+	extern void sun4c_update_mmu_cache(struct vm_area_struct *,unsigned long,pte_t);
+	extern pgd_t *sun4c_pgd_offset(struct mm_struct *,unsigned long);
+	extern pte_t *sun4c_pte_offset(pmd_t *,unsigned long);
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+	pgd_t *pgd;
+	pte_t *pte;
+
+	if(text_fault)
+		address = regs->pc;
+
+	pgd = sun4c_pgd_offset(mm, address);
+	pte = sun4c_pte_offset((pmd_t *) pgd, address);
+
+	/* This conditional is 'interesting'. */
+	if(pgd_val(*pgd) && !(write && !(pte_val(*pte) & _SUN4C_PAGE_WRITE))
+	   && (pte_val(*pte) & _SUN4C_PAGE_VALID))
+		/* XXX Very bad, can't do this optimization when VMA arg is actually
+		 * XXX used by update_mmu_cache()!
+		 */
+		sun4c_update_mmu_cache((struct vm_area_struct *) 0, address, *pte);
+	else
+		do_sparc_fault(regs, text_fault, write, address);
+}
+
 /* This always deals with user addresses. */
 inline void force_user_fault(unsigned long address, int write)
 {
 	struct vm_area_struct *vma;
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
 
-	vma = find_vma(current, address);
+#if 0
+	printk("wf<pid=%d,wr=%d,addr=%08lx>\n",
+	       tsk->pid, write, address);
+#endif
+	down(&mm->mmap_sem);
+	vma = find_vma(mm, address);
 	if(!vma)
 		goto bad_area;
 	if(vma->vm_start <= address)
@@ -233,18 +303,25 @@
 		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
 			goto bad_area;
 	handle_mm_fault(vma, address, write);
+	up(&mm->mmap_sem);
 	return;
 bad_area:
-	current->tss.sig_address = address;
-	current->tss.sig_desc = SUBSIG_NOMAPPING;
-	send_sig(SIGSEGV, current, 1);
+	up(&mm->mmap_sem);
+#if 0
+	printk("Window whee %s [%d]: segfaults at %08lx\n",
+	       tsk->comm, tsk->pid, address);
+#endif
+	tsk->tss.sig_address = address;
+	tsk->tss.sig_desc = SUBSIG_NOMAPPING;
+	send_sig(SIGSEGV, tsk, 1);
 	return;
 }
 
 void window_overflow_fault(void)
 {
-	unsigned long sp = current->tss.rwbuf_stkptrs[0];
+	unsigned long sp;
 
+	sp = current->tss.rwbuf_stkptrs[0];
 	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
 		force_user_fault(sp + 0x38, 1);
 	force_user_fault(sp, 1);
@@ -259,8 +336,9 @@
 
 void window_ret_fault(struct pt_regs *regs)
 {
-	unsigned long sp = regs->u_regs[UREG_FP];
+	unsigned long sp;
 
+	sp = regs->u_regs[UREG_FP];
 	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
 		force_user_fault(sp + 0x38, 0);
 	force_user_fault(sp, 0);
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/generic.c linux/arch/sparc/mm/generic.c
--- v2.1.8/linux/arch/sparc/mm/generic.c	Thu Apr 25 13:22:05 1996
+++ linux/arch/sparc/mm/generic.c	Sat Nov  9 10:12:16 1996
@@ -1,4 +1,4 @@
-/* $Id: generic.c,v 1.2 1996/04/25 06:09:30 davem Exp $
+/* $Id: generic.c,v 1.4 1996/10/27 08:36:41 davem Exp $
  * generic.c: Generic Sparc mm routines that are not dependent upon
  *            MMU type but are Sparc specific.
  *
@@ -12,13 +12,34 @@
 #include <asm/pgtable.h>
 #include <asm/page.h>
 
+
+/* Allocate a block of RAM which is aligned to its size.
+ * This procedure can be used until the call to mem_init().
+ */
+void *sparc_init_alloc(unsigned long *kbrk, unsigned long size)
+{
+        unsigned long mask = size - 1;
+        unsigned long ret;
+
+        if(!size)
+                return 0x0;
+        if(size & mask) {
+                prom_printf("panic: sparc_init_alloc botch\n");
+                prom_halt();
+        }
+        ret = (*kbrk + mask) & ~mask;
+        *kbrk = ret + size;
+        memset((void*) ret, 0, size);
+        return (void*) ret;
+}
+
 static inline void forget_pte(pte_t page)
 {
 	if (pte_none(page))
 		return;
 	if (pte_present(page)) {
 		unsigned long addr = pte_page(page);
-		if (addr >= high_memory || PageReserved(mem_map+MAP_NR(addr)))
+		if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr)))
 			return;
 		free_page(addr);
 		if (current->mm->rss <= 0)
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/init.c linux/arch/sparc/mm/init.c
--- v2.1.8/linux/arch/sparc/mm/init.c	Wed Oct  9 08:55:18 1996
+++ linux/arch/sparc/mm/init.c	Sat Nov  9 10:12:17 1996
@@ -1,7 +1,8 @@
-/*  $Id: init.c,v 1.37 1996/04/25 06:09:33 davem Exp $
+/*  $Id: init.c,v 1.42 1996/10/27 08:36:44 davem Exp $
  *  linux/arch/sparc/mm/init.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be)
  */
 
 #include <linux/config.h>
@@ -16,6 +17,9 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/swap.h>
+#ifdef CONFIG_BLK_DEV_INITRD
+#include <linux/blk.h>
+#endif
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -27,6 +31,7 @@
 extern void show_net_buffers(void);
 
 struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+unsigned long sparc_unmapped_base;
 
 /*
  * BAD_PAGE is the page that is used for page faults when linux
@@ -61,7 +66,7 @@
 	printk("\nMem-info:\n");
 	show_free_areas();
 	printk("Free swap:       %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
-	i = MAP_NR(high_memory);
+	i = max_mapnr;
 	while (i-- > 0) {
 		total++;
 		if (PageReserved(mem_map + i))
@@ -118,10 +123,12 @@
 	case sun4c:
 	case sun4e:
 		start_mem = sun4c_paging_init(start_mem, end_mem);
+		sparc_unmapped_base = 0xe0000000;
 		break;
 	case sun4m:
 	case sun4d:
 		start_mem = srmmu_paging_init(start_mem, end_mem);
+		sparc_unmapped_base = 0x50000000;
 		break;
 	default:
 		prom_printf("paging_init: Cannot init paging on this Sparc\n");
@@ -130,9 +137,7 @@
 		prom_halt();
 	};
 
-	/* Initialize the protection map with non-constant values
-	 * MMU dependent values.
-	 */
+	/* Initialize the protection map with non-constant, MMU dependent values. */
 	protection_map[0] = PAGE_NONE;
 	protection_map[1] = PAGE_READONLY;
 	protection_map[2] = PAGE_COPY;
@@ -157,6 +162,7 @@
 extern int min_free_pages;
 extern int free_pages_low;
 extern int free_pages_high;
+extern void srmmu_frob_mem_map(unsigned long);
 
 int physmem_mapped_contig = 1;
 
@@ -165,7 +171,9 @@
 	unsigned long addr, tmp2 = 0;
 
 	if(physmem_mapped_contig) {
-		for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE) {
+		for(addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
+			if(addr >= KERNBASE && addr < start_mem)
+				addr = start_mem;
 			for(tmp2=0; sp_banks[tmp2].num_bytes != 0; tmp2++) {
 				unsigned long phys_addr = (addr - PAGE_OFFSET);
 				unsigned long base = sp_banks[tmp2].base_addr;
@@ -177,8 +185,12 @@
 			}
 		}
 	} else {
-		for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE)
-			mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+		if((sparc_cpu_model == sun4m) || (sparc_cpu_model == sun4d)) {
+			srmmu_frob_mem_map(start_mem);
+		} else {
+			for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE)
+				mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+		}
 	}
 }
 
@@ -193,42 +205,51 @@
 	memset((void *) ZERO_PAGE, 0, PAGE_SIZE);
 
 	end_mem &= PAGE_MASK;
-	high_memory = end_mem;
+	max_mapnr = MAP_NR(end_mem);
+	high_memory = (void *) end_mem;
 
 	start_mem = PAGE_ALIGN(start_mem);
 
-	addr = PAGE_OFFSET;
+	addr = KERNBASE;
 	while(addr < start_mem) {
-		mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+#ifdef CONFIG_BLK_DEV_INITRD
+		if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
+			mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+		else
+#endif	
+			mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
 		addr += PAGE_SIZE;
 	}
 
 	taint_real_pages(start_mem, end_mem);
 	for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
 		if(PageReserved(mem_map + MAP_NR(addr))) {
-			if (addr < (unsigned long) &etext)
+			if ((addr < (unsigned long) &etext) && (addr >= KERNBASE))
 				codepages++;
-			else if(addr < start_mem)
+			else if((addr < start_mem) && (addr >= KERNBASE))
 				datapages++;
 			continue;
 		}
 		mem_map[MAP_NR(addr)].count = 1;
-		free_page(addr);
+#ifdef CONFIG_BLK_DEV_INITRD
+		if (!initrd_start ||
+		    (addr < initrd_start || addr >= initrd_end))
+#endif
+			free_page(addr);
 	}
 
 	tmp2 = nr_free_pages << PAGE_SHIFT;
 
-	printk("Memory: %luk available (%dk kernel code, %dk data)\n",
+	printk("Memory: %luk available (%dk kernel code, %dk data) [%08lx,%08lx]\n",
 	       tmp2 >> 10,
 	       codepages << (PAGE_SHIFT-10),
-	       datapages << (PAGE_SHIFT-10));
+	       datapages << (PAGE_SHIFT-10), PAGE_OFFSET, end_mem);
 
 	min_free_pages = nr_free_pages >> 7;
 	if(min_free_pages < 16)
 		min_free_pages = 16;
 	free_pages_low = min_free_pages + (min_free_pages >> 1);
 	free_pages_high = min_free_pages + min_free_pages;
-
 }
 
 void si_meminfo(struct sysinfo *val)
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/loadmmu.c linux/arch/sparc/mm/loadmmu.c
--- v2.1.8/linux/arch/sparc/mm/loadmmu.c	Sun Apr 21 17:32:01 1996
+++ linux/arch/sparc/mm/loadmmu.c	Sat Nov  9 10:12:17 1996
@@ -1,4 +1,4 @@
-/* $Id: loadmmu.c,v 1.33 1996/04/21 10:32:26 davem Exp $
+/* $Id: loadmmu.c,v 1.36 1996/10/27 08:36:46 davem Exp $
  * loadmmu.c:  This code loads up all the mm function pointers once the
  *             machine type has been determined.  It also sets the static
  *             mmu values such as PAGE_NONE, etc.
@@ -13,6 +13,8 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 
+unsigned long page_offset = 0xf0000000;
+
 struct ctx_list *ctx_list_pool;
 struct ctx_list ctx_free;
 struct ctx_list ctx_used;
@@ -39,6 +41,8 @@
 void  (*mmu_release_scsi_one)(char *, unsigned long, struct linux_sbus *sbus);
 void  (*mmu_release_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
 
+void  (*mmu_map_dma_area)(unsigned long addr, int len);
+
 void (*update_mmu_cache)(struct vm_area_struct *vma, unsigned long address, pte_t pte);
 
 #ifdef __SMP__
@@ -104,6 +108,7 @@
 void (*pgd_clear)(pgd_t *);
 
 pte_t (*mk_pte)(unsigned long, pgprot_t);
+pte_t (*mk_pte_phys)(unsigned long, pgprot_t);
 pte_t (*mk_pte_io)(unsigned long, pgprot_t, int);
 void (*pgd_set)(pgd_t *, pmd_t *);
 pte_t (*pte_modify)(pte_t, pgprot_t);
@@ -149,7 +154,6 @@
 		break;
 	case sun4m:
 	case sun4d:
-	case sun4e:
 		ld_mmu_srmmu();
 		break;
 	default:
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.8/linux/arch/sparc/mm/srmmu.c	Mon May  6 12:26:03 1996
+++ linux/arch/sparc/mm/srmmu.c	Sat Nov  9 10:12:20 1996
@@ -1,14 +1,15 @@
-/* $Id: srmmu.c,v 1.62 1996/04/25 09:11:47 davem Exp $
+/* $Id: srmmu.c,v 1.103 1996/10/31 06:28:35 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
- * Copyright (C) 1996 Eddie C. Dost    (ecd@pool.informatik.rwth-aachen.de)
+ * Copyright (C) 1996 Eddie C. Dost    (ecd@skynet.be)
  */
 
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/malloc.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -34,8 +35,11 @@
 
 enum mbus_module srmmu_modtype;
 unsigned int hwbug_bitmask;
-int hyper_cache_size;
-int hyper_line_size;
+int vac_cache_size;
+int vac_line_size;
+int vac_badbits;
+
+extern unsigned long sparc_iobase_vaddr;
 
 #ifdef __SMP__
 extern void smp_capture(void);
@@ -45,6 +49,8 @@
 #define smp_release()
 #endif /* !(__SMP__) */
 
+/* #define USE_CHUNK_ALLOC 1 */
+
 static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
 static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
 
@@ -72,12 +78,37 @@
 static struct srmmu_trans {
 	unsigned long vbase;
 	unsigned long pbase;
-	int size;
+	unsigned long size;
 } srmmu_map[SPARC_PHYS_BANKS];
 
-static int can_cache_ptables = 0;
 static int viking_mxcc_present = 0;
 
+void srmmu_frob_mem_map(unsigned long start_mem)
+{
+	unsigned long bank_start, bank_end;
+	unsigned long addr;
+	int i;
+
+	/* First, mark all pages as invalid. */
+	for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
+		mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+
+	start_mem = PAGE_ALIGN(start_mem);
+	for(i = 0; srmmu_map[i].size; i++) {
+		bank_start = srmmu_map[i].vbase;
+		bank_end = bank_start + srmmu_map[i].size;
+		while(bank_start < bank_end) {
+			if((bank_start >= KERNBASE) &&
+			   (bank_start < start_mem)) {
+				bank_start += PAGE_SIZE;
+				continue;
+			}
+			mem_map[MAP_NR(bank_start)].flags &= ~(1<<PG_reserved);
+			bank_start += PAGE_SIZE;
+		}
+	}
+}
+
 /* Physical memory can be _very_ non-contiguous on the sun4m, especially
  * the SS10/20 class machines and with the latest openprom revisions.
  * So we have to crunch the free page pool.
@@ -88,8 +119,9 @@
 
 	for(i=0; srmmu_map[i].size != 0; i++) {
 		if(srmmu_map[i].vbase <= vaddr &&
-		   (srmmu_map[i].vbase + srmmu_map[i].size > vaddr))
+		   (srmmu_map[i].vbase + srmmu_map[i].size > vaddr)) {
 			return (vaddr - srmmu_map[i].vbase) + srmmu_map[i].pbase;
+		}
 	}
 	return 0xffffffffUL;
 }
@@ -112,11 +144,11 @@
  */
 static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
 {
-#if CONFIG_AP1000
+#if MEM_BUS_SPACE
   /* the AP1000 has its memory on bus 8, not 0 like suns do */
-  if (!(value&0xf0000000))
-    value |= 0x80000000;
-  if (value == 0x80000000) value = 0;
+  if (!(value&KERNBASE))
+    value |= MEM_BUS_SPACE<<28;
+  if (value == MEM_BUS_SPACE<<28) value = 0;
 #endif
 	__asm__ __volatile__("swap [%2], %0\n\t" :
 			     "=&r" (value) :
@@ -144,8 +176,13 @@
 static unsigned long srmmu_pmd_page(pmd_t pmd)
 { return srmmu_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
 
+static inline int srmmu_device_memory(pte_t pte) 
+{
+	return (pte_val(pte)>>28) != MEM_BUS_SPACE;
+}
+
 static unsigned long srmmu_pte_page(pte_t pte)
-{ return srmmu_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
+{ return srmmu_device_memory(pte)?~0:srmmu_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
 
 static int srmmu_pte_none(pte_t pte)          { return !pte_val(pte); }
 static int srmmu_pte_present(pte_t pte)
@@ -189,6 +226,9 @@
 static pte_t srmmu_mk_pte(unsigned long page, pgprot_t pgprot)
 { pte_t pte; pte_val(pte) = ((srmmu_v2p(page)) >> 4) | pgprot_val(pgprot); return pte; }
 
+static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot)
+{ pte_t pte; pte_val(pte) = ((page) >> 4) | pgprot_val(pgprot); return pte; }
+
 static pte_t srmmu_mk_pte_io(unsigned long page, pgprot_t pgprot, int space)
 {
 	pte_t pte;
@@ -198,21 +238,24 @@
 
 static void srmmu_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
 { 
-	srmmu_set_entry(ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
+	set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
 }
 
 static void srmmu_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
 {
-	srmmu_set_entry(pgdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pmdp) >> 4)));
+	set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pmdp) >> 4)));
 }
 
 static void srmmu_pmd_set(pmd_t * pmdp, pte_t * ptep)
 {
-	srmmu_set_entry(pmdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) ptep) >> 4)));
+	set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) ptep) >> 4)));
 }
 
 static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
-{ pte_val(pte) = (pte_val(pte) & ~0xff) | pgprot_val(newprot); return pte; }
+{
+	pte_val(pte) = (pte_val(pte) & SRMMU_CHG_MASK) | pgprot_val(newprot);
+	return pte;
+}
 
 /* to find an entry in a top-level page table... */
 static pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address)
@@ -235,15 +278,29 @@
 /* This must update the context table entry for this process. */
 static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
-	if(tsk->mm->context != NO_CONTEXT)
+	if(tsk->mm->context != NO_CONTEXT) {
+		flush_cache_mm(current->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+		flush_tlb_mm(current->mm);
+	}
 }
 
 static inline void srmmu_uncache_page(unsigned long addr)
 {
 	pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, addr);
-	pmd_t *pmdp = srmmu_pmd_offset(pgdp, addr);
-	pte_t *ptep = srmmu_pte_offset(pmdp, addr);
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	if((pgd_val(*pgdp) & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
+		ptep = (pte_t *) pgdp;
+	} else {
+		pmdp = srmmu_pmd_offset(pgdp, addr);
+		if((pmd_val(*pmdp) & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
+			ptep = (pte_t *) pmdp;
+		} else {
+			ptep = srmmu_pte_offset(pmdp, addr);
+		}
+	}
 
 	flush_cache_page_to_uncache(addr);
 	set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE)));
@@ -253,9 +310,19 @@
 static inline void srmmu_recache_page(unsigned long addr)
 {
 	pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, addr);
-	pmd_t *pmdp = srmmu_pmd_offset(pgdp, addr);
-	pte_t *ptep = srmmu_pte_offset(pmdp, addr);
+	pmd_t *pmdp;
+	pte_t *ptep;
 
+	if((pgd_val(*pgdp) & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
+		ptep = (pte_t *) pgdp;
+	} else {
+		pmdp = srmmu_pmd_offset(pgdp, addr);
+		if((pmd_val(*pmdp) & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
+			ptep = (pte_t *) pmdp;
+		} else {
+			ptep = srmmu_pte_offset(pmdp, addr);
+		}
+	}
 	set_pte(ptep, __pte((pte_val(*ptep) | SRMMU_CACHE)));
 	flush_tlb_page_for_cbit(addr);
 }
@@ -264,21 +331,259 @@
 {
 	unsigned long page = get_free_page(GFP_KERNEL);
 
-	if (can_cache_ptables)
-		return page;
-
-	if(page)
-		srmmu_uncache_page(page);
 	return page;
 }
 
 static inline void srmmu_putpage(unsigned long page)
 {
-	if (!can_cache_ptables)
-		srmmu_recache_page(page);
 	free_page(page);
 }
 
+#ifdef USE_CHUNK_ALLOC
+
+#define LC_HIGH_WATER	128
+#define BC_HIGH_WATER	32
+
+static unsigned long *lcnks = 0;
+static unsigned long *bcnks = 0;
+static int lcwater = 0;
+static int bcwater = 0;
+static int chunk_pages = 0;
+static int clct_pages = 0;
+
+#define RELAX_JIFFIES	16
+
+static int lcjiffies;
+static int bcjiffies;
+
+struct chunk {
+	struct chunk *next;
+	struct chunk *prev;
+	struct chunk *npage;
+	struct chunk *ppage;
+	int count;
+};
+
+static int garbage_calls = 0;
+
+#define OTHER_PAGE(p,q)	(((unsigned long)(p) ^ (unsigned long)(q)) & PAGE_MASK)
+
+static inline int garbage_collect(unsigned long **cnks, int n, int cpp)
+{
+	struct chunk *root = (struct chunk *)*cnks;
+	struct chunk *p, *q, *curr, *next;
+	int water = n;
+
+	next = root->next;
+	curr = root->prev = root->next = root->npage = root->ppage = root;
+	root->count = 1;
+
+	garbage_calls++;
+
+	while (--n) {
+		p = next;
+		next = next->next;
+
+		if (OTHER_PAGE(p, curr)) {
+
+			q = curr->npage;
+			while (q != curr) {
+				if (!OTHER_PAGE(p, q))
+					break;
+				q = q->npage;
+			}
+
+			if (q == curr) {
+
+				(p->npage = curr->npage)->ppage = p;
+				curr->npage = p;
+				p->ppage = curr;
+
+				p->next = p->prev = p;
+				p->count = 1;
+
+				curr = p;
+
+				continue;
+			}
+			curr = q;
+		}
+
+		(p->next = curr->next)->prev = p;
+		curr->next = p;
+		p->prev = curr;
+
+		if (++curr->count == cpp) {
+
+			q = curr->npage;
+			if (curr == q) {
+
+				srmmu_putpage((unsigned long)curr & PAGE_MASK);
+				water -= cpp;
+
+				clct_pages++;
+				chunk_pages--;
+
+				if (--n) {
+					p = next;
+					next = next->next;
+
+					curr = root->prev =
+						root->next = root->npage =
+						root->ppage = root = p;
+					root->count = 1;
+
+					continue;
+				}
+				return 0;
+			}
+
+			if (curr == root)
+				root = q;
+
+			curr->ppage->npage = q;
+			q->ppage = curr->ppage;
+
+			srmmu_putpage((unsigned long)curr & PAGE_MASK);
+			water -= cpp;
+
+			clct_pages++;
+			chunk_pages--;
+
+			curr = q;
+		}
+	}
+
+	p = root;
+	while (p->npage != root) {
+		p->prev->next = p->npage;
+		p = p->npage;
+	}
+
+	*cnks = (unsigned long *)root;
+	return water;
+}
+
+
+static inline unsigned long *get_small_chunk(void)
+{
+	unsigned long *rval;
+	unsigned long flags;
+
+	save_and_cli(flags);
+	if(lcwater) {
+		lcwater--;
+		rval = lcnks;
+		lcnks = (unsigned long *) *rval;
+	} else {
+		rval = (unsigned long *) __get_free_page(GFP_KERNEL);
+
+		if(!rval) {
+			restore_flags(flags);
+			return 0;
+		}
+		chunk_pages++;
+
+		lcnks = (rval + 64);
+
+		/* Cache stomping, I know... */
+		*(rval + 64) = (unsigned long) (rval + 128);
+		*(rval + 128) = (unsigned long) (rval + 192);
+		*(rval + 192) = (unsigned long) (rval + 256);
+		*(rval + 256) = (unsigned long) (rval + 320);
+		*(rval + 320) = (unsigned long) (rval + 384);
+		*(rval + 384) = (unsigned long) (rval + 448);
+		*(rval + 448) = (unsigned long) (rval + 512);
+		*(rval + 512) = (unsigned long) (rval + 576);
+		*(rval + 576) = (unsigned long) (rval + 640);
+		*(rval + 640) = (unsigned long) (rval + 704);
+		*(rval + 704) = (unsigned long) (rval + 768);
+		*(rval + 768) = (unsigned long) (rval + 832);
+		*(rval + 832) = (unsigned long) (rval + 896);
+		*(rval + 896) = (unsigned long) (rval + 960);
+		*(rval + 960) = 0;
+		lcwater = 15;
+	}
+	lcjiffies = jiffies;
+	restore_flags(flags);
+	memset(rval, 0, 256);
+	return rval;
+}
+
+static inline void free_small_chunk(unsigned long *it)
+{
+	unsigned long flags;
+
+	save_and_cli(flags);
+	*it = (unsigned long) lcnks;
+	lcnks = it;
+	lcwater++;
+
+	if ((lcwater > LC_HIGH_WATER) &&
+	    (jiffies > lcjiffies + RELAX_JIFFIES))
+		lcwater = garbage_collect(&lcnks, lcwater, 16);
+
+	restore_flags(flags);
+}
+
+static inline unsigned long *get_big_chunk(void)
+{
+	unsigned long *rval;
+	unsigned long flags;
+
+	save_and_cli(flags);
+	if(bcwater) {
+		bcwater--;
+		rval = bcnks;
+		bcnks = (unsigned long *) *rval;
+	} else {
+		rval = (unsigned long *) __get_free_page(GFP_KERNEL);
+
+		if(!rval) {
+			restore_flags(flags);
+			return 0;
+		}
+		chunk_pages++;
+
+		bcnks = (rval + 256);
+
+		/* Cache stomping, I know... */
+		*(rval + 256) = (unsigned long) (rval + 512);
+		*(rval + 512) = (unsigned long) (rval + 768);
+		*(rval + 768) = 0;
+		bcwater = 3;
+	}
+	bcjiffies = jiffies;
+	restore_flags(flags);
+	memset(rval, 0, 1024);
+	return rval;
+}
+
+static inline void free_big_chunk(unsigned long *it)
+{
+	unsigned long flags;
+
+	save_and_cli(flags);
+	*it = (unsigned long) bcnks;
+	bcnks = it;
+	bcwater++;
+
+	if ((bcwater > BC_HIGH_WATER) &&
+	    (jiffies > bcjiffies + RELAX_JIFFIES))
+		bcwater = garbage_collect(&bcnks, bcwater, 4);
+
+	restore_flags(flags);
+}
+
+#define NEW_PGD() (pgd_t *) get_big_chunk()
+#define NEW_PMD() (pmd_t *) get_small_chunk()
+#define NEW_PTE() (pte_t *) get_small_chunk()
+#define FREE_PGD(chunk) free_big_chunk((unsigned long *)(chunk))
+#define FREE_PMD(chunk) free_small_chunk((unsigned long *)(chunk))
+#define FREE_PTE(chunk) free_small_chunk((unsigned long *)(chunk))
+
+#else
+
 /* The easy versions. */
 #define NEW_PGD() (pgd_t *) srmmu_getpage()
 #define NEW_PMD() (pmd_t *) srmmu_getpage()
@@ -287,6 +592,8 @@
 #define FREE_PMD(chunk) srmmu_putpage((unsigned long)(chunk))
 #define FREE_PTE(chunk) srmmu_putpage((unsigned long)(chunk))
 
+#endif
+
 /*
  * Allocate and free page tables. The xxx_kernel() versions are
  * used to allocate a kernel page table - this turns on ASN bits
@@ -329,7 +636,8 @@
 {
 	address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
 	if(srmmu_pgd_none(*pgd)) {
-		pmd_t *page = NEW_PMD();
+		pmd_t *page;
+		page = NEW_PMD();
 		if(srmmu_pgd_none(*pgd)) {
 			if(page) {
 				pgd_set(pgd, page);
@@ -415,21 +723,90 @@
 	return NEW_PGD();
 }
 
-static void srmmu_set_pte(pte_t *ptep, pte_t pteval)
+static void srmmu_set_pte_cacheable(pte_t *ptep, pte_t pteval)
+{
+	srmmu_set_entry(ptep, pte_val(pteval));
+}
+
+static void srmmu_set_pte_nocache_hyper(pte_t *ptep, pte_t pteval)
+{
+	unsigned long flags;
+
+	save_and_cli(flags);
+	srmmu_set_entry(ptep, pte_val(pteval));
+	hyper_flush_cache_page(((unsigned long)ptep) & PAGE_MASK);
+	restore_flags(flags);
+}
+
+static void srmmu_set_pte_nocache_cypress(pte_t *ptep, pte_t pteval)
 {
+	register unsigned long a, b, c, d, e, f, g;
+	unsigned long line, page;
+
 	srmmu_set_entry(ptep, pte_val(pteval));
+	page = ((unsigned long)ptep) & PAGE_MASK;
+	line = (page + PAGE_SIZE) - 0x100;
+	a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+	goto inside;
+	do {
+		line -= 0x100;
+	inside:
+		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+				     "sta %%g0, [%0 + %2] %1\n\t"
+				     "sta %%g0, [%0 + %3] %1\n\t"
+				     "sta %%g0, [%0 + %4] %1\n\t"
+				     "sta %%g0, [%0 + %5] %1\n\t"
+				     "sta %%g0, [%0 + %6] %1\n\t"
+				     "sta %%g0, [%0 + %7] %1\n\t"
+				     "sta %%g0, [%0 + %8] %1\n\t" : :
+				     "r" (line),
+				     "i" (ASI_M_FLUSH_PAGE),
+				     "r" (a), "r" (b), "r" (c), "r" (d),
+				     "r" (e), "r" (f), "r" (g));
+	} while(line != page);
+}
+
+static void srmmu_set_pte_nocache_nomxccvik(pte_t *ptep, pte_t pteval)
+{
+	unsigned long paddr = srmmu_v2p(((unsigned long)ptep));
+	unsigned long vaddr;
+	int set;
+	int i;
+
+	set = (paddr >> 5) & 0x7f;
+	vaddr = (KERNBASE + PAGE_SIZE) | (set << 5);
+	srmmu_set_entry(ptep, pteval);
+	for (i = 0; i < 8; i++) {
+		__asm__ __volatile__ ("ld [%0], %%g0" : : "r" (vaddr));
+		vaddr += PAGE_SIZE;
+	}
 }
 
 static void srmmu_quick_kernel_fault(unsigned long address)
 {
-	printk("Penguin faults at address %08lx\n", address);
-	panic("Srmmu bolixed...");
+#ifdef __SMP__
+	printk("CPU[%d]: Kernel faults at addr=0x%08lx\n",
+	       smp_processor_id(), address);
+	while (1) ;
+#else
+	printk("Kernel faults at addr=0x%08lx\n", address);
+	printk("PTE=%08lx\n", srmmu_hwprobe((address & PAGE_MASK)));
+	die_if_kernel("SRMMU bolixed...", current->tss.kregs);
+#endif
 }
 
-static inline void alloc_context(struct mm_struct *mm)
+static inline void alloc_context(struct task_struct *tsk)
 {
+	struct mm_struct *mm = tsk->mm;
 	struct ctx_list *ctxp;
 
+#if CONFIG_AP1000
+        if (tsk->taskid >= MPP_TASK_BASE) {
+		mm->context = MPP_CONTEXT_BASE + (tsk->taskid - MPP_TASK_BASE);
+		return;
+	}
+#endif
+
 	ctxp = ctx_free.next;
 	if(ctxp != &ctx_free) {
 		remove_from_ctx_list(ctxp);
@@ -452,19 +829,28 @@
 	mm->context = ctxp->ctx_number;
 }
 
+static inline void free_context(int context)
+{
+	struct ctx_list *ctx_old;
+
+#if CONFIG_AP1000
+	if (context >= MPP_CONTEXT_BASE)
+		return; /* nothing to do! */
+#endif
+	
+	ctx_old = ctx_list_pool + context;
+	remove_from_ctx_list(ctx_old);
+	add_to_free_ctxlist(ctx_old);
+}
+
+
 static void srmmu_switch_to_context(struct task_struct *tsk)
 {
-	/* Kernel threads can execute in any context and so can tasks
-	 * sleeping in the middle of exiting. If this task has already
-	 * been allocated a piece of the mmu realestate, just jump to
-	 * it.
-	 */
-	if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
-	   (tsk->flags & PF_EXITING))
-		return;
 	if(tsk->mm->context == NO_CONTEXT) {
-		alloc_context(tsk->mm);
+		alloc_context(tsk);
+		flush_cache_mm(current->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+		flush_tlb_mm(current->mm);
 	}
 	srmmu_set_context(tsk->mm->context);
 }
@@ -493,7 +879,22 @@
 	else
 		tmp |= SRMMU_PRIV;
 	flush_page_to_ram(virt_addr);
-	srmmu_set_entry(ptep, tmp);
+	set_pte(ptep, tmp);
+	flush_tlb_all();
+}
+
+void srmmu_unmapioaddr(unsigned long virt_addr)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	pgdp = srmmu_pgd_offset(init_task.mm, virt_addr);
+	pmdp = srmmu_pmd_offset(pgdp, virt_addr);
+	ptep = srmmu_pte_offset(pmdp, virt_addr);
+
+	/* No need to flush uncacheable page. */
+	set_pte(ptep, pte_val(srmmu_mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED)));
 	flush_tlb_all();
 }
 
@@ -516,33 +917,22 @@
  */
 struct task_struct *srmmu_alloc_task_struct(void)
 {
-	unsigned long page;
-
-	page = get_free_page(GFP_KERNEL);
-	if(!page)
-		return (struct task_struct *) 0;
-	return (struct task_struct *) page;
+	return (struct task_struct *) kmalloc(sizeof(struct task_struct), GFP_KERNEL);
 }
 
 unsigned long srmmu_alloc_kernel_stack(struct task_struct *tsk)
 {
-	unsigned long pages;
-
-	pages = __get_free_pages(GFP_KERNEL, 2, 0);
-	if(!pages)
-		return 0;
-	memset((void *) pages, 0, (PAGE_SIZE << 2));
-	return pages;
+	return __get_free_pages(GFP_KERNEL, 1, 0);
 }
 
 static void srmmu_free_task_struct(struct task_struct *tsk)
 {
-	free_page((unsigned long) tsk);
+	kfree(tsk);
 }
 
 static void srmmu_free_kernel_stack(unsigned long stack)
 {
-	free_pages(stack, 2);
+	free_pages(stack, 1);
 }
 
 /* Tsunami flushes.  It's page level tlb invalidation is not very
@@ -604,8 +994,6 @@
 /* Tsunami does not have a Copy-back style virtual cache. */
 static void tsunami_flush_page_to_ram(unsigned long page)
 {
-	tsunami_flush_icache();
-	tsunami_flush_dcache();
 }
 
 /* However, Tsunami is not IO coherent. */
@@ -615,22 +1003,10 @@
 	tsunami_flush_dcache();
 }
 
-/* TLB flushes seem to upset the tsunami sometimes, I can't figure out
- * what the hell is going on.  All I see is a tlb flush (page or whole,
- * there is no consistent pattern) and then total local variable corruption
- * in the procedure who called us after return.  Usually triggerable
- * by "cool" programs like crashme and bonnie.  I played around a bit
- * and adding a bunch of forced nops seems to make the problems all
- * go away. (missed instruction fetches possibly? ugh...)
- */
-#define TSUNAMI_SUCKS do { nop(); nop(); nop(); nop(); nop(); \
-	                   nop(); nop(); nop(); nop(); nop(); } while(0)
-
 static void tsunami_flush_tlb_all(void)
 {
 	module_stats.invall++;
 	srmmu_flush_whole_tlb();
-	TSUNAMI_SUCKS;
 }
 
 static void tsunami_flush_tlb_mm(struct mm_struct *mm)
@@ -640,7 +1016,6 @@
 	if(mm->context != NO_CONTEXT) {
 #endif
 		srmmu_flush_whole_tlb();
-		TSUNAMI_SUCKS;
 #ifndef __SMP__
         }
 #endif
@@ -653,7 +1028,6 @@
 	if(mm->context != NO_CONTEXT) {
 #endif
 		srmmu_flush_whole_tlb();
-		TSUNAMI_SUCKS;
 #ifndef __SMP__
 	}
 #endif
@@ -667,12 +1041,15 @@
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
+		unsigned long flags;
+
+		save_and_cli(flags);
 		octx = srmmu_get_context();
 
 		srmmu_set_context(mm->context);
 		srmmu_flush_tlb_page(page);
-		TSUNAMI_SUCKS;
 		srmmu_set_context(octx);
+		restore_flags(flags);
 #ifndef __SMP__
 	}
 #endif
@@ -812,36 +1189,14 @@
 
 static void viking_flush_cache_mm(struct mm_struct *mm)
 {
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-#ifndef __SMP__
-	}
-#endif
 }
 
 static void viking_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-#ifndef __SMP__
-	}
-#endif
 }
 
 static void viking_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
-#ifndef __SMP__
-	struct mm_struct *mm = vma->vm_mm;
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-#ifndef __SMP__
-	}
-#endif
 }
 
 /* Non-mxcc vikings are copy-back but are pure-physical so no flushing. */
@@ -849,21 +1204,16 @@
 {
 }
 
-/* Viking is IO cache coherent. */
-static void viking_flush_page_for_dma(unsigned long page)
-{
-}
-
 static void viking_mxcc_flush_page(unsigned long page)
 {
-	unsigned long ppage = srmmu_hwprobe(page);
+	unsigned long ppage = srmmu_v2p(page & PAGE_MASK);
 	unsigned long paddr0, paddr1;
 
-	if (!ppage)
+	if (ppage == 0xffffffffUL)
 		return;
 
-	paddr0 = (ppage >> 28) | 0x10;		/* Set cacheable bit. */
-	paddr1 = (ppage << 4) & PAGE_MASK;
+	paddr0 = 0x10;			/* Set cacheable bit. */
+	paddr1 = ppage;
 
 	/* Read the page's data through the stream registers,
 	 * and write it back to memory. This will issue
@@ -897,14 +1247,15 @@
 
 static void viking_no_mxcc_flush_page(unsigned long page)
 {
-	unsigned long ppage = srmmu_hwprobe(page) >> 8;
+	unsigned long ppage = srmmu_v2p(page & PAGE_MASK);
 	int set, block;
 	unsigned long ptag[2];
 	unsigned long vaddr;
 	int i;
 
-	if (!ppage)
+	if (ppage == 0xffffffffUL)
 		return;
+	ppage >>= 12;
 
 	for (set = 0; set < 128; set++) {
 		for (block = 0; block < 4; block++) {
@@ -937,9 +1288,15 @@
 	}
 }
 
+/* Viking is IO cache coherent, but really only on MXCC. */
+static void viking_flush_page_for_dma(unsigned long page)
+{
+}
+
 static void viking_flush_tlb_all(void)
 {
 	module_stats.invall++;
+	flush_user_windows();
 	srmmu_flush_whole_tlb();
 }
 
@@ -951,6 +1308,7 @@
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
+		flush_user_windows();
 		octx = srmmu_get_context();
 		srmmu_set_context(mm->context);
 		srmmu_flush_tlb_ctx();
@@ -968,12 +1326,27 @@
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
+		flush_user_windows();
 		octx = srmmu_get_context();
 		srmmu_set_context(mm->context);
-		start &= SRMMU_PMD_MASK;
-		while(start < end) {
-			srmmu_flush_tlb_segment(start);
-			start += SRMMU_PMD_SIZE;
+		if((start - end) < SRMMU_PMD_SIZE) {
+			start &= PAGE_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_page(start);
+				start += PAGE_SIZE;
+			}
+		} else if((start - end) < SRMMU_PGDIR_SIZE) {
+			start &= SRMMU_PMD_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_segment(start);
+				start += SRMMU_PMD_SIZE;
+			}
+		} else {
+			start &= SRMMU_PGDIR_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_region(start);
+				start += SRMMU_PGDIR_SIZE;
+			}
 		}
 		srmmu_set_context(octx);
 #ifndef __SMP__
@@ -990,6 +1363,7 @@
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
+		flush_user_windows();
 		octx = srmmu_get_context();
 		srmmu_set_context(mm->context);
 		srmmu_flush_tlb_page(page);
@@ -1005,97 +1379,330 @@
 }
 
 /* Cypress flushes. */
-
-static void cypress_flush_tlb_all(void)
+static void cypress_flush_cache_all(void)
 {
-	module_stats.invall++;
-	srmmu_flush_whole_tlb();
+	volatile unsigned long cypress_sucks;
+	unsigned long faddr, tagval;
+
+	flush_user_windows();
+	for(faddr = 0; faddr < 0x10000; faddr += 0x20) {
+		__asm__ __volatile__("lda [%1 + %2] %3, %0\n\t" :
+				     "=r" (tagval) :
+				     "r" (faddr), "r" (0x40000),
+				     "i" (ASI_M_DATAC_TAG));
+
+		/* If modified and valid, kick it. */
+		if((tagval & 0x60) == 0x60)
+			cypress_sucks = *(unsigned long *)(0xf0020000 + faddr);
+	}
 }
 
-static void cypress_flush_tlb_mm(struct mm_struct *mm)
+static void cypress_flush_cache_mm(struct mm_struct *mm)
 {
+	unsigned long flags, faddr;
 	int octx;
 
-	module_stats.invmm++;
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
+		register unsigned long a, b, c, d, e, f, g;
+		flush_user_windows();
+		save_and_cli(flags);
 		octx = srmmu_get_context();
 		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_ctx();
+		a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+		faddr = (0x10000 - 0x100);
+		goto inside;
+		do {
+			faddr -= 0x100;
+		inside:
+			__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+					     "sta %%g0, [%0 + %2] %1\n\t"
+					     "sta %%g0, [%0 + %3] %1\n\t"
+					     "sta %%g0, [%0 + %4] %1\n\t"
+					     "sta %%g0, [%0 + %5] %1\n\t"
+					     "sta %%g0, [%0 + %6] %1\n\t"
+					     "sta %%g0, [%0 + %7] %1\n\t"
+					     "sta %%g0, [%0 + %8] %1\n\t" : :
+					     "r" (faddr), "i" (ASI_M_FLUSH_CTX),
+					     "r" (a), "r" (b), "r" (c), "r" (d),
+					     "r" (e), "r" (f), "r" (g));
+		} while(faddr);
 		srmmu_set_context(octx);
+		restore_flags(flags);
 #ifndef __SMP__
 	}
 #endif
 }
 
-static void cypress_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void cypress_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
+	unsigned long flags, faddr;
 	int octx;
 
-	module_stats.invrnge++;
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
+		register unsigned long a, b, c, d, e, f, g;
+		flush_user_windows();
+		save_and_cli(flags);
 		octx = srmmu_get_context();
 		srmmu_set_context(mm->context);
+		a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
 		start &= SRMMU_PMD_MASK;
 		while(start < end) {
-			srmmu_flush_tlb_segment(start);
+			faddr = (start + (0x10000 - 0x100));
+			goto inside;
+			do {
+				faddr -= 0x100;
+			inside:
+				__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+						     "sta %%g0, [%0 + %2] %1\n\t"
+						     "sta %%g0, [%0 + %3] %1\n\t"
+						     "sta %%g0, [%0 + %4] %1\n\t"
+						     "sta %%g0, [%0 + %5] %1\n\t"
+						     "sta %%g0, [%0 + %6] %1\n\t"
+						     "sta %%g0, [%0 + %7] %1\n\t"
+						     "sta %%g0, [%0 + %8] %1\n\t" : :
+						     "r" (faddr),
+						     "i" (ASI_M_FLUSH_SEG),
+						     "r" (a), "r" (b), "r" (c), "r" (d),
+						     "r" (e), "r" (f), "r" (g));
+			} while (faddr != start);
 			start += SRMMU_PMD_SIZE;
 		}
 		srmmu_set_context(octx);
+		restore_flags(flags);
 #ifndef __SMP__
 	}
 #endif
 }
 
-static void cypress_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+static void cypress_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
-	int octx;
 	struct mm_struct *mm = vma->vm_mm;
+	unsigned long flags, line;
+	int octx;
 
-	module_stats.invpg++;
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
+		register unsigned long a, b, c, d, e, f, g;
+		flush_user_windows();
+		save_and_cli(flags);
 		octx = srmmu_get_context();
 		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_page(page);
+		a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+		page &= PAGE_MASK;
+		line = (page + PAGE_SIZE) - 0x100;
+		goto inside;
+		do {
+			line -= 0x100;
+		inside:
+				__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+						     "sta %%g0, [%0 + %2] %1\n\t"
+						     "sta %%g0, [%0 + %3] %1\n\t"
+						     "sta %%g0, [%0 + %4] %1\n\t"
+						     "sta %%g0, [%0 + %5] %1\n\t"
+						     "sta %%g0, [%0 + %6] %1\n\t"
+						     "sta %%g0, [%0 + %7] %1\n\t"
+						     "sta %%g0, [%0 + %8] %1\n\t" : :
+						     "r" (line),
+						     "i" (ASI_M_FLUSH_PAGE),
+						     "r" (a), "r" (b), "r" (c), "r" (d),
+						     "r" (e), "r" (f), "r" (g));
+		} while(line != page);
 		srmmu_set_context(octx);
+		restore_flags(flags);
 #ifndef __SMP__
 	}
 #endif
 }
 
-/* Hypersparc flushes.  Very nice chip... */
-static void hypersparc_flush_cache_all(void)
+/* Cypress is copy-back, at least that is how we configure it. */
+static void cypress_flush_page_to_ram(unsigned long page)
+{
+	register unsigned long a, b, c, d, e, f, g;
+	unsigned long line;
+
+	a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+	page &= PAGE_MASK;
+	line = (page + PAGE_SIZE) - 0x100;
+	goto inside;
+	do {
+		line -= 0x100;
+	inside:
+		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+				     "sta %%g0, [%0 + %2] %1\n\t"
+				     "sta %%g0, [%0 + %3] %1\n\t"
+				     "sta %%g0, [%0 + %4] %1\n\t"
+				     "sta %%g0, [%0 + %5] %1\n\t"
+				     "sta %%g0, [%0 + %6] %1\n\t"
+				     "sta %%g0, [%0 + %7] %1\n\t"
+				     "sta %%g0, [%0 + %8] %1\n\t" : :
+				     "r" (line),
+				     "i" (ASI_M_FLUSH_PAGE),
+				     "r" (a), "r" (b), "r" (c), "r" (d),
+				     "r" (e), "r" (f), "r" (g));
+	} while(line != page);
+}
+
+/* Cypress is also IO cache coherent. */
+static void cypress_flush_page_for_dma(unsigned long page)
 {
-	flush_user_windows();
-	hyper_flush_unconditional_combined();
-	hyper_flush_whole_icache();
 }
 
-static void hypersparc_flush_cache_mm(struct mm_struct *mm)
+static void cypress_flush_page_to_uncache(unsigned long page)
+{
+	register unsigned long a, b, c, d, e, f, g;
+	unsigned long line;
+
+	a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+	page &= PAGE_MASK;
+	line = (page + PAGE_SIZE) - 0x100;
+	goto inside;
+	do {
+		line -= 0x100;
+	inside:
+		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+				     "sta %%g0, [%0 + %2] %1\n\t"
+				     "sta %%g0, [%0 + %3] %1\n\t"
+				     "sta %%g0, [%0 + %4] %1\n\t"
+				     "sta %%g0, [%0 + %5] %1\n\t"
+				     "sta %%g0, [%0 + %6] %1\n\t"
+				     "sta %%g0, [%0 + %7] %1\n\t"
+				     "sta %%g0, [%0 + %8] %1\n\t" : :
+				     "r" (line),
+				     "i" (ASI_M_FLUSH_PAGE),
+				     "r" (a), "r" (b), "r" (c), "r" (d),
+				     "r" (e), "r" (f), "r" (g));
+	} while(line != page);
+}
+
+static void cypress_flush_tlb_all(void)
+{
+	module_stats.invall++;
+	srmmu_flush_whole_tlb();
+}
+
+static void cypress_flush_tlb_mm(struct mm_struct *mm)
 {
+	int octx;
+
+	module_stats.invmm++;
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
-		flush_user_windows();
-		hyper_flush_unconditional_combined();
-		hyper_flush_whole_icache();
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_ctx();
+		srmmu_set_context(octx);
 #ifndef __SMP__
 	}
 #endif
 }
 
-static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void cypress_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	int octx;
+	module_stats.invrnge++;
+
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		if((start - end) < SRMMU_PMD_SIZE) {
+			start &= PAGE_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_page(start);
+				start += PAGE_SIZE;
+			}
+		} else if((start - end) < SRMMU_PGDIR_SIZE) {
+			start &= SRMMU_PMD_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_segment(start);
+				start += SRMMU_PMD_SIZE;
+			}
+		} else {
+			start &= SRMMU_PGDIR_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_region(start);
+				start += SRMMU_PGDIR_SIZE;
+			}
+		}
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
+}
+
+static void cypress_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	int octx;
+	struct mm_struct *mm = vma->vm_mm;
+
+	module_stats.invpg++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_page(page);
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
+}
+
+static void cypress_flush_tlb_page_for_cbit(unsigned long page)
+{
+	srmmu_flush_tlb_page(page);
+}
+
+/* Hypersparc flushes.  Very nice chip... */
+static void hypersparc_flush_cache_all(void)
+{
+	flush_user_windows();
+	hyper_flush_unconditional_combined();
+	hyper_flush_whole_icache();
+}
+
+static void hypersparc_flush_cache_mm(struct mm_struct *mm)
 {
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
 		flush_user_windows();
-		hyper_flush_unconditional_combined();
+		hyper_flush_cache_user();
+		hyper_flush_whole_icache();
+#ifndef __SMP__
+	}
+#endif
+}
+
+/* Boy was my older implementation inefficient... */
+static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	volatile unsigned long clear;
+	int octx;
+
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		octx = srmmu_get_context();
+		start &= PAGE_MASK;
+		srmmu_set_context(mm->context);
+		while(start < end) {
+			if(srmmu_hwprobe(start))
+				hyper_flush_cache_page(start);
+			start += PAGE_SIZE;
+		}
+		clear = srmmu_get_fstatus();
+		srmmu_set_context(octx);
 		hyper_flush_whole_icache();
 #ifndef __SMP__
 	}
@@ -1142,11 +1749,6 @@
 /* HyperSparc is IO cache coherent. */
 static void hypersparc_flush_page_for_dma(unsigned long page)
 {
-	volatile unsigned long clear;
-
-	if(srmmu_hwprobe(page))
-		hyper_flush_cache_page(page);
-	clear = srmmu_get_fstatus();
 }
 
 static void hypersparc_flush_cache_page_to_uncache(unsigned long page)
@@ -1194,10 +1796,24 @@
 
 		octx = srmmu_get_context();
 		srmmu_set_context(mm->context);
-		start &= SRMMU_PMD_MASK;
-		while(start < end) {
-			srmmu_flush_tlb_segment(start);
-			start += SRMMU_PMD_SIZE;
+		if((start - end) < SRMMU_PMD_SIZE) {
+			start &= PAGE_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_page(start);
+				start += PAGE_SIZE;
+			}
+		} else if((start - end) < SRMMU_PGDIR_SIZE) {
+			start &= SRMMU_PMD_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_segment(start);
+				start += SRMMU_PMD_SIZE;
+			}
+		} else {
+			start &= SRMMU_PGDIR_MASK;
+			while(start < end) {
+				srmmu_flush_tlb_region(start);
+				start += SRMMU_PGDIR_SIZE;
+			}
 		}
 		srmmu_set_context(octx);
 
@@ -1234,45 +1850,26 @@
 static void hypersparc_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
 {
 	hyper_flush_whole_icache();
-	srmmu_set_entry(ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
+	set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
 }
 
 static void hypersparc_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
 	if(tsk->mm->context != NO_CONTEXT) {
-		hyper_flush_whole_icache();
+		flush_cache_mm(current->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+		flush_tlb_mm(current->mm);
 	}
 }
 
-static void hypersparc_set_pte(pte_t *ptep, pte_t pteval)
-{
-	/* xor is your friend */
-	__asm__ __volatile__("rd	%%psr, %%g1\n\t"
-			     "wr	%%g1, %4, %%psr\n\t"
-			     "nop; nop; nop;\n\t"
-			     "swap	[%0], %1\n\t"
-			     "wr	%%g1, 0x0, %%psr\n\t"
-			     "nop; nop; nop;\n\t" :
-			     "=r" (ptep), "=r" (pteval) :
-			     "0" (ptep), "1" (pteval), "i" (PSR_ET) :
-			     "g1");
-}
-
 static void hypersparc_switch_to_context(struct task_struct *tsk)
 {
-	/* Kernel threads can execute in any context and so can tasks
-	 * sleeping in the middle of exiting. If this task has already
-	 * been allocated a piece of the mmu realestate, just jump to
-	 * it.
-	 */
 	hyper_flush_whole_icache();
-	if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
-	   (tsk->flags & PF_EXITING))
-		return;
 	if(tsk->mm->context == NO_CONTEXT) {
-		alloc_context(tsk->mm);
+		alloc_context(tsk);
+		flush_cache_mm(current->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+		flush_tlb_mm(current->mm);
 	}
 	srmmu_set_context(tsk->mm->context);
 }
@@ -1280,45 +1877,28 @@
 /* IOMMU things go here. */
 
 #define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
-static unsigned long first_dvma_page, last_dvma_page;
 
 #define IOPERM        (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
 #define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ)
 
-static inline void srmmu_map_dvma_pages_for_iommu(struct iommu_struct *iommu)
+static inline void srmmu_map_dvma_pages_for_iommu(struct iommu_struct *iommu,
+						  unsigned long kern_end)
 {
-	unsigned long first = first_dvma_page;
-	unsigned long last = last_dvma_page;
-	iopte_t *iopte;
+	unsigned long first = page_offset;
+	unsigned long last = kern_end;
+	iopte_t *iopte = iommu->page_table;
 
-	iopte = iommu->page_table;
-	iopte += ((DVMA_VADDR - iommu->start) >> PAGE_SHIFT);
+	iopte += ((first - iommu->start) >> PAGE_SHIFT);
 	while(first <= last) {
 		iopte_val(*iopte++) = MKIOPTE(srmmu_v2p(first));
 		first += PAGE_SIZE;
 	}
 }
 
-void srmmu_uncache_iommu_page_table(unsigned long start, int size)
-{
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
-	unsigned long end = start + size;
-
-	while(start < end) {
-		pgdp = srmmu_pgd_offset(init_task.mm, start);
-		pmdp = srmmu_pmd_offset(pgdp, start);
-		ptep = srmmu_pte_offset(pmdp, start);
-		pte_val(*ptep) &= ~SRMMU_CACHE;
-		start += PAGE_SIZE;
-	}
-}
-
 unsigned long iommu_init(int iommund, unsigned long memory_start,
 			 unsigned long memory_end, struct linux_sbus *sbus)
 {
-	int impl, vers, ptsize;
+	unsigned int impl, vers, ptsize;
 	unsigned long tmp;
 	struct iommu_struct *iommu;
 	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
@@ -1326,7 +1906,8 @@
 	memory_start = LONG_ALIGN(memory_start);
 	iommu = (struct iommu_struct *) memory_start;
 	memory_start += sizeof(struct iommu_struct);
-	prom_getproperty(iommund, "reg", (void *) iommu_promregs, sizeof(iommu_promregs));
+	prom_getproperty(iommund, "reg", (void *) iommu_promregs,
+			 sizeof(iommu_promregs));
 	iommu->regs = (struct iommu_regs *)
 		sparc_alloc_io(iommu_promregs[0].phys_addr, 0, (PAGE_SIZE * 3),
 			       "IOMMU registers", iommu_promregs[0].which_io, 0x0);
@@ -1336,10 +1917,30 @@
 	vers = (iommu->regs->control & IOMMU_CTRL_VERS) >> 24;
 	tmp = iommu->regs->control;
 	tmp &= ~(IOMMU_CTRL_RNGE);
-	tmp |= (IOMMU_RNGE_64MB | IOMMU_CTRL_ENAB);
+	switch(page_offset & 0xf0000000) {
+	case 0xf0000000:
+		tmp |= (IOMMU_RNGE_256MB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0xf0000000;
+		break;
+	case 0xe0000000:
+		tmp |= (IOMMU_RNGE_512MB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0xe0000000;
+		break;
+	case 0xd0000000:
+	case 0xc0000000:
+		tmp |= (IOMMU_RNGE_1GB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0xc0000000;
+		break;
+	case 0xb0000000:
+	case 0xa0000000:
+	case 0x90000000:
+	case 0x80000000:
+		tmp |= (IOMMU_RNGE_2GB | IOMMU_CTRL_ENAB);
+		iommu->plow = iommu->start = 0x80000000;
+		break;
+	}
 	iommu->regs->control = tmp;
 	iommu_invalidate(iommu->regs);
-	iommu->plow = iommu->start = 0xfc000000;
 	iommu->end = 0xffffffff;
 
 	/* Allocate IOMMU page table */
@@ -1354,10 +1955,24 @@
 
 	/* Initialize new table. */
 	flush_cache_all();
-	srmmu_uncache_iommu_page_table((unsigned long) iommu->page_table, ptsize);
-	flush_tlb_all();
+	if(viking_mxcc_present) {
+		unsigned long start = (unsigned long) iommu->page_table;
+		unsigned long end = (start + ptsize);
+		while(start < end) {
+			viking_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	} else if(flush_page_for_dma == viking_no_mxcc_flush_page) {
+		unsigned long start = (unsigned long) iommu->page_table;
+		unsigned long end = (start + ptsize);
+		while(start < end) {
+			viking_no_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	}
 	memset(iommu->page_table, 0, ptsize);
-	srmmu_map_dvma_pages_for_iommu(iommu);
+	srmmu_map_dvma_pages_for_iommu(iommu, memory_end);
+	flush_tlb_all();
 	iommu->regs->base = srmmu_v2p((unsigned long) iommu->page_table) >> 4;
 	iommu_invalidate(iommu->regs);
 
@@ -1367,103 +1982,74 @@
 	return memory_start;
 }
 
-static char *srmmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+void iommu_sun4d_init(int sbi_node, struct linux_sbus *sbus)
 {
-	struct iommu_struct *iommu = sbus->iommu;
-	unsigned long page = (unsigned long) vaddr;
-	unsigned long start, end, offset;
-	iopte_t *iopte;
+	u32 *iommu;
+	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
 
-	offset = page & ~PAGE_MASK;
-	page &= PAGE_MASK;
+	prom_getproperty(sbi_node, "reg", (void *) iommu_promregs,
+			 sizeof(iommu_promregs));
+	iommu = (u32 *)
+		sparc_alloc_io(iommu_promregs[2].phys_addr, 0, (PAGE_SIZE * 16),
+			       "XPT", iommu_promregs[2].which_io, 0x0);
+	if(!iommu)
+		panic("Cannot map External Page Table.");
 
-	start = iommu->plow;
-	end = KADB_DEBUGGER_BEGVM; /* Don't step on kadb/prom. */
-	iopte = iommu->lowest;
-	while(start < end) {
-		if(!(iopte_val(*iopte) & IOPTE_VALID))
-			break;
-		iopte++;
-		start += PAGE_SIZE;
+	/* Initialize new table. */
+	flush_cache_all();
+	if(viking_mxcc_present) {
+		unsigned long start = (unsigned long) iommu;
+		unsigned long end = (start + 16 * PAGE_SIZE);
+		while(start < end) {
+			viking_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	} else if(flush_page_for_dma == viking_no_mxcc_flush_page) {
+		unsigned long start = (unsigned long) iommu;
+		unsigned long end = (start + 16 * PAGE_SIZE);
+		while(start < end) {
+			viking_no_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
 	}
+	memset(iommu, 0, 16 * PAGE_SIZE);
+	flush_tlb_all();
+
+	sbus->iommu = (struct iommu_struct *)iommu;
+}
 
-	flush_page_for_dma(page);
-	vaddr = (char *) (start | offset);
-	iopte_val(*iopte) = MKIOPTE(srmmu_v2p(page));
-	iommu_invalidate_page(iommu->regs, start);
-	iommu->lowest = iopte + 1;
-	iommu->plow = start + PAGE_SIZE;
+static char *srmmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+	unsigned long page = ((unsigned long) vaddr) & PAGE_MASK;
 
+	while(page < ((unsigned long)(vaddr + len))) {
+		flush_page_for_dma(page);
+		page += PAGE_SIZE;
+	}
 	return vaddr;
 }
 
 static void srmmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
-	struct iommu_struct *iommu = sbus->iommu;
-        unsigned long page, start, end, offset;
-	iopte_t *iopte = iommu->lowest;
+        unsigned long page;
 
-	start = iommu->plow;
-	end = KADB_DEBUGGER_BEGVM;
 	while(sz >= 0) {
 		page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
-		offset = ((unsigned long) sg[sz].addr) & ~PAGE_MASK;
-		while(start < end) {
-			if(!(iopte_val(*iopte) & IOPTE_VALID))
-				break;
-			iopte++;
-			start += PAGE_SIZE;
+		while(page < (unsigned long)(sg[sz].addr + sg[sz].len)) {
+			flush_page_for_dma(page);
+			page += PAGE_SIZE;
 		}
-		if(start == KADB_DEBUGGER_BEGVM)
-			panic("Wheee, iomapping overflow.");
-		flush_page_for_dma(page);
-		sg[sz].alt_addr = (char *) (start | offset);
-		iopte_val(*iopte) = MKIOPTE(srmmu_v2p(page));
-		iommu_invalidate_page(iommu->regs, start);
-		iopte++;
-		start += PAGE_SIZE;
+		sg[sz].dvma_addr = (char *) (sg[sz].addr);
 		sz--;
 	}
-	iommu->lowest = iopte;
-	iommu->plow = start;
 }
 
 static void srmmu_release_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
 {
-	struct iommu_struct *iommu = sbus->iommu;
-	unsigned long page = (unsigned long) vaddr;
-	iopte_t *iopte;
-
-	if(len > PAGE_SIZE)
-		panic("Can only handle page sized IOMMU mappings.");
-	page &= PAGE_MASK;
-	iopte = iommu->page_table + ((page - iommu->start) >> PAGE_SHIFT);
-	iopte_val(*iopte) = 0;
-	iommu_invalidate_page(iommu->regs, page);
-	if(iopte < iommu->lowest) {
-		iommu->lowest = iopte;
-		iommu->plow = page;
-	}
 }
 
 static void srmmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
-	struct iommu_struct *iommu = sbus->iommu;
-	unsigned long page;
-	iopte_t *iopte;
-
-	while(sz >= 0) {
-		page = ((unsigned long)sg[sz].alt_addr) & PAGE_MASK;
-		iopte = iommu->page_table + ((page - iommu->start) >> PAGE_SHIFT);
-		iopte_val(*iopte) = 0;
-		iommu_invalidate_page(iommu->regs, page);
-		if(iopte < iommu->lowest) {
-			iommu->lowest = iopte;
-			iommu->plow = page;
-		}
-		sg[sz].alt_addr = 0;
-		sz--;
-	}
 }
 
 static unsigned long mempool;
@@ -1479,27 +2065,27 @@
 /* Some dirty hacks to abstract away the painful boot up init. */
 static inline unsigned long srmmu_early_paddr(unsigned long vaddr)
 {
-	return ((vaddr - PAGE_OFFSET) + kbpage);
+	return ((vaddr - KERNBASE) + kbpage);
 }
 
 static inline void srmmu_early_pgd_set(pgd_t *pgdp, pmd_t *pmdp)
 {
-	srmmu_set_entry(pgdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) pmdp) >> 4)));
+	set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) pmdp) >> 4)));
 }
 
 static inline void srmmu_early_pmd_set(pmd_t *pmdp, pte_t *ptep)
 {
-	srmmu_set_entry(pmdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) ptep) >> 4)));
+	set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) ptep) >> 4)));
 }
 
 static inline unsigned long srmmu_early_pgd_page(pgd_t pgd)
 {
-	return (((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4) - kbpage) + PAGE_OFFSET;
+	return (((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4) - kbpage) + KERNBASE;
 }
 
 static inline unsigned long srmmu_early_pmd_page(pmd_t pmd)
 {
-	return (((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4) - kbpage) + PAGE_OFFSET;
+	return (((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4) - kbpage) + KERNBASE;
 }
 
 static inline pmd_t *srmmu_early_pmd_offset(pgd_t *dir, unsigned long address)
@@ -1512,26 +2098,6 @@
 	return (pte_t *) srmmu_early_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
 }
 
-/* Allocate a block of RAM which is aligned to its size.
- * This procedure can be used until the call to mem_init().
- */
-static void *srmmu_init_alloc(unsigned long *kbrk, unsigned long size)
-{
-	unsigned long mask = size - 1;
-	unsigned long ret;
-
-	if(!size)
-		return 0x0;
-	if(size & mask) {
-		prom_printf("panic: srmmu_init_alloc botch\n");
-		prom_halt();
-	}
-	ret = (*kbrk + mask) & ~mask;
-	*kbrk = ret + size;
-	memset((void*) ret, 0, size);
-	return (void*) ret;
-}
-
 static inline void srmmu_allocate_ptable_skeleton(unsigned long start, unsigned long end)
 {
 	pgd_t *pgdp;
@@ -1541,12 +2107,12 @@
 	while(start < end) {
 		pgdp = srmmu_pgd_offset(init_task.mm, start);
 		if(srmmu_pgd_none(*pgdp)) {
-			pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
+			pmdp = sparc_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
 			srmmu_early_pgd_set(pgdp, pmdp);
 		}
 		pmdp = srmmu_early_pmd_offset(pgdp, start);
 		if(srmmu_pmd_none(*pmdp)) {
-			ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
+			ptep = sparc_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
 			srmmu_early_pmd_set(pmdp, ptep);
 		}
 		start = (start + SRMMU_PMD_SIZE) & SRMMU_PMD_MASK;
@@ -1596,7 +2162,7 @@
 			continue;
 		}
 		if(srmmu_pgd_none(*pgdp)) {
-			pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
+			pmdp = sparc_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
 			srmmu_early_pgd_set(pgdp, pmdp);
 		}
 		pmdp = srmmu_early_pmd_offset(pgdp, start);
@@ -1606,7 +2172,7 @@
 			continue;
 		}
 		if(srmmu_pmd_none(*pmdp)) {
-			ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
+			ptep = sparc_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
 			srmmu_early_pmd_set(pmdp, ptep);
 		}
 		ptep = srmmu_early_pte_offset(pmdp, start);
@@ -1615,187 +2181,408 @@
 	}
 }
 
-static inline void srmmu_map_dvma_pages_for_cpu(unsigned long first, unsigned long last)
+static void srmmu_map_dma_area(unsigned long addr, int len)
 {
-	unsigned long start;
+	unsigned long page, end;
 	pgprot_t dvma_prot;
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
+	struct iommu_struct *iommu = SBus_chain->iommu;
+	iopte_t *iopte = iommu->page_table;
+	iopte_t *iopte_first = iopte;
 
-	start = DVMA_VADDR;
-	if (viking_mxcc_present)
+	if(viking_mxcc_present)
 		dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
 	else
 		dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
-	while(first <= last) {
-		pgdp = srmmu_pgd_offset(init_task.mm, start);
-		pmdp = srmmu_pmd_offset(pgdp, start);
-		ptep = srmmu_pte_offset(pmdp, start);
 
-		srmmu_set_entry(ptep, pte_val(srmmu_mk_pte(first, dvma_prot)));
+	iopte += ((addr - iommu->start) >> PAGE_SHIFT);
+	end = PAGE_ALIGN((addr + len));
+	while(addr < end) {
+		page = get_free_page(GFP_KERNEL);
+		if(!page) {
+			prom_printf("alloc_dvma: Cannot get a dvma page\n");
+			prom_halt();
+		} else {
+			pgd_t *pgdp;
+			pmd_t *pmdp;
+			pte_t *ptep;
 
-		first += PAGE_SIZE;
-		start += PAGE_SIZE;
-	}
+			pgdp = srmmu_pgd_offset(init_task.mm, addr);
+			pmdp = srmmu_pmd_offset(pgdp, addr);
+			ptep = srmmu_pte_offset(pmdp, addr);
+
+			set_pte(ptep, pte_val(srmmu_mk_pte(page, dvma_prot)));
 
-	/* Uncache DVMA pages. */
-	if (!viking_mxcc_present) {
-		first = first_dvma_page;
-		last = last_dvma_page;
-		while(first <= last) {
-			pgdp = srmmu_pgd_offset(init_task.mm, first);
-			pmdp = srmmu_pmd_offset(pgdp, first);
-			ptep = srmmu_pte_offset(pmdp, first);
-			pte_val(*ptep) &= ~SRMMU_CACHE;
-			first += PAGE_SIZE;
+			iopte_val(*iopte++) = MKIOPTE(srmmu_v2p(page));
 		}
+		addr += PAGE_SIZE;
 	}
+	flush_cache_all();
+	if(viking_mxcc_present) {
+		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
+		while(start < end) {
+			viking_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	} else if(flush_page_for_dma == viking_no_mxcc_flush_page) {
+		unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+		unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
+		while(start < end) {
+			viking_no_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	}
+	flush_tlb_all();
+	iommu_invalidate(iommu->regs);
 }
 
-static void srmmu_map_kernel(unsigned long start, unsigned long end)
+/* #define DEBUG_MAP_KERNEL */
+
+#ifdef DEBUG_MAP_KERNEL
+#define MKTRACE(foo) prom_printf foo
+#else
+#define MKTRACE(foo)
+#endif
+
+static int lots_of_ram = 0;
+static int large_pte_optimize = 1;
+
+#define KERNEL_PTE(page_shifted) ((page_shifted)|SRMMU_CACHE|SRMMU_PRIV|SRMMU_VALID)
+
+/* Create a third-level SRMMU 16MB page mapping. */
+static inline void do_large_mapping(unsigned long vaddr, unsigned long phys_base)
+{
+	pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, vaddr);
+	unsigned long big_pte;
+
+	MKTRACE(("dlm[v<%08lx>-->p<%08lx>]", vaddr, phys_base));
+	big_pte = KERNEL_PTE(phys_base >> 4);
+	pgd_val(*pgdp) = big_pte;
+}
+
+/* Create second-level SRMMU 256K medium sized page mappings. */
+static inline void do_medium_mapping(unsigned long vaddr, unsigned long vend,
+				     unsigned long phys_base)
 {
-	unsigned long last_page;
-	int srmmu_bank, phys_bank, i;
 	pgd_t *pgdp;
 	pmd_t *pmdp;
-	pte_t *ptep;
+	unsigned long medium_pte;
 
-	end = PAGE_ALIGN(end);
+	MKTRACE(("dmm[v<%08lx,%08lx>-->p<%08lx>]", vaddr, vend, phys_base));
+	while(vaddr < vend) {
+		pgdp = srmmu_pgd_offset(init_task.mm, vaddr);
+		pmdp = srmmu_early_pmd_offset(pgdp, vaddr);
+		medium_pte = KERNEL_PTE(phys_base >> 4);
+		pmd_val(*pmdp) = medium_pte;
+		phys_base += SRMMU_PMD_SIZE;
+		vaddr += SRMMU_PMD_SIZE;
+	}
+}
 
-	if(start == (KERNBASE + PAGE_SIZE)) {
-		unsigned long pte;
-		unsigned long tmp;
-
-		pgdp = srmmu_pgd_offset(init_task.mm, KERNBASE);
-		pmdp = srmmu_early_pmd_offset(pgdp, KERNBASE);
-		ptep = srmmu_early_pte_offset(pmdp, KERNBASE);
-
-		/* Put a real mapping in for the KERNBASE page. */
-		tmp = kbpage;
-		pte = (tmp) >> 4;
-		pte |= (SRMMU_CACHE | SRMMU_PRIV | SRMMU_VALID);
-		pte_val(*ptep) = pte;
-	}
-
-	/* Copy over mappings prom already gave us. */
-	last_page = (srmmu_hwprobe(start) & SRMMU_PTE_PMASK) << 4;
-	while((srmmu_hwprobe(start) & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
-		unsigned long tmp;
+/* Create a normal set of SRMMU page mappings for the virtual range
+ * START to END, using physical pages beginning at PHYS_BASE.
+ */
+static inline void do_small_mapping(unsigned long start, unsigned long end,
+				     unsigned long phys_base)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
 
+	MKTRACE(("dsm[v<%08lx,%08lx>-->p<%08lx>]", start, end, phys_base));
+	while(start < end) {
 		pgdp = srmmu_pgd_offset(init_task.mm, start);
 		pmdp = srmmu_early_pmd_offset(pgdp, start);
 		ptep = srmmu_early_pte_offset(pmdp, start);
-		tmp = srmmu_hwprobe(start);
-		tmp &= ~(0xff);
-		tmp |= (SRMMU_CACHE | SRMMU_PRIV | SRMMU_VALID);
-		pte_val(*ptep) = tmp;
+
+		pte_val(*ptep) = KERNEL_PTE(phys_base >> 4);
+		phys_base += PAGE_SIZE;
 		start += PAGE_SIZE;
-		tmp = (srmmu_hwprobe(start) & SRMMU_PTE_PMASK) << 4;
+	}
+}
 
-		/* Never a cross bank boundary, thank you. */
-		if(tmp != last_page + PAGE_SIZE)
-			break;
-		last_page = tmp;
+/* Look in the sp_bank for the given physical page, return the
+ * index number the entry was found in, or -1 for not found.
+ */
+static inline int find_in_spbanks(unsigned long phys_page)
+{
+	int entry;
+
+	for(entry = 0; sp_banks[entry].num_bytes; entry++) {
+		unsigned long start = sp_banks[entry].base_addr;
+		unsigned long end = start + sp_banks[entry].num_bytes;
+
+		if((start <= phys_page) && (phys_page < end))
+			return entry;
 	}
+	return -1;
+}
+
+/* Find an spbank entry not mapped as of yet, TAKEN_VECTOR is an
+ * array of char's, each member indicating if that spbank is mapped
+ * yet or not.
+ */
+static inline int find_free_spbank(char *taken_vector)
+{
+	int entry;
+
+	for(entry = 0; sp_banks[entry].num_bytes; entry++)
+		if(!taken_vector[entry])
+			break;
+	return entry;
+}
 
-	/* Ok, that was assumed to be one full bank, begin
-	 * construction of srmmu_map[].
+/* Same as above, but with a given bank size limit BLIMIT. */
+static inline int find_free_spbank_limited(char *taken_vector, unsigned long limit)
+{
+	int entry;
+
+	for(entry = 0; sp_banks[entry].num_bytes; entry++)
+		if(!taken_vector[entry] &&
+		   (sp_banks[entry].num_bytes < limit))
+			break;
+	return entry;
+}
+
+/* Map sp_bank entry SP_ENTRY, starting at virtual address VBASE.
+ * This routine is expected to update the srmmu_map and try as
+ * hard as possible to use 16MB level-one SRMMU pte's when at all
+ * possible to get short termination and faster translations.
+ */
+static inline unsigned long map_spbank(unsigned long vbase, int sp_entry)
+{
+	unsigned long pstart = sp_banks[sp_entry].base_addr;
+	unsigned long vstart = vbase;
+	unsigned long vend = vbase + sp_banks[sp_entry].num_bytes;
+	static int srmmu_bank = 0;
+
+	/* If physically not aligned on 16MB boundry, just shortcut
+	 * right here by mapping them with 4k normal pages, and bumping
+	 * the next virtual address to the next 16MB boundry.  You can
+	 * get this with various RAM configurations due to the way in
+	 * which the PROM carves out it's own chunks of memory.
 	 */
-	for(phys_bank = 0; sp_banks[phys_bank].num_bytes != 0; phys_bank++) {
-		if(kbpage >= sp_banks[phys_bank].base_addr &&
-		   (kbpage <
-		    (sp_banks[phys_bank].base_addr + sp_banks[phys_bank].num_bytes)))
-			break; /* found it */
-	}
-	srmmu_bank = 0;
-	srmmu_map[srmmu_bank].vbase = KERNBASE;
-	srmmu_map[srmmu_bank].pbase = sp_banks[phys_bank].base_addr;
-	srmmu_map[srmmu_bank].size = sp_banks[phys_bank].num_bytes;
-	if(kbpage != sp_banks[phys_bank].base_addr) {
-		prom_printf("Detected PenguinPages, getting out of here.\n");
-		prom_halt();
-#if 0
-		srmmu_map[srmmu_bank].pbase = kbpage;
-		srmmu_map[srmmu_bank].size -=
-			(kbpage - sp_banks[phys_bank].base_addr);
-#endif
+	if(pstart & ~SRMMU_PGDIR_MASK) {
+		do_small_mapping(vstart, vend, pstart);
+		vstart = SRMMU_PGDIR_ALIGN(vend);
+		goto finish_up;
+	}
+	while(vstart < vend) {
+		unsigned long coverage, next_aligned;
+		if(vstart & ~SRMMU_PMD_MASK) {
+			next_aligned = SRMMU_PMD_ALIGN(vstart);
+			if(next_aligned <= vend) {
+				coverage = (next_aligned - vstart);
+				do_small_mapping(vstart, next_aligned, pstart);
+			} else {
+				coverage = (vend - vstart);
+				do_small_mapping(vstart, vend, pstart);
+			}
+		} else if(vstart & ~SRMMU_PGDIR_MASK) {
+			next_aligned = SRMMU_PGDIR_ALIGN(vstart);
+			if(next_aligned <= vend) {
+				coverage = (next_aligned - vstart);
+				do_medium_mapping(vstart, next_aligned, pstart);
+			} else {
+				coverage = (vend - vstart);
+				do_small_mapping(vstart, vend, pstart);
+			}
+		} else {
+			coverage = SRMMU_PGDIR_SIZE;
+			if(large_pte_optimize || ((vstart+coverage)<=vend)) {
+				do_large_mapping(vstart, pstart);
+			} else {
+				coverage = (vend - vstart);
+				do_small_mapping(vstart, vend, pstart);
+			}
+		}
+		vstart += coverage; pstart += coverage;
 	}
-	/* Prom didn't map all of this first bank, fill
-	 * in the rest by hand.
-	 */
-	while(start < (srmmu_map[srmmu_bank].vbase + srmmu_map[srmmu_bank].size)) {
-		unsigned long pteval;
+finish_up:
+	srmmu_map[srmmu_bank].vbase = vbase;
+	srmmu_map[srmmu_bank].pbase = sp_banks[sp_entry].base_addr;
+	srmmu_map[srmmu_bank].size = sp_banks[sp_entry].num_bytes;
+	MKTRACE(("SRMMUBANK[v<%08lx>p<%08lx>s<%08lx>]", vbase, sp_banks[sp_entry].base_addr, sp_banks[sp_entry].num_bytes));
+	srmmu_bank++;
+	return vstart;
+}
 
-		pgdp = srmmu_pgd_offset(init_task.mm, start);
-		pmdp = srmmu_early_pmd_offset(pgdp, start);
-		ptep = srmmu_early_pte_offset(pmdp, start);
+static inline void memprobe_error(char *msg)
+{
+	prom_printf(msg);
+	prom_printf("Halting now...\n");
+	prom_halt();
+}
 
-		pteval = (start - KERNBASE + srmmu_map[srmmu_bank].pbase) >> 4;
-		pteval |= (SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV);
-		pte_val(*ptep) = pteval;
-		start += PAGE_SIZE;
-	}
+/* Assumptions: The bank given to the kernel from the prom/bootloader
+ * is part of a full bank which is at least 4MB in size and begins at
+ * 0xf0000000 (ie. KERNBASE).
+ */
+static void map_kernel(void)
+{
+	unsigned long raw_pte, physpage;
+	unsigned long vaddr, tally, low_base;
+	char etaken[SPARC_PHYS_BANKS];
+	int entry;
+
+	/* Step 1: Clear out sp_banks taken map. */
+	MKTRACE(("map_kernel: clearing etaken vector... "));
+	for(entry = 0; entry < SPARC_PHYS_BANKS; entry++)
+		etaken[entry] = 0;
+
+	low_base = KERNBASE;
+
+	/* Step 2: Calculate 'lots_of_ram'. */
+	tally = 0;
+	for(entry = 0; sp_banks[entry].num_bytes; entry++)
+		tally += sp_banks[entry].num_bytes;
+	if(tally >= (0xfd000000 - KERNBASE))
+		lots_of_ram = 1;
+	else
+		lots_of_ram = 0;
+	MKTRACE(("tally=%08lx lots_of_ram<%d>\n", tally, lots_of_ram));
 
-	/* Mark this sp_bank invalid... */
-	sp_banks[phys_bank].base_addr |= 1;
-	srmmu_bank++;
+	/* Step 3: Fill in KERNBASE base pgd.  Lots of sanity checking here. */
+	raw_pte = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
+	if((raw_pte & SRMMU_ET_MASK) != SRMMU_ET_PTE)
+		memprobe_error("Wheee, kernel not mapped at all by boot loader.\n");
+	physpage = (raw_pte & SRMMU_PTE_PMASK) << 4;
+	physpage -= PAGE_SIZE;
+	if(physpage & ~(SRMMU_PGDIR_MASK))
+		memprobe_error("Wheee, kernel not mapped on 16MB physical boundry.\n");
+	entry = find_in_spbanks(physpage);
+	if(entry == -1 || (sp_banks[entry].base_addr != physpage))
+		memprobe_error("Kernel mapped in non-existant memory.\n");
+	MKTRACE(("map_kernel: map_spbank(vbase=%08x, entry<%d>)[%08lx,%08lx]\n", KERNBASE, entry, sp_banks[entry].base_addr, sp_banks[entry].num_bytes));
+	if(((KERNBASE + (sp_banks[entry].num_bytes)) > 0xfd000000) ||
+	   ((KERNBASE + (sp_banks[entry].num_bytes)) < KERNBASE)) {
+		unsigned long orig_base = sp_banks[entry].base_addr;
+		unsigned long orig_len = sp_banks[entry].num_bytes;
+		unsigned long can_map = (0xfd000000 - KERNBASE);
+		
+		/* Map a partial bank in this case, adjust the base
+		 * and the length, but don't mark it used.
+		 */
+		sp_banks[entry].num_bytes = can_map;
+		MKTRACE(("wheee really big mapping [%08lx,%08lx]", orig_base, can_map));
+		vaddr = map_spbank(KERNBASE, entry);
+		MKTRACE(("vaddr now %08lx ", vaddr));
+		sp_banks[entry].base_addr = orig_base + can_map;
+		sp_banks[entry].num_bytes = orig_len - can_map;
+		MKTRACE(("adjust[%08lx,%08lx]\n", (orig_base + can_map), (orig_len - can_map)));
+		MKTRACE(("map_kernel: skipping first loop\n"));
+		goto loop_skip;
+	}
+	vaddr = map_spbank(KERNBASE, entry);
+	etaken[entry] = 1;
+
+	/* Step 4: Map what we can above KERNBASE. */
+	MKTRACE(("map_kernel: vaddr=%08lx, entering first loop\n", vaddr));
+	for(;;) {
+		unsigned long bank_size;
+
+		MKTRACE(("map_kernel: ffsp()"));
+		entry = find_free_spbank(&etaken[0]);
+		bank_size = sp_banks[entry].num_bytes;
+		MKTRACE(("<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size));
+		if(!bank_size)
+			break;
+		if(((vaddr + bank_size) >= 0xfd000000) ||
+		   ((vaddr + bank_size) < KERNBASE)) {
+			unsigned long orig_base = sp_banks[entry].base_addr;
+			unsigned long orig_len = sp_banks[entry].num_bytes;
+			unsigned long can_map = (0xfd000000 - vaddr);
 
-	/* Now, deal with what is left. */
-	while(start < end) {
-		unsigned long baddr;
-		int btg;
+			/* Map a partial bank in this case, adjust the base
+			 * and the length, but don't mark it used.
+			 */
+			sp_banks[entry].num_bytes = can_map;
+			MKTRACE(("wheee really big mapping [%08lx,%08lx]", orig_base, can_map));
+			vaddr = map_spbank(vaddr, entry);
+			MKTRACE(("vaddr now %08lx ", vaddr));
+			sp_banks[entry].base_addr = orig_base + can_map;
+			sp_banks[entry].num_bytes = orig_len - can_map;
+			MKTRACE(("adjust[%08lx,%08lx]\n", (orig_base + can_map), (orig_len - can_map)));
+			break;
+		}
+		if(!bank_size)
+			break;
 
-		/* Find a usable cluster of physical ram. */
-		for(i=0; sp_banks[i].num_bytes != 0; i++)
-			if(!(sp_banks[i].base_addr & 1))
-				break;
-		if(sp_banks[i].num_bytes == 0)
+		/* Ok, we can map this one, do it. */
+		MKTRACE(("map_spbank(%08lx,entry<%d>) ", vaddr, entry));
+		vaddr = map_spbank(vaddr, entry);
+		etaken[entry] = 1;
+		MKTRACE(("vaddr now %08lx\n", vaddr));
+	}
+	MKTRACE(("\n"));
+	/* If not lots_of_ram, assume we did indeed map it all above. */
+loop_skip:
+	if(!lots_of_ram)
+		goto check_and_return;
+	
+	/* Step 5: Map the rest (if any) right below KERNBASE. */
+	MKTRACE(("map_kernel: doing low mappings... "));
+	tally = 0;
+	for(entry = 0; sp_banks[entry].num_bytes; entry++) {
+		if(!etaken[entry])
+			tally += SRMMU_PGDIR_ALIGN(sp_banks[entry].num_bytes);
+	}
+	if(!tally)
+		memprobe_error("Whee, lots_of_ram yet no low pages to map.\n");
+	low_base = (KERNBASE - tally);
+	MKTRACE(("tally=%08lx low_base=%08lx\n", tally, low_base));
+
+	/* Ok, now map 'em. */
+	MKTRACE(("map_kernel: Allocate pt skeleton (%08lx, %08x)\n",low_base,KERNBASE));
+	srmmu_allocate_ptable_skeleton(low_base, KERNBASE);
+	vaddr = low_base;
+	MKTRACE(("map_kernel: vaddr=%08lx Entering second loop for low maps.\n", vaddr));
+	for(;;) {
+		unsigned long bank_size;
+
+		entry = find_free_spbank(&etaken[0]);
+		bank_size = sp_banks[entry].num_bytes;
+		MKTRACE(("map_kernel: e<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size));
+		if(!bank_size)
 			break;
+		if((vaddr + bank_size) > KERNBASE)
+			memprobe_error("Wheee, kernel low mapping overflow.\n");
+		MKTRACE(("map_spbank(%08lx, %d) ", vaddr, entry));
+		vaddr = map_spbank(vaddr, entry);
+		etaken[entry] = 1;
+		tally -= SRMMU_PGDIR_ALIGN(bank_size);
+		MKTRACE(("Now, vaddr=%08lx tally=%08lx\n", vaddr, tally));
+	}
+	MKTRACE(("\n"));
+	if(tally)
+		memprobe_error("Wheee, did not map all of low mappings.\n");
+check_and_return:
+	/* Step 6: Sanity check, make sure we did it all. */
+	MKTRACE(("check_and_return: "));
+	for(entry = 0; sp_banks[entry].num_bytes; entry++) {
+		MKTRACE(("e[%d]=%d ", entry, etaken[entry]));
+		if(!etaken[entry]) {
+			MKTRACE(("oops\n"));
+			memprobe_error("Some bank did not get mapped.\n");
+		}
+	}
+	MKTRACE(("success\n"));
+	init_task.mm->mmap->vm_start = page_offset = low_base;
+	return; /* SUCCESS! */
+}
 
-		/* Add it to srmmu_map */
-		srmmu_map[srmmu_bank].vbase = start;
-		srmmu_map[srmmu_bank].pbase = sp_banks[i].base_addr;
-		srmmu_map[srmmu_bank].size = sp_banks[i].num_bytes;
-		srmmu_bank++;
-
-		btg = sp_banks[i].num_bytes;
-		baddr = sp_banks[i].base_addr;
-		while(btg) {
-			pgdp = srmmu_pgd_offset(init_task.mm, start);
-			pmdp = srmmu_early_pmd_offset(pgdp, start);
-			ptep = srmmu_early_pte_offset(pmdp, start);
-			pte_val(*ptep) = (SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV);
-			pte_val(*ptep) |= (baddr >> 4);
+unsigned long srmmu_endmem_fixup(unsigned long mem_end_now)
+{
+	unsigned long tally = 0;
+	int i;
 
-			baddr += PAGE_SIZE;
-			start += PAGE_SIZE;
-			btg -= PAGE_SIZE;
-		}
-		sp_banks[i].base_addr |= 1;
-	}
-	if(start < end) {
-		prom_printf("weird, didn't use all of physical memory... ");
-		prom_halt();
-	}
-	for(phys_bank = 0; sp_banks[phys_bank].num_bytes != 0; phys_bank++)
-		sp_banks[phys_bank].base_addr &= ~1;
-#if 0
-	for(i = 0; srmmu_map[i].size != 0; i++) {
-		prom_printf("srmmu_map[%d]: vbase=%08lx pbase=%08lx size=%d\n",
-			    i, srmmu_map[i].vbase,
-			    srmmu_map[i].pbase, srmmu_map[i].size);
-	}
-	prom_getchar();
-	for(i = 0; sp_banks[i].num_bytes != 0; i++) {
-		prom_printf("sp_banks[%d]: base_addr=%08lx num_bytes=%d\n",
-			    i,
-			    sp_banks[i].base_addr,
-			    sp_banks[i].num_bytes);
+	for(i = 0; sp_banks[i].num_bytes; i++)
+		tally += SRMMU_PGDIR_ALIGN(sp_banks[i].num_bytes);
+	if(tally < (0x0d000000UL)) {
+		return KERNBASE + tally;
+	} else {
+		return 0xfd000000UL;
 	}
-	prom_getchar();
-	prom_halt();
-#endif
 }
 
 /* Paging initialization on the Sparc Reference MMU. */
@@ -1809,18 +2596,15 @@
 
 unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
 {
-	unsigned long ptables_start, first_mapped_page;
+	unsigned long ptables_start;
 	int i, cpunode;
 	char node_str[128];
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
 
-	physmem_mapped_contig = 0; /* for init.c:taint_real_pages() */
+	sparc_iobase_vaddr = 0xfd000000;    /* 16MB of IOSPACE on all sun4m's. */
+	physmem_mapped_contig = 0;	    /* for init.c:taint_real_pages()   */
 
 #if CONFIG_AP1000
-        printk("Forcing num_contexts to 1024\n");
-        num_contexts = 1024;
+        num_contexts = AP_NUM_CONTEXTS;
 #else
 	/* Find the number of contexts on the srmmu. */
 	cpunode = prom_getchild(prom_root_node);
@@ -1840,65 +2624,60 @@
 		
 	ptables_start = mempool = PAGE_ALIGN(start_mem);
 	memset(swapper_pg_dir, 0, PAGE_SIZE);
-	first_mapped_page = KERNBASE;
-	kbpage = srmmu_hwprobe(KERNBASE);
-	if((kbpage & SRMMU_ET_MASK) != SRMMU_ET_PTE) {
-		kbpage = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
-		kbpage = (kbpage & SRMMU_PTE_PMASK) << 4;
-		kbpage -= PAGE_SIZE;
-		first_mapped_page += PAGE_SIZE;
-	} else
-		kbpage = (kbpage & SRMMU_PTE_PMASK) << 4;
+	kbpage = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
+	kbpage = (kbpage & SRMMU_PTE_PMASK) << 4;
+	kbpage -= PAGE_SIZE;
 
 	srmmu_allocate_ptable_skeleton(KERNBASE, end_mem);
 #if CONFIG_SUN_IO
-	srmmu_allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_END);
+	srmmu_allocate_ptable_skeleton(sparc_iobase_vaddr, IOBASE_END);
 	srmmu_allocate_ptable_skeleton(DVMA_VADDR, DVMA_END);
 #endif
 
-	/* Steal DVMA pages now, I still don't like how we waste all this. */
 	mempool = PAGE_ALIGN(mempool);
-	first_dvma_page = mempool;
-	last_dvma_page = (mempool + (DVMA_LEN) - PAGE_SIZE);
-	mempool = last_dvma_page + PAGE_SIZE;
-
 #if CONFIG_AP1000
         ap_inherit_mappings();
 #else
         srmmu_inherit_prom_mappings(0xfe400000,(LINUX_OPPROM_ENDVM-PAGE_SIZE));
 #endif
-	srmmu_map_kernel(first_mapped_page, end_mem);
-#if CONFIG_SUN_IO
-	srmmu_map_dvma_pages_for_cpu(first_dvma_page, last_dvma_page);
+	map_kernel();
+#if CONFIG_AP1000
+	/* the MSC wants this aligned on a 16k boundary */
+	srmmu_context_table = 
+	  sparc_init_alloc(&mempool, 
+			   num_contexts*sizeof(ctxd_t)<0x4000?
+			   0x4000:
+			   num_contexts*sizeof(ctxd_t));
+#else
+	srmmu_context_table = sparc_init_alloc(&mempool, num_contexts*sizeof(ctxd_t));
 #endif
-	srmmu_context_table = srmmu_init_alloc(&mempool, num_contexts*sizeof(ctxd_t));
 	srmmu_ctx_table_phys = (ctxd_t *) srmmu_v2p((unsigned long) srmmu_context_table);
 	for(i = 0; i < num_contexts; i++)
 		ctxd_set(&srmmu_context_table[i], swapper_pg_dir);
 
 	start_mem = PAGE_ALIGN(mempool);
 
-	/* Some SRMMU's are _very_ stupid indeed. */
-	if(!can_cache_ptables) {
-		for( ; ptables_start < start_mem; ptables_start += PAGE_SIZE) {
-			pgdp = srmmu_pgd_offset(init_task.mm, ptables_start);
-			pmdp = srmmu_early_pmd_offset(pgdp, ptables_start);
-			ptep = srmmu_early_pte_offset(pmdp, ptables_start);
-			pte_val(*ptep) &= ~SRMMU_CACHE;
-		}
+	flush_cache_all();
+	if(flush_page_for_dma == viking_no_mxcc_flush_page) {
+		unsigned long start = ptables_start;
+		unsigned long end = start_mem;
 
-		pgdp = srmmu_pgd_offset(init_task.mm, (unsigned long)swapper_pg_dir);
-		pmdp = srmmu_early_pmd_offset(pgdp, (unsigned long)swapper_pg_dir);
-		ptep = srmmu_early_pte_offset(pmdp, (unsigned long)swapper_pg_dir);
-		pte_val(*ptep) &= ~SRMMU_CACHE;
+		while(start < end) {
+			viking_no_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
 	}
-
-	flush_cache_all();
 	srmmu_set_ctable_ptr((unsigned long) srmmu_ctx_table_phys);
 	flush_tlb_all();
 	poke_srmmu();
 
+#if CONFIG_AP1000
+	/* on the AP we don't put the top few contexts into the free
+	   context list as these are reserved for parallel tasks */
+	start_mem = sparc_context_init(start_mem, MPP_CONTEXT_BASE);
+#else
 	start_mem = sparc_context_init(start_mem, num_contexts);
+#endif
 	start_mem = free_area_init(start_mem, end_mem);
 
 	return PAGE_ALIGN(start_mem);
@@ -1914,19 +2693,24 @@
 		"invrnge\t\t: %d\n"
 		"invpg\t\t: %d\n"
 		"contexts\t: %d\n"
-		"big_chunks\t: %d\n"
-		"little_chunks\t: %d\n",
-		srmmu_name,
+#ifdef USE_CHUNK_ALLOC
+		"big chunks\t: %d\n"
+		"little chunks\t: %d\n"
+		"chunk pages\t: %d\n"
+		"garbage\t\t: %d\n"
+		"garbage hits\t: %d\n"
+#endif
+		, srmmu_name,
 		module_stats.invall,
 		module_stats.invmm,
 		module_stats.invrnge,
 		module_stats.invpg,
-		num_contexts,
-#if 0
-		num_big_chunks,
-		num_little_chunks
-#else
-		0, 0
+		num_contexts
+#ifdef USE_CHUNK_ALLOC
+		, bcwater, lcwater,
+		chunk_pages,
+		garbage_calls,
+		clct_pages
 #endif
 		);
 	return srmmuinfo;
@@ -1938,16 +2722,13 @@
 
 static void srmmu_exit_hook(void)
 {
-	struct ctx_list *ctx_old;
 	struct mm_struct *mm = current->mm;
 
-	if(mm->context != NO_CONTEXT) {
+	if(mm->context != NO_CONTEXT && mm->count == 1) {
 		flush_cache_mm(mm);
 		ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
 		flush_tlb_mm(mm);
-		ctx_old = ctx_list_pool + mm->context;
-		remove_from_ctx_list(ctx_old);
-		add_to_free_ctxlist(ctx_old);
+		free_context(mm->context);
 		mm->context = NO_CONTEXT;
 	}
 }
@@ -1955,18 +2736,64 @@
 static void srmmu_flush_hook(void)
 {
 	if(current->tss.flags & SPARC_FLAG_KTHREAD) {
-		alloc_context(current->mm);
+		alloc_context(current);
+		flush_cache_mm(current->mm);
 		ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
+		flush_tlb_mm(current->mm);
 		srmmu_set_context(current->mm->context);
 	}
 }
 
+static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma,
+				       unsigned long address, pte_t pte)
+{
+#if 0
+	struct inode *inode;
+	struct vm_area_struct *vmaring;
+	unsigned long offset, vaddr;
+	unsigned long start;
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	if (!(vma->vm_flags & VM_WRITE) ||
+	    !(vma->vm_flags & VM_SHARED))
+		return;
+
+	inode = vma->vm_inode;
+	if (!inode)
+		return;
+
+	offset = (address & PAGE_MASK) - vma->vm_start;
+	vmaring = inode->i_mmap; 
+	do {
+		vaddr = vmaring->vm_start + offset;
+
+		if ((vaddr ^ address) & vac_badbits) {
+			start = vma->vm_start;
+			while (start < vma->vm_end) {
+				pgdp = srmmu_pgd_offset(vma->vm_mm, start);
+				pmdp = srmmu_pmd_offset(pgdp, start);
+				ptep = srmmu_pte_offset(pmdp, start);
+
+				flush_cache_page_to_uncache(start);
+				set_pte(ptep, __pte((pte_val(*ptep) &
+					      ~SRMMU_CACHE)));
+				flush_tlb_page_for_cbit(start);
+
+				start += PAGE_SIZE;
+			}
+			return;
+		}
+	} while ((vmaring = vmaring->vm_next_share) != inode->i_mmap);
+#endif
+}
+
 static void hypersparc_exit_hook(void)
 {
-	struct ctx_list *ctx_old;
 	struct mm_struct *mm = current->mm;
 
-	if(mm->context != NO_CONTEXT) {
+	if(mm->context != NO_CONTEXT && mm->count == 1) {
 		/* HyperSparc is copy-back, any data for this
 		 * process in a modified cache line is stale
 		 * and must be written back to main memory now
@@ -1975,9 +2802,7 @@
 		flush_cache_mm(mm);
 		ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
 		flush_tlb_mm(mm);
-		ctx_old = ctx_list_pool + mm->context;
-		remove_from_ctx_list(ctx_old);
-		add_to_free_ctxlist(ctx_old);
+		free_context(mm->context);
 		mm->context = NO_CONTEXT;
 	}
 }
@@ -1985,21 +2810,52 @@
 static void hypersparc_flush_hook(void)
 {
 	if(current->tss.flags & SPARC_FLAG_KTHREAD) {
-		alloc_context(current->mm);
+		alloc_context(current);
 		flush_cache_mm(current->mm);
 		ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
+		flush_tlb_mm(current->mm);
 		srmmu_set_context(current->mm->context);
 	}
 }
 
 /* Init various srmmu chip types. */
-void srmmu_is_bad(void)
+static void srmmu_is_bad(void)
 {
 	prom_printf("Could not determine SRMMU chip type.\n");
 	prom_halt();
 }
 
-void poke_hypersparc(void)
+static void init_vac_layout(void)
+{
+	int nd, cache_lines;
+	char node_str[128];
+
+	nd = prom_getchild(prom_root_node);
+	while((nd = prom_getsibling(nd)) != 0) {
+		prom_getstring(nd, "device_type", node_str, sizeof(node_str));
+		if(!strcmp(node_str, "cpu"))
+			break;
+	}
+	if(nd == 0) {
+		prom_printf("No CPU nodes found, halting.\n");
+		prom_halt();
+	}
+
+	vac_line_size = prom_getint(nd, "cache-line-size");
+	if (vac_line_size == -1) {
+		prom_printf("can't determine cache-line-size, halting.\n");
+		prom_halt();
+	}
+	cache_lines = prom_getint(nd, "cache-nlines");
+	if (cache_lines == -1) {
+		prom_printf("can't determine cache-nlines, halting.\n");
+		prom_halt();
+	}
+	vac_cache_size = cache_lines * vac_line_size;
+	vac_badbits = (vac_cache_size - 1) & PAGE_MASK;
+}
+
+static void poke_hypersparc(void)
 {
 	volatile unsigned long clear;
 	unsigned long mreg = srmmu_get_mmureg();
@@ -2019,20 +2875,13 @@
 	clear = srmmu_get_fstatus();
 }
 
-void init_hypersparc(void)
+static void init_hypersparc(void)
 {
-	unsigned long mreg = srmmu_get_mmureg();
-
 	srmmu_name = "ROSS HyperSparc";
-	can_cache_ptables = 0;
-	if(mreg & HYPERSPARC_CSIZE) {
-		hyper_cache_size = (256 * 1024);
-		hyper_line_size = 64;
-	} else {
-		hyper_cache_size = (128 * 1024);
-		hyper_line_size = 32;
-	}
 
+	init_vac_layout();
+
+	set_pte = srmmu_set_pte_nocache_hyper;
 	flush_cache_all = hypersparc_flush_cache_all;
 	flush_cache_mm = hypersparc_flush_cache_mm;
 	flush_cache_range = hypersparc_flush_cache_range;
@@ -2052,41 +2901,67 @@
 	switch_to_context = hypersparc_switch_to_context;
 	mmu_exit_hook = hypersparc_exit_hook;
 	mmu_flush_hook = hypersparc_flush_hook;
+	update_mmu_cache = srmmu_vac_update_mmu_cache;
 	sparc_update_rootmmu_dir = hypersparc_update_rootmmu_dir;
-	set_pte = hypersparc_set_pte;
 	poke_srmmu = poke_hypersparc;
 }
 
-void poke_cypress(void)
+static void poke_cypress(void)
 {
 	unsigned long mreg = srmmu_get_mmureg();
+	unsigned long faddr;
+	volatile unsigned long clear;
+
+	clear = srmmu_get_faddr();
+	clear = srmmu_get_fstatus();
+
+	for(faddr = 0x0; faddr < 0x10000; faddr += 20) {
+		__asm__ __volatile__("sta %%g0, [%0 + %1] %2\n\t"
+				     "sta %%g0, [%0] %2\n\t" : :
+				     "r" (faddr), "r" (0x40000),
+				     "i" (ASI_M_DATAC_TAG));
+	}
+
+	/* And one more, for our good neighbor, Mr. Broken Cypress. */
+	clear = srmmu_get_faddr();
+	clear = srmmu_get_fstatus();
 
-	mreg &= ~CYPRESS_CMODE;
-	mreg |= CYPRESS_CENABLE;
+	mreg |= (CYPRESS_CENABLE | CYPRESS_CMODE);
 	srmmu_set_mmureg(mreg);
 }
 
-void init_cypress_common(void)
+static void init_cypress_common(void)
 {
-	can_cache_ptables = 0;
+	init_vac_layout();
+
+	set_pte = srmmu_set_pte_nocache_cypress;
+	flush_cache_all = cypress_flush_cache_all;
+	flush_cache_mm = cypress_flush_cache_mm;
+	flush_cache_range = cypress_flush_cache_range;
+	flush_cache_page = cypress_flush_cache_page;
+
 	flush_tlb_all = cypress_flush_tlb_all;
 	flush_tlb_mm = cypress_flush_tlb_mm;
 	flush_tlb_page = cypress_flush_tlb_page;
 	flush_tlb_range = cypress_flush_tlb_range;
-	poke_srmmu = poke_cypress;
 
-	/* XXX Need to write cache flushes for this one... XXX */
+	flush_page_to_ram = cypress_flush_page_to_ram;
+	flush_page_for_dma = cypress_flush_page_for_dma;
+	flush_cache_page_to_uncache = cypress_flush_page_to_uncache;
+	flush_tlb_page_for_cbit = cypress_flush_tlb_page_for_cbit;
 
+	update_mmu_cache = srmmu_vac_update_mmu_cache;
+	poke_srmmu = poke_cypress;
 }
 
-void init_cypress_604(void)
+static void init_cypress_604(void)
 {
 	srmmu_name = "ROSS Cypress-604(UP)";
 	srmmu_modtype = Cypress;
 	init_cypress_common();
 }
 
-void init_cypress_605(unsigned long mrev)
+static void init_cypress_605(unsigned long mrev)
 {
 	srmmu_name = "ROSS Cypress-605(MP)";
 	if(mrev == 0xe) {
@@ -2103,7 +2978,7 @@
 	init_cypress_common();
 }
 
-void poke_swift(void)
+static void poke_swift(void)
 {
 	unsigned long mreg = srmmu_get_mmureg();
 
@@ -2124,7 +2999,7 @@
 }
 
 #define SWIFT_MASKID_ADDR  0x10003018
-void init_swift(void)
+static void init_swift(void)
 {
 	unsigned long swift_rev;
 
@@ -2195,7 +3070,7 @@
 	poke_srmmu = poke_swift;
 }
 
-void poke_tsunami(void)
+static void poke_tsunami(void)
 {
 	unsigned long mreg = srmmu_get_mmureg();
 
@@ -2206,7 +3081,7 @@
 	srmmu_set_mmureg(mreg);
 }
 
-void init_tsunami(void)
+static void init_tsunami(void)
 {
 	/* Tsunami's pretty sane, Sun and TI actually got it
 	 * somewhat right this time.  Fujitsu should have
@@ -2215,7 +3090,6 @@
 
 	srmmu_name = "TI Tsunami";
 	srmmu_modtype = Tsunami;
-	can_cache_ptables = 1;
 
 	flush_cache_all = tsunami_flush_cache_all;
 	flush_cache_mm = tsunami_flush_cache_mm;
@@ -2235,28 +3109,24 @@
 	poke_srmmu = poke_tsunami;
 }
 
-void poke_viking(void)
+static void poke_viking(void)
 {
 	unsigned long mreg = srmmu_get_mmureg();
 	static int smp_catch = 0;
 
 	if(viking_mxcc_present) {
-		unsigned long mxcc_control;
+		unsigned long mxcc_control = mxcc_get_creg();
 
-		__asm__ __volatile__("set -1, %%g2\n\t"
-				     "set -1, %%g3\n\t"
-				     "stda %%g2, [%1] %2\n\t"
-				     "lda [%3] %2, %0\n\t" :
-				     "=r" (mxcc_control) :
-				     "r" (MXCC_EREG), "i" (ASI_M_MXCC),
-				     "r" (MXCC_CREG) : "g2", "g3");
 		mxcc_control |= (MXCC_CTL_ECE | MXCC_CTL_PRE | MXCC_CTL_MCE);
-		mxcc_control &= ~(MXCC_CTL_PARE | MXCC_CTL_RRC);
-		mreg &= ~(VIKING_PCENABLE);
-		__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
-				     "r" (mxcc_control), "r" (MXCC_CREG),
-				     "i" (ASI_M_MXCC));
-		srmmu_set_mmureg(mreg);
+		mxcc_control &= ~(MXCC_CTL_RRC);
+		mxcc_set_creg(mxcc_control);
+
+		/* We don't need memory parity checks.
+		 * XXX This is a mess, have to dig out later. ecd.
+		viking_mxcc_turn_off_parity(&mreg, &mxcc_control);
+		 */
+
+		/* We do cache ptables on MXCC. */
 		mreg |= VIKING_TCENABLE;
 	} else {
 		unsigned long bpreg;
@@ -2275,12 +3145,6 @@
 		}
 	}
 
-	viking_unlock_icache();
-	viking_flush_icache();
-#if 0
-	viking_unlock_dcache();
-	viking_flush_dcache();
-#endif
 	mreg |= VIKING_SPENABLE;
 	mreg |= (VIKING_ICENABLE | VIKING_DCENABLE);
 	mreg |= VIKING_SBENABLE;
@@ -2288,13 +3152,21 @@
 #if CONFIG_AP1000
         mreg &= ~(VIKING_SBENABLE);
 #endif
+	srmmu_set_mmureg(mreg);
+
+
 #ifdef __SMP__
-	mreg &= ~(VIKING_SBENABLE);
+	/* Avoid unnecessary cross calls. */
+	flush_cache_all = local_flush_cache_all;
+	flush_page_to_ram = local_flush_page_to_ram;
+	flush_page_for_dma = local_flush_page_for_dma;
+	if (viking_mxcc_present) {
+		flush_cache_page_to_uncache = local_flush_cache_page_to_uncache;
+	}
 #endif
-	srmmu_set_mmureg(mreg);
 }
 
-void init_viking(void)
+static void init_viking(void)
 {
 	unsigned long mreg = srmmu_get_mmureg();
 
@@ -2305,7 +3177,7 @@
 
 		srmmu_name = "TI Viking";
 		viking_mxcc_present = 0;
-		can_cache_ptables = 0;
+		set_pte = srmmu_set_pte_nocache_nomxccvik;
 
 		bpreg = viking_get_bpreg();
 		bpreg &= ~(VIKING_ACTION_MIX);
@@ -2314,11 +3186,21 @@
 		msi_set_sync();
 
 		flush_cache_page_to_uncache = viking_no_mxcc_flush_page;
+
+		/* We need this to make sure old viking takes no hits
+		 * on it's cache for dma snoops to workaround the
+		 * "load from non-cacheable memory" interrupt bug.
+		 * This is only necessary because of the new way in
+		 * which we use the IOMMU.
+		 */
+		flush_page_for_dma = viking_no_mxcc_flush_page;
 	} else {
 		srmmu_name = "TI Viking/MXCC";
 		viking_mxcc_present = 1;
-		can_cache_ptables = 1;
 		flush_cache_page_to_uncache = viking_mxcc_flush_page;
+
+		/* MXCC vikings lack the DMA snooping bug. */
+		flush_page_for_dma = viking_flush_page_for_dma;
 	}
 
 	flush_cache_all = viking_flush_cache_all;
@@ -2332,7 +3214,6 @@
 	flush_tlb_range = viking_flush_tlb_range;
 
 	flush_page_to_ram = viking_flush_page_to_ram;
-	flush_page_for_dma = viking_flush_page_for_dma;
 	flush_tlb_page_for_cbit = viking_flush_tlb_page_for_cbit;
 
 	poke_srmmu = poke_viking;
@@ -2364,6 +3245,8 @@
 			/* Uniprocessor Cypress */
 			init_cypress_604();
 			break;
+		case 12:
+			/* _REALLY OLD_ Cypress MP chips... */
 		case 13:
 		case 14:
 		case 15:
@@ -2476,7 +3359,7 @@
 	pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF;
 	    
 	/* Functions */
-	set_pte = srmmu_set_pte;
+	set_pte = srmmu_set_pte_cacheable;
 	switch_to_context = srmmu_switch_to_context;
 	pmd_align = srmmu_pmd_align;
 	pgdir_align = srmmu_pgdir_align;
@@ -2503,6 +3386,7 @@
 	pgd_clear = srmmu_pgd_clear;
 
 	mk_pte = srmmu_mk_pte;
+	mk_pte_phys = srmmu_mk_pte_phys;
 	pgd_set = srmmu_pgd_set;
 	mk_pte_io = srmmu_mk_pte_io;
 	pte_modify = srmmu_pte_modify;
@@ -2539,6 +3423,8 @@
 	mmu_get_scsi_sgl = srmmu_get_scsi_sgl;
 	mmu_release_scsi_one = srmmu_release_scsi_one;
 	mmu_release_scsi_sgl = srmmu_release_scsi_sgl;
+
+	mmu_map_dma_area = srmmu_map_dma_area;
 
 	mmu_info = srmmu_mmu_info;
         mmu_v2p = srmmu_v2p;
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c
--- v2.1.8/linux/arch/sparc/mm/sun4c.c	Tue Apr 23 12:31:46 1996
+++ linux/arch/sparc/mm/sun4c.c	Sat Nov  9 10:12:21 1996
@@ -1,6 +1,9 @@
-/* sun4c.c: Doing in software what should be done in hardware.
+/* $Id: sun4c.c,v 1.121 1996/11/01 20:36:27 ecd Exp $
+ * sun4c.c: Doing in software what should be done in hardware.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1996 Andrew Tridgell (Andrew.Tridgell@anu.edu.au)
  */
 
 #include <linux/kernel.h>
@@ -13,9 +16,72 @@
 #include <asm/machines.h>
 #include <asm/memreg.h>
 #include <asm/processor.h>
+#include <asm/auxio.h>
+#include <asm/io.h>
+#include <asm/oplib.h>
+#include <asm/openprom.h>
 
 extern int num_segmaps, num_contexts;
 
+/* Small structure for ease of handling in the low level kernel fault
+ * handler. This holds all information necessary, like the sun4c_ufree_ring
+ * for user segments.
+ */
+struct sun4c_segment_info {
+	unsigned long vaddr;
+	unsigned char pseg;
+};
+struct sun4c_segment_info *sun4c_kernel_next;
+
+#define SUN4C_KERNEL_BUCKETS 32
+#define SUN4C_KERNEL_BSIZE (sizeof(struct sun4c_segment_info) \
+			    * SUN4C_KERNEL_BUCKETS)
+
+#ifndef MAX
+#define MAX(a,b) ((a)<(b)?(b):(a))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+
+#define KGPROF_PROFILING 0
+#if KGPROF_PROFILING
+#define KGPROF_DEPTH 3 /* this needs to match the code below */
+#define KGPROF_SIZE 100
+static struct {
+	unsigned addr[KGPROF_DEPTH];
+	unsigned count;
+} kgprof_counters[KGPROF_SIZE];
+
+/* just call this function from whatever function you think needs it then
+   look at /proc/cpuinfo to see where the function is being called from
+   and how often. This gives a type of "kernel gprof" */
+#define NEXT_PROF(prev,lvl) (prev>PAGE_OFFSET?__builtin_return_address(lvl):0)
+static inline void kgprof_profile(void)
+{
+	unsigned ret[KGPROF_DEPTH];
+	int i,j;
+	/* you can't use a variable argument to __builtin_return_address() */
+	ret[0] = (unsigned)__builtin_return_address(0);
+	ret[1] = (unsigned)NEXT_PROF(ret[0],1);
+	ret[2] = (unsigned)NEXT_PROF(ret[1],2);
+
+	for (i=0;i<KGPROF_SIZE && kgprof_counters[i].addr[0];i++) {
+		for (j=0;j<KGPROF_DEPTH;j++) 
+			if (ret[j] != kgprof_counters[i].addr[j]) break;
+		if (j==KGPROF_DEPTH) break;
+	}
+	if (i<KGPROF_SIZE) {		
+		for (j=0;j<KGPROF_DEPTH;j++)
+			kgprof_counters[i].addr[j] = ret[j];
+		kgprof_counters[i].count++;
+	}
+}
+#endif
+
+
 /* Flushing the cache. */
 struct sun4c_vac_props sun4c_vacinfo;
 static int ctxflushes, segflushes, pageflushes;
@@ -63,30 +129,68 @@
 			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 					     "r" (vaddr), "i" (ASI_HWFLUSHCONTEXT));
 	} else {
-		int incr = sun4c_vacinfo.linesize;
-		for(vaddr=0; vaddr < sun4c_vacinfo.num_bytes; vaddr+=incr)
-			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
-					     "r" (vaddr), "i" (ASI_FLUSHCTX));
+		/* AJT: possibly read the tags and avoid flushing the ones that 
+		   are above 0xf0000000 so the kernel isn't flushed all the time */
+		__asm__ __volatile__("add	%1, %1, %%g1\n\t"
+				     "add	%1, %%g1, %%g2\n\t"
+				     "add	%1, %%g2, %%g3\n\t"
+				     "add	%1, %%g3, %%g4\n\t"
+				     "add	%1, %%g4, %%g5\n\t"
+				     "add	%1, %%g5, %%o4\n\t"
+				     "add	%1, %%o4, %%o5\n"
+				     "1:\n\t"
+				     "subcc	%0, %%o5, %0\n\t"
+				     "sta	%%g0, [%0] %2\n\t"
+				     "sta	%%g0, [%0 + %1] %2\n\t"
+				     "sta	%%g0, [%0 + %%g1] %2\n\t"
+				     "sta	%%g0, [%0 + %%g2] %2\n\t"
+				     "sta	%%g0, [%0 + %%g3] %2\n\t"
+				     "sta	%%g0, [%0 + %%g4] %2\n\t"
+				     "sta	%%g0, [%0 + %%g5] %2\n\t"
+				     "bg	1b\n\t"
+				     " sta	%%g0, [%0 + %%o4] %2\n\t" : :
+				     "r" (sun4c_vacinfo.num_bytes),
+				     "r" (sun4c_vacinfo.linesize),
+				     "i" (ASI_FLUSHCTX) :
+				     "g1", "g2", "g3", "g4", "g5", "o4", "o5");
 	}
 }
 
 /* Scrape the segment starting at ADDR from the virtual cache. */
 static inline void sun4c_flush_segment(unsigned long addr)
 {
-	unsigned long end;
-
 	segflushes++;
 	addr &= SUN4C_REAL_PGDIR_MASK;
-	end = (addr + sun4c_vacinfo.num_bytes);
 	if(sun4c_vacinfo.do_hwflushes) {
+		unsigned long end = (addr + sun4c_vacinfo.num_bytes);
+
 		for( ; addr < end; addr += PAGE_SIZE)
 			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 					     "r" (addr), "i" (ASI_HWFLUSHSEG));
 	} else {
-		int incr = sun4c_vacinfo.linesize;
-		for( ; addr < end; addr += incr)
-			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
-					     "r" (addr), "i" (ASI_FLUSHSEG));
+		__asm__ __volatile__("add	%2, %2, %%g1\n\t"
+				     "add	%2, %%g1, %%g2\n\t"
+				     "add	%2, %%g2, %%g3\n\t"
+				     "add	%2, %%g3, %%g4\n\t"
+				     "add	%2, %%g4, %%g5\n\t"
+				     "add	%2, %%g5, %%o4\n\t"
+				     "add	%2, %%o4, %%o5\n"
+				     "1:\n\t"
+				     "subcc	%1, %%o5, %1\n\t"
+				     "sta	%%g0, [%0] %3\n\t"
+				     "sta	%%g0, [%0 + %2] %3\n\t"
+				     "sta	%%g0, [%0 + %%g1] %3\n\t"
+				     "sta	%%g0, [%0 + %%g2] %3\n\t"
+				     "sta	%%g0, [%0 + %%g3] %3\n\t"
+				     "sta	%%g0, [%0 + %%g4] %3\n\t"
+				     "sta	%%g0, [%0 + %%g5] %3\n\t"
+				     "sta	%%g0, [%0 + %%o4] %3\n\t"
+				     "bg	1b\n\t"
+				     " add	%0, %%o5, %0\n\t" : :
+				     "r" (addr), "r" (sun4c_vacinfo.num_bytes),
+				     "r" (sun4c_vacinfo.linesize),
+				     "i" (ASI_FLUSHSEG) :
+				     "g1", "g2", "g3", "g4", "g5", "o4", "o5");
 	}
 }
 
@@ -100,21 +204,37 @@
 		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 				     "r" (addr), "i" (ASI_HWFLUSHPAGE));
 	} else {
-		unsigned long end = addr + PAGE_SIZE;
-		int incr = sun4c_vacinfo.linesize;
-
-		for( ; addr < end; addr += incr)
-			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
-					     "r" (addr), "i" (ASI_FLUSHPG));
+		__asm__ __volatile__("add	%2, %2, %%g1\n\t"
+				     "add	%2, %%g1, %%g2\n\t"
+				     "add	%2, %%g2, %%g3\n\t"
+				     "add	%2, %%g3, %%g4\n\t"
+				     "add	%2, %%g4, %%g5\n\t"
+				     "add	%2, %%g5, %%o4\n\t"
+				     "add	%2, %%o4, %%o5\n"
+				     "1:\n\t"
+				     "subcc	%1, %%o5, %1\n\t"
+				     "sta	%%g0, [%0] %3\n\t"
+				     "sta	%%g0, [%0 + %2] %3\n\t"
+				     "sta	%%g0, [%0 + %%g1] %3\n\t"
+				     "sta	%%g0, [%0 + %%g2] %3\n\t"
+				     "sta	%%g0, [%0 + %%g3] %3\n\t"
+				     "sta	%%g0, [%0 + %%g4] %3\n\t"
+				     "sta	%%g0, [%0 + %%g5] %3\n\t"
+				     "sta	%%g0, [%0 + %%o4] %3\n\t"
+				     "bg	1b\n\t"
+				     " add	%0, %%o5, %0\n\t" : :
+				     "r" (addr), "r" (PAGE_SIZE),
+				     "r" (sun4c_vacinfo.linesize),
+				     "i" (ASI_FLUSHPG) :
+				     "g1", "g2", "g3", "g4", "g5", "o4", "o5");
 	}
 }
 
 /* The sun4c's do have an on chip store buffer.  And the way you
  * clear them out isn't so obvious.  The only way I can think of
  * to accomplish this is to read the current context register,
- * store the same value there, then do a bunch of nops for the
- * pipeline to clear itself completely.  This is only used for
- * dealing with memory errors, so it is not that critical.
+ * store the same value there, then read an external hardware
+ * register.
  */
 void sun4c_complete_all_stores(void)
 {
@@ -122,9 +242,7 @@
 
 	_unused = sun4c_get_context();
 	sun4c_set_context(_unused);
-	nop(); nop(); nop(); nop();
-	nop(); nop(); nop(); nop();
-	/* Is that enough? */
+	_unused = *AUXREG;
 }
 
 /* Bootup utility functions. */
@@ -156,13 +274,11 @@
 		for(vaddr = LINUX_OPPROM_ENDVM; vaddr; vaddr += SUN4C_REAL_PGDIR_SIZE)
 			sun4c_put_segmap(vaddr, invalid_segment);
 	}
-	sun4c_set_context(ctx);
+	sun4c_set_context(savectx);
 }
 
 void sun4c_probe_vac(void)
 {
-	int propval;
-
 	sun4c_disable_vac();
 	sun4c_vacinfo.num_bytes = prom_getintdefault(prom_root_node,
 						     "vac-size", 65536);
@@ -183,11 +299,12 @@
 		prom_halt();
 	};
 
-	propval = prom_getintdefault(prom_root_node, "vac_hwflush", -1);
-	sun4c_vacinfo.do_hwflushes = (propval == -1 ?
-				      prom_getintdefault(prom_root_node,
-							 "vac-hwflush", 0) :
-				      propval);
+	/* Only vac-hwflush (with a dash) is reliable, weitek
+	 * power-up processor claims vac_hwflush (underscore)
+	 * yet crashes if you try to use hardware based flushes.
+	 */
+	sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node,
+							"vac-hwflush", 0);
 
 	if(sun4c_vacinfo.num_bytes != 65536) {
 		prom_printf("WEIRD Sun4C VAC cache size, tell davem");
@@ -198,17 +315,100 @@
 	sun4c_enable_vac();
 }
 
+/* Patch instructions for the low level kernel fault handler. */
+extern unsigned long invalid_segment_patch1, invalid_segment_patch1_ff;
+extern unsigned long invalid_segment_patch2, invalid_segment_patch2_ff;
+extern unsigned long num_context_patch1, num_context_patch1_16;
+extern unsigned long num_context_patch2, num_context_patch2_16;
+extern unsigned long sun4c_kernel_buckets_patch;
+extern unsigned long sun4c_kernel_buckets_patch_32;
+
+#define PATCH_INSN(src, dst) do {	\
+		daddr = &(dst);		\
+		iaddr = &(src);		\
+		*daddr = *iaddr;	\
+	} while (0);
+
+static void patch_kernel_fault_handler(void)
+{
+	unsigned long *iaddr, *daddr;
+
+	switch (num_segmaps) {
+		case 128:
+			/* Default, nothing to do. */
+			break;
+		case 256:
+			PATCH_INSN(invalid_segment_patch1_ff,
+				   invalid_segment_patch1);
+			PATCH_INSN(invalid_segment_patch2_ff,
+				   invalid_segment_patch2);
+			break;
+		default:
+			prom_printf("Unhandled number of segmaps: %d\n",
+				    num_segmaps);
+			prom_halt();
+	}
+	switch (num_contexts) {
+		case 8:
+			/* Default, nothing to do. */
+			break;
+		case 16:
+			PATCH_INSN(num_context_patch1_16,
+				   num_context_patch1);
+			PATCH_INSN(num_context_patch2_16,
+				   num_context_patch2);
+			break;
+		default:
+			prom_printf("Unhandled number of contexts: %d\n",
+				    num_contexts);
+			prom_halt();
+	}
+	switch (SUN4C_KERNEL_BUCKETS) {
+		case 16:
+			/* Default, nothing to do. */
+			break;
+		case 32:
+			PATCH_INSN(sun4c_kernel_buckets_patch_32,
+				   sun4c_kernel_buckets_patch);
+			break;
+		default:
+			prom_printf("Unhandled number of kernel buckets: %d\n",
+				    SUN4C_KERNEL_BUCKETS);
+			prom_halt();
+	}
+}
+
 static void sun4c_probe_mmu(void)
 {
 	num_segmaps = prom_getintdefault(prom_root_node, "mmu-npmg", 128);
 	num_contexts = prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
+	patch_kernel_fault_handler();
+}
+
+volatile unsigned long *sun4c_memerr_reg = 0;
+
+void sun4c_probe_memerr_reg(void)
+{
+	int node;
+	struct linux_prom_registers regs[1];
+
+	node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(prom_root_node, "memory-error");
+	if (!node)
+		return;
+	prom_getproperty(node, "reg", (char *)regs, sizeof(regs));
+	sun4c_memerr_reg = sparc_alloc_io(regs[0].phys_addr, 0,
+					  regs[0].reg_size,
+					  "memory parity error",
+					  regs[0].which_io, 0);
 }
 
 static inline void sun4c_init_ss2_cache_bug(void)
 {
 	extern unsigned long start;
 
-	if(idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) {
+	if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) ||
+	   (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX))) {
 		/* Whee.. */
 		printk("SS2 cache bug detected, uncaching trap table page\n");
 		sun4c_flush_page((unsigned int) &start);
@@ -217,19 +417,28 @@
 	}
 }
 
-static inline unsigned long sun4c_init_alloc_dvma_pages(unsigned long start_mem)
+/* Addr is always aligned on a page boundry for us already. */
+static void sun4c_map_dma_area(unsigned long addr, int len)
 {
-	unsigned long addr, pte;
+	unsigned long page, end;
 
-	for(addr = DVMA_VADDR; addr < DVMA_END; addr += PAGE_SIZE) {
-		pte = (start_mem - PAGE_OFFSET) >> PAGE_SHIFT;
-		pte |= (_SUN4C_PAGE_VALID | _SUN4C_PAGE_WRITE | _SUN4C_PAGE_NOCACHE);
-		sun4c_put_pte(addr, pte);
-		start_mem += PAGE_SIZE;
+	end = PAGE_ALIGN((addr + len));
+	while(addr < end) {
+		page = get_free_page(GFP_KERNEL);
+		if(!page) {
+			prom_printf("alloc_dvma: Cannot get a dvma page\n");
+			prom_halt();
+		}
+		sun4c_flush_page(page);
+		page -= PAGE_OFFSET;
+		page >>= PAGE_SHIFT;
+		page |= (_SUN4C_PAGE_VALID | _SUN4C_PAGE_WRITE | _SUN4C_PAGE_NOCACHE);
+		sun4c_put_pte(addr, page);
+		addr += PAGE_SIZE;
 	}
-	return start_mem;
 }
 
+
 /* TLB management. */
 struct sun4c_mmu_entry {
 	struct sun4c_mmu_entry *next;
@@ -313,10 +522,15 @@
 };
 static struct sun4c_mmu_ring sun4c_context_ring[16]; /* used user entries */
 static struct sun4c_mmu_ring sun4c_ufree_ring;       /* free user entries */
-static struct sun4c_mmu_ring sun4c_kernel_ring;      /* used kernel entries */
-static struct sun4c_mmu_ring sun4c_kfree_ring;       /* free kernel entries */
 
-static inline void sun4c_init_rings(void)
+static inline void sun4c_next_kernel_bucket(struct sun4c_segment_info **next)
+{
+	(*next)++;
+	*next = (struct sun4c_segment_info *)
+			((unsigned long)*next & ~SUN4C_KERNEL_BSIZE);
+}
+
+static inline void sun4c_init_rings(unsigned long *mempool)
 {
 	int i;
 	for(i=0; i<16; i++) {
@@ -327,12 +541,9 @@
 	}
 	sun4c_ufree_ring.ringhd.next = sun4c_ufree_ring.ringhd.prev =
 		&sun4c_ufree_ring.ringhd;
-	sun4c_kernel_ring.ringhd.next = sun4c_kernel_ring.ringhd.prev =
-		&sun4c_kernel_ring.ringhd;
-	sun4c_kfree_ring.ringhd.next = sun4c_kfree_ring.ringhd.prev =
-		&sun4c_kfree_ring.ringhd;
-	sun4c_ufree_ring.num_entries = sun4c_kernel_ring.num_entries =
-		sun4c_kfree_ring.num_entries = 0;
+	sun4c_ufree_ring.num_entries = 0;
+	/* This needs to be aligned to twice it's size for speed. */
+	sun4c_kernel_next = sparc_init_alloc(mempool, 2 * SUN4C_KERNEL_BSIZE);
 }
 
 static inline void add_ring(struct sun4c_mmu_ring *ring, struct sun4c_mmu_entry *entry)
@@ -376,23 +587,6 @@
         add_ring(sun4c_context_ring+ctx, entry);
 }
 
-static inline void free_kernel_entry(struct sun4c_mmu_entry *entry, struct sun4c_mmu_ring *ring)
-{
-        remove_ring(ring, entry);
-        add_ring(&sun4c_kfree_ring, entry);
-}
-
-static inline void assign_kernel_entry(struct sun4c_mmu_entry *entry, struct sun4c_mmu_ring *ring) 
-{
-        remove_ring(ring, entry);
-        add_ring(&sun4c_kernel_ring, entry);
-}
-
-static inline void reassign_kernel_entry(struct sun4c_mmu_entry *entry)
-{
-	recycle_ring(&sun4c_kernel_ring, entry);
-}
-
 static void sun4c_init_fill_kernel_ring(int howmany)
 {
 	int i;
@@ -403,7 +597,9 @@
 				break;
 		mmu_entry_pool[i].locked = 1;
 		sun4c_init_clean_segmap(i);
-		add_ring(&sun4c_kfree_ring, &mmu_entry_pool[i]);
+		sun4c_kernel_next->vaddr = 0;
+		sun4c_kernel_next->pseg = mmu_entry_pool[i].pseg;
+		sun4c_next_kernel_bucket(&sun4c_kernel_next);
 		howmany--;
 	}
 }
@@ -425,8 +621,6 @@
 	int savectx, ctx;
 
 	savectx = sun4c_get_context();
-	flush_user_windows();
-	sun4c_flush_segment(kentry->vaddr);
 	for(ctx = 0; ctx < num_contexts; ctx++) {
 		sun4c_set_context(ctx);
 		sun4c_put_segmap(kentry->vaddr, invalid_segment);
@@ -439,7 +633,6 @@
 	int savectx, ctx;
 
 	savectx = sun4c_get_context();
-	flush_user_windows();
 	for(ctx = 0; ctx < num_contexts; ctx++) {
 		sun4c_set_context(ctx);
 		sun4c_put_segmap(kentry->vaddr, kentry->pseg);
@@ -449,7 +642,7 @@
 
 static inline void sun4c_user_unmap(struct sun4c_mmu_entry *uentry)
 {
-	sun4c_flush_segment(uentry->vaddr);
+	/* PM: need flush_user_windows() ?? */
 	sun4c_put_segmap(uentry->vaddr, invalid_segment);
 }
 
@@ -473,6 +666,7 @@
 	this_entry = crp->ringhd.next;
 	flush_user_windows();
 	sun4c_set_context(ctx);
+	sun4c_flush_context();
 	while(crp->num_entries) {
 		next_entry = this_entry->next;
 		sun4c_user_unmap(this_entry);
@@ -482,13 +676,15 @@
 	sun4c_set_context(savectx);
 }
 
-static inline void sun4c_demap_one(struct sun4c_mmu_ring *crp, unsigned char ctx)
+static inline void sun4c_demap_one(struct sun4c_mmu_ring *crp,unsigned char ctx)
 {
-	struct sun4c_mmu_entry *entry = crp->ringhd.next;
+	/* by using .prev we get a kind of "lru" algorithm */
+	struct sun4c_mmu_entry *entry = crp->ringhd.prev;
 	int savectx = sun4c_get_context();
 
 	flush_user_windows();
 	sun4c_set_context(ctx);
+	sun4c_flush_segment(entry->vaddr);
 	sun4c_user_unmap(entry);
 	free_user_entry(ctx, entry);
 	sun4c_set_context(savectx);
@@ -500,39 +696,24 @@
  */
 static inline struct sun4c_mmu_entry *sun4c_user_strategy(void)
 {
+	struct ctx_list *next_one;
 	struct sun4c_mmu_ring *rp = 0;
-	unsigned char mmuhog, i, ctx = 0;
+	unsigned char ctx;
 
 	/* If some are free, return first one. */
 	if(sun4c_ufree_ring.num_entries)
 		return sun4c_ufree_ring.ringhd.next;
 
-	/* Else free one up. */
-	mmuhog = 0;
-	for(i=0; i < num_contexts; i++) {
-		if(sun4c_context_ring[i].num_entries > mmuhog) {
-			rp = &sun4c_context_ring[i];
-			mmuhog = rp->num_entries;
-			ctx = i;
-		}
-	}
-	sun4c_demap_one(rp, ctx);
-	return sun4c_ufree_ring.ringhd.next;
-}
-
-static inline struct sun4c_mmu_entry *sun4c_kernel_strategy(void)
-{
-	struct sun4c_mmu_entry *this_entry;
+	/* Grab one from the LRU context. */
+	next_one = ctx_used.next;
+	while (sun4c_context_ring[next_one->ctx_number].num_entries == 0)
+		next_one = next_one->next;
 
-	/* If some are free, return first one. */
-	if(sun4c_kfree_ring.num_entries)
-		return sun4c_kfree_ring.ringhd.next;
+	ctx = next_one->ctx_number;
+	rp = &sun4c_context_ring[ctx];
 
-	/* Else free one up. */
-	this_entry = sun4c_kernel_ring.ringhd.prev;
-	sun4c_kernel_unmap(this_entry);
-	free_kernel_entry(this_entry, &sun4c_kernel_ring);
-	return sun4c_kfree_ring.ringhd.next;
+	sun4c_demap_one(rp,ctx);
+	return sun4c_ufree_ring.ringhd.next;
 }
 
 static inline void alloc_user_segment(unsigned long address, unsigned char ctx)
@@ -546,18 +727,6 @@
 	sun4c_user_map(entry);
 }
 
-static inline void alloc_kernel_segment(unsigned long address)
-{
-	struct sun4c_mmu_entry *entry;
-
-	address &= SUN4C_REAL_PGDIR_MASK;
-	entry = sun4c_kernel_strategy();
-
-	assign_kernel_entry(entry, &sun4c_kfree_ring);
-	entry->vaddr = address;
-	sun4c_kernel_map(entry);
-}
-
 /* XXX Just like kernel tlb replacement we'd like to have a low level
  * XXX equivalent for user faults which need not go through the mm
  * XXX subsystem just to load a mmu entry.  But this might not be as
@@ -565,60 +734,68 @@
  * XXX for this process, which we currently don't lock into the mmu
  * XXX so we would fault with traps off... must think about this...
  */
-static void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
 {
 	unsigned long flags;
+#if 0
+	struct inode *inode;
+	struct vm_area_struct *vmaring;
+	unsigned long offset, vaddr;
+	unsigned long start;
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+#endif
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	address &= PAGE_MASK;
 	if(sun4c_get_segmap(address) == invalid_segment)
 		alloc_user_segment(address, sun4c_get_context());
 	sun4c_put_pte(address, pte_val(pte));
-	restore_flags(flags);
-}
-
-/* READ THIS:  If you put any diagnostic printing code in any of the kernel
- *             fault handling code you will lose badly.  This is the most
- *             delicate piece of code in the entire kernel, atomicity of
- *             kernel tlb replacement must be guaranteed.  This is why we
- *             have separate user and kernel allocation rings to alleviate
- *             as many bad interactions as possible.
- *
- * XXX Someday make this into a fast in-window trap handler to avoid
- * XXX any and all races.  *High* priority, also for performance.
- */
-static void sun4c_quick_kernel_fault(unsigned long address)
-{
-	unsigned long end, flags;
 
-	save_flags(flags); cli();
-	address &= SUN4C_REAL_PGDIR_MASK;
-	end = address + SUN4C_REAL_PGDIR_SIZE;
-	if(sun4c_get_segmap(address) == invalid_segment)
-		alloc_kernel_segment(address);
+#if 0
+	if (!(vma->vm_flags & VM_WRITE) ||
+	    !(vma->vm_flags & VM_SHARED))
+		goto done;
+
+	inode = vma->vm_inode;
+	if (!inode)
+		goto done;
+
+	offset = (address & PAGE_MASK) - vma->vm_start;
+	vmaring = inode->i_mmap; 
+	do {
+		vaddr = vmaring->vm_start + offset;
+
+		if (S4CVAC_BADALIAS(vaddr, address)) {
+			start = vma->vm_start;
+			while (start < vma->vm_end) {
+				pgdp = pgd_offset(vma->vm_mm, start);
+				pmdp = pmd_offset(pgdp, start);
+				ptep = pte_offset(pmdp, start);
+
+				if (sun4c_get_pte(start) & _SUN4C_PAGE_VALID)
+				sun4c_put_pte(start, sun4c_get_pte(start) |
+						     _SUN4C_PAGE_NOCACHE);
 
-	if(address < SUN4C_VMALLOC_START) {
-		unsigned long pte;
-		pte = (address - PAGE_OFFSET) >> PAGE_SHIFT;
-		pte |= pgprot_val(SUN4C_PAGE_KERNEL);
-		/* Stupid pte tricks... */
-		while(address < end) {
-			sun4c_put_pte(address, pte++);
-			address += PAGE_SIZE;
+				start += PAGE_SIZE;
+			}
+			goto done;
 		}
-	} else {
-		pte_t *ptep;
+	} while ((vmaring = vmaring->vm_next_share) != inode->i_mmap);
 
-		ptep = (pte_t *) (PAGE_MASK & pgd_val(swapper_pg_dir[address>>SUN4C_PGDIR_SHIFT]));
-		ptep = (ptep + ((address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1)));
-		while(address < end) {
-			sun4c_put_pte(address, pte_val(*ptep++));
-			address += PAGE_SIZE;
-		}
-	}
+done:
+#endif
 	restore_flags(flags);
 }
 
+/* This is now a fast in-window trap handler to avoid any and all races. */
+static void sun4c_quick_kernel_fault(unsigned long address)
+{
+        printk("Kernel faults at addr=0x%08lx\n", address);
+        panic("sun4c fault handler bolixed...");
+}
+
 /*
  * 4 page buckets for task struct and kernel stack allocation.
  *
@@ -654,30 +831,32 @@
 #define BUCKET_PTE_PAGE(pte)   \
         (PAGE_OFFSET + (((pte) & 0xffff) << PAGE_SHIFT))
 
-static inline void get_task_segment(unsigned long addr)
+static inline void get_locked_segment(unsigned long addr)
 {
 	struct sun4c_mmu_entry *stolen;
 	unsigned long flags;
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	addr &= SUN4C_REAL_PGDIR_MASK;
 	stolen = sun4c_user_strategy();
 	remove_ring(&sun4c_ufree_ring, stolen);
 	stolen->vaddr = addr;
+	flush_user_windows();
 	sun4c_kernel_map(stolen);
 	restore_flags(flags);
 }
 
-static inline void free_task_segment(unsigned long addr)
+static inline void free_locked_segment(unsigned long addr)
 {
 	struct sun4c_mmu_entry *entry;
 	unsigned long flags;
 	unsigned char pseg;
 
-	save_flags(flags); cli();
+	save_and_cli(flags);
 	addr &= SUN4C_REAL_PGDIR_MASK;
 	pseg = sun4c_get_segmap(addr);
 	entry = &mmu_entry_pool[pseg];
+	flush_user_windows();
 	sun4c_flush_segment(addr);
 	sun4c_kernel_unmap(entry);
 	add_ring(&sun4c_ufree_ring, entry);
@@ -695,7 +874,7 @@
 		if(sun4c_bucket[start] != BUCKET_EMPTY)
 			return;
 	/* Entire segment empty, release it. */
-	free_task_segment(BUCKET_ADDR(entry));
+	free_locked_segment(BUCKET_ADDR(entry));
 }
 
 static struct task_struct *sun4c_alloc_task_struct(void)
@@ -721,7 +900,7 @@
 	addr = BUCKET_ADDR(entry);
 	sun4c_bucket[entry] = (struct task_bucket *) addr;
 	if(sun4c_get_segmap(addr) == invalid_segment)
-		get_task_segment(addr);
+		get_locked_segment(addr);
 	sun4c_put_pte(addr, BUCKET_PTE(page));
 	return (struct task_struct *) addr;
 }
@@ -729,45 +908,36 @@
 static unsigned long sun4c_alloc_kernel_stack(struct task_struct *tsk)
 {
 	unsigned long saddr = (unsigned long) tsk;
-	unsigned long page[3];
+	unsigned long page[2];
 
 	if(!saddr)
 		return 0;
-	page[0] = get_free_page(GFP_KERNEL);
+	page[0] = __get_free_page(GFP_KERNEL);
 	if(!page[0])
 		return 0;
-	page[1] = get_free_page(GFP_KERNEL);
+	page[1] = __get_free_page(GFP_KERNEL);
 	if(!page[1]) {
 		free_page(page[0]);
 		return 0;
 	}
-	page[2] = get_free_page(GFP_KERNEL);
-	if(!page[2]) {
-		free_page(page[0]);
-		free_page(page[1]);
-		return 0;
-	}
-	saddr += PAGE_SIZE;
+	saddr += (PAGE_SIZE << 1);
 	sun4c_put_pte(saddr, BUCKET_PTE(page[0]));
 	sun4c_put_pte(saddr + PAGE_SIZE, BUCKET_PTE(page[1]));
-	sun4c_put_pte(saddr + (PAGE_SIZE<<1), BUCKET_PTE(page[2]));
 	return saddr;
 }
 
 static void sun4c_free_kernel_stack(unsigned long stack)
 {
-	unsigned long page[3];
+	unsigned long page[2];
 
 	page[0] = BUCKET_PTE_PAGE(sun4c_get_pte(stack));
 	page[1] = BUCKET_PTE_PAGE(sun4c_get_pte(stack+PAGE_SIZE));
-	page[2] = BUCKET_PTE_PAGE(sun4c_get_pte(stack+(PAGE_SIZE<<1)));
-	sun4c_flush_segment(stack & SUN4C_REAL_PGDIR_MASK);
+	sun4c_flush_page(stack);
+	sun4c_flush_page(stack + PAGE_SIZE);
 	sun4c_put_pte(stack, 0);
 	sun4c_put_pte(stack + PAGE_SIZE, 0);
-	sun4c_put_pte(stack + (PAGE_SIZE<<1), 0);
 	free_page(page[0]);
 	free_page(page[1]);
-	free_page(page[2]);
 }
 
 static void sun4c_free_task_struct(struct task_struct *tsk)
@@ -776,7 +946,7 @@
 	unsigned long page = BUCKET_PTE_PAGE(sun4c_get_pte(tsaddr));
 	int entry = BUCKET_NUM(tsaddr);
 
-	sun4c_flush_segment(tsaddr & SUN4C_REAL_PGDIR_MASK);
+	sun4c_flush_page(tsaddr);
 	sun4c_put_pte(tsaddr, 0);
 	sun4c_bucket[entry] = BUCKET_EMPTY;
 	free_page(page);
@@ -797,6 +967,7 @@
 
 static unsigned long sun4c_iobuffer_start;
 static unsigned long sun4c_iobuffer_end;
+static unsigned long sun4c_iobuffer_high;
 static unsigned long *sun4c_iobuffer_map;
 static int iobuffer_map_size;
 
@@ -812,11 +983,14 @@
 	unsigned long vpage;
 	unsigned long pte;
 	unsigned long apage;
+	unsigned long high;
+	unsigned long flags;
 
 	npages = (((unsigned long)vaddr & ~PAGE_MASK) +
 		  size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
 
 	scan = 0;
+	save_and_cli(flags);
 	for (;;) {
 		scan = find_next_zero_bit(sun4c_iobuffer_map,
 					  iobuffer_map_size, scan);
@@ -829,6 +1003,13 @@
 	}
 
 found:
+	high = ((base + npages) << PAGE_SHIFT) + sun4c_iobuffer_start;
+	high = SUN4C_REAL_PGDIR_ALIGN(high);
+	while (high > sun4c_iobuffer_high) {
+		get_locked_segment(sun4c_iobuffer_high);
+		sun4c_iobuffer_high += SUN4C_REAL_PGDIR_SIZE;
+	}
+
 	vpage = ((unsigned long) vaddr) & PAGE_MASK;
 	for (scan = base; scan < base+npages; scan++) {
 		pte = ((vpage-PAGE_OFFSET) >> PAGE_SHIFT);
@@ -840,10 +1021,12 @@
 		sun4c_put_pte(apage, pte);
 		vpage += PAGE_SIZE;
 	}
+	restore_flags(flags);
 	return (char *) ((base << PAGE_SHIFT) + sun4c_iobuffer_start +
 			 (((unsigned long) vaddr) & ~PAGE_MASK));
 
 abend:
+	restore_flags(flags);
 	printk("DMA vaddr=0x%p size=%08lx\n", vaddr, size);
 	panic("Out of iobuffer table");
 	return 0;
@@ -852,6 +1035,8 @@
 static void sun4c_unlockarea(char *vaddr, unsigned long size)
 {
 	unsigned long vpage, npages;
+	unsigned long flags;
+	int scan, high;
 
 	vpage = (unsigned long)vaddr & PAGE_MASK;
 	npages = (((unsigned long)vaddr & ~PAGE_MASK) +
@@ -863,6 +1048,20 @@
 			  sun4c_iobuffer_map);
 		vpage += PAGE_SIZE;
 	}
+
+	/* garbage collect */
+	save_and_cli(flags);
+	scan = (sun4c_iobuffer_high - sun4c_iobuffer_start) >> PAGE_SHIFT;
+	while (scan >= 0 && !sun4c_iobuffer_map[scan >> 5])
+		scan -= 32;
+	scan += 32;
+	high = sun4c_iobuffer_start + (scan << PAGE_SHIFT);
+	high = SUN4C_REAL_PGDIR_ALIGN(high) + SUN4C_REAL_PGDIR_SIZE;
+	while (high < sun4c_iobuffer_high) {
+		sun4c_iobuffer_high -= SUN4C_REAL_PGDIR_SIZE;
+		free_locked_segment(sun4c_iobuffer_high);
+	}
+	restore_flags(flags);
 }
 
 /* Note the scsi code at init time passes to here buffers
@@ -875,7 +1074,7 @@
 	unsigned long page;
 
 	page = ((unsigned long) bufptr) & PAGE_MASK;
-	if(page > high_memory)
+	if(MAP_NR(page) > max_mapnr)
 		return bufptr; /* already locked */
 	return sun4c_lockarea(bufptr, len);
 }
@@ -883,7 +1082,7 @@
 static void sun4c_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
 	while(sz >= 0) {
-		sg[sz].alt_addr = sun4c_lockarea(sg[sz].addr, sg[sz].len);
+		sg[sz].dvma_addr = sun4c_lockarea(sg[sz].addr, sg[sz].len);
 		sz--;
 	}
 }
@@ -900,8 +1099,7 @@
 static void sun4c_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
 	while(sz >= 0) {
-		sun4c_unlockarea(sg[sz].alt_addr, sg[sz].len);
-		sg[sz].alt_addr = 0;
+		sun4c_unlockarea(sg[sz].dvma_addr, sg[sz].len);
 		sz--;
 	}
 }
@@ -926,7 +1124,8 @@
 		prom_halt();
 	}
 
-	sun4c_iobuffer_start = SUN4C_REAL_PGDIR_ALIGN(sun4c_taskstack_end);
+	sun4c_iobuffer_start = sun4c_iobuffer_high =
+				SUN4C_REAL_PGDIR_ALIGN(sun4c_taskstack_end);
 	sun4c_iobuffer_end = SUN4C_LOCK_END;
 	bitmap_size = (sun4c_iobuffer_end - sun4c_iobuffer_start) >> PAGE_SHIFT;
 	bitmap_size = (bitmap_size + 7) >> 3;
@@ -937,104 +1136,136 @@
 	start_mem += bitmap_size;
 
 	/* Now get us some mmu entries for I/O maps. */
-	sun4c_init_lock_area(sun4c_iobuffer_start, sun4c_iobuffer_end);
+	/* sun4c_init_lock_area(sun4c_iobuffer_start, sun4c_iobuffer_end); */
 	sun4c_kstack_vma.vm_mm = init_task.mm;
 	sun4c_kstack_vma.vm_start = sun4c_taskstack_start;
 	sun4c_kstack_vma.vm_end = sun4c_taskstack_end;
 	sun4c_kstack_vma.vm_page_prot = PAGE_SHARED;
 	sun4c_kstack_vma.vm_flags = VM_READ | VM_WRITE | VM_EXEC;
-	insert_vm_struct(&init_task, &sun4c_kstack_vma);
+	insert_vm_struct(&init_mm, &sun4c_kstack_vma);
 	return start_mem;
 }
 
 /* Cache flushing on the sun4c. */
 static void sun4c_flush_cache_all(void)
 {
-	unsigned long start, end;
-
 	/* Clear all tags in the sun4c cache.
 	 * The cache is write through so this is safe.
 	 */
-	start = AC_CACHETAGS;
-	end = start + sun4c_vacinfo.num_bytes;
 	flush_user_windows();
-	while(start < end) {
-		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
-				     "r" (start), "i" (ASI_CONTROL));
-		start += sun4c_vacinfo.linesize;
-	}
+	__asm__ __volatile__("add	%2, %2, %%g1\n\t"
+			     "add	%2, %%g1, %%g2\n\t"
+			     "add	%2, %%g2, %%g3\n\t"
+			     "add	%2, %%g3, %%g4\n\t"
+			     "add	%2, %%g4, %%g5\n\t"
+			     "add	%2, %%g5, %%o4\n\t"
+			     "add	%2, %%o4, %%o5\n"
+			     "1:\n\t"
+			     "subcc	%1, %%o5, %1\n\t"
+			     "sta	%%g0, [%0] %3\n\t"
+			     "sta	%%g0, [%0 + %2] %3\n\t"
+			     "sta	%%g0, [%0 + %%g1] %3\n\t"
+			     "sta	%%g0, [%0 + %%g2] %3\n\t"
+			     "sta	%%g0, [%0 + %%g3] %3\n\t"
+			     "sta	%%g0, [%0 + %%g4] %3\n\t"
+			     "sta	%%g0, [%0 + %%g5] %3\n\t"
+			     "sta	%%g0, [%0 + %%o4] %3\n\t"
+			     "bg	1b\n\t"
+			     " add	%0, %%o5, %0\n\t" : :
+			     "r" (AC_CACHETAGS),
+			     "r" (sun4c_vacinfo.num_bytes),
+			     "r" (sun4c_vacinfo.linesize),
+			     "i" (ASI_CONTROL) :
+			     "g1", "g2", "g3", "g4", "g5", "o4", "o5");
 }
 
 static void sun4c_flush_cache_mm(struct mm_struct *mm)
 {
-	unsigned long flags;
 	int octx;
 
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
 		octx = sun4c_get_context();
-		save_flags(flags); cli();
 		flush_user_windows();
 		sun4c_set_context(mm->context);
 		sun4c_flush_context();
 		sun4c_set_context(octx);
-		restore_flags(flags);
 #ifndef __SMP__
 	}
 #endif
 }
 
+
 static void sun4c_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	unsigned long flags;
-	int size, octx;
+	int size, size2, octx, i;
+	unsigned long start2,end2;
+	struct sun4c_mmu_entry *entry,*entry2;
+	
+	/* don't flush kernel memory as its always valid in
+	   all contexts */
+	if (start >= PAGE_OFFSET)
+		return;
+
+#if KGPROF_PROFILING
+	kgprof_profile();
+#endif
 
 #ifndef __SMP__
 	if(mm->context != NO_CONTEXT) {
 #endif
-		size = start - end;
+		size = end - start;
 
-		flush_user_windows();
-
-		if(size >= sun4c_vacinfo.num_bytes)
-			goto flush_it_all;
-
-		save_flags(flags); cli();
 		octx = sun4c_get_context();
+		flush_user_windows();
 		sun4c_set_context(mm->context);
 
-		if(size <= (PAGE_SIZE << 1)) {
-			start &= PAGE_MASK;
-			while(start < end) {
-				sun4c_flush_page(start);
-				start += PAGE_SIZE;
-			};
-		} else {
-			start &= SUN4C_REAL_PGDIR_MASK;
-			while(start < end) {
-				sun4c_flush_segment(start);
-				start += SUN4C_REAL_PGDIR_SIZE;
+		entry = sun4c_context_ring[mm->context].ringhd.next;
+		i = sun4c_context_ring[mm->context].num_entries;
+		while (i--) {
+			entry2 = entry->next;
+			if (entry->vaddr < start || entry->vaddr >= end) 
+				goto next_entry;
+
+			start2 = MAX(start,entry->vaddr);
+			end2 = MIN(end,entry->vaddr+SUN4C_REAL_PGDIR_SIZE);
+			size2 = end2 - start2;
+
+			if (size2 <= (PAGE_SIZE << 3)) {
+				start2 &= PAGE_MASK;
+				while(start2 < end2) {
+					sun4c_flush_page(start2);
+					start2 += PAGE_SIZE;
+				}
+			} else {
+				start2 &= SUN4C_REAL_PGDIR_MASK;
+				sun4c_flush_segment(start2);
+				/* we are betting that the entry will not be 
+				   needed for a while */
+				sun4c_user_unmap(entry);
+				free_user_entry(mm->context, entry);
 			}
+
+		next_entry:
+			entry = entry2;
 		}
 		sun4c_set_context(octx);
-		restore_flags(flags);
 #ifndef __SMP__
 	}
 #endif
-	return;
-
-flush_it_all:
-	/* Cache size bounded flushing, thank you. */
-	sun4c_flush_cache_all();
 }
 
 static void sun4c_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
-	unsigned long flags;
 	int octx;
 	struct mm_struct *mm = vma->vm_mm;
 
+	/* don't flush kernel memory as its always valid in
+	   all contexts */
+	if (page >= PAGE_OFFSET)
+		return;
+
 	/* Sun4c has no separate I/D caches so cannot optimize for non
 	 * text page flushes.
 	 */
@@ -1042,12 +1273,10 @@
 	if(mm->context != NO_CONTEXT) {
 #endif
 		octx = sun4c_get_context();
-		save_flags(flags); cli();
 		flush_user_windows();
 		sun4c_set_context(mm->context);
 		sun4c_flush_page(page);
 		sun4c_set_context(octx);
-		restore_flags(flags);
 #ifndef __SMP__
 	}
 #endif
@@ -1067,21 +1296,21 @@
 
 static void sun4c_flush_tlb_all(void)
 {
-	struct sun4c_mmu_entry *this_entry, *next_entry;
 	unsigned long flags;
-	int savectx, ctx;
+	int savectx, ctx, entry;
 
-	save_flags(flags); cli();
-	this_entry = sun4c_kernel_ring.ringhd.next;
+	save_and_cli(flags);
 	savectx = sun4c_get_context();
-	while(sun4c_kernel_ring.num_entries) {
-		next_entry = this_entry->next;
-		for(ctx = 0; ctx < num_contexts; ctx++) {
-			sun4c_set_context(ctx);
-			sun4c_put_segmap(this_entry->vaddr, invalid_segment);
+	for (entry = 0; entry < SUN4C_KERNEL_BUCKETS; entry++) {
+		if (sun4c_kernel_next->vaddr) {
+			for(ctx = 0; ctx < num_contexts; ctx++) {
+				sun4c_set_context(ctx);
+				sun4c_put_segmap(sun4c_kernel_next->vaddr,
+						 invalid_segment);
+			}
+			sun4c_kernel_next->vaddr = 0;
 		}
-		free_kernel_entry(this_entry, &sun4c_kernel_ring);
-		this_entry = next_entry;
+		sun4c_next_kernel_bucket(&sun4c_kernel_next);
 	}
 	sun4c_set_context(savectx);
 	restore_flags(flags);
@@ -1100,7 +1329,9 @@
 		savectx = sun4c_get_context();
 		ctx = mm->context;
 		this_entry = crp->ringhd.next;
+		flush_user_windows();
 		sun4c_set_context(mm->context);
+		sun4c_flush_context();
 		while(crp->num_entries) {
 			next_entry = this_entry->next;
 			sun4c_user_unmap(this_entry);
@@ -1115,30 +1346,38 @@
 
 static void sun4c_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	struct sun4c_mmu_entry *this_entry;
-	unsigned char pseg, savectx;
+	struct sun4c_mmu_entry *entry,*entry2;
+	unsigned char savectx;
+	int i;
 
 #ifndef __SMP__
 	if(mm->context == NO_CONTEXT)
 		return;
 #endif
-	flush_user_windows();
+
+#if KGPROF_PROFILING
+	kgprof_profile();
+#endif
+
 	savectx = sun4c_get_context();
 	sun4c_set_context(mm->context);
 	start &= SUN4C_REAL_PGDIR_MASK;
-	while(start < end) {
-		pseg = sun4c_get_segmap(start);
-		if(pseg == invalid_segment)
-			goto next_one;
-		this_entry = &mmu_entry_pool[pseg];
-		sun4c_put_segmap(this_entry->vaddr, invalid_segment);
-		free_user_entry(mm->context, this_entry);
-	next_one:
-		start += SUN4C_REAL_PGDIR_SIZE;
+
+	entry = sun4c_context_ring[mm->context].ringhd.next;
+	i = sun4c_context_ring[mm->context].num_entries;
+	while (i--) {
+		entry2 = entry->next;
+		if (entry->vaddr >= start && entry->vaddr < end) {
+			sun4c_flush_segment(entry->vaddr);
+			sun4c_user_unmap(entry);
+			free_user_entry(mm->context, entry);
+		}
+		entry = entry2;
 	}
 	sun4c_set_context(savectx);
 }
 
+
 static void sun4c_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
 	struct mm_struct *mm = vma->vm_mm;
@@ -1171,7 +1410,7 @@
 	*ptep = pte;
 }
 
-/* static */ void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
+void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
 		     int bus_type, int rdonly)
 {
 	unsigned long page_entry;
@@ -1185,6 +1424,12 @@
 	sun4c_put_pte(virt_addr, page_entry);
 }
 
+void sun4c_unmapioaddr(unsigned long virt_addr)
+{
+	sun4c_flush_page(virt_addr); /* XXX P3: Is it necessary for I/O page? */
+	sun4c_put_pte(virt_addr, 0);
+}
+
 static inline void sun4c_alloc_context(struct mm_struct *mm)
 {
 	struct ctx_list *ctxp;
@@ -1207,7 +1452,8 @@
 	ctxp->ctx_mm->context = NO_CONTEXT;
 	ctxp->ctx_mm = mm;
 	mm->context = ctxp->ctx_number;
-	sun4c_demap_context(&sun4c_context_ring[ctxp->ctx_number], ctxp->ctx_number);
+	sun4c_demap_context(&sun4c_context_ring[ctxp->ctx_number],
+			    ctxp->ctx_number);
 }
 
 #if some_day_soon /* We need some tweaking to start using this */
@@ -1227,17 +1473,19 @@
 
 static void sun4c_switch_to_context(struct task_struct *tsk)
 {
-	/* Kernel threads can execute in any context and so can tasks
-	 * sleeping in the middle of exiting. If this task has already
-	 * been allocated a piece of the mmu realestate, just jump to
-	 * it.
-	 */
-	if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
-	   (tsk->flags & PF_EXITING))
-		return;
-	if(tsk->mm->context == NO_CONTEXT)
+	struct ctx_list *ctx;
+
+	if(tsk->mm->context == NO_CONTEXT) {
 		sun4c_alloc_context(tsk->mm);
+		goto set_context;
+	}
+
+	/* Update the LRU ring of contexts. */
+	ctx = ctx_list_pool + tsk->mm->context;
+	remove_from_ctx_list(ctx);
+	add_to_used_ctxlist(ctx);
 
+set_context:
 	sun4c_set_context(tsk->mm->context);
 }
 
@@ -1254,7 +1502,7 @@
 	struct ctx_list *ctx_old;
 	struct mm_struct *mm = current->mm;
 
-	if(mm->context != NO_CONTEXT) {
+	if(mm->context != NO_CONTEXT && mm->count == 1) {
 		sun4c_demap_context(&sun4c_context_ring[mm->context], mm->context);
 		ctx_old = ctx_list_pool + mm->context;
 		remove_from_ctx_list(ctx_old);
@@ -1263,7 +1511,11 @@
 	}
 }
 
+#if KGPROF_PROFILING
+static char s4cinfo[10240];
+#else
 static char s4cinfo[512];
+#endif
 
 static char *sun4c_mmu_info(void)
 {
@@ -1278,6 +1530,7 @@
 		"vaclinesize\t: %d bytes\n"
 		"mmuctxs\t\t: %d\n"
 		"mmupsegs\t: %d\n"
+		"kernelpsegs\t: %d\n"
 		"usedpsegs\t: %d\n"
 		"ufreepsegs\t: %d\n"
 		"context\t\t: %d flushes\n"
@@ -1288,10 +1541,28 @@
 		sun4c_vacinfo.linesize,
 		num_contexts,
 		(invalid_segment + 1),
+		invalid_segment - used_user_entries -
+			sun4c_ufree_ring.num_entries + 1,
 		used_user_entries,
 		sun4c_ufree_ring.num_entries,
 		ctxflushes, segflushes, pageflushes);
 
+#if KGPROF_PROFILING
+	{
+		char *p = s4cinfo + strlen(s4cinfo);
+		int i,j;
+		sprintf(p,"kgprof profiling:\n"); p += strlen(p);
+		for (i=0;i<KGPROF_SIZE && kgprof_counters[i].addr[0];i++) {
+			sprintf(p,"%5d  ",kgprof_counters[i].count); p += strlen(p);
+			for (j=0;j<KGPROF_DEPTH;j++) {
+				sprintf(p,"%08x ",kgprof_counters[i].addr[j]);
+				p += strlen(p);
+			}
+			sprintf(p,"\n"); p += strlen(p);
+		}
+	}
+#endif
+
 	return s4cinfo;
 }
 
@@ -1325,7 +1596,8 @@
 static int sun4c_pmd_none(pmd_t pmd)		{ return !pmd_val(pmd); }
 static int sun4c_pmd_bad(pmd_t pmd)
 {
-	return (pmd_val(pmd) & ~PAGE_MASK) != PGD_TABLE || pmd_val(pmd) > high_memory;
+	return (pmd_val(pmd) & ~PAGE_MASK) != PGD_TABLE ||
+		MAP_NR(pmd_val(pmd)) > max_mapnr;
 }
 
 static int sun4c_pmd_present(pmd_t pmd)	        { return pmd_val(pmd) & PGD_PRESENT; }
@@ -1360,6 +1632,11 @@
 	return __pte(((page - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(pgprot));
 }
 
+static pte_t sun4c_mk_pte_phys(unsigned long phys_page, pgprot_t pgprot)
+{
+	return __pte((phys_page >> PAGE_SHIFT) | pgprot_val(pgprot));
+}
+
 static pte_t sun4c_mk_pte_io(unsigned long page, pgprot_t pgprot, int space)
 {
 	return __pte(((page - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(pgprot));
@@ -1381,7 +1658,7 @@
 }
 
 /* to find an entry in a page-table-directory */
-static pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address)
+pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address)
 {
 	return mm->pgd + (address >> SUN4C_PGDIR_SHIFT);
 }
@@ -1393,7 +1670,7 @@
 }
 
 /* Find an entry in the third-level page table.. */ 
-static pte_t *sun4c_pte_offset(pmd_t * dir, unsigned long address)
+pte_t *sun4c_pte_offset(pmd_t * dir, unsigned long address)
 {
 	return (pte_t *) sun4c_pmd_page(*dir) +	((address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1));
 }
@@ -1501,7 +1778,6 @@
 	return (pgd_t *) get_free_page(GFP_KERNEL);
 }
 
-#define SUN4C_KERNEL_BUCKETS   16
 extern unsigned long free_area_init(unsigned long, unsigned long);
 extern unsigned long sparc_context_init(unsigned long, int);
 extern unsigned long end;
@@ -1510,6 +1786,7 @@
 {
 	int i, cnt;
 	unsigned long kernel_end;
+	extern unsigned long sparc_iobase_vaddr;
 
 	kernel_end = (unsigned long) &end;
 	kernel_end += (SUN4C_REAL_PGDIR_SIZE * 3);
@@ -1517,11 +1794,11 @@
 	sun4c_probe_mmu();
 	invalid_segment = (num_segmaps - 1);
 	sun4c_init_mmu_entry_pool();
-	sun4c_init_rings();
+	sun4c_init_rings(&start_mem);
 	sun4c_init_map_kernelprom(kernel_end);
 	sun4c_init_clean_mmu(kernel_end);
 	sun4c_init_fill_kernel_ring(SUN4C_KERNEL_BUCKETS);
-	sun4c_init_lock_area(IOBASE_VADDR, IOBASE_END);
+	sun4c_init_lock_area(sparc_iobase_vaddr, IOBASE_END);
 	sun4c_init_lock_area(DVMA_VADDR, DVMA_END);
 	start_mem = sun4c_init_lock_areas(start_mem);
 	sun4c_init_fill_user_ring();
@@ -1534,7 +1811,7 @@
 		PGD_TABLE | (unsigned long) pg0;
 	sun4c_init_ss2_cache_bug();
 	start_mem = PAGE_ALIGN(start_mem);
-	start_mem = sun4c_init_alloc_dvma_pages(start_mem);
+	/* start_mem = sun4c_init_alloc_dvma_pages(start_mem); */
 	start_mem = sparc_context_init(start_mem, num_contexts);
 	start_mem = free_area_init(start_mem, end_mem);
 	cnt = 0;
@@ -1631,6 +1908,7 @@
 	pgd_clear = sun4c_pgd_clear;
 
 	mk_pte = sun4c_mk_pte;
+	mk_pte_phys = sun4c_mk_pte_phys;
 	mk_pte_io = sun4c_mk_pte_io;
 	pte_modify = sun4c_pte_modify;
 	pgd_offset = sun4c_pgd_offset;
@@ -1666,6 +1944,8 @@
 	mmu_get_scsi_sgl = sun4c_get_scsi_sgl;
 	mmu_release_scsi_one = sun4c_release_scsi_one;
 	mmu_release_scsi_sgl = sun4c_release_scsi_sgl;
+
+	mmu_map_dma_area = sun4c_map_dma_area;
 
         mmu_v2p = sun4c_v2p;
         mmu_p2v = sun4c_p2v;
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/bootstr.c linux/arch/sparc/prom/bootstr.c
--- v2.1.8/linux/arch/sparc/prom/bootstr.c	Sun Apr 21 12:30:32 1996
+++ linux/arch/sparc/prom/bootstr.c	Sat Nov  9 10:12:23 1996
@@ -1,50 +1,67 @@
-/* $Id: bootstr.c,v 1.5 1996/04/04 16:30:53 tridge Exp $
+/* $Id: bootstr.c,v 1.11 1996/07/27 05:02:06 zaitcev Exp $
  * bootstr.c:  Boot string/argument acquisition from the PROM.
  *
  * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
 #include <linux/config.h>
+#include <linux/string.h>
 #include <asm/oplib.h>
 
-static char barg_buf[256];
+#define BARG_LEN  256
+static char barg_buf[BARG_LEN];
+static char fetched = 0;
 
 char *
 prom_getbootargs(void)
 {
 	int iter;
-	char *cp;
+	char *cp, *arg;
+
+	/* This check saves us from a panic when bootfd patches args. */
+	if (fetched) {
+		return barg_buf;
+	}
 
 	switch(prom_vers) {
 	case PROM_V0:
 		cp = barg_buf;
-		for(iter = 0; iter < 8; iter++) {
-			strcpy(cp, (*(romvec->pv_v0bootargs))->argv[iter]);
-			cp += strlen(cp); *cp++=' ';
+		/* Start from 1 and go over fd(0,0,0)kernel */
+		for(iter = 1; iter < 8; iter++) {
+			arg = (*(romvec->pv_v0bootargs))->argv[iter];
+			if(arg == 0) break;
+			while(*arg != 0) {
+				/* Leave place for space and null. */
+				if(cp >= barg_buf + BARG_LEN-2){
+					/* We might issue a warning here. */
+					break;
+				}
+				*cp++ = *arg++;
+			}
+			*cp++ = ' ';
 		}
 		*cp = 0;
 		break;
 	case PROM_V2:
 	case PROM_V3:
-		cp = barg_buf;
-		strcpy(cp, *romvec->pv_v2bootargs.bootpath);
-		cp += strlen(cp);
-		*cp++ = ' ';
-		strcpy(cp, *romvec->pv_v2bootargs.bootargs);
-		cp += strlen(cp);
-		*cp = 0;
+		/*
+		 * V3 PROM cannot supply as with more than 128 bytes
+		 * of an argument. But a smart bootstrap loader can.
+		 */
+		strncpy(barg_buf, *romvec->pv_v2bootargs.bootargs, BARG_LEN-1);
 		break;
         case PROM_AP1000:
 	  /*
 	   * Get message from host boot process.
 	   */
 #if CONFIG_AP1000
-                ap_getbootargs(barg_buf);
-#endif          
+                ap_getbootargs(barg_buf, BARG_LEN);
+#endif
                 break;
 	default:
-		barg_buf[0] = 0;
 		break;
 	}
+
+	fetched = 1;
 	return barg_buf;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/console.c linux/arch/sparc/prom/console.c
--- v2.1.8/linux/arch/sparc/prom/console.c	Sun Apr 21 12:30:32 1996
+++ linux/arch/sparc/prom/console.c	Sat Nov  9 10:12:24 1996
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.8 1996/04/05 07:44:35 tridge Exp $
+/* $Id: console.c,v 1.9 1996/09/19 20:27:17 davem Exp $
  * console.c: Routines that deal with sending and receiving IO
  *            to/from the current console device using the PROM.
  *
@@ -6,8 +6,12 @@
  */
 
 #include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
+#include <asm/system.h>
 #include <linux/string.h>
 
 /* Non blocking get character from console input device, returns -1
@@ -17,23 +21,32 @@
 prom_nbgetchar(void)
 {
 	static char inc;
+	int i = -1;
+	unsigned long flags;
 
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
-		return (*(romvec->pv_nbgetchar))();
+		i = (*(romvec->pv_nbgetchar))();
 		break;
 	case PROM_V2:
 	case PROM_V3:
 	case PROM_P1275:
-		if( (*(romvec->pv_v2devops).v2_dev_read)(*romvec->pv_v2bootargs.fd_stdin , &inc, 0x1) == 1)
-			return inc;
-		return -1;
+		if( (*(romvec->pv_v2devops).v2_dev_read)(*romvec->pv_v2bootargs.fd_stdin , &inc, 0x1) == 1) {
+			i = inc;
+		} else {
+			i = -1;
+		}
 		break;
 	case PROM_AP1000:
-		return -1;
+		i = -1;
 		break;
 	};
-	return 0; /* Ugh, we could spin forever on unsupported proms ;( */
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return i; /* Ugh, we could spin forever on unsupported proms ;( */
 }
 
 /* Non blocking put character to console device, returns -1 if
@@ -43,30 +56,41 @@
 prom_nbputchar(char c)
 {
 	static char outc;
+	unsigned long flags;
+	int i = -1;
 
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
-		return (*(romvec->pv_nbputchar))(c);
+		i = (*(romvec->pv_nbputchar))(c);
 		break;
 	case PROM_V2:
 	case PROM_V3:
 	case PROM_P1275:
 		outc = c;
 		if( (*(romvec->pv_v2devops).v2_dev_write)(*romvec->pv_v2bootargs.fd_stdout, &outc, 0x1) == 1)
-			return 0;
-		return -1;
+			i = 0;
+		else
+			i = -1;
 		break;
 	case PROM_AP1000:
 #if CONFIG_AP1000
 		{
 		  extern void ap_putchar(char );
 		  ap_putchar(c);
-		  return 0;
+		  i = 0;
 		}
+#else
+                i = -1;
 #endif
+	
 		break;
 	};
-	return 0; /* Ugh, we could spin forever on unsupported proms ;( */
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return i; /* Ugh, we could spin forever on unsupported proms ;( */
 }
 
 /* Blocking version of get character routine above. */
@@ -90,6 +114,7 @@
 enum prom_input_device
 prom_query_input_device()
 {
+	unsigned long flags;
 	int st_p;
 	char propb[64];
 	char *p;
@@ -107,7 +132,12 @@
 		};
 	case PROM_V3:
 	case PROM_P1275:
+		save_flags(flags); cli();
 		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin);
+		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+				     "r" (&current_set[smp_processor_id()]) :
+				     "memory");
+		restore_flags(flags);
 		if(prom_node_has_property(st_p, "keyboard"))
 			return PROMDEV_IKBD;
 		prom_getproperty(st_p, "device_type", propb, sizeof(propb));
@@ -133,6 +163,7 @@
 enum prom_output_device
 prom_query_output_device()
 {
+	unsigned long flags;
 	int st_p;
 	char propb[64];
 	char *p;
@@ -149,7 +180,12 @@
 	case PROM_V2:
 	case PROM_V3:
 	case PROM_P1275:
+		save_flags(flags); cli();
 		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout);
+		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+				     "r" (&current_set[smp_processor_id()]) :
+				     "memory");
+		restore_flags(flags);
 		propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb));
 		if (propl >= 0 && propl == sizeof("display") &&
 			strncmp("display", propb, sizeof("display")) == 0)
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/devmap.c linux/arch/sparc/prom/devmap.c
--- v2.1.8/linux/arch/sparc/prom/devmap.c	Sat Nov 25 02:59:58 1995
+++ linux/arch/sparc/prom/devmap.c	Sat Nov  9 10:12:24 1996
@@ -1,9 +1,13 @@
-/* $Id: devmap.c,v 1.2 1995/11/25 00:59:56 davem Exp $
+/* $Id: devmap.c,v 1.3 1996/09/19 20:27:19 davem Exp $
  * promdevmap.c:  Map device/IO areas to virtual addresses.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
@@ -20,16 +24,33 @@
 char *
 prom_mapio(char *vhint, int ios, unsigned int paddr, unsigned int num_bytes)
 {
-	if((num_bytes == 0) || (paddr == 0)) return (char *) 0x0;
-	return (*(romvec->pv_v2devops.v2_dumb_mmap))(vhint, ios, paddr,
-						     num_bytes);
+	unsigned long flags;
+	char *ret;
+
+	save_flags(flags); cli();
+	if((num_bytes == 0) || (paddr == 0)) ret = (char *) 0x0;
+	else
+	ret = (*(romvec->pv_v2devops.v2_dumb_mmap))(vhint, ios, paddr,
+						    num_bytes);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
 }
 
 /* Unmap an IO/device area that was mapped using the above routine. */
 void
 prom_unmapio(char *vaddr, unsigned int num_bytes)
 {
+	unsigned long flags;
+
 	if(num_bytes == 0x0) return;
+	save_flags(flags); cli();
 	(*(romvec->pv_v2devops.v2_dumb_munmap))(vaddr, num_bytes);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 	return;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/devops.c linux/arch/sparc/prom/devops.c
--- v2.1.8/linux/arch/sparc/prom/devops.c	Sun Apr 21 12:30:32 1996
+++ linux/arch/sparc/prom/devops.c	Sat Nov  9 10:12:25 1996
@@ -1,8 +1,11 @@
-/* $Id: devops.c,v 1.4 1996/04/04 16:30:58 tridge Exp $
+/* $Id: devops.c,v 1.6 1996/10/12 12:37:38 davem Exp $
  * devops.c:  Device operations using the PROM.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
@@ -15,42 +18,54 @@
 prom_devopen(char *dstr)
 {
 	int handle;
+	unsigned long flags;
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
 		handle = (*(romvec->pv_v0devops.v0_devopen))(dstr);
-		if(handle == 0) return -1;
-		return handle;
+		if(handle == 0) handle = -1;
 		break;
 	case PROM_V2:
 	case PROM_V3:
 	case PROM_P1275:
 		handle = (*(romvec->pv_v2devops.v2_dev_open))(dstr);
-		return handle;
 		break;
         case PROM_AP1000:
+	default:
+		handle = -1;
 		break;
 	};
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 
-	return -1;
+	return handle;
 }
 
 /* Close the device described by device handle 'dhandle'. */
-void
-prom_close(int dhandle)
+int
+prom_devclose(int dhandle)
 {
+	unsigned long flags;
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
 		(*(romvec->pv_v0devops.v0_devclose))(dhandle);
-		return;
+		break;
 	case PROM_V2:
 	case PROM_V3:
 	case PROM_P1275:
 		(*(romvec->pv_v2devops.v2_dev_close))(dhandle);
-		return;
+		break;
         case PROM_AP1000:
-		return;
+		break;
 	};
-	return;
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return 0;
 }
 
 /* Seek to specified location described by 'seekhi' and 'seeklo'
@@ -59,6 +74,8 @@
 void
 prom_seek(int dhandle, unsigned int seekhi, unsigned int seeklo)
 {
+	unsigned long flags;
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
 		(*(romvec->pv_v0devops.v0_seekdev))(dhandle, seekhi, seeklo);
@@ -71,6 +88,10 @@
         case PROM_AP1000:
 		break;
 	};
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 
 	return;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/memory.c linux/arch/sparc/prom/memory.c
--- v2.1.8/linux/arch/sparc/prom/memory.c	Thu Apr 25 13:22:06 1996
+++ linux/arch/sparc/prom/memory.c	Sat Nov  9 10:12:26 1996
@@ -1,10 +1,11 @@
-/* $Id: memory.c,v 1.7 1996/04/25 06:09:46 davem Exp $
+/* $Id: memory.c,v 1.8 1996/07/12 05:14:56 tridge Exp $
  * memory.c: Prom routine for acquiring various bits of information
  *           about RAM on the machine, both virtual and physical.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 
 #include <asm/openprom.h>
@@ -178,21 +179,22 @@
 		break;
 
         case PROM_AP1000:
-          /* really simple memory map */
-          prom_phys_total[0].start_adr = 0x00000000;
-          prom_phys_total[0].num_bytes = 0x01000000; /* 16MB */
-          prom_phys_total[0].theres_more = 0x0;
-          prom_prom_taken[0].start_adr = 0x00000000; 
-          prom_prom_taken[0].num_bytes = 0x00000000;
-          prom_prom_taken[0].theres_more = 0x0;
-          prom_phys_avail[0].start_adr = 0x00000000;
-          prom_phys_avail[0].num_bytes = 0x01000000; /* 16MB */
-          prom_phys_avail[0].theres_more = 0x0;
-          prom_sortmemlist(prom_phys_total);
-          prom_sortmemlist(prom_prom_taken);
-          prom_sortmemlist(prom_phys_avail);
-          printk("Initialised AP1000 memory lists (forced 16MB)\n");
-          break;
+#if CONFIG_AP1000
+		/* really simple memory map */
+		prom_phys_total[0].start_adr = 0x00000000;
+		prom_phys_total[0].num_bytes = ap_memory_size();
+		prom_phys_total[0].theres_more = 0x0;
+		prom_prom_taken[0].start_adr = 0x00000000; 
+		prom_prom_taken[0].num_bytes = 0x00000000;
+		prom_prom_taken[0].theres_more = 0x0;
+		prom_phys_avail[0].start_adr = 0x00000000;
+		prom_phys_avail[0].num_bytes = prom_phys_total[0].num_bytes;
+		prom_phys_avail[0].theres_more = 0x0;
+		prom_sortmemlist(prom_phys_total);
+		prom_sortmemlist(prom_prom_taken);
+		prom_sortmemlist(prom_phys_avail);
+#endif
+		break;
 	};
 
 	/* Link all the lists into the top-level descriptor. */
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/misc.c linux/arch/sparc/prom/misc.c
--- v2.1.8/linux/arch/sparc/prom/misc.c	Sun Apr 21 12:30:32 1996
+++ linux/arch/sparc/prom/misc.c	Sat Nov  9 10:12:27 1996
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.8 1996/04/17 23:03:23 davem Exp $
+/* $Id: misc.c,v 1.11 1996/10/12 13:12:58 davem Exp $
  * misc.c:  Miscellaneous prom functions that don't belong
  *          anywhere else.
  *
@@ -6,32 +6,47 @@
  */
 
 #include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
+#include <asm/auxio.h>
 
 /* Reset and reboot the machine with the command 'bcommand'. */
 void
 prom_reboot(char *bcommand)
 {
+	unsigned long flags;
+	save_flags(flags); cli();
 	(*(romvec->pv_reboot))(bcommand);
 	/* Never get here. */
-	return;
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 }
 
 /* Forth evaluate the expression contained in 'fstring'. */
 void
 prom_feval(char *fstring)
 {
-	if(!fstring || fstring[0] == 0) return;
+	unsigned long flags;
+	if(!fstring || fstring[0] == 0)
+		return;
+	save_flags(flags); cli();
 	if(prom_vers == PROM_V0)
 		(*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring);
 	else
 		(*(romvec->pv_fortheval.v2_eval))(fstring);
-	return;
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 }
 
 /* We want to do this more nicely some day. */
-#if CONFIG_SUN_CONSOLE
+#ifdef CONFIG_SUN_CONSOLE
 extern void console_restore_palette(void);
 extern void set_palette(void);
 extern int serial_console;
@@ -41,36 +56,49 @@
  * prom command.
  */
 void
-prom_halt(void)
+prom_cmdline(void)
 {
 	extern void kernel_enter_debugger(void);
 	extern void install_obp_ticker(void);
 	extern void install_linux_ticker(void);
+	unsigned long flags;
     
 	kernel_enter_debugger();
-#if CONFIG_SUN_CONSOLE
+#ifdef CONFIG_SUN_CONSOLE
 	if(!serial_console)
 		console_restore_palette ();
 #endif
 	install_obp_ticker();
+	save_flags(flags); cli();
 	(*(romvec->pv_abort))();
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 	install_linux_ticker();
-#if CONFIG_SUN_CONSOLE
+#ifdef CONFIG_SUN_AUXIO
+	TURN_ON_LED;
+#endif
+#ifdef CONFIG_SUN_CONSOLE
 	if(!serial_console)
 		set_palette ();
 #endif
-	return;
 }
 
 /* Drop into the prom, but completely terminate the program.
  * No chance of continuing.
  */
 void
-prom_die(void)
+prom_halt(void)
 {
+	unsigned long flags;
+	save_flags(flags); cli();
 	(*(romvec->pv_halt))();
 	/* Never get here. */
-	return;
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 }
 
 typedef void (*sfunc_t)(void);
@@ -79,13 +107,12 @@
 void
 prom_setsync(sfunc_t funcp)
 {
-#if CONFIG_AP1000
-  printk("not doing setsync\n");
-  return;
+#ifdef CONFIG_AP1000
+	printk("not doing setsync\n");
+	return;
 #endif
 	if(!funcp) return;
 	*romvec->pv_synchook = funcp;
-	return;
 }
 
 /* Get the idprom and stuff it into buffer 'idbuf'.  Returns the
@@ -93,7 +120,7 @@
  * has space for.  Returns 0xff on error.
  */
 unsigned char
-prom_getidp(char *idbuf, int num_bytes)
+prom_get_idprom(char *idbuf, int num_bytes)
 {
 	int len;
 
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/mp.c linux/arch/sparc/prom/mp.c
--- v2.1.8/linux/arch/sparc/prom/mp.c	Sun Apr 21 12:30:32 1996
+++ linux/arch/sparc/prom/mp.c	Sat Nov  9 10:12:27 1996
@@ -1,10 +1,14 @@
-/* $Id: mp.c,v 1.5 1996/04/04 16:31:06 tridge Exp $
+/* $Id: mp.c,v 1.6 1996/09/19 20:27:25 davem Exp $
  * mp.c:  OpenBoot Prom Multiprocessor support routines.  Don't call
  *        these on a UP or else you will halt and catch fire. ;)
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
@@ -16,18 +20,28 @@
 int
 prom_startcpu(int cpunode, struct linux_prom_registers *ctable_reg, int ctx, char *pc)
 {
+	int ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
 	case PROM_V2:
 	case PROM_AP1000:
+	default:
+		ret = -1;
 		break;
 	case PROM_V3:
 	case PROM_P1275:
-		return (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc);
+		ret = (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc);
 		break;
 	};
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 
-	return -1;
+	return ret;
 }
 
 /* Stop CPU with device prom-tree node 'cpunode'.
@@ -36,18 +50,28 @@
 int
 prom_stopcpu(int cpunode)
 {
+	int ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
 	case PROM_V2:
 	case PROM_AP1000:
+	default:
+		ret = -1;
 		break;
 	case PROM_V3:
 	case PROM_P1275:
-		return (*(romvec->v3_cpustop))(cpunode);
+		ret = (*(romvec->v3_cpustop))(cpunode);
 		break;
 	};
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 
-	return -1;
+	return ret;
 }
 
 /* Make CPU with device prom-tree node 'cpunode' idle.
@@ -56,18 +80,28 @@
 int
 prom_idlecpu(int cpunode)
 {
+	int ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
 	case PROM_V2:
 	case PROM_AP1000:
+	default:
+		ret = -1;
 		break;
 	case PROM_V3:
 	case PROM_P1275:
-		return (*(romvec->v3_cpuidle))(cpunode);
+		ret = (*(romvec->v3_cpuidle))(cpunode);
 		break;
 	};
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 
-	return -1;
+	return ret;
 }
 
 /* Resume the execution of CPU with nodeid 'cpunode'.
@@ -76,16 +110,26 @@
 int
 prom_restartcpu(int cpunode)
 {
+	int ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
 	switch(prom_vers) {
 	case PROM_V0:
 	case PROM_V2:
 	case PROM_AP1000:
+	default:
+		ret = -1;
 		break;
 	case PROM_V3:
 	case PROM_P1275:
-		return (*(romvec->v3_cpuresume))(cpunode);
+		ret = (*(romvec->v3_cpuresume))(cpunode);
 		break;
 	};
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 
-	return -1;
+	return ret;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/ranges.c linux/arch/sparc/prom/ranges.c
--- v2.1.8/linux/arch/sparc/prom/ranges.c	Sat Nov 25 03:00:14 1995
+++ linux/arch/sparc/prom/ranges.c	Sat Nov  9 10:12:29 1996
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.4 1995/11/25 01:00:12 davem Exp $
+/* $Id: ranges.c,v 1.6 1996/11/03 08:12:01 davem Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -6,10 +6,11 @@
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/system.h>
 
 struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
-struct linux_prom_ranges promlib_sbus_ranges[PROMREG_MAX];
-int num_obio_ranges, num_sbus_ranges;
+int num_obio_ranges;
 
 /* Adjust register values based upon the ranges parameters. */
 void
@@ -27,8 +28,6 @@
 		regp[regc].which_io = rangep[rngc].ot_parent_space;
 		regp[regc].phys_addr += rangep[rngc].ot_parent_base;
 	}
-
-	return;
 }
 
 void
@@ -46,45 +45,35 @@
 		ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
 		ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
 	}
-
-	return;
 }
 
 /* Apply probed obio ranges to registers passed, if no ranges return. */
 void
 prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs)
 {
-	if(!num_obio_ranges) return;
-	prom_adjust_regs(regs, nregs, promlib_obio_ranges, num_obio_ranges);
-	return;
+	if(num_obio_ranges)
+		prom_adjust_regs(regs, nregs, promlib_obio_ranges, num_obio_ranges);
 }
 
 /* Apply probed sbus ranges to registers passed, if no ranges return. */
 void
-prom_apply_sbus_ranges(struct linux_prom_registers *regs, int nregs)
+prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs, int nregs)
 {
-	if(!num_sbus_ranges) return;
-	prom_adjust_regs(regs, nregs, promlib_sbus_ranges, num_sbus_ranges);
-	return;
+	if(sbus->num_sbus_ranges)
+	prom_adjust_regs(regs, nregs, sbus->sbus_ranges, sbus->num_sbus_ranges);
 }
 
 void
 prom_ranges_init(void)
 {
-	int node, obio_node, sbus_node;
+	int node, obio_node;
 	int success;
 
 	num_obio_ranges = 0;
-	num_sbus_ranges = 0;
 
 	/* Check for obio and sbus ranges. */
 	node = prom_getchild(prom_root_node);
 	obio_node = prom_searchsiblings(node, "obio");
-	sbus_node = prom_searchsiblings(node, "iommu");
-	if(sbus_node) {
-		sbus_node = prom_getchild(sbus_node);
-		sbus_node = prom_searchsiblings(sbus_node, "sbus");
-	}
 
 	if(obio_node) {
 		success = prom_getproperty(obio_node, "ranges",
@@ -94,17 +83,63 @@
 			num_obio_ranges = (success/sizeof(struct linux_prom_ranges));
 	}
 
-	if(sbus_node) {
-		success = prom_getproperty(sbus_node, "ranges",
-					   (char *) promlib_sbus_ranges,
-					   sizeof(promlib_sbus_ranges));
-		if(success != -1)
-			num_sbus_ranges = (success/sizeof(struct linux_prom_ranges));
-	}
-
-	if(num_obio_ranges || num_sbus_ranges)
-		prom_printf("PROMLIB: obio_ranges %d sbus_ranges %d\n",
-			    num_obio_ranges, num_sbus_ranges);
+	if(num_obio_ranges)
+		prom_printf("PROMLIB: obio_ranges %d\n", num_obio_ranges);
 
 	return;
+}
+
+void
+prom_sbus_ranges_init(int parentnd, struct linux_sbus *sbus)
+{
+	int success;
+	
+	sbus->num_sbus_ranges = 0;
+	if(sparc_cpu_model == sun4c)
+		return;
+	success = prom_getproperty(sbus->prom_node, "ranges",
+				   (char *) sbus->sbus_ranges,
+				   sizeof (sbus->sbus_ranges));
+	if (success != -1)
+		sbus->num_sbus_ranges = (success/sizeof(struct linux_prom_ranges));
+	if (sparc_cpu_model == sun4d) {
+		struct linux_prom_ranges iounit_ranges[PROMREG_MAX];
+		int num_iounit_ranges;
+		
+		success = prom_getproperty(parentnd, "ranges",
+				   	(char *) iounit_ranges,
+				   	sizeof (iounit_ranges));
+		if (success != -1) {
+			num_iounit_ranges = (success/sizeof(struct linux_prom_ranges));
+			prom_adjust_ranges (sbus->sbus_ranges, sbus->num_sbus_ranges, iounit_ranges, num_iounit_ranges);
+		}
+	}
+}
+
+void
+prom_apply_generic_ranges (int node, int parent, struct linux_prom_registers *regs, int nregs)
+{
+	int success;
+	int num_ranges;
+	struct linux_prom_ranges ranges[PROMREG_MAX];
+	
+	success = prom_getproperty(node, "ranges",
+				   (char *) ranges,
+				   sizeof (ranges));
+	if (success != -1) {
+		num_ranges = (success/sizeof(struct linux_prom_ranges));
+		if (parent) {
+			struct linux_prom_ranges parent_ranges[PROMREG_MAX];
+			int num_parent_ranges;
+		
+			success = prom_getproperty(parent, "ranges",
+				   		   (char *) parent_ranges,
+				   		   sizeof (parent_ranges));
+			if (success != -1) {
+				num_parent_ranges = (success/sizeof(struct linux_prom_ranges));
+				prom_adjust_ranges (ranges, num_ranges, parent_ranges, num_parent_ranges);
+			}
+		}
+		prom_adjust_regs(regs, nregs, ranges, num_ranges);
+	}
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/segment.c linux/arch/sparc/prom/segment.c
--- v2.1.8/linux/arch/sparc/prom/segment.c	Sat Nov 25 03:00:16 1995
+++ linux/arch/sparc/prom/segment.c	Sat Nov  9 10:12:30 1996
@@ -1,4 +1,4 @@
-/* $Id: segment.c,v 1.2 1995/11/25 01:00:14 davem Exp $
+/* $Id: segment.c,v 1.3 1996/09/19 20:27:28 davem Exp $
  * segment.c:  Prom routine to map segments in other contexts before
  *             a standalone is completely mapped.  This is for sun4 and
  *             sun4c architectures only.
@@ -6,6 +6,9 @@
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
@@ -15,6 +18,12 @@
 void
 prom_putsegment(int ctx, unsigned long vaddr, int segment)
 {
+	unsigned long flags;
+	save_flags(flags); cli();
 	(*(romvec->pv_setctxt))(ctx, (char *) vaddr, segment);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
 	return;
 }
diff -u --recursive --new-file v2.1.8/linux/arch/sparc/prom/tree.c linux/arch/sparc/prom/tree.c
--- v2.1.8/linux/arch/sparc/prom/tree.c	Mon May  6 12:26:04 1996
+++ linux/arch/sparc/prom/tree.c	Sat Nov  9 10:12:30 1996
@@ -1,4 +1,4 @@
-/* $Id: tree.c,v 1.8 1996/04/04 16:31:09 tridge Exp $
+/* $Id: tree.c,v 1.12 1996/10/12 12:37:40 davem Exp $
  * tree.c: Basic device tree traversal/scanning for the Linux
  *         prom library.
  *
@@ -7,6 +7,9 @@
 
 #include <linux/config.h>
 #include <linux/string.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
@@ -19,16 +22,31 @@
 int
 prom_getchild(int node)
 {
-	int cnode;
+	int cnode, ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
 
 #if CONFIG_AP1000
         printk("prom_getchild -> 0\n");
+	restore_flags(flags);
         return 0;
+#else
+	if(node == -1) {
+		ret = 0;
+	} else {
+		cnode = prom_nodeops->no_child(node);
+		if((cnode == 0) || (cnode == -1))
+			ret = 0;
+		else
+			ret = cnode;
+	}
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
 #endif
-	if(node == -1) return 0;
-	cnode = prom_nodeops->no_child(node);
-	if((cnode == 0) || (cnode == -1)) return 0;
-	return cnode;
 }
 
 /* Return the next sibling of node 'node' or zero if no more siblings
@@ -37,16 +55,31 @@
 int
 prom_getsibling(int node)
 {
-	int sibnode;
+	int sibnode, ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
 
 #if CONFIG_AP1000
         printk("prom_getsibling -> 0\n");
+	restore_flags(flags);
         return 0;
+#else
+	if(node == -1) {
+		ret = 0;
+	} else {
+		sibnode = prom_nodeops->no_nextnode(node);
+		if((sibnode == 0) || (sibnode == -1))
+			ret = 0;
+		else
+			ret = sibnode;
+	}
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
 #endif
-	if(node == -1) return 0;
-	sibnode = prom_nodeops->no_nextnode(node);
-	if((sibnode == 0) || (sibnode == -1)) return 0;
-	return sibnode;
 }
 
 /* Return the length in bytes of property 'prop' at node 'node'.
@@ -55,12 +88,25 @@
 int
 prom_getproplen(int node, char *prop)
 {
+	int ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+
 #if CONFIG_AP1000
         printk("prom_getproplen(%s) -> -1\n",prop);
+	restore_flags(flags);
         return -1;
 #endif
-	if((!node) || (!prop)) return -1;
-	return prom_nodeops->no_proplen(node, prop);
+	if((!node) || (!prop))
+		ret = -1;
+	else
+		ret = prom_nodeops->no_proplen(node, prop);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
 }
 
 /* Acquire a property 'prop' at node 'node' and place it in
@@ -70,17 +116,28 @@
 int
 prom_getproperty(int node, char *prop, char *buffer, int bufsize)
 {
-	int plen;
+	int plen, ret;
+	unsigned long flags;
+
+	save_flags(flags); cli();
 
 #if CONFIG_AP1000
         printk("prom_getproperty(%s) -> -1\n",prop);
-        return -1;
+	restore_flags(flags);
+	return -1;
 #endif
 	plen = prom_getproplen(node, prop);
-	if((plen > bufsize) || (plen == 0) || (plen == -1)) return -1;
-
-	/* Ok, things seem all right. */
-	return prom_nodeops->no_getprop(node, prop, buffer);
+	if((plen > bufsize) || (plen == 0) || (plen == -1))
+		ret = -1;
+	else {
+		/* Ok, things seem all right. */
+		ret = prom_nodeops->no_getprop(node, prop, buffer);
+	}
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
 }
 
 /* Acquire an integer property and return its value.  Returns -1
@@ -173,6 +230,7 @@
 int
 prom_searchsiblings(int node_start, char *nodename)
 {
+
 	int thisnode, error;
 
 	for(thisnode = node_start; thisnode;
@@ -187,13 +245,41 @@
 	return 0;
 }
 
+/* Gets name in the form prom v2+ uses it (name@x,yyyyy or name (if no reg)) */
+int 
+prom_getname (int node, char *buffer, int len)
+{
+	int i;
+	struct linux_prom_registers reg[PROMREG_MAX];
+	
+	i = prom_getproperty (node, "name", buffer, len);
+	if (i <= 0) return -1;
+	buffer [i] = 0;
+	len -= i;
+	i = prom_getproperty (node, "reg", (char *)reg, sizeof (reg));
+	if (i <= 0) return 0;
+	if (len < 11) return -1;
+	buffer = strchr (buffer, 0);
+	sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr);
+	return 0;
+}
+
 /* Return the first property type for node 'node'.
  */
 char *
 prom_firstprop(int node)
 {
+	unsigned long flags;
+	char *ret;
+
 	if(node == -1) return "";
-	return prom_nodeops->no_nextprop(node, (char *) 0x0);
+	save_flags(flags); cli();
+	ret = prom_nodeops->no_nextprop(node, (char *) 0x0);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
 }
 
 /* Return the property type string after property type 'oprop'
@@ -203,8 +289,17 @@
 char *
 prom_nextprop(int node, char *oprop)
 {
+	char *ret;
+	unsigned long flags;
+
 	if(node == -1) return "";
-	return prom_nodeops->no_nextprop(node, oprop);
+	save_flags(flags); cli();
+	ret = prom_nodeops->no_nextprop(node, oprop);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
 }
 
 int
@@ -226,7 +321,48 @@
 int
 prom_setprop(int node, char *pname, char *value, int size)
 {
+	unsigned long flags;
+	int ret;
+
 	if(size == 0) return 0;
 	if((pname == 0) || (value == 0)) return 0;
-	return prom_nodeops->no_setprop(node, pname, value, size);
+	save_flags(flags); cli();
+	ret = prom_nodeops->no_setprop(node, pname, value, size);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+			     "r" (&current_set[smp_processor_id()]) :
+			     "memory");
+	restore_flags(flags);
+	return ret;
+}
+
+int
+prom_inst2pkg(int inst)
+{
+	int node;
+	unsigned long flags;
+	
+	save_flags(flags); cli();
+	node = (*romvec->pv_v2devops.v2_inst2pkg)(inst);
+	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+		"r" (&current_set[smp_processor_id()]) :
+		"memory");
+	restore_flags(flags);
+	if (node == -1) return 0;
+	return node;
+}
+
+/* Return 'node' assigned to a particular prom 'path'
+ * FIXME: Should work for v0 as well
+ */
+int
+prom_pathtoinode(char *path)
+{
+	int node, inst;
+	
+	inst = prom_devopen (path);
+	if (inst == -1) return 0;
+	node = prom_inst2pkg (inst);
+	prom_devclose (inst);
+	if (node == -1) return 0;
+	return node;
 }
diff -u --recursive --new-file v2.1.8/linux/drivers/Makefile linux/drivers/Makefile
--- v2.1.8/linux/drivers/Makefile	Mon Apr 22 10:59:39 1996
+++ linux/drivers/Makefile	Tue Nov 12 10:32:40 1996
@@ -38,7 +38,7 @@
   endif
 endif
 
-ifdef CONFIG_CD_NO_IDESCSI
+ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
 SUB_DIRS += cdrom
 MOD_SUB_DIRS += cdrom
 endif
diff -u --recursive --new-file v2.1.8/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.1.8/linux/drivers/block/Makefile	Sun Nov 10 20:12:08 1996
+++ linux/drivers/block/Makefile	Tue Nov 12 10:32:48 1996
@@ -20,6 +20,7 @@
 M_OBJS   :=
 MOD_LIST_NAME := BLOCK_MODULES
 LX_OBJS :=
+MX_OBJS :=
 
 ifeq ($(CONFIG_BLK_DEV_FD),y)
 L_OBJS += floppy.o
@@ -50,10 +51,12 @@
 endif
 
 ifeq ($(CONFIG_BLK_DEV_IDE),y)
-L_OBJS += ide.o ide-probe.o
+LX_OBJS += ide.o
+L_OBJS += ide-probe.o
 else
   ifeq ($(CONFIG_BLK_DEV_IDE),m)
-  M_OBJS += ide.o ide-probe.o
+  MX_OBJS += ide.o
+  M_OBJS += ide-probe.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.8/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.8/linux/drivers/block/ide-cd.c	Sun Nov 10 20:12:09 1996
+++ linux/drivers/block/ide-cd.c	Tue Nov 12 10:32:49 1996
@@ -1,5 +1,14 @@
+/* #define VERBOSE_IDE_CD_ERRORS 1 */
 /*
  * linux/drivers/block/ide-cd.c
+ * ATAPI cd-rom driver.  To be used with ide.c.
+ * See Documentation/cdrom/ide-cd for usage information.
+ *
+ * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996  Erik Andersen <andersee@et.byu.edu>
+ *
+ * May be copied or modified under the terms of the GNU General Public License
+ * see linux/COPYING for more information.
  *
  * 1.00  Oct 31, 1994 -- Initial version.
  * 1.01  Nov  2, 1994 -- Fixed problem with starting request in
@@ -108,17 +117,46 @@
  * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
  * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
  *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
- * 3.17a Oct 31, 1996 -- Added module and DMA support.
+ * 3.18  Oct 31, 1996 -- Added module and DMA support.
+ *                       
+ *                       
+ * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
+ *                                 Erik B. Andersen <andersee@et.byu.edu>
+ *                     -- Newer Creative drives don't always set the error
+ *                          register correctly.  Make sure we see media changes
+ *                          regardless.
+ *                     -- Integrate with generic cdrom driver.
+ *                     -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
+ *                          a patch from Ciro Cattuto <>.
+ *                     -- Call set_device_ro.
+ *                     -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ *                          ioctls, based on patch by Erik Andersen
+ *                     -- Add some probes of drive capability during setup.
  *
- * NOTE: Direct audio reads will only work on some types of drive.
- * So far, i've received reports of success for Sony and Toshiba drives.
+ * 4.01  Nov 11, 1996  -- Split into ide-cd.c and ide-cd.h
+ *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE 
+ *                          ioctls in favor of a generalized approach 
+ *                          using the generic cdrom driver.
+ *                     -- Fully integrated with the 2.1.X kernel.
+ *                     -- Other stuff that I forgot (lots of changes)
+ *
+ *
+ * MOSTLY DONE LIST:
+ *  Query the drive to find what features are available
+ *   before trying to use them.
+ *
+ * TO DO LIST:
+ *  Avoid printing error messages for expected errors from the drive.
+ *    (If you are using a cd changer, you may get errors in the kernel
+       logs that are completly expected.  Don't complpain to me about this,
+       unless you have a patch to fix it.  I am working on it...)
+ *  Reset unlocks drive?
+ *  Implement ide_cdrom_disc_status using the generic cdrom interface
+ *  Implement ide_cdrom_select_speed using the generic cdrom interface
+ *  Fix ide_cdrom_reset so that it works (it does nothing right now)
+ *  -- Suggestions are welcome.  Patches that work are more welcome though.
  *
- * ATAPI cd-rom driver.  To be used with ide.c.
- * See Documentation/cdrom/ide-cd for usage information.
  *
- * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
- * May be copied or modified under the terms of the GNU General Public License
- * (../../COPYING).
  */
 
 
@@ -144,389 +182,24 @@
 #include <asm/unaligned.h>
 
 #include "ide.h"
+#include "ide-cd.h"
 
 
-
-/* Turn this on to have the driver print out the meanings of the
-   ATAPI error codes.  This will use up additional kernel-space
-   memory, though. */
-
-#ifndef VERBOSE_IDE_CD_ERRORS
-#define VERBOSE_IDE_CD_ERRORS 0
-#endif
-
-
-/* Turning this on will remove code to work around various nonstandard
-   ATAPI implementations.  If you know your drive follows the standard,
-   this will give you a slightly smaller kernel. */
-
-#ifndef STANDARD_ATAPI
-#define STANDARD_ATAPI 0
-#endif
-
-
-/* Turning this on will disable the door-locking functionality.
-   This is apparently needed for supermount. */
-
-#ifndef NO_DOOR_LOCKING
-#define NO_DOOR_LOCKING 0
-#endif
-
-
-/* Size of buffer to allocate, in blocks, for audio reads. */
-
-#ifndef CDROM_NBLOCKS_BUFFER
-#define CDROM_NBLOCKS_BUFFER 8
-#endif
-
-
-/************************************************************************/
-
-#define SECTOR_SIZE 512
-#define SECTOR_BITS 9
-#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
-
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-/* special command codes for strategy routine. */
-#define PACKET_COMMAND        4315
-#define REQUEST_SENSE_COMMAND 4316
-#define RESET_DRIVE_COMMAND   4317
-
-/* Some ATAPI command opcodes (just like SCSI).
-   (Some other cdrom-specific codes are in cdrom.h.) */
-#define TEST_UNIT_READY         0x00
-#define REQUEST_SENSE           0x03
-#define START_STOP              0x1b
-#define ALLOW_MEDIUM_REMOVAL    0x1e
-#define READ_CAPACITY		0x25
-#define READ_10                 0x28
-#define MODE_SENSE_10           0x5a
-#define MODE_SELECT_10          0x55
-#define READ_CD                 0xbe
-
-#define LOAD_UNLOAD             0xa6
-
-
-/* ATAPI sense keys (mostly copied from scsi.h). */
-
-#define NO_SENSE                0x00
-#define RECOVERED_ERROR         0x01
-#define NOT_READY               0x02
-#define MEDIUM_ERROR            0x03
-#define HARDWARE_ERROR          0x04
-#define ILLEGAL_REQUEST         0x05
-#define UNIT_ATTENTION          0x06
-#define DATA_PROTECT            0x07
-#define ABORTED_COMMAND         0x0b
-#define MISCOMPARE              0x0e
-
-/* We want some additional flags for cd-rom drives.
-   To save space in the ide_drive_t struct, use some fields which
-   doesn't make sense for cd-roms -- `bios_sect' and `bios_head'. */
-
-/* Configuration flags.  These describe the capabilities of the drive.
-   They generally do not change after initialization, unless we learn
-   more about the drive from stuff failing. */
-struct ide_cd_config_flags {
-	__u8 drq_interrupt    : 1; /* Device sends an interrupt when ready
-				      for a packet command. */
-	__u8 no_doorlock      : 1; /* Drive cannot lock the door. */
-#if ! STANDARD_ATAPI
-	__u8 old_readcd       : 1; /* Drive uses old READ CD opcode. */
-	__u8 playmsf_as_bcd   : 1; /* PLAYMSF command takes BCD args. */
-	__u8 tocaddr_as_bcd   : 1; /* TOC addresses are in BCD. */
-	__u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
-	__u8 subchan_as_bcd   : 1; /* Subchannel info is in BCD. */
-#endif  /* not STANDARD_ATAPI */
-	__u8 reserved         : 1;
-};
-#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_sect))
-
- 
-/* State flags.  These give information about the current state of the
-   drive, and will change during normal operation. */
-struct ide_cd_state_flags {
-	__u8 media_changed : 1; /* Driver has noticed a media change. */
-	__u8 toc_valid     : 1; /* Saved TOC information is current. */
-	__u8 door_locked   : 1; /* We think that the drive door is locked. */
-	__u8 eject_on_close: 1; /* Drive should eject when device is closed. */
-	__u8 sanyo_slot    : 2; /* Sanyo 3 CD changer support */
-	__u8 reserved      : 2;
-};
-#define CDROM_STATE_FLAGS(drive)  ((struct ide_cd_state_flags *)&((drive)->bios_head))
-
-
-struct atapi_request_sense {
-	unsigned char error_code : 7;
-	unsigned char valid      : 1;
-	byte reserved1;
-	unsigned char sense_key  : 4;
-	unsigned char reserved2  : 1;
-	unsigned char ili        : 1;
-	unsigned char reserved3  : 2;
-	byte info[4];
-	byte sense_len;
-	byte command_info[4];
-	byte asc;
-	byte ascq;
-	byte fru;
-	byte sense_key_specific[3];
-};
-
-struct packet_command {
-	char *buffer;
-	int buflen;
-	int stat;
-	struct atapi_request_sense *sense_data;
-	unsigned char c[12];
-};
-
-
-/* Structure of a MSF cdrom address. */
-struct atapi_msf {
-	byte reserved;
-	byte minute;
-	byte second;
-	byte frame;
-};
-
-
-/* Space to hold the disk TOC. */
-
-#define MAX_TRACKS 99
-struct atapi_toc_header {
-	unsigned short toc_length;
-	byte first_track;
-	byte last_track;
-};
-
-struct atapi_toc_entry {
-	byte reserved1;
-	unsigned control : 4;
-	unsigned adr     : 4;
-	byte track;
-	byte reserved2;
-	union {
-		unsigned lba;
-		struct atapi_msf msf;
-	} addr;
-};
-
-struct atapi_toc {
-	int    last_session_lba;
-	int    xa_flag;
-	unsigned capacity;
-	struct atapi_toc_header hdr;
-	struct atapi_toc_entry  ent[MAX_TRACKS+1];
-	  /* One extra for the leadout. */
-};
-
-
-/* This structure is annoyingly close to, but not identical with,
-   the cdrom_subchnl structure from cdrom.h. */
-struct atapi_cdrom_subchnl 
-{
-	u_char  acdsc_reserved;
-	u_char  acdsc_audiostatus;
-	u_short acdsc_length;
-	u_char  acdsc_format;
-
-	u_char  acdsc_adr:	4;
-	u_char  acdsc_ctrl:	4;
-	u_char  acdsc_trk;
-	u_char  acdsc_ind;
-	union {
-		struct atapi_msf msf;
-		int	lba;
-	} acdsc_absaddr;
-	union {
-		struct atapi_msf msf;
-		int	lba;
-	} acdsc_reladdr;
-};
-
-
-/* Extra per-device info for cdrom drives. */
-struct cdrom_info {
-
-	/* Buffer for table of contents.  NULL if we haven't allocated
-	   a TOC buffer for this device yet. */
-
-	struct atapi_toc *toc;
-
-	/* Sector buffer.  If a read request wants only the first part
-	   of a cdrom block, we cache the rest of the block here,
-	   in the expectation that that data is going to be wanted soon.
-	   SECTOR_BUFFERED is the number of the first buffered sector,
-	   and NSECTORS_BUFFERED is the number of sectors in the buffer.
-	   Before the buffer is allocated, we should have
-	   SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
-
-	unsigned long sector_buffered;
-	unsigned long nsectors_buffered;
-	char *sector_buffer;
-
-	/* The result of the last successful request sense command
-	   on this device. */
-	struct atapi_request_sense sense_data;
-
-	struct request request_sense_request;
-	struct packet_command request_sense_pc;
-	int dma;
-};
-
-
-#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
-
-
-
 /****************************************************************************
- * Descriptions of ATAPI error codes.
+ * Generic packet command support and error handling routines.
  */
 
-#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
-
-#if VERBOSE_IDE_CD_ERRORS
-
-/* From Table 124 of the ATAPI 1.2 spec. */
-
-char *sense_key_texts[16] = {
-	"No sense data",
-	"Recovered error",
-	"Not ready",
-	"Medium error",
-	"Hardware error",
-	"Illegal request",
-	"Unit attention",
-	"Data protect",
-	"(reserved)",
-	"(reserved)",
-	"(reserved)",
-	"Aborted command",
-	"(reserved)",
-	"(reserved)",
-	"Miscompare",
-	"(reserved)",
-};
-
-
-/* From Table 125 of the ATAPI 1.2 spec. */
-
-struct {
-	short asc_ascq;
-	char *text;
-} sense_data_texts[] = {
-	{ 0x0000, "No additional sense information" },
-	{ 0x0011, "Audio play operation in progress" },
-	{ 0x0012, "Audio play operation paused" },
-	{ 0x0013, "Audio play operation successfully completed" },
-	{ 0x0014, "Audio play operation stopped due to error" },
-	{ 0x0015, "No current audio status to return" },
-
-	{ 0x0200, "No seek complete" },
-
-	{ 0x0400, "Logical unit not ready - cause not reportable" },
-	{ 0x0401,
-	  "Logical unit not ready - in progress (sic) of becoming ready" },
-	{ 0x0402, "Logical unit not ready - initializing command required" },
-	{ 0x0403, "Logical unit not ready - manual intervention required" },
-
-	{ 0x0600, "No reference position found" },
-
-	{ 0x0900, "Track following error" },
-	{ 0x0901, "Tracking servo failure" },
-	{ 0x0902, "Focus servo failure" },
-	{ 0x0903, "Spindle servo failure" },
-
-	{ 0x1100, "Unrecovered read error" },
-	{ 0x1106, "CIRC unrecovered error" },
-
-	{ 0x1500, "Random positioning error" },
-	{ 0x1501, "Mechanical positioning error" },
-	{ 0x1502, "Positioning error detected by read of medium" },
-
-	{ 0x1700, "Recovered data with no error correction applied" },
-	{ 0x1701, "Recovered data with retries" },
-	{ 0x1702, "Recovered data with positive head offset" },
-	{ 0x1703, "Recovered data with negative head offset" },
-	{ 0x1704, "Recovered data with retries and/or CIRC applied" },
-	{ 0x1705, "Recovered data using previous sector ID" },
-
-	{ 0x1800, "Recovered data with error correction applied" },
-	{ 0x1801, "Recovered data with error correction and retries applied" },
-	{ 0x1802, "Recovered data - the data was auto-reallocated" },
-	{ 0x1803, "Recovered data with CIRC" },
-	{ 0x1804, "Recovered data with L-EC" },
-	{ 0x1805, "Recovered data - recommend reassignment" },
-	{ 0x1806, "Recovered data - recommend rewrite" },
-
-	{ 0x1a00, "Parameter list length error" },
-
-	{ 0x2000, "Invalid command operation code" },
-
-	{ 0x2100, "Logical block address out of range" },
-
-	{ 0x2400, "Invalid field in command packet" },
-
-	{ 0x2600, "Invalid field in parameter list" },
-	{ 0x2601, "Parameter not supported" },
-	{ 0x2602, "Parameter value invalid" },
-	{ 0x2603, "Threshold parameters not supported" },
-
-	{ 0x2800, "Not ready to ready transition, medium may have changed" },
-
-	{ 0x2900, "Power on, reset or bus device reset occurred" },
 
-	{ 0x2a00, "Parameters changed" },
-	{ 0x2a01, "Mode parameters changed" },
-
-	{ 0x3000, "Incompatible medium installed" },
-	{ 0x3001, "Cannot read medium - unknown format" },
-	{ 0x3002, "Cannot read medium - incompatible format" },
-
-	{ 0x3700, "Rounded parameter" },
-
-	{ 0x3900, "Saving parameters not supported" },
-
-	{ 0x3a00, "Medium not present" },
-
-	{ 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
-	{ 0x3f01, "Microcode has been changed" },
-	{ 0x3f02, "Changed operating definition" },
-	{ 0x3f03, "Inquiry data has changed" },
-
-	{ 0x4000, "Diagnostic failure on component (ASCQ)" },
-
-	{ 0x4400, "Internal ATAPI CD-ROM drive failure" },
-
-	{ 0x4e00, "Overlapped commands attempted" },
-
-	{ 0x5300, "Media load or eject failed" },
-	{ 0x5302, "Medium removal prevented" },
-
-	{ 0x5700, "Unable to recover table of contents" },
-
-	{ 0x5a00, "Operator request or state change input (unspecified)" },
-	{ 0x5a01, "Operator medium removal request" },
-
-	{ 0x5b00, "Threshold condition met" },
-
-	{ 0x5c00, "Status change" },
-
-	{ 0x6300, "End of user area encountered on this track" },
-
-	{ 0x6400, "Illegal mode for this track" },
-
-	{ 0xbf00, "Loss of streaming" },
-};
-#endif
-
-
-
-/****************************************************************************
- * Generic packet command support and error handling routines.
- */
+/* Mark that we've seen a media change, and invalidate our internal
+   buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
+	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+	info->nsectors_buffered = 0;
+}
 
 
 static
@@ -534,15 +207,22 @@
 			       struct atapi_request_sense *reqbuf,
 			       struct packet_command *failed_command)
 {
-	/* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
-	   Workman (and probably other programs) uses this command to poll
-	   the drive, and we don't want to fill the syslog
-	   with useless errors. */
-	if (failed_command &&
-	    failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
-	    (reqbuf->sense_key == NOT_READY ||
-	     reqbuf->sense_key == UNIT_ATTENTION))
-		return;
+	if (reqbuf->sense_key == NOT_READY ||
+	    reqbuf->sense_key == UNIT_ATTENTION) {
+		/* Make good and sure we've seen this potential media change.
+		   Some drives (i.e. Creative) fail to present the correct
+		   sense key in the error register. */
+		cdrom_saw_media_change (drive);
+
+
+		/* Don't print not ready or unit attention errors for
+		   READ_SUBCHANNEL.  Workman (and probably other programs)
+		   uses this command to poll the drive, and we don't want
+		   to fill the syslog with useless errors. */
+		if (failed_command &&
+		    failed_command->c[0] == SCMD_READ_SUBCHANNEL)
+			return;
+	}
 
 #if VERBOSE_IDE_CD_ERRORS
 	{
@@ -726,18 +406,6 @@
 }
 
 
-/* Mark that we've seen a media change, and invalidate our internal
-   buffers. */
-static void cdrom_saw_media_change (ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	
-	CDROM_STATE_FLAGS (drive)->media_changed = 1;
-	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
-	info->nsectors_buffered = 0;
-}
-
-
 /* Returns 0 if the request should be continued.
    Returns 1 if the request was ended. */
 static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
@@ -793,7 +461,7 @@
 				   with this command, and we don't want
 				   to uselessly fill up the syslog. */
 				if (pc->c[0] != SCMD_READ_SUBCHANNEL)
-					printk ("%s : tray open or drive not ready\n",
+					printk ("%s: tray open or drive not ready\n",
 						drive->name);
 			} else if (sense_key == UNIT_ATTENTION) {
 				/* Check for media change. */
@@ -899,10 +567,11 @@
 	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
 	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
 	OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
-
+ 
 	if (info->dma)
 		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
 
+
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
 		ide_set_handler (drive, handler, WAIT_CMD);
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
@@ -947,7 +616,7 @@
 }
 
 
-
+
 /****************************************************************************
  * Block read functions.
  */
@@ -1065,7 +734,7 @@
 	}
 
 	if (cdrom_decode_status (drive, 0, &stat)) return;
-
+ 
 	if (dma) {
 		if (!dma_error) {
 			for (i = rq->nr_sectors; i > 0;) {
@@ -1077,6 +746,7 @@
 		return;
 	}
 
+
 	/* Read the interrupt reason and the transfer length. */
 	ireason = IN_BYTE (IDE_NSECTOR_REG);
 	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
@@ -1333,7 +1003,7 @@
 
 
 
-
+
 /****************************************************************************
  * Execute all other packet commands.
  */
@@ -1557,7 +1227,7 @@
 	}
 }
 
-
+
 /****************************************************************************
  * cdrom driver request routine.
  */
@@ -1578,7 +1248,7 @@
 }
 
 
-
+
 /****************************************************************************
  * Ioctl handling.
  *
@@ -1644,10 +1314,12 @@
 	pc.sense_data = reqbuf;
 	pc.c[0] = TEST_UNIT_READY;
 
+#if ! STANDARD_ATAPI
         /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
            switch CDs instead of supporting the LOAD_UNLOAD opcode   */
 
         pc.c[7] = CDROM_STATE_FLAGS (drive)->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
 
 	return cdrom_queue_packet_command (drive, &pc);
 }
@@ -1677,21 +1349,21 @@
 		stat = cdrom_queue_packet_command (drive, &pc);
 	}
 
+	/* If we got an illegal field error, the drive
+	   probably cannot lock the door. */
+	if (stat != 0 &&
+	    reqbuf->sense_key == ILLEGAL_REQUEST &&
+	    reqbuf->asc == 0x24) {
+		printk ("%s: door locking not supported\n",
+			drive->name);
+		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+		stat = 0;
+	}
+
 	if (stat == 0)
 		CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
-	else {
-		/* If we got an illegal field error, the drive
-		   probably cannot lock the door. */
-		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
-		    reqbuf->asc == 0x24) {
-			printk ("%s: door locking not supported\n",
-				drive->name);
-			CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
-			stat = 0;
-			CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
-		}
-	}
-  return stat;
+
+	return stat;
 }
 
 
@@ -1918,7 +1590,7 @@
 	pc.c[0] = SCMD_READ_SUBCHANNEL;
 	pc.c[1] = 2;     /* MSF addressing */
 	pc.c[2] = 0x40;  /* request subQ data */
-	pc.c[3] = format,
+	pc.c[3] = format;
 	pc.c[7] = (buflen >> 8);
 	pc.c[8] = (buflen & 0xff);
 	return cdrom_queue_packet_command (drive, &pc);
@@ -2064,7 +1736,6 @@
 {
 	struct packet_command pc;
 	struct atapi_request_sense my_reqbuf;
-	int stat;
 
 	if (reqbuf == NULL)
 		reqbuf = &my_reqbuf;
@@ -2076,7 +1747,7 @@
 	pc.buflen = buflen;
 
 #if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->old_readcd)
+	if (CDROM_CONFIG_FLAGS (drive)->nec260)
 		pc.c[0] = 0xd4;
 	else
 #endif  /* not STANDARD_ATAPI */
@@ -2092,24 +1763,7 @@
 	else
 		pc.c[9] = 0x10;
 
-	stat = cdrom_queue_packet_command (drive, &pc);
-
-#if ! STANDARD_ATAPI
-	/* If the drive doesn't recognize the READ CD opcode, retry the command
-	   with an older opcode for that command. */
-	if (stat && reqbuf->sense_key == ILLEGAL_REQUEST &&
-	    reqbuf->asc == 0x20 &&
-	    CDROM_CONFIG_FLAGS (drive)->old_readcd == 0) {
-		printk ("%s: Drive does not recognize READ_CD;"
-			"trying opcode 0xd4\n",
-			drive->name);
-		CDROM_CONFIG_FLAGS (drive)->old_readcd = 1;
-		return cdrom_read_block (drive, format, lba, nblocks,
-					 buf, buflen, reqbuf);
-	}
-#endif  /* not STANDARD_ATAPI */
-
-	return stat;
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -2118,23 +1772,26 @@
 cdrom_load_unload (ide_drive_t *drive, int slot,
 		   struct atapi_request_sense *reqbuf)
 {
+#if ! STANDARD_ATAPI
 	/* if the drive is a Sanyo 3 CD changer then TEST_UNIT_READY
            (used in the cdrom_check_status function) is used to 
            switch CDs instead of LOAD_UNLOAD */
 
 	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
 
-        	if ((slot == 1) || (slot == 2)) {
+        	if ((slot == 1) || (slot == 2))
 			CDROM_STATE_FLAGS (drive)->sanyo_slot = slot;
-		} else if (slot >= 0) {
+		else if (slot >= 0)
 			CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
-		} else {
+		else
 			return 0;
-		}
 
-		return cdrom_check_status (drive, NULL);
+		return cdrom_check_status (drive, reqbuf);
 
-	} else {
+	}
+	else
+#endif /*not STANDARD_ATAPI */
+	{
 
 		/* ATAPI Rev. 2.2+ standard for requesting switching of
                    CDs in a multiplatter device */
@@ -2153,169 +1810,299 @@
 }
 
 
-int ide_cdrom_ioctl (ide_drive_t *drive, struct inode *inode,
-		     struct file *file, unsigned int cmd, unsigned long arg)
+/* This gets the mechanism status per ATAPI draft spec 2.6 */
+static int
+cdrom_read_mech_status (ide_drive_t *drive, char *buf, int buflen,
+			struct atapi_request_sense *reqbuf)
 {
-	struct cdrom_info *info = drive->driver_data;
-	
-	switch (cmd) {
-	case CDROMEJECT: {
-		int stat;
+	struct packet_command pc;
 
-		if (drive->usage > 1)
-			return -EBUSY;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-		stat = cdrom_lockdoor (drive, 0, NULL);
-		if (stat) return stat;
+	pc.buffer = buf;
+	pc.buflen = buflen;
+	pc.c[0] = MECHANISM_STATUS;
+	pc.c[8] = (buflen >> 8);
+	pc.c[9] = (buflen & 0xff);
+	return cdrom_queue_packet_command (drive, &pc);
+}
 
-		return cdrom_eject (drive, 0, NULL);
-	}
 
-	case CDROMCLOSETRAY: {
+/* Read the drive mechanism status and slot table into our internal buffer.
+   If the buffer does not yet exist, allocate it. */
+static int
+cdrom_read_changer_info (ide_drive_t *drive)
+{
+	int nslots;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (info->changer_info)
+		nslots = info->changer_info->hdr.nslots;
+
+	else {
+		struct atapi_mechstat_header mechbuf;
 		int stat;
-		if (drive->usage > 1)
-			return -EBUSY;
 
-		stat = cdrom_eject (drive, 1, NULL);
-		if (stat) return stat;
+		stat = cdrom_read_mech_status (drive,
+					       (char *)&mechbuf,
+					       sizeof (mechbuf),
+					       NULL);
+		if (stat)
+			return stat;
 
-		return cdrom_lockdoor (drive, 1, NULL);
-	}
+		nslots = mechbuf.nslots;
+		info->changer_info =
+			(struct atapi_changer_info *)
+			kmalloc (sizeof (struct atapi_changer_info) +
+				 nslots * sizeof (struct atapi_slot),
+				 GFP_KERNEL);
 
-	case CDROMEJECT_SW: {
-		CDROM_STATE_FLAGS (drive)->eject_on_close = arg;
-		return 0;
+		if (info->changer_info == NULL)
+			return -ENOMEM;
 	}
 
-	case CDROMPAUSE:
-		return cdrom_pause (drive, 1, NULL);
-
-	case CDROMRESUME:
-		return cdrom_pause (drive, 0, NULL);
+	return cdrom_read_mech_status
+		(drive,
+		 (char *)&info->changer_info->hdr,
+		 sizeof (struct atapi_mechstat_header) +
+		 nslots * sizeof (struct atapi_slot),
+		 NULL);
+}
 
-	case CDROMSTART:
-		return cdrom_startstop (drive, 1, NULL);
 
-	case CDROMSTOP: {
-		int stat;
+static
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+			 unsigned int cmd, unsigned long arg)
+			 
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-		stat = cdrom_startstop (drive, 0, NULL);
-		if (stat) return stat;
-		/* pit says the Dolphin needs this. */
-		return cdrom_eject (drive, 1, NULL);
-      }
 
-	case CDROMPLAYMSF: {
+	switch (cmd) {
+	case CDROMREADMODE1:
+	case CDROMREADMODE2: {
 		struct cdrom_msf msf;
-		int stat, lba_start, lba_end;
+		int blocksize, format, stat, lba;
+		struct atapi_toc *toc;
+		char *buf;
+
+		if (cmd == CDROMREADMODE1) {
+			blocksize = CD_FRAMESIZE;
+			format = 2;
+		} else {
+			blocksize = CD_FRAMESIZE_RAW0;
+			format = 3;
+		}
 
-		stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
+		stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
 		if (stat) return stat;
 
-		copy_from_user (&msf, (void *) arg, sizeof(msf));
+		copy_from_user (&msf, (void *)arg, sizeof (msf));
 
-		lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
-					msf.cdmsf_frame0);
-		lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
-				      msf.cdmsf_frame1) + 1;
+		lba = msf_to_lba (msf.cdmsf_min0,
+				  msf.cdmsf_sec0,
+				  msf.cdmsf_frame0);
+	
+		/* Make sure the TOC is up to date. */
+		stat = cdrom_read_toc (drive, NULL);
+		if (stat) return stat;
 
-		if (lba_end <= lba_start) return -EINVAL;
+		toc = info->toc;
 
-		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+		if (lba < 0 || lba >= toc->capacity)
+			return -EINVAL;
+
+		buf = (char *) kmalloc (CD_FRAMESIZE_RAW0, GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+
+		stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize,
+					 NULL);
+		if (stat == 0)
+			copy_to_user ((char *)arg, buf, blocksize);
+
+		kfree (buf);
+		return stat;
 	}
 
-	/* Like just about every other Linux cdrom driver, we ignore the
-	   index part of the request here. */
-	case CDROMPLAYTRKIND: {
-		int stat, lba_start, lba_end;
-		struct cdrom_ti ti;
-		struct atapi_toc_entry *first_toc, *last_toc;
+	/* Read 2352 byte blocks from audio tracks. */
+	case CDROMREADAUDIO: {
+		int stat, lba;
+		struct atapi_toc *toc;
+		struct cdrom_read_audio ra;
+		char *buf;
 
-		stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
+		/* Make sure the TOC is up to date. */
+		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
 
-		copy_from_user (&ti, (void *) arg, sizeof(ti));
+		toc = info->toc;
 
-		stat = cdrom_get_toc_entry (drive, ti.cdti_trk0, &first_toc,
-					    NULL);
-		if (stat) return stat;
-		stat = cdrom_get_toc_entry (drive, ti.cdti_trk1, &last_toc,
-					    NULL);
+		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
 		if (stat) return stat;
 
-		if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
-		lba_start = first_toc->addr.lba;
-		lba_end   = last_toc->addr.lba;
+		copy_from_user (&ra, (void *)arg, sizeof (ra));
 
-		if (lba_end <= lba_start) return -EINVAL;
+		if (ra.nframes < 0 || ra.nframes > toc->capacity)
+			return -EINVAL;
+		else if (ra.nframes == 0)
+			return 0;
 
-		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
-	}
+		stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
+				    ra.nframes * CD_FRAMESIZE_RAW);
+		if (stat) return stat;
 
-	case CDROMREADTOCHDR: {
-		int stat;
-		struct cdrom_tochdr tochdr;
-		struct atapi_toc *toc;
+		if (ra.addr_format == CDROM_MSF)
+			lba = msf_to_lba (ra.addr.msf.minute,
+					  ra.addr.msf.second,
+					  ra.addr.msf.frame);
+		else if (ra.addr_format == CDROM_LBA)
+			lba = ra.addr.lba;
+		else
+			return -EINVAL;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (tochdr));
-		if (stat) return stat;
+		if (lba < 0 || lba >= toc->capacity)
+			return -EINVAL;
 
-		/* Make sure our saved TOC is valid. */
-		stat = cdrom_read_toc (drive, NULL);
-		if (stat) return stat;
+		buf = (char *) kmalloc (CDROM_NBLOCKS_BUFFER*CD_FRAMESIZE_RAW,
+					GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
 
-		toc = info->toc;
-		tochdr.cdth_trk0 = toc->hdr.first_track;
-		tochdr.cdth_trk1 = toc->hdr.last_track;
+		while (ra.nframes > 0) {
+			int this_nblocks = ra.nframes;
+			if (this_nblocks > CDROM_NBLOCKS_BUFFER)
+				this_nblocks = CDROM_NBLOCKS_BUFFER;
+			stat = cdrom_read_block
+				(drive, 1, lba, this_nblocks,
+				 buf, this_nblocks * CD_FRAMESIZE_RAW, NULL);
+			if (stat) break;
 
-		copy_to_user ((void *) arg, &tochdr, sizeof (tochdr));
+			copy_to_user (ra.buf, buf,
+				     this_nblocks * CD_FRAMESIZE_RAW);
+			ra.buf += this_nblocks * CD_FRAMESIZE_RAW;
+			ra.nframes -= this_nblocks;
+			lba += this_nblocks;
+		}
 
+		kfree (buf);
 		return stat;
 	}
 
-	case CDROMREADTOCENTRY: {
+ 	case CDROMSETSPINDOWN: {
+ 		char spindown;
+ 		char buffer[16];
+ 		int stat;
+ 
+ 		stat = verify_area (VERIFY_READ, (void *) arg,
+ 				    sizeof (char));
+ 		if (stat) return stat;
+ 
+ 		copy_from_user (&spindown, (void *) arg, sizeof(char));
+ 
+ 		stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
+ 					 sizeof (buffer), NULL);
+ 		if (stat) return stat;
+
+ 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+ 		return cdrom_mode_select (drive, PAGE_CDROM, buffer,
+ 					  sizeof (buffer), NULL);			
+ 	} 
+ 
+ 	case CDROMGETSPINDOWN: {
+ 		char spindown;
+ 		char buffer[16];
+ 		int stat;
+ 
+ 		stat = verify_area (VERIFY_WRITE, (void *) arg,
+                                    sizeof (char));
+ 		if (stat) return stat;
+ 
+ 		stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
+                                         sizeof (buffer), NULL);
+ 		if (stat) return stat;
+ 
+ 		spindown = buffer[11] & 0x0f;
+ 
+ 		copy_to_user ((void *) arg, &spindown, sizeof (char));
+ 
+ 		return 0;
+ 	}
+  
+#ifdef ALLOW_TEST_PACKETS
+	case 0x1234: {
 		int stat;
-		struct cdrom_tocentry tocentry;
-		struct atapi_toc_entry *toce;
+		struct packet_command pc;
+		int len, lena;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (tocentry));
-		if (stat) return stat;
+		memset (&pc, 0, sizeof (pc));
 
-		copy_from_user (&tocentry, (void *) arg, sizeof (tocentry));
+		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
+		if (stat) return stat;
+		copy_from_user (&pc.c, (void *) arg, sizeof (pc.c));
+		arg += sizeof (pc.c);
 
-		stat = cdrom_get_toc_entry (drive, tocentry.cdte_track, &toce,
-					    NULL);
+		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
 		if (stat) return stat;
+		copy_from_user (&len, (void *) arg , sizeof (len));
+		arg += sizeof (len);
 
-		tocentry.cdte_ctrl = toce->control;
-		tocentry.cdte_adr  = toce->adr;
+		lena = len;
+		if (lena  < 0) lena = -lena;
 
-		if (tocentry.cdte_format == CDROM_MSF) {
-			/* convert to MSF */
-			lba_to_msf (toce->addr.lba,
-				    &tocentry.cdte_addr.msf.minute,
-				    &tocentry.cdte_addr.msf.second,
-				    &tocentry.cdte_addr.msf.frame);
-		} else
-			tocentry.cdte_addr.lba = toce->addr.lba;
+		{
+			char buf[lena];
+			if (len > 0) {
+				stat = verify_area (VERIFY_WRITE,
+						    (void *) arg, len);
+				if (stat) return stat;
+			}
+			else if (len < 0) {
+				stat = verify_area (VERIFY_READ,
+						    (void *) arg, -len);
+				if (stat) return stat;
+				copy_from_user (buf, (void*)arg, -len);
+			}
+
+			if (len != 0) {
+				pc.buflen = len;
+				pc.buffer = buf;
+			}
 
-		copy_to_user ((void *) arg, &tocentry, sizeof (tocentry));
+			stat = cdrom_queue_packet_command (drive, &pc);
+
+			if (len > 0)
+				copy_to_user ((void *)arg, buf, len);
+		}
 
 		return stat;
 	}
+#endif
+
+	default:
+		return -EINVAL;
+	}
+
+}
+
+
+
+static
+int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
+			   unsigned int cmd, void *arg)
+			   
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
+	switch (cmd) {
 	case CDROMSUBCHNL: {
 		struct atapi_cdrom_subchnl scbuf;
 		int stat;
-		struct cdrom_subchnl subchnl;
-
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (subchnl));
-		if (stat) return stat;
-
-		copy_from_user (&subchnl, (void *) arg, sizeof (subchnl));
+		struct cdrom_subchnl *subchnl = (struct cdrom_subchnl *)arg;
 
 		stat = cdrom_read_subchannel (drive, 1, /* current position */
 					      (char *)&scbuf, sizeof (scbuf),
@@ -2331,401 +2118,427 @@
 			scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
 #endif /* not STANDARD_ATAPI */
 
-		if (subchnl.cdsc_format == CDROM_MSF) {
-			subchnl.cdsc_absaddr.msf.minute =
-				scbuf.acdsc_absaddr.msf.minute;
-			subchnl.cdsc_absaddr.msf.second =
-				scbuf.acdsc_absaddr.msf.second;
-			subchnl.cdsc_absaddr.msf.frame =
-				scbuf.acdsc_absaddr.msf.frame;
-
-			subchnl.cdsc_reladdr.msf.minute =
-				scbuf.acdsc_reladdr.msf.minute;
-			subchnl.cdsc_reladdr.msf.second =
-				scbuf.acdsc_reladdr.msf.second;
-			subchnl.cdsc_reladdr.msf.frame =
-				scbuf.acdsc_reladdr.msf.frame;
-		} else {
-			subchnl.cdsc_absaddr.lba =
-				msf_to_lba (scbuf.acdsc_absaddr.msf.minute,
-					    scbuf.acdsc_absaddr.msf.second,
-					    scbuf.acdsc_absaddr.msf.frame);
-			subchnl.cdsc_reladdr.lba =
-				msf_to_lba (scbuf.acdsc_reladdr.msf.minute,
-					    scbuf.acdsc_reladdr.msf.second,
-					    scbuf.acdsc_reladdr.msf.frame);
-		}
-
-		subchnl.cdsc_audiostatus = scbuf.acdsc_audiostatus;
-		subchnl.cdsc_ctrl = scbuf.acdsc_ctrl;
-		subchnl.cdsc_trk  = scbuf.acdsc_trk;
-		subchnl.cdsc_ind  = scbuf.acdsc_ind;
+		subchnl->cdsc_absaddr.msf.minute =
+			scbuf.acdsc_absaddr.msf.minute;
+		subchnl->cdsc_absaddr.msf.second =
+			scbuf.acdsc_absaddr.msf.second;
+		subchnl->cdsc_absaddr.msf.frame =
+			scbuf.acdsc_absaddr.msf.frame;
+
+		subchnl->cdsc_reladdr.msf.minute =
+			scbuf.acdsc_reladdr.msf.minute;
+		subchnl->cdsc_reladdr.msf.second =
+			scbuf.acdsc_reladdr.msf.second;
+		subchnl->cdsc_reladdr.msf.frame =
+			scbuf.acdsc_reladdr.msf.frame;
+
+		subchnl->cdsc_audiostatus = scbuf.acdsc_audiostatus;
+		subchnl->cdsc_ctrl = scbuf.acdsc_ctrl;
+		subchnl->cdsc_trk  = scbuf.acdsc_trk;
+		subchnl->cdsc_ind  = scbuf.acdsc_ind;
 
-		copy_to_user ((void *) arg, &subchnl, sizeof (subchnl));
-
-		return stat;
+		return 0;
 	}
 
-	case CDROMVOLCTRL: {
-		struct cdrom_volctrl volctrl;
-		char buffer[24], mask[24];
+	case CDROMREADTOCHDR: {
 		int stat;
+		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+		struct atapi_toc *toc;
 
-		stat = verify_area (VERIFY_READ, (void *) arg,
-				    sizeof (volctrl));
+		/* Make sure our saved TOC is valid. */
+		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
-		copy_from_user (&volctrl, (void *) arg, sizeof (volctrl));
 
-		stat = cdrom_mode_sense (drive, 0x0e, 0, buffer,
-					 sizeof (buffer), NULL);
-		if (stat) return stat;
-		stat = cdrom_mode_sense (drive, 0x0e, 1, mask,
-					 sizeof (buffer), NULL);
+		toc = info->toc;
+		tochdr->cdth_trk0 = toc->hdr.first_track;
+		tochdr->cdth_trk1 = toc->hdr.last_track;
+
+		return 0;
+	}
+
+	case CDROMREADTOCENTRY: {
+		int stat;
+		struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
+		struct atapi_toc_entry *toce;
+
+		stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce,
+					    NULL);
 		if (stat) return stat;
 
-		buffer[1] = buffer[2] = 0;
+		tocentry->cdte_ctrl = toce->control;
+		tocentry->cdte_adr  = toce->adr;
+		tocentry->cdte_format = CDROM_LBA;
+		tocentry->cdte_addr.lba = toce->addr.lba;
+
+		return 0;
+	}
 
-		buffer[17] = volctrl.channel0 & mask[17];
-		buffer[19] = volctrl.channel1 & mask[19];
-		buffer[21] = volctrl.channel2 & mask[21];
-		buffer[23] = volctrl.channel3 & mask[23];
+	case CDROMPLAYMSF: {
+		struct cdrom_msf *msf = (struct cdrom_msf *) arg;
+		int lba_start, lba_end;
 
-		return cdrom_mode_select (drive, 0x0e, buffer,
-					  sizeof (buffer), NULL);
+		lba_start = msf_to_lba (msf->cdmsf_min0, msf->cdmsf_sec0,
+					msf->cdmsf_frame0);
+		lba_end = msf_to_lba (msf->cdmsf_min1, msf->cdmsf_sec1,
+				      msf->cdmsf_frame1) + 1;
+
+		if (lba_end <= lba_start) return -EINVAL;
+
+		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
 	}
 
-	case CDROMVOLREAD: {
-		struct cdrom_volctrl volctrl;
-		char buffer[24];
-		int stat;
+	/* Like just about every other Linux cdrom driver, we ignore the
+	   index part of the request here. */
+	case CDROMPLAYTRKIND: {
+		int stat, lba_start, lba_end;
+		struct cdrom_ti *ti = (struct cdrom_ti *)arg;
+		struct atapi_toc_entry *first_toc, *last_toc;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (volctrl));
+		stat = cdrom_get_toc_entry (drive, ti->cdti_trk0, &first_toc,
+					    NULL);
 		if (stat) return stat;
-
-		stat = cdrom_mode_sense (drive, 0x0e, 0, buffer,
-					 sizeof (buffer), NULL);
+		stat = cdrom_get_toc_entry (drive, ti->cdti_trk1, &last_toc,
+					    NULL);
 		if (stat) return stat;
 
-		volctrl.channel0 = buffer[17];
-		volctrl.channel1 = buffer[19];
-		volctrl.channel2 = buffer[21];
-		volctrl.channel3 = buffer[23];
+		if (ti->cdti_trk1 != CDROM_LEADOUT) ++last_toc;
+		lba_start = first_toc->addr.lba;
+		lba_end   = last_toc->addr.lba;
 
-		copy_to_user ((void *) arg, &volctrl, sizeof (volctrl));
+		if (lba_end <= lba_start) return -EINVAL;
 
-		return 0;
+		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
 	}
 
-	case CDROMMULTISESSION: {
-		struct cdrom_multisession ms_info;
-		struct atapi_toc *toc;
+	case CDROMVOLCTRL: {
+		struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
+		char buffer[24], mask[24];
 		int stat;
 
-		stat = verify_area (VERIFY_WRITE, (void *)arg,
-				    sizeof (ms_info));
+		stat = cdrom_mode_sense (drive, PAGE_AUDIO, 0, buffer,
+					 sizeof (buffer), NULL);
+		if (stat) return stat;
+		stat = cdrom_mode_sense (drive, PAGE_AUDIO, 1, mask,
+					 sizeof (buffer), NULL);
 		if (stat) return stat;
 
-		copy_from_user (&ms_info, (void *)arg, sizeof (ms_info));
+		buffer[1] = buffer[2] = 0;
 
-		/* Make sure the TOC information is valid. */
-		stat = cdrom_read_toc (drive, NULL);
-		if (stat) return stat;
+		buffer[17] = volctrl->channel0 & mask[17];
+		buffer[19] = volctrl->channel1 & mask[19];
+		buffer[21] = volctrl->channel2 & mask[21];
+		buffer[23] = volctrl->channel3 & mask[23];
 
-		toc = info->toc;
+		return cdrom_mode_select (drive, PAGE_AUDIO, buffer,
+					  sizeof (buffer), NULL);
+	}
 
-		if (ms_info.addr_format == CDROM_MSF)
-			lba_to_msf (toc->last_session_lba,
-				    &ms_info.addr.msf.minute,
-				    &ms_info.addr.msf.second,
-				    &ms_info.addr.msf.frame);
-		else if (ms_info.addr_format == CDROM_LBA)
-			ms_info.addr.lba = toc->last_session_lba;
-		else
-			return -EINVAL;
+	case CDROMVOLREAD: {
+		struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
+		char buffer[24];
+		int stat;
 
-		ms_info.xa_flag = toc->xa_flag;
+		stat = cdrom_mode_sense (drive, PAGE_AUDIO, 0, buffer,
+					 sizeof (buffer), NULL);
+		if (stat) return stat;
 
-		copy_to_user ((void *)arg, &ms_info, sizeof (ms_info));
+		volctrl->channel0 = buffer[17];
+		volctrl->channel1 = buffer[19];
+		volctrl->channel2 = buffer[21];
+		volctrl->channel3 = buffer[23];
 
 		return 0;
 	}
 
-	/* Read 2352 byte blocks from audio tracks. */
-	case CDROMREADAUDIO: {
-		int stat, lba;
-		struct atapi_toc *toc;
-		struct cdrom_read_audio ra;
-		char *buf;
-
-		/* Make sure the TOC is up to date. */
-		stat = cdrom_read_toc (drive, NULL);
-		if (stat) return stat;
+	case CDROMSTART:
+		return cdrom_startstop (drive, 1, NULL);
 
-		toc = info->toc;
+	case CDROMSTOP: {
+		int stat;
 
-		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
+		stat = cdrom_startstop (drive, 0, NULL);
 		if (stat) return stat;
+		/* pit says the Dolphin needs this. */
+		return cdrom_eject (drive, 1, NULL);
+	}
 
-		copy_from_user (&ra, (void *)arg, sizeof (ra));
+	case CDROMPAUSE:
+		return cdrom_pause (drive, 1, NULL);
 
-		if (ra.nframes < 0 || ra.nframes > toc->capacity)
-			return -EINVAL;
-		else if (ra.nframes == 0)
-			return 0;
+	case CDROMRESUME:
+		return cdrom_pause (drive, 0, NULL);
 
-		stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
-				    ra.nframes * CD_FRAMESIZE_RAW);
-		if (stat) return stat;
 
-		if (ra.addr_format == CDROM_MSF)
-			lba = msf_to_lba (ra.addr.msf.minute,
-					  ra.addr.msf.second,
-					  ra.addr.msf.frame);
-		else if (ra.addr_format == CDROM_LBA)
-			lba = ra.addr.lba;
-		else
-			return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+}
 
-		if (lba < 0 || lba >= toc->capacity)
-			return -EINVAL;
 
-		buf = (char *) kmalloc (CDROM_NBLOCKS_BUFFER*CD_FRAMESIZE_RAW,
-					GFP_KERNEL);
-		if (buf == NULL)
-			return -ENOMEM;
+static
+int ide_cdrom_reset (struct cdrom_device_info *cdi)
+{
 
-		while (ra.nframes > 0) {
-			int this_nblocks = ra.nframes;
-			if (this_nblocks > CDROM_NBLOCKS_BUFFER)
-				this_nblocks = CDROM_NBLOCKS_BUFFER;
-			stat = cdrom_read_block
-				(drive, 1, lba, this_nblocks,
-				 buf, this_nblocks * CD_FRAMESIZE_RAW, NULL);
-			if (stat) break;
+/* This doesn't work reliably yet, and so it is currently just a stub. */
 
-			copy_to_user (ra.buf, buf,
-				     this_nblocks * CD_FRAMESIZE_RAW);
-			ra.buf += this_nblocks * CD_FRAMESIZE_RAW;
-			ra.nframes -= this_nblocks;
-			lba += this_nblocks;
-		}
+#if 0 
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct request req;
+	ide_init_drive_cmd (&req);
+	req.cmd = RESET_DRIVE_COMMAND;
+	return ide_do_drive_cmd (drive, &req, ide_wait);
+#endif
 
-		kfree (buf);
-		return stat;
-	}
+/* For now, just return 0, as if things worked...	*/
+	return 0;
 
-	case CDROMREADMODE1:
-	case CDROMREADMODE2: {
-		struct cdrom_msf msf;
-		int blocksize, format, stat, lba;
-		struct atapi_toc *toc;
-		char *buf;
 
-		if (cmd == CDROMREADMODE1) {
-			blocksize = CD_FRAMESIZE;
-			format = 2;
-		} else {
-			blocksize = CD_FRAMESIZE_RAW0;
-			format = 3;
-		}
+}
 
-		stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
-		if (stat) return stat;
 
-		copy_from_user (&msf, (void *)arg, sizeof (msf));
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 
-		lba = msf_to_lba (msf.cdmsf_min0,
-				  msf.cdmsf_sec0,
-				  msf.cdmsf_frame0);
-	
-		/* Make sure the TOC is up to date. */
-		stat = cdrom_read_toc (drive, NULL);
+	if (position) {
+		int stat = cdrom_lockdoor (drive, 0, NULL);
 		if (stat) return stat;
+	}
 
-		toc = info->toc;
-
-		if (lba < 0 || lba >= toc->capacity)
-			return -EINVAL;
-
-		buf = (char *) kmalloc (CD_FRAMESIZE_RAW0, GFP_KERNEL);
-		if (buf == NULL)
-			return -ENOMEM;
+	return cdrom_eject (drive, !position, NULL);
+}
 
-		stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize,
-					 NULL);
-		if (stat == 0)
-			copy_to_user ((char *)arg, buf, blocksize);
 
-		kfree (buf);
-		return stat;
-	}
+static
+int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	return cdrom_lockdoor (drive, lock, NULL);
+}
 
-	case CDROM_GET_UPC: {
-		int stat;
-		char mcnbuf[24];
-		struct cdrom_mcn mcn;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (mcn));
-		if (stat) return stat;
+static
+int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-		stat = cdrom_read_subchannel (drive, 2, /* get MCN */
-					      mcnbuf, sizeof (mcnbuf),
-					      NULL);
-		if (stat) return stat;
+	struct atapi_request_sense my_reqbuf;
+	int stat;
+	int nslots, curslot;
 
-		memcpy (mcn.medium_catalog_number, mcnbuf+9,
-			sizeof (mcn.medium_catalog_number)-1);
-		mcn.medium_catalog_number[sizeof (mcn.medium_catalog_number)-1]
-			= '\0';
+	if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) {
+		printk ("%s: Not a changer.", drive->name);
+		return -EINVAL;
+	}
 
-		copy_to_user ((void *) arg, &mcn, sizeof (mcn));
+#if ! STANDARD_ATAPI
+	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+		nslots = 3;
+		curslot = CDROM_STATE_FLAGS (drive)->sanyo_slot;
+		if (curslot == 3)
+			curslot = 0;
+	}
+	else
+#endif /* not STANDARD_ATAPI */
+	{
+		stat = cdrom_read_changer_info (drive);
+		if (stat)
+			return stat;
 
-		return stat;
+		nslots = info->changer_info->hdr.nslots;
+		curslot = info->changer_info->hdr.curslot;
 	}
 
-	case CDROMLOADFROMSLOT:
-		printk ("%s: Use CDROM_SELECT_DISC "
-			" instead of CDROMLOADFROMSLOT.\n", drive->name);
-		/* Fall through. */
+	if (slot == curslot)
+		return curslot;
 
-	case CDROM_SELECT_DISC: {
-		struct atapi_request_sense my_reqbuf;
-		int stat;
+	if (slot == CDSL_CURRENT)
+		return curslot;
 
-		if (drive->usage > 1)
-			return -EBUSY;
+	if (slot != CDSL_NONE && (slot < 0 || slot >= nslots))
+		return -EINVAL;
 
-		(void) cdrom_load_unload (drive, -1, NULL);
+	if (drive->usage > 1)
+		return -EBUSY;
 
-                cdrom_saw_media_change (drive);
-                if (arg == -1) {
-			(void) cdrom_lockdoor (drive, 0, NULL);
-			return 0;
+	stat = cdrom_check_status (drive, &my_reqbuf);
+	if (stat && my_reqbuf.sense_key == NOT_READY)
+		return (-ENOENT);
+
+	if (slot == CDSL_NONE) {
+		(void) cdrom_load_unload (drive, -1, NULL);
+		cdrom_saw_media_change (drive);
+		(void) cdrom_lockdoor (drive, 0, NULL);
+		return 0;
+	}
+	else {
+		if (
+#if ! STANDARD_ATAPI
+		    CDROM_STATE_FLAGS (drive)->sanyo_slot == 0 &&
+#endif
+		    info->changer_info->slots[slot].disc_present
+		    == 0) {
+			printk ("%s: Requested slot does not contain a CD.\n",
+				drive->name);
+			return (-ENOENT);
 		}
-		(void) cdrom_load_unload (drive, (int)arg, NULL);
 
+		stat = cdrom_load_unload (drive, slot, NULL);
+		cdrom_saw_media_change (drive);
+		if (stat)
+			return stat;
+			
 		stat = cdrom_check_status (drive, &my_reqbuf);
-		if (stat && my_reqbuf.sense_key == NOT_READY) {
+		if (stat && my_reqbuf.sense_key == NOT_READY)
 			return -ENOENT;
-		}
 
-		/* And try to read the TOC information now. */
-		return cdrom_read_toc (drive, &my_reqbuf);
+		if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) {
+			stat = cdrom_read_toc (drive, &my_reqbuf);
+			if (stat)
+				return stat;
+			return slot;
+		}
+		else
+			return stat;
 	}
+}
 
-#if 0 /* Doesn't work reliably yet. */
-	case CDROMRESET: {
-		struct request req;
-		ide_init_drive_cmd (&req);
-		req.cmd = RESET_DRIVE_COMMAND;
-		return ide_do_drive_cmd (drive, &req, ide_wait);
+
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (slot_nr == CDSL_CURRENT) {
+
+		struct atapi_request_sense my_reqbuf;
+		int stat = cdrom_check_status (drive, &my_reqbuf);
+		if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION)
+			return CDS_DISC_OK;
+
+		if (my_reqbuf.sense_key == NOT_READY) {
+			/* With my NEC260, at least, we can't distinguish
+			   between tray open and tray closed but no disc
+			   inserted. */
+			return CDS_TRAY_OPEN; 
+		}
+
+		return CDS_DRIVE_NOT_READY;
 	}
-#endif
 
- 
-#ifdef TEST
-	case 0x1234: {
-		int stat;
-		struct packet_command pc;
-		int len, lena;
+#if ! STANDARD_ATAPI
+	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0)
+		return CDS_NO_INFO;
+#endif /* not STANDARD_ATAPI */
 
-		memset (&pc, 0, sizeof (pc));
+	else {
+		struct atapi_changer_info *ci;
+		int stat = cdrom_read_changer_info (drive);
+		if (stat < 0)
+			return stat;
+		ci = info->changer_info;
 
-		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
-		if (stat) return stat;
-		copy_from_user (&pc.c, (void *) arg, sizeof (pc.c));
-		arg += sizeof (pc.c);
+		if (ci->slots[slot_nr].disc_present)
+			return CDS_DISC_OK;
+		else
+			return CDS_NO_DISC;
+	}
+}
 
-		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
-		if (stat) return stat;
-		copy_from_user (&len, (void *) arg , sizeof (len));
-		arg += sizeof (len);
 
-		if (len > 0) {
-			stat = verify_area (VERIFY_WRITE, (void *) arg, len);
-			if (stat) return stat;
-		}
+static
+int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
+				struct cdrom_multisession *ms_info)
+{
+	int stat;
+	struct atapi_toc *toc;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-		lena = len;
-		if (lena  < 0) lena = 0;
+	/* Make sure the TOC information is valid. */
+	stat = cdrom_read_toc (drive, NULL);
+	if (stat) return stat;
 
-		{
-			char buf[lena];
-			if (len > 0) {
-				pc.buflen = len;
-				pc.buffer = buf;
-			}
+	toc = info->toc;
+	ms_info->addr.lba = toc->last_session_lba;
+	ms_info->xa_flag = toc->xa_flag;
 
-			stat = cdrom_queue_packet_command (drive, &pc);
+	return 0;
+}
 
-			if (len > 0)
-				copy_to_user ((void *)arg, buf, len);
-		}
 
-		return stat;
-	}
-#endif
+static
+int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
+		       struct cdrom_mcn *mcn_info)
+{
+	int stat;
+	char mcnbuf[24];
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 
-	default:
-		return -EPERM;
-	}
+	stat = cdrom_read_subchannel (drive, 2, /* get MCN */
+				      mcnbuf, sizeof (mcnbuf),
+				      NULL);
+	if (stat) return stat;
+
+	memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
+		sizeof (mcn_info->medium_catalog_number)-1);
+	mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
+		= '\0';
 
+	return 0;
 }
 
 
-
+
 /****************************************************************************
  * Other driver requests (open, close, check media change).
  */
 
-int ide_cdrom_check_media_change (ide_drive_t *drive)
+static
+int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
+				       int slot_nr)
 {
-	int retval;
-
-	(void) cdrom_check_status (drive, NULL);
-
-	retval = CDROM_STATE_FLAGS (drive)->media_changed;
-	CDROM_STATE_FLAGS (drive)->media_changed = 0;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-	return retval;
-}
+	int retval;
 
+	if (slot_nr == CDSL_CURRENT) {
+		(void) cdrom_check_status (drive, NULL);
+		retval = CDROM_STATE_FLAGS (drive)->media_changed;
+		CDROM_STATE_FLAGS (drive)->media_changed = 0;
+	}
 
-int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
-{
-  /* no write access */
-	if (fp->f_mode & 2) {
-		--drive->usage;
-		return -EROFS;
+#if ! STANDARD_ATAPI
+	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+		retval = 0;
 	}
+#endif /* not STANDARD_ATAPI */
 
-	MOD_INC_USE_COUNT;
+	else {
+		struct atapi_changer_info *ci;
+		int stat = cdrom_read_changer_info (drive);
+		if (stat < 0)
+			return stat;
+		ci = info->changer_info;
 
-	/* If this is the first open, check the drive status. */
-	if (drive->usage == 1) {
-		int stat;
-		struct atapi_request_sense my_reqbuf;
-		my_reqbuf.sense_key = 0;
+		/* This test may be redundant with cdrom.c. */
+		if (slot_nr < 0 || slot_nr >= ci->hdr.nslots)
+			return -EINVAL;
 
-		/* Get the drive status. */
-		stat = cdrom_check_status (drive, &my_reqbuf);
+		retval = ci->slots[slot_nr].change;
+	}
 
-		/* If the tray is open, try to close it. */
-		if (stat && my_reqbuf.sense_key == NOT_READY) {
-			cdrom_eject (drive, 1, &my_reqbuf);
-			stat = cdrom_check_status (drive, &my_reqbuf);
-		}
+	return retval;
+}
 
-		/* If things worked ok, lock the door and read the
-		   TOC information. */
-		if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) {
-			(void) cdrom_lockdoor (drive, 1, &my_reqbuf);
-			(void) cdrom_read_toc (drive, &my_reqbuf);
-		}
-	}
 
+static
+int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
+{
 	return 0;
 }
 
@@ -2734,32 +2547,116 @@
  * Close down the device.  Invalidate all cached blocks.
  */
 
-void ide_cdrom_release (struct inode *inode, struct file *file,
-			ide_drive_t *drive)
+static
+void ide_cdrom_release_real (struct cdrom_device_info *cdi)
 {
-	if (drive->usage == 0) {
-		invalidate_buffers (inode->i_rdev);
-
-		/* Unlock the door. */
-		(void) cdrom_lockdoor (drive, 0, NULL);
-
-		/* Do an eject if we were requested to do so. */
-		if (CDROM_STATE_FLAGS (drive)->eject_on_close)
-			(void) cdrom_eject (drive, 0, NULL);
-	}
-	MOD_DEC_USE_COUNT;
 }
 
 
-
+
 /****************************************************************************
  * Device initialization.
  */
+static
+struct cdrom_device_ops ide_cdrom_dops = {
+	ide_cdrom_open_real,    /* open */
+	ide_cdrom_release_real, /* release */
+	ide_cdrom_drive_status, /* drive_status */
+	0, /* disc_status */
+	ide_cdrom_check_media_change_real, /* media_changed */
+	ide_cdrom_tray_move,    /* tray_move */
+	ide_cdrom_lock_door,    /* lock_door */
+	0, /* select_speed */
+	ide_cdrom_select_disc, /* select_disc */
+	ide_cdrom_get_last_session, /* get_last_session */
+	ide_cdrom_get_mcn, /* get_mcn */
+	ide_cdrom_reset, /* reset */
+	ide_cdrom_audio_ioctl, /* audio_ioctl */
+	ide_cdrom_dev_ioctl,   /* dev_ioctl */
+	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK
+	| CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
+	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO,  /* capability */
+	0 /* n_minors */
+};
+
+
+static int ide_cdrom_register (ide_drive_t *drive, int nslots)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *devinfo = &info->devinfo;
+	int minor = (drive->select.b.unit)<<PARTN_BITS;
+
+	devinfo->dev = MKDEV (HWIF(drive)->major, minor);
+	devinfo->ops = &ide_cdrom_dops;
+	devinfo->mask = 0;
+	*(int *)&devinfo->speed = 0;
+	*(int *)&devinfo->capacity = nslots;
+	devinfo->handle = (void *) drive;
+
+	return register_cdrom (devinfo, drive->name);
+}
+
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+	int stat, nslots;
+ 	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
+
+	nslots = 0;
+
+	if (CDROM_CONFIG_FLAGS (drive)->nec260)
+		return nslots;
+
+	stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
+				 (char *)&buf, sizeof (buf), NULL);
+	if (stat)
+		return nslots;
+
+	if (buf.cap.lock == 0)
+		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+
+#if ! STANDARD_ATAPI
+	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+		nslots = 3;
+	}
+
+	else
+#endif /* not STANDARD_ATAPI */
+	if (buf.cap.mechtype == mechtype_individual_changer ||
+	    buf.cap.mechtype == mechtype_cartridge_changer) {
+		struct atapi_mechstat_header mechbuf;
+
+		stat = cdrom_read_mech_status (drive, (char*)&mechbuf,
+					       sizeof (mechbuf), NULL);
+		if (!stat) {
+			CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+			CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1;
+			nslots = mechbuf.nslots;
+		}
+	}
+
+	if (CDROM_CONFIG_FLAGS (drive)->is_changer)
+		printk (" %s: ATAPI CDROM changer with %d slots\n", 
+			drive->name, nslots);
+
+	return nslots;
+}
+
 
 void ide_cdrom_setup (ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
-		
+	int nslots;
+
+	kdev_t dev = MKDEV (HWIF (drive)->major,
+			    drive->select.b.unit << PARTN_BITS);
+
+	set_device_ro (dev, 1);
 	blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] =
 		CD_FRAMESIZE;
 
@@ -2770,27 +2667,27 @@
 	CDROM_STATE_FLAGS (drive)->toc_valid     = 0;
 	CDROM_STATE_FLAGS (drive)->door_locked   = 0;
 
-	/* Turn this off by default, since many people don't like it. */
-	CDROM_STATE_FLAGS (drive)->eject_on_close= 0;
-
 #if NO_DOOR_LOCKING
 	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
 #else
 	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
 #endif
 
-	/* by default Sanyo 3 CD changer support is turned off and
-           ATAPI Rev 2.2+ standard support for CD changers is used */
-	CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
-
 	if (drive->id != NULL)
 		CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
 			((drive->id->config & 0x0060) == 0x20);
 	else
 		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
 
+	CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
+	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
+
 #if ! STANDARD_ATAPI
-	CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
+	/* by default Sanyo 3 CD changer support is turned off and
+           ATAPI Rev 2.2+ standard support for CD changers is used */
+	CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
+
+	CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
 	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0;
@@ -2818,10 +2715,13 @@
 		else if (strcmp (drive->id->model,
 				 "NEC CD-ROM DRIVE:260") == 0 &&
 			 strcmp (drive->id->fw_rev, "1.01") == 0) {
-			/* Old NEC260 (not R). */
+			/* Old NEC260 (not R).
+			   This drive was released before the 1.2 version
+			   of the spec. */
 			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->nec260         = 1;
 		}
 
 		else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
@@ -2832,7 +2732,7 @@
 		}
 
 		/* Sanyo 3 CD changer uses a non-standard command 
-                   for CD changing */
+                   for CD changing. */
                 else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
                          (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0)) {
 			/* uses CD in slot 0 when value is set to 3 */
@@ -2846,8 +2746,42 @@
 	info->sector_buffer     = NULL;
 	info->sector_buffered   = 0;
 	info->nsectors_buffered = 0;
+	info->changer_info      = NULL;
+
+	nslots = ide_cdrom_probe_capabilities (drive);
+
+	if (ide_cdrom_register (drive, nslots))
+		printk ("%s: Can't register\n", drive->name);
+}
+
+/* Forwarding functions to generic routines. */
+
+int ide_cdrom_ioctl (ide_drive_t *drive,
+		     struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	return cdrom_fops.ioctl (inode, file, cmd, arg);
+}
+
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+	return cdrom_fops.open (ip, fp);
+}
+
+void ide_cdrom_release (struct inode *inode, struct file *file,
+			ide_drive_t *drive)
+{
+	cdrom_fops.release (inode, file);
+}
+
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+	return cdrom_fops.check_media_change
+		(MKDEV (HWIF (drive)->major,
+			(drive->select.b.unit)<<PARTN_BITS));
 }
 
+
 int ide_cdrom_cleanup(ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
@@ -2876,7 +2810,8 @@
 	1,				/* supports_dma */
 	ide_cdrom_cleanup,		/* cleanup */
 	ide_do_rw_cdrom,		/* do_request */
-	NULL,				/* ??? or perhaps cdrom_end_request? */
+	NULL,				/* ??? or perhaps
+	cdrom_end_request? */
 	ide_cdrom_ioctl,		/* ioctl */
 	ide_cdrom_open,			/* open */
 	ide_cdrom_release,		/* release */
@@ -2886,6 +2821,27 @@
 	NULL				/* special */
 };
 
+
+#ifdef MODULE
+int init_module (void)
+{
+	return ide_cdrom_init();
+}
+
+void cleanup_module(void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+
+	while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
+		if (ide_cdrom_cleanup (drive)) {
+			printk ("%s: cleanup_module() called while still busy\n", drive->name);
+			failed++;
+		}
+	ide_unregister_module (&ide_cdrom_module);
+}
+#endif /* MODULE */
+ 
 int ide_cdrom_init (void)
 {
 	ide_drive_t *drive;
@@ -2913,39 +2869,6 @@
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
-
-#ifdef MODULE
-int init_module (void)
-{
-	return ide_cdrom_init();
-}
-
-void cleanup_module(void)
-{
-	ide_drive_t *drive;
-	int failed = 0;
-
-	while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
-		if (ide_cdrom_cleanup (drive)) {
-			printk ("%s: cleanup_module() called while still busy\n", drive->name);
-			failed++;
-		}
-	ide_unregister_module (&ide_cdrom_module);
-}
-#endif /* MODULE */
-
-
-/*
- * TODO (for 2.1?):
- *  Avoid printing error messages for expected errors from the drive.
- *  Integrate with generic cdrom driver.
- *  Query the drive to find what features are available
- *   before trying to use them.
- *  Integrate spindown time adjustment patch.
- *  CDROMRESET ioctl.
- *  Better support for changers.
- */
-
 
 
 /*==========================================================================*/
diff -u --recursive --new-file v2.1.8/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.1.8/linux/drivers/block/ide-cd.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/ide-cd.h	Tue Nov 12 10:32:49 1996
@@ -0,0 +1,553 @@
+#ifndef _IDE_CD_H
+#define _IDE_CD_H
+/*
+ *  linux/drivers/block/ide_modes.h
+ *
+ *  Copyright (C) 1996  Erik Andersen
+ */
+
+/* Turn this on to have the driver print out the meanings of the
+   ATAPI error codes.  This will use up additional kernel-space
+   memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 0
+#endif
+
+
+/* Turn this on to have the driver print out the meanings of the
+   ATAPI error codes.  This will use up additional kernel-space
+   memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 0
+#endif
+
+
+/* Turning this on will remove code to work around various nonstandard
+   ATAPI implementations.  If you know your drive follows the standard,
+   this will give you a slightly smaller kernel. */
+
+#ifndef STANDARD_ATAPI
+#define STANDARD_ATAPI 0
+#endif
+
+
+/* Turning this on will disable the door-locking functionality.
+   This is apparently needed for supermount. */
+
+#ifndef NO_DOOR_LOCKING
+#define NO_DOOR_LOCKING 0
+#endif
+
+
+/* Size of buffer to allocate, in blocks, for audio reads. */
+
+#ifndef CDROM_NBLOCKS_BUFFER
+#define CDROM_NBLOCKS_BUFFER 8
+#endif
+
+
+/************************************************************************/
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* special command codes for strategy routine. */
+#define PACKET_COMMAND        4315
+#define REQUEST_SENSE_COMMAND 4316
+#define RESET_DRIVE_COMMAND   4317
+
+/*
+ * For controlling drive spindown time.
+ */
+#define CDROMGETSPINDOWN        0x531d
+#define CDROMSETSPINDOWN        0x531e
+ 
+
+/* Some ATAPI command opcodes (just like SCSI).
+   (Some other cdrom-specific codes are in cdrom.h.) */
+#define TEST_UNIT_READY         0x00
+#define REQUEST_SENSE           0x03
+#define START_STOP              0x1b
+#define ALLOW_MEDIUM_REMOVAL    0x1e
+#define READ_CAPACITY		0x25
+#define READ_10                 0x28
+#define MODE_SENSE_10           0x5a
+#define MODE_SELECT_10          0x55
+#define READ_CD                 0xbe
+
+#define LOAD_UNLOAD             0xa6
+#define MECHANISM_STATUS        0xbd
+
+
+/* Page codes for mode sense/set */
+
+#define PAGE_READERR            0x01
+#define PAGE_CDROM              0x0d
+#define PAGE_AUDIO              0x0e
+#define PAGE_CAPABILITIES       0x2a
+#define PAGE_ALL                0x3f
+
+
+/* ATAPI sense keys (mostly copied from scsi.h). */
+
+#define NO_SENSE                0x00
+#define RECOVERED_ERROR         0x01
+#define NOT_READY               0x02
+#define MEDIUM_ERROR            0x03
+#define HARDWARE_ERROR          0x04
+#define ILLEGAL_REQUEST         0x05
+#define UNIT_ATTENTION          0x06
+#define DATA_PROTECT            0x07
+#define ABORTED_COMMAND         0x0b
+#define MISCOMPARE              0x0e
+
+/* We want some additional flags for cd-rom drives.
+   To save space in the ide_drive_t struct, use some fields which
+   doesn't make sense for cd-roms -- `bios_cyl' and `bios_head'. */
+
+/* Configuration flags.  These describe the capabilities of the drive.
+   They generally do not change after initialization, unless we learn
+   more about the drive from stuff failing. */
+struct ide_cd_config_flags {
+	__u8 drq_interrupt    : 1; /* Device sends an interrupt when ready
+				      for a packet command. */
+	__u8 no_doorlock      : 1; /* Drive cannot lock the door. */
+	__u8 nec260           : 1; /* Drive is a pre-1.2 NEC 260 drive. */
+	__u8 playmsf_as_bcd   : 1; /* PLAYMSF command takes BCD args. */
+	__u8 tocaddr_as_bcd   : 1; /* TOC addresses are in BCD. */
+	__u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
+	__u8 subchan_as_bcd   : 1; /* Subchannel info is in BCD. */
+	__u8 is_changer       : 1; /* Drive is a changer. */
+	__u8 supp_disc_present: 1; /* Changer can report exact contents
+				      of slots. */
+	__u8 reserved         : 7;
+};
+#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_cyl))
+
+ 
+/* State flags.  These give information about the current state of the
+   drive, and will change during normal operation. */
+struct ide_cd_state_flags {
+	__u8 media_changed : 1; /* Driver has noticed a media change. */
+	__u8 toc_valid     : 1; /* Saved TOC information is current. */
+	__u8 door_locked   : 1; /* We think that the drive door is locked. */
+	__u8 sanyo_slot    : 2; /* Sanyo 3 CD changer support */
+	__u8 reserved      : 3;
+};
+#define CDROM_STATE_FLAGS(drive)  ((struct ide_cd_state_flags *)&((drive)->bios_head))
+
+
+struct atapi_request_sense {
+	unsigned char error_code : 7;
+	unsigned char valid      : 1;
+	byte reserved1;
+	unsigned char sense_key  : 4;
+	unsigned char reserved2  : 1;
+	unsigned char ili        : 1;
+	unsigned char reserved3  : 2;
+	byte info[4];
+	byte sense_len;
+	byte command_info[4];
+	byte asc;
+	byte ascq;
+	byte fru;
+	byte sense_key_specific[3];
+};
+
+struct packet_command {
+	char *buffer;
+	int buflen;
+	int stat;
+	struct atapi_request_sense *sense_data;
+	unsigned char c[12];
+};
+
+/* Structure of a MSF cdrom address. */
+struct atapi_msf {
+	byte reserved;
+	byte minute;
+	byte second;
+	byte frame;
+};
+
+/* Space to hold the disk TOC. */
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+	unsigned short toc_length;
+	byte first_track;
+	byte last_track;
+};
+
+struct atapi_toc_entry {
+	byte reserved1;
+	unsigned control : 4;
+	unsigned adr     : 4;
+	byte track;
+	byte reserved2;
+	union {
+		unsigned lba;
+		struct atapi_msf msf;
+	} addr;
+};
+
+struct atapi_toc {
+	int    last_session_lba;
+	int    xa_flag;
+	unsigned capacity;
+	struct atapi_toc_header hdr;
+	struct atapi_toc_entry  ent[MAX_TRACKS+1];
+	  /* One extra for the leadout. */
+};
+
+
+/* This structure is annoyingly close to, but not identical with,
+   the cdrom_subchnl structure from cdrom.h. */
+struct atapi_cdrom_subchnl {
+ 	u_char  acdsc_reserved;
+ 	u_char  acdsc_audiostatus;
+ 	u_short acdsc_length;
+	u_char  acdsc_format;
+
+	u_char  acdsc_adr:	4;
+	u_char  acdsc_ctrl:	4;
+	u_char  acdsc_trk;
+	u_char  acdsc_ind;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_absaddr;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_reladdr;
+};
+
+
+typedef enum {
+	mechtype_caddy = 0,
+	mechtype_tray  = 1,
+	mechtype_popup = 2,
+	mechtype_individual_changer = 4,
+	mechtype_cartridge_changer  = 5
+} mechtype_t;
+
+
+struct atapi_capabilities_page {
+	unsigned page_code           : 6;
+	unsigned reserved1           : 1;
+	unsigned parameters_saveable : 1;
+
+	byte     page_length;
+
+	/* Drive supports read from CD-R discs (orange book, part II) */
+        unsigned cd_r_read           : 1; /* reserved in 1.2 */
+	/* Drive supports read from CD-E discs (orange book, part III) */
+        unsigned cd_e_read           : 1; /* reserved in 1.2 */
+	/* Drive supports reading CD-R discs with addressing method 2 */
+	unsigned method2             : 1; /* reserved in 1.2 */
+	unsigned reserved2           : 5;
+
+	/* Drive supports write to CD-R discs (orange book, part II) */
+        unsigned cd_r_write          : 1; /* reserved in 1.2 */
+	/* Drive supports write to CD-E discs (orange book, part III) */
+        unsigned cd_e_write          : 1; /* reserved in 1.2 */
+	unsigned reserved3           : 6;
+
+	/* Drive supports audio play operations. */
+	unsigned audio_play          : 1;
+	/* Drive can deliver a composite audio/video data stream. */
+	unsigned composite           : 1;
+	/* Drive supports digital output on port 1. */
+	unsigned digport1            : 1;
+	/* Drive supports digital output on port 2. */
+	unsigned digport2            : 1;
+	/* Drive can read mode 2, form 1 (XA) data. */
+	unsigned mode2_form1         : 1;
+	/* Drive can read mode 2, form 2 data. */
+	unsigned mode2_form2         : 1;
+	/* Drive can read multisession discs. */
+	unsigned multisession        : 1;
+	unsigned reserved4           : 1;
+
+	/* Drive can read Red Book audio data. */
+	unsigned cdda                : 1;
+	/* Drive can continue a read cdda operation from a loss of streaming.*/
+	unsigned cdda_accurate       : 1;
+	/* Subchannel reads can return combined R-W information. */
+	unsigned rw_supported        : 1;
+	/* R-W data will be returned deinterleaved and error corrected. */
+	unsigned rw_corr             : 1;
+	/* Drive supports C2 error pointers. */
+	unsigned c2_pointers         : 1;
+	/* Drive can return International Standard Recording Code info. */
+	unsigned isrc                : 1;
+	/* Drive can return Media Catalog Number (UPC) info. */
+	unsigned upc                 : 1;
+	unsigned reserved5           : 1;
+
+	/* Drive can lock the door. */
+	unsigned lock                : 1;
+	/* Present state of door lock. */
+	unsigned lock_state          : 1;
+	/* State of prevent/allow jumper. */
+	unsigned prevent_jumper      : 1;
+	/* Drive can eject a disc or changer cartridge. */
+	unsigned eject               : 1;
+	unsigned reserved6           : 1;
+	/* Drive mechanism types. */
+	mechtype_t mechtype          : 3;
+
+	/* Audio level for each channel can be controlled independently. */
+	unsigned separate_volume     : 1;
+	/* Audio for each channel can be muted independently. */
+	unsigned separate_mute       : 1;
+	/* Changer can report exact contents of slots. */
+	unsigned disc_present        : 1;  /* reserved in 1.2 */
+	/* Drive supports software slot selection. */
+	unsigned sss                 : 1;  /* reserved in 1.2 */
+	unsigned reserved7           : 4;
+
+	/* Note: the following four fields are returned in big-endian form. */
+	/* Maximum speed (in kB/s). */
+	unsigned short maxspeed;
+	/* Number of discrete volume levels. */
+	unsigned short n_vol_levels;
+	/* Size of cache in drive, in kB. */
+	unsigned short buffer_size;
+	/* Current speed (in kB/s). */
+	unsigned short curspeed;
+
+	/* Truncate the structure here, so we don't have headaches reading
+	   from older drives. */
+};
+
+
+struct atapi_mechstat_header {
+	unsigned curslot       : 5;
+	unsigned changer_state : 2;
+	unsigned fault         : 1;
+
+	unsigned reserved1     : 5;
+	unsigned mech_state    : 3;
+
+	byte     curlba[3];
+	byte     nslots;
+	unsigned short slot_tablelen;
+};
+
+
+struct atapi_slot {
+	unsigned change       : 1;
+	unsigned reserved1    : 6;
+	unsigned disc_present : 1;
+
+	byte reserved2[3];
+};
+
+
+struct atapi_changer_info {
+	struct atapi_mechstat_header hdr;
+	struct atapi_slot slots[0];
+};
+
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+	/* Buffer for table of contents.  NULL if we haven't allocated
+	   a TOC buffer for this device yet. */
+
+	struct atapi_toc *toc;
+
+	/* Sector buffer.  If a read request wants only the first part
+	   of a cdrom block, we cache the rest of the block here,
+	   in the expectation that that data is going to be wanted soon.
+	   SECTOR_BUFFERED is the number of the first buffered sector,
+	   and NSECTORS_BUFFERED is the number of sectors in the buffer.
+	   Before the buffer is allocated, we should have
+	   SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
+
+	unsigned long sector_buffered;
+	unsigned long nsectors_buffered;
+	char *sector_buffer;
+
+	/* The result of the last successful request sense command
+	   on this device. */
+	struct atapi_request_sense sense_data;
+
+	struct request request_sense_request;
+	struct packet_command request_sense_pc;
+	int dma;
+	/* Buffer to hold mechanism status and changer slot table. */
+	struct atapi_changer_info *changer_info;
+
+
+        /* Per-device info needed by cdrom.c generic driver. */
+        struct cdrom_device_info devinfo;
+};
+
+
+#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
+
+
+/****************************************************************************
+ * Descriptions of ATAPI error codes.
+ */
+
+#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
+
+#if VERBOSE_IDE_CD_ERRORS
+
+/* From Table 124 of the ATAPI 1.2 spec.
+   Unchanged in Table 140 of the ATAPI 2.6 draft standard. */
+
+char *sense_key_texts[16] = {
+	"No sense data",
+	"Recovered error",
+	"Not ready",
+	"Medium error",
+	"Hardware error",
+	"Illegal request",
+	"Unit attention",
+	"Data protect",
+	"(reserved)",
+	"(reserved)",
+	"(reserved)",
+	"Aborted command",
+	"(reserved)",
+	"(reserved)",
+	"Miscompare",
+	"(reserved)",
+};
+
+
+/* From Table 125 of the ATAPI 1.2 spec.,
+   with additions from Tables 141 and 142 of the ATAPI 2.6 draft standard. */
+
+struct {
+	short asc_ascq;
+	char *text;
+} sense_data_texts[] = {
+	{ 0x0000, "No additional sense information" },
+
+	{ 0x0011, "Audio play operation in progress" },
+	{ 0x0012, "Audio play operation paused" },
+	{ 0x0013, "Audio play operation successfully completed" },
+	{ 0x0014, "Audio play operation stopped due to error" },
+	{ 0x0015, "No current audio status to return" },
+
+	{ 0x0100, "Mechanical positioning or changer error" },
+
+	{ 0x0200, "No seek complete" },
+
+	{ 0x0400, "Logical unit not ready - cause not reportable" },
+	{ 0x0401,
+	  "Logical unit not ready - in progress (sic) of becoming ready" },
+	{ 0x0402, "Logical unit not ready - initializing command required" },
+	{ 0x0403, "Logical unit not ready - manual intervention required" },
+
+	{ 0x0501, "Media load - eject failed" },
+
+	{ 0x0600, "No reference position found" },
+
+	{ 0x0900, "Track following error" },
+	{ 0x0901, "Tracking servo failure" },
+	{ 0x0902, "Focus servo failure" },
+	{ 0x0903, "Spindle servo failure" },
+
+	{ 0x1100, "Unrecovered read error" },
+	{ 0x1106, "CIRC unrecovered error" },
+
+	{ 0x1500, "Random positioning error" },
+	{ 0x1501, "Mechanical positioning or changer error" },
+	{ 0x1502, "Positioning error detected by read of medium" },
+
+	{ 0x1700, "Recovered data with no error correction applied" },
+	{ 0x1701, "Recovered data with retries" },
+	{ 0x1702, "Recovered data with positive head offset" },
+	{ 0x1703, "Recovered data with negative head offset" },
+	{ 0x1704, "Recovered data with retries and/or CIRC applied" },
+	{ 0x1705, "Recovered data using previous sector ID" },
+
+	{ 0x1800, "Recovered data with error correction applied" },
+	{ 0x1801, "Recovered data with error correction and retries applied" },
+	{ 0x1802, "Recovered data - the data was auto-reallocated" },
+	{ 0x1803, "Recovered data with CIRC" },
+	{ 0x1804, "Recovered data with L-EC" },
+	/* Following two not in 2.6. */
+	{ 0x1805, "Recovered data - recommend reassignment" },
+	{ 0x1806, "Recovered data - recommend rewrite" },
+
+	{ 0x1a00, "Parameter list length error" },
+
+	{ 0x2000, "Invalid command operation code" },
+
+	{ 0x2100, "Logical block address out of range" },
+
+	{ 0x2400, "Invalid field in command packet" },
+
+	{ 0x2600, "Invalid field in parameter list" },
+	{ 0x2601, "Parameter not supported" },
+	{ 0x2602, "Parameter value invalid" },
+	/* Following code not in 2.6. */
+	{ 0x2603, "Threshold parameters not supported" },
+
+	{ 0x2800, "Not ready to ready transition, medium may have changed" },
+
+	{ 0x2900, "Power on, reset or bus device reset occurred" },
+
+	{ 0x2a00, "Parameters changed" },
+	{ 0x2a01, "Mode parameters changed" },
+
+	{ 0x3000, "Incompatible medium installed" },
+	{ 0x3001, "Cannot read medium - unknown format" },
+	{ 0x3002, "Cannot read medium - incompatible format" },
+
+	/* Following code not in 2.6. */
+	{ 0x3700, "Rounded parameter" },
+
+	{ 0x3900, "Saving parameters not supported" },
+
+	{ 0x3a00, "Medium not present" },
+
+	{ 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
+	{ 0x3f01, "Microcode has been changed" },
+	/* Following two not in 2.6. */
+	{ 0x3f02, "Changed operating definition" },
+	{ 0x3f03, "Inquiry data has changed" },
+
+	{ 0x4000, "Diagnostic failure on component (ASCQ)" },
+
+	{ 0x4400, "Internal ATAPI CD-ROM drive failure" },
+
+	{ 0x4e00, "Overlapped commands attempted" },
+
+	{ 0x5300, "Media load or eject failed" },
+	{ 0x5302, "Medium removal prevented" },
+
+	{ 0x5700, "Unable to recover table of contents" },
+
+	{ 0x5a00, "Operator request or state change input (unspecified)" },
+	{ 0x5a01, "Operator medium removal request" },
+
+	/* Following two not in 2.6. */
+	{ 0x5b00, "Threshold condition met" },
+	{ 0x5c00, "Status change" },
+
+	{ 0x6300, "End of user area encountered on this track" },
+
+	{ 0x6400, "Illegal mode for this track" },
+
+	{ 0xb900, "Play operation oborted (sic)" },
+
+	{ 0xbf00, "Loss of streaming" },
+};
+#endif
+
+
+#endif /* _IDE_CD_H */
diff -u --recursive --new-file v2.1.8/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.8/linux/drivers/block/ide.h	Sun Nov 10 20:12:10 1996
+++ linux/drivers/block/ide.h	Tue Nov 12 15:15:18 1996
@@ -1,3 +1,5 @@
+#ifndef _IDE_H
+#define _IDE_H
 /*
  *  linux/drivers/block/ide.h
  *
@@ -594,3 +596,5 @@
 #else
 #define IS_PROMISE_DRIVE (0)	/* auto-NULLs out Promise code */
 #endif /* CONFIG_BLK_DEV_PROMISE */
+
+#endif /* _IDE_H */
diff -u --recursive --new-file v2.1.8/linux/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile
--- v2.1.8/linux/drivers/cdrom/Makefile	Sun May 12 08:13:07 1996
+++ linux/drivers/cdrom/Makefile	Tue Nov 12 10:32:49 1996
@@ -20,16 +20,6 @@
 M_OBJS   :=
 MOD_LIST_NAME := CDROM_MODULES
 
-# The following if's should be generalized (ORed) for all drivers that 
-# use the generic interface of cdrom.c 
-ifeq ($(CONFIG_CM206),y)
-L_OBJS += cdrom.o
-else
-  ifeq ($(CONFIG_CM206),m)
-  M_OBJS += cdrom.o
-  endif
-endif 
-
 ifeq ($(CONFIG_AZTCD),y)
 L_OBJS += aztcd.o
 else
@@ -100,9 +90,11 @@
 
 ifeq ($(CONFIG_CM206),y)
 L_OBJS += cm206.o
+C = 1 
 else
   ifeq ($(CONFIG_CM206),m)
   M_OBJS += cm206.o
+  CM = 1
   endif
 endif #CONFIG_CM206
 
@@ -136,4 +128,20 @@
   endif
 endif #CONFIG_ISP16_CDI
 
+ifeq ($(CONFIG_BLK_DEV_IDECD),y)
+USE_GENERIC_CD=1
+else
+  ifeq ($(CONFIG_BLK_DEV_IDECD),m)
+    USE_MODULAR_GENERIC_CD=1
+  endif
+endif #CONFIG_BLK_DEV_IDECD
+
+ifdef USE_GENERIC_CD
+L_OBJS += cdrom.o
+else
+  ifdef USE_MODULAR_GENERIC_CD
+  M_OBJS += cdrom.o
+  endif
+endif #The Makefile configuration for the generic cdrom interface
+  
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.8/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.8/linux/drivers/cdrom/cdrom.c	Fri Nov  1 17:13:14 1996
+++ linux/drivers/cdrom/cdrom.c	Tue Nov 12 10:32:49 1996
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <asm/fcntl.h>
+#include <asm/segment.h>
 #include <asm/uaccess.h>
 
 #include <linux/cdrom.h>
@@ -22,14 +23,14 @@
 
 #define FM_WRITE	0x2                 /* file mode write bit */
 
-#define VERSION "$Id: cdrom.c,v 0.8 1996/08/10 10:52:11 david Exp $"
+#define VERSION "Generic CD-ROM driver, v 1.21 1996/11/08 03:24:49"
 
 /* Not-exported routines. */
-int cdrom_open(struct inode *ip, struct file *fp);
-void cdrom_release(struct inode *ip, struct file *fp);
-int cdrom_ioctl(struct inode *ip, struct file *fp,
+static int cdrom_open(struct inode *ip, struct file *fp);
+static void cdrom_release(struct inode *ip, struct file *fp);
+static int cdrom_ioctl(struct inode *ip, struct file *fp,
 				unsigned int cmd, unsigned long arg);
-int cdrom_media_changed(kdev_t dev);
+static int cdrom_media_changed(kdev_t dev);
 
 struct file_operations cdrom_fops =
 {
@@ -48,7 +49,7 @@
         NULL                            /* revalidate */
 };
 
-static struct cdrom_device_ops *cdromdevs[MAX_BLKDEV] = {
+static struct cdrom_device_info *cdromdevs[MAX_BLKDEV] = {
 	NULL,
 };
 
@@ -63,14 +64,15 @@
 /* We don't use $name$ yet, but it could be used for the /proc
  * filesystem in the future, or for other purposes.  
  */
-int register_cdrom(int major, char *name, struct cdrom_device_ops *cdo)
+int register_cdrom(struct cdrom_device_info *cdi, char *name)
 {
-        int *change_capability = &cdo->capability; /* hack, gcc complains OK */
-	
+        int major = MAJOR (cdi->dev);
+        struct cdrom_device_ops *cdo = cdi->ops;
+        int *change_capability = (int *)&cdo->capability; /* hack */
+
         if (major < 0 || major >= MAX_BLKDEV)
                 return -1;
-        if (cdo->open_files == NULL || cdo->open == NULL || 
-            cdo->release == NULL)
+        if (cdo->open == NULL || cdo->release == NULL)
                 return -2;
         ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
         ENSURE(lock_door, CDC_LOCK);
@@ -79,26 +81,50 @@
         ENSURE(get_last_session, CDC_MULTI_SESSION);
         ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
         ENSURE(media_changed, CDC_MEDIA_CHANGED);
-        cdromdevs[major] = cdo;
-        cdo->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK;
-        cdo->mc_flags = 0;
+        if (cdromdevs[major]==NULL) cdo->n_minors = 0;
+        else cdo->n_minors++; 
+        cdi->next = cdromdevs[major];
+        cdromdevs[major] = cdi;
+        cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK;
+        cdi->mc_flags = 0;
         return 0;
 }
 #undef ENSURE
 
-int unregister_cdrom(int major, char *name)
+int unregister_cdrom(struct cdrom_device_info *unreg)
 {
+        struct cdrom_device_info *cdi, *prev;
+        int major = MAJOR (unreg->dev);
+
         if (major < 0 || major >= MAX_BLKDEV)
                 return -1;
-        if (cdromdevs[major] == NULL)
+
+        prev = NULL;
+        cdi = cdromdevs[major];
+        while (cdi != NULL && cdi->dev != unreg->dev) {
+                prev = cdi;
+                cdi = cdi->next;
+        }
+
+        if (cdi == NULL)
                 return -2;
-        cdromdevs[major] = NULL;
+        if (prev)
+                prev->next = cdi->next;
+        else
+                cdromdevs[major] = cdi->next;
+        cdi->ops->n_minors--;
         return 0;
 }
 
-/* We need our own cdrom error types! This is a temporary solution. */
+static
+struct cdrom_device_info *cdrom_find_device (kdev_t dev)
+{
+        struct cdrom_device_info *cdi = cdromdevs[MAJOR (dev)];
 
-#define ENOMEDIUM EAGAIN				/* no medium in removable device */
+        while (cdi != NULL && cdi->dev != dev)
+                cdi = cdi->next;
+        return cdi;
+}
 
 /* We use the open-option O_NONBLOCK to indicate that the
  * purpose of opening is only for subsequent ioctl() calls; no device
@@ -108,78 +134,89 @@
  * is in their own interest: device control becomes a lot easier
  * this way.
  */
-int open_for_data(struct cdrom_device_ops *, kdev_t);
+static
+int open_for_data(struct cdrom_device_info * cdi);
 
+static
 int cdrom_open(struct inode *ip, struct file *fp)
 {
 	kdev_t dev = ip->i_rdev;
-        struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)];
+        struct cdrom_device_info *cdi = cdrom_find_device(dev);
         int purpose = !!(fp->f_flags & O_NONBLOCK);
+        int ret=0;
 
-        if (cdo == NULL || MINOR(dev) >= cdo->minors)
+        if (cdi == NULL)
                 return -ENODEV;
         if (fp->f_mode & FM_WRITE)
                 return -EROFS;				
-        purpose = purpose || !(cdo->options & CDO_USE_FFLAGS);
-        if (cdo->open_files(dev) || purpose) 
-                return cdo->open(dev, purpose); 
+        purpose = purpose || !(cdi->options & CDO_USE_FFLAGS);
+        if (cdi->use_count || purpose) 
+                ret = cdi->ops->open(cdi, purpose); 
         else 
-                return open_for_data(cdo, dev);
+                ret = open_for_data(cdi);
+        if (!ret) cdi->use_count++;
+        return ret;
 }
 
-int open_for_data(struct cdrom_device_ops * cdo, kdev_t dev)
+static
+int open_for_data(struct cdrom_device_info * cdi)
 {
         int ret;
+        struct cdrom_device_ops *cdo = cdi->ops;
         if (cdo->drive_status != NULL) {
-                int ds = cdo->drive_status(dev);
+                int ds = cdo->drive_status(cdi, CDSL_CURRENT);
                 if (ds == CDS_TRAY_OPEN) {
                         /* can/may i close it? */
-                        if (cdo->capability & ~cdo->mask & CDC_CLOSE_TRAY &&
-                            cdo->options & CDO_AUTO_CLOSE) {
-                                if (cdo->tray_move(dev, 0))
+                        if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
+                            cdi->options & CDO_AUTO_CLOSE) {
+                                if (cdo->tray_move(cdi,0))
                                         return -EIO;
                         } else
-                                return -ENOMEDIUM; /* can't close: too bad */
-                        ds = cdo->drive_status(dev);
+                                return -ENXIO; /* can't close: too bad */
+                        ds = cdo->drive_status(cdi, CDSL_CURRENT);
                         if (ds == CDS_NO_DISC)
-                                return -ENOMEDIUM;
+                                return -ENXIO;
                 }
         }
         if (cdo->disc_status != NULL) {
-                int ds = cdo->disc_status(dev);
+                int ds = cdo->disc_status(cdi);
                 if (ds == CDS_NO_DISC)
-                        return -ENOMEDIUM;
-                if (cdo->options & CDO_CHECK_TYPE &&
+                        return -ENXIO;
+                if (cdi->options & CDO_CHECK_TYPE &&
                     ds != CDS_DATA_1)
                         return -ENODATA;
         }
         /* all is well, we can open the device */
-        ret = cdo->open(dev, 0); /* open for data */
-        if (cdo->capability & ~cdo->mask & CDC_LOCK &&
-            cdo->options & CDO_LOCK)
-                cdo->lock_door(dev, 1);
+        ret = cdo->open(cdi, 0); /* open for data */
+        if (cdo->capability & ~cdi->mask & CDC_LOCK &&
+            cdi->options & CDO_LOCK)
+                cdo->lock_door(cdi, 1);
         return ret;
 }
 
 /* Admittedly, the logic below could be performed in a nicer way. */
+static
 void cdrom_release(struct inode *ip, struct file *fp)
 {
         kdev_t dev = ip->i_rdev;
-        struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)];
+        struct cdrom_device_info *cdi = cdrom_find_device (dev);
+        struct cdrom_device_ops *cdo;
 	
-        if (cdo == NULL || MINOR(dev) >= cdo->minors)
+        if (cdi == NULL)
                 return;
-        if (cdo->open_files(dev) == 1 && /* last process that closes dev */
-            cdo->options & CDO_LOCK &&  
-            cdo->capability & ~cdo->mask & CDC_LOCK) 
-                cdo->lock_door(dev, 0);
-        cdo->release(dev);
-        if (cdo->open_files(dev) == 0) { /* last process that closes dev */
+        cdo = cdi->ops;        
+        if (cdi->use_count == 1 &&      /* last process that closes dev*/
+            cdi->options & CDO_LOCK &&  
+            cdo->capability & ~cdi->mask & CDC_LOCK) 
+                cdo->lock_door(cdi, 0);
+        cdo->release(cdi);
+        if (cdi->use_count > 0) cdi->use_count--;
+        if (cdi->use_count == 0) {      /* last process that closes dev*/
                 sync_dev(dev);
                 invalidate_buffers(dev);
-                if (cdo->options & CDO_AUTO_EJECT &&
-                    cdo->capability & ~cdo->mask & CDC_OPEN_TRAY)
-                        cdo->tray_move(dev, 1);
+                if (cdi->options & CDO_AUTO_EJECT &&
+                    cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)
+                        cdo->tray_move(cdi, 1);
         }
 }
 
@@ -187,40 +224,32 @@
  * ioctl. The main problem now is that we must double-buffer the
  * low-level implementation, to assure that the VFS and the user both
  * see a medium change once.
- *
- * For now, i've implemented only 16 minor devs (half a long), i have to
- * think of a better solution... $Queue$ is either 0 or 1. Queue 0 is
- * in the lower 16 bits, queue 1 in the higher 16 bits.
  */
 
-int media_changed(kdev_t dev, int queue)
+static
+int media_changed(struct cdrom_device_info *cdi, int queue)
 {
-        unsigned int major = MAJOR(dev);
-        unsigned int minor = MINOR(dev);
-        struct cdrom_device_ops *cdo = cdromdevs[major];
-        int ret;
-        unsigned long mask = 1 << (16 * queue + minor);
+        unsigned int mask = (1 << (queue & 1));
+        int ret = !!(cdi->mc_flags & mask);
 
-        queue &= 1;
-        if (cdo == NULL || minor >= 16)
-                return -1;
-        ret = !!(cdo->mc_flags & mask); /* changed since last call? */
-        if (cdo->media_changed(dev)) {
-                cdo->mc_flags |= 0x10001 << minor; /* set bit on both queues */
+        /* changed since last call? */
+        if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
+                cdi->mc_flags = 0x3;    /* set bit on both queues */
                 ret |= 1;
         }
-        cdo->mc_flags &= ~mask;         /* clear bit */
+        cdi->mc_flags &= ~mask;         /* clear bit */
         return ret;
 }
 
+static
 int cdrom_media_changed(kdev_t dev)
 {
-        struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)];
-        if (cdo == NULL || MINOR(dev) >= cdo->minors)
+        struct cdrom_device_info *cdi = cdrom_find_device (dev);
+        if (cdi == NULL)
                 return -ENODEV;
-        if (cdo->media_changed == NULL)
+        if (cdi->ops->media_changed == NULL)
                 return -EINVAL;
-        return media_changed(dev, 0);
+        return media_changed(cdi, 0);
 }
 
 /* Requests to the low-level drivers will /always/ be done in the
@@ -239,8 +268,9 @@
    meaningful format indicated above.
  */
 
-#undef current							/* set in sched.h */
+#undef current                          /* set in sched.h */
 
+static
 void sanitize_format(union cdrom_addr *addr,
                      u_char * current, u_char requested)
 {
@@ -288,14 +318,17 @@
  * cdo->dev_ioctl. Note that as a result of this, no
  * memory-verification is performed for these ioctls.
  */
+static
 int cdrom_ioctl(struct inode *ip, struct file *fp,
                 unsigned int cmd, unsigned long arg)
 {
         kdev_t dev = ip->i_rdev;
-        struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)];
+        struct cdrom_device_info *cdi = cdrom_find_device (dev);
+        struct cdrom_device_ops *cdo;
 
-        if (cdo == NULL || MINOR(dev) >= cdo->minors)
+        if (cdi == NULL)
                 return -ENODEV;
+        cdo = cdi->ops;
         /* the first few commands do not deal with audio capabilities, but
            only with routines in cdrom device operations. */
         switch (cmd) {
@@ -310,7 +343,7 @@
                 GETARG(struct cdrom_multisession, ms_info);
                 requested_format = ms_info.addr_format;
                 ms_info.addr_format = CDROM_LBA;
-                cdo->get_last_session(dev, &ms_info);
+                cdo->get_last_session(cdi, &ms_info);
                 sanitize_format(&ms_info.addr, &ms_info.addr_format, 
                                 requested_format);
                 PUTARG(struct cdrom_multisession, ms_info);
@@ -318,52 +351,63 @@
         }
                 
         case CDROMEJECT:
-                if (cdo->open_files(dev) == 1 && 
-                    cdo->capability & ~cdo->mask & CDC_OPEN_TRAY)
-                        return cdo->tray_move(dev, 1);
-                else
+                if (cdo->capability & ~cdi->mask & CDC_OPEN_TRAY) {
+			if (cdi->use_count == 1)
+				return cdo->tray_move(cdi, 1);
+			else
+				return -EBUSY;
+		} else
                         return -EINVAL;
 		
         case CDROMCLOSETRAY:
-                if (cdo->open_files(dev) == 1 && 
-                    cdo->capability & ~cdo->mask & CDC_CLOSE_TRAY)
-                        return cdo->tray_move(dev, 0);
+                if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY)
+			return cdo->tray_move(cdi, 0);
                 else
                         return -EINVAL;
                 
         case CDROMEJECT_SW:
-                cdo->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
+                cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
                 if (arg)
-                        cdo->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
+                        cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
                 return 0;
 
         case CDROM_MEDIA_CHANGED:
-                if (cdo->capability & ~cdo->mask & CDC_MEDIA_CHANGED)
-                        return media_changed(dev, 1);
+        	if (cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED) {
+                	if (arg == CDSL_CURRENT)
+                        	return media_changed(cdi, 1);
+                	else if ((int)arg < cdi->capacity &&
+                                 cdo->capability & ~cdi->mask
+                                 &CDC_SELECT_DISC)
+                        	return cdo->media_changed (cdi, arg);
+                        else
+                        	return -EINVAL;
+                }
                 else
                         return -EINVAL;
                 
         case CDROM_SET_OPTIONS:
-                cdo->options |= (int) arg;
-                return cdo->options;
+                cdi->options |= (int) arg;
+                return cdi->options;
 		
         case CDROM_CLEAR_OPTIONS:
-                cdo->options &= ~(int) arg;
-                return cdo->options;
+                cdi->options &= ~(int) arg;
+                return cdi->options;
                 
         case CDROM_SELECT_SPEED:
-                if (0 <= arg && arg <= cdo->speed &&
-                    cdo->capability & ~cdo->mask & CDC_SELECT_SPEED)
-                        return cdo->select_speed(dev, arg);
+                if ((int)arg <= cdi->speed &&
+                    cdo->capability & ~cdi->mask & CDC_SELECT_SPEED)
+                        return cdo->select_speed(cdi, arg);
                 else
                         return -EINVAL;
-                
+
         case CDROM_SELECT_DISC:
-                if (0 <= arg && arg <= cdo->capacity &&
-                    cdo->capability & ~cdo->mask & CDC_SELECT_DISC)
-                        return cdo->select_disc(dev, arg);
+                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
+                	return cdo->select_disc(cdi, arg);
+                if ((int)arg < cdi->capacity &&
+                    cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
+			return cdo->select_disc(cdi, arg);
                 else
-                        return -EINVAL;
+			return -EINVAL;
 		
 /* The following function is implemented, although very few audio
  * discs give Universal Product Code information, which should just be
@@ -374,7 +418,7 @@
                 struct cdrom_mcn mcn;
                 if (!(cdo->capability & CDC_MCN))
                         return -EINVAL;
-                if (!cdo->get_mcn(dev, &mcn)) {
+                if (!cdo->get_mcn(cdi, &mcn)) {
                         PUTARG(struct cdrom_mcn, mcn);
                         return 0;
                 }
@@ -382,17 +426,24 @@
         }
                 
         case CDROM_DRIVE_STATUS:
-                if (cdo->drive_status == NULL)
-			return -EINVAL;
-		else
-			return cdo->drive_status(dev);
+                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
+                	return cdo->drive_status(cdi, arg);
+                if (cdo->drive_status == NULL ||
+                            ! (cdo->capability & ~cdi->mask & CDC_SELECT_DISC
+                            && (int)arg < cdi->capacity)) 
+                	return -EINVAL;
+                else
+			return cdo->drive_status(cdi, arg);
 		
         case CDROM_DISC_STATUS:
 		if (cdo->disc_status == NULL)
 			return -EINVAL;
 		else
-			return cdo->disc_status(dev);
-		
+			return cdo->disc_status(cdi);
+
+        case CDROM_CHANGER_NSLOTS:
+        	return cdi->capacity;
+
 /* The following is not implemented, because there are too many
  * different data type. We could support /1/ raw mode, that is large
  * enough to hold everything.
@@ -403,7 +454,7 @@
                 struct cdrom_msf msf;
                 char buf[CD_FRAMESIZE];
                 GETARG(struct cdrom_msf, msf);
-                if (!cdo->read_audio(dev, cmd, &msf, &buf)) {
+                if (!cdo->read_audio(dev, cmd, &msf, &buf, cdi)) {
                         PUTARG(char *, buf);
                         return 0;
                 }
@@ -424,10 +475,12 @@
                         GETARG(struct cdrom_subchnl, q);
                         requested = q.cdsc_format;
                         q.cdsc_format = CDROM_MSF;
-                        if (!cdo->audio_ioctl(dev, cmd, &q)) {
+                        if (!cdo->audio_ioctl(cdi, cmd, &q)) {
                                 back = q.cdsc_format; /* local copy */
-                                sanitize_format(&q.cdsc_absaddr, &back, requested);
-                                sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
+                                sanitize_format(&q.cdsc_absaddr, &back,
+                                requested);
+                                sanitize_format(&q.cdsc_reladdr,
+                                &q.cdsc_format, requested);
                                 PUTARG(struct cdrom_subchnl, q);
                                 return 0;
                         } else
@@ -436,7 +489,7 @@
                 case CDROMREADTOCHDR: {
                         struct cdrom_tochdr header;
                         GETARG(struct cdrom_tochdr, header);
-                        if (!cdo->audio_ioctl(dev, cmd, &header)) {
+                        if (!cdo->audio_ioctl(cdi, cmd, &header)) {
                                 PUTARG(struct cdrom_tochdr, header);
                                 return 0;
                         } else
@@ -449,8 +502,9 @@
                         requested_format = entry.cdte_format;
                         /* make interface to low-level uniform */
                         entry.cdte_format = CDROM_MSF; 
-                        if (!(cdo->audio_ioctl(dev, cmd, &entry))) {
-                                sanitize_format(&entry.cdte_addr, &entry.cdte_format, requested_format);
+                        if (!(cdo->audio_ioctl(cdi, cmd, &entry))) {
+                                sanitize_format(&entry.cdte_addr,
+                                &entry.cdte_format, requested_format);
                                 PUTARG(struct cdrom_tocentry, entry);
                                 return 0;
                         } else
@@ -459,21 +513,21 @@
                 case CDROMPLAYMSF: {
                         struct cdrom_msf msf;
                         GETARG(struct cdrom_msf, msf);
-                        return cdo->audio_ioctl(dev, cmd, &msf);
+                        return cdo->audio_ioctl(cdi, cmd, &msf);
                 }
                 case CDROMPLAYTRKIND: {
                         struct cdrom_ti track_index;
                         GETARG(struct cdrom_ti, track_index);
-                        return cdo->audio_ioctl(dev, cmd, &track_index);
+                        return cdo->audio_ioctl(cdi, cmd, &track_index);
                 }
                 case CDROMVOLCTRL: {
                         struct cdrom_volctrl volume;
                         GETARG(struct cdrom_volctrl, volume);
-                        return cdo->audio_ioctl(dev, cmd, &volume);
+                        return cdo->audio_ioctl(cdi, cmd, &volume);
                 }
                 case CDROMVOLREAD: {
                         struct cdrom_volctrl volume;
-                        if (!cdo->audio_ioctl(dev, cmd, &volume)) {
+                        if (!cdo->audio_ioctl(cdi, cmd, &volume)) {
                                 PUTARG(struct cdrom_volctrl, volume);
                                 return 0;
                         }
@@ -483,30 +537,32 @@
                 case CDROMSTOP:
                 case CDROMPAUSE:
                 case CDROMRESUME:
-			return cdo->audio_ioctl(dev, cmd, NULL);
+			return cdo->audio_ioctl(cdi, cmd, NULL);
 		} /* switch */
 
 	if (cdo->dev_ioctl != NULL)     /* device specific ioctls? */
-		return cdo->dev_ioctl(dev, cmd, arg);
+		return cdo->dev_ioctl(cdi, cmd, arg);
 	return -EINVAL;
 }
 
 #ifdef MODULE
 int init_module(void)
 {
-	printk(KERN_INFO "Module inserted " VERSION "\n");
+	printk(KERN_INFO "Module inserted: " VERSION "\n");
 	return 0;
 }
 
 void cleanup_module(void)
 {
+	/*
 	printk(KERN_INFO "Module cdrom removed\n");
+	*/
 }
 
 #endif
 /*
  * Local variables:
  * comment-column: 40
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux-obj/include -Wall -Wstrict-prototypes -O2 -m486 -c cdrom.c -o cdrom.o"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I. -I/usr/src/linux-obj/include -Wall -Wstrict-prototypes -O2 -m486 -c cdrom.c -o cdrom.o"
  * End:
  */
diff -u --recursive --new-file v2.1.8/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.1.8/linux/drivers/char/keyboard.c	Sun Nov 10 20:12:10 1996
+++ linux/drivers/char/keyboard.c	Tue Nov 12 13:08:43 1996
@@ -1,8 +1,6 @@
 /*
  * linux/drivers/char/keyboard.c
  *
- * Keyboard driver for Linux v0.99 using Latin-1.
- *
  * Written for linux by Johan Myreen as a translation from
  * the assembly version by Linus (with diacriticals added)
  *
@@ -14,7 +12,7 @@
  * Added decr/incr_console, dynamic keymaps, Unicode support,
  * dynamic function/string keys, led setting,  Sept 1994
  * `Sticky' modifier keys, 951006.
- * 
+ * 11-11-96: SAK should now work in the raw mode (Martin Mares)
  */
 
 #define KEYBOARD_IRQ 1
@@ -128,6 +126,10 @@
 	do_ignore, do_ignore, do_ignore
 };
 
+/* Key types processed even in raw modes */
+
+#define TYPES_ALLOWED_IN_RAW_MODE ((1 << KT_SPEC) | (1 << KT_SHIFT))
+
 typedef void (*void_fnp)(void);
 typedef void (void_fn)(void);
 
@@ -143,6 +145,8 @@
 	decr_console,	incr_console,	spawn_console,	bare_num
 };
 
+#define SPECIALS_ALLOWED_IN_RAW_MODE (1 << KVAL(K_SAK))
+
 /* maximum values each key_handler can handle */
 const int max_vals[] = {
 	255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
@@ -501,13 +505,10 @@
 	} else
  		rep = set_bit(keycode, key_down);
 
-	if (raw_mode)
-		return;
-
 	if (kbd->kbdmode == VC_MEDIUMRAW) {
 		/* soon keycodes will require more than one byte */
  		put_queue(keycode + up_flag);
-		return;
+		raw_mode = 1;	/* Most key classes will be ignored */
  	}
 
  	/*
@@ -539,6 +540,8 @@
 
 			if (type >= 0xf0) {
 			    type -= 0xf0;
+			    if (raw_mode && ! (TYPES_ALLOWED_IN_RAW_MODE & (1 << type)))
+				return;
 			    if (type == KT_LETTER) {
 				type = KT_LATIN;
 				if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
@@ -552,7 +555,7 @@
 			      kbd->slockstate = 0;
 			} else {
 			    /* maybe only if (kbd->kbdmode == VC_UNICODE) ? */
-			    if (!up_flag)
+			    if (!up_flag && !raw_mode)
 			      to_utf8(keysym);
 			}
 		} else {
@@ -761,18 +764,14 @@
 
 static void SAK(void)
 {
-	do_SAK(tty);
-#if 0
 	/*
-	 * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
-	 * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
-	 * handling.
-	 * 
-	 * We should do this some day --- the whole point of a secure
-	 * attention key is that it should be guaranteed to always
-	 * work.
+	 * SAK should also work in all raw modes and reset
+	 * them properly.
 	 */
+
+	do_SAK(tty);
 	reset_vc(fg_console);
+#if 0
 	do_unblank_screen();	/* not in interrupt routine? */
 #endif
 }
@@ -791,6 +790,9 @@
 	if (up_flag)
 		return;
 	if (value >= SIZE(spec_fn_table))
+		return;
+	if ((kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) &&
+	    !(SPECIALS_ALLOWED_IN_RAW_MODE & (1 << value)))
 		return;
 	spec_fn_table[value]();
 }
diff -u --recursive --new-file v2.1.8/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.8/linux/drivers/net/Config.in	Fri Nov  1 17:13:17 1996
+++ linux/drivers/net/Config.in	Sun Nov 10 19:12:56 1996
@@ -24,18 +24,20 @@
 fi
 bool 'Radio network interfaces' CONFIG_NET_RADIO
 if [ "$CONFIG_NET_RADIO" != "n" ]; then
-  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'BAYCOM ser12 and par96 driver for AX.25' CONFIG_BAYCOM
-    tristate 'Soundcard modem driver for AX.25' CONFIG_SOUNDMODEM
-  fi
-  if [ "$CONFIG_AX25" = "y" ]; then
-    bool 'Gracilis PackeTwin support' CONFIG_PT
-    bool 'Ottawa PI and PI/2 support' CONFIG_PI
+  if [ "$CONFIG_AX25" != "n" ]; then
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'BAYCOM ser12 and par96 driver for AX.25' CONFIG_BAYCOM
+      tristate 'Soundcard modem driver for AX.25' CONFIG_SOUNDMODEM
+    fi
+    dep_tristate 'Serial port KISS driver for AX.25' CONFIG_MKISS $CONFIG_AX25
+    dep_tristate 'BPQ Ethernet driver for AX.25' CONFIG_BPQETHER $CONFIG_AX25
+    dep_tristate 'Gracilis PackeTwin support' CONFIG_PT $CONFIG_AX25
+    dep_tristate 'Ottawa PI and PI/2 support' CONFIG_PI $CONFIG_AX25
   fi
+  tristate 'Z8530 SCC KISS emulation driver for AX.25' CONFIG_SCC
   tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
   tristate 'WaveLAN support' CONFIG_WAVELAN
   tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC
-  tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
 fi
 #
 #	Ethernet
diff -u --recursive --new-file v2.1.8/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.8/linux/drivers/net/Makefile	Fri Nov  1 17:13:17 1996
+++ linux/drivers/net/Makefile	Sun Nov 10 19:12:56 1996
@@ -426,12 +426,36 @@
   endif
 endif
 
+ifeq ($(CONFIG_MKISS),y)
+L_OBJS += mkiss.o
+else
+  ifeq ($(CONFIG_MKISS),m)
+  M_OBJS += mkiss.o
+  endif
+endif
+
 ifeq ($(CONFIG_PI),y)
 L_OBJS += pi2.o
+else
+  ifeq ($(CONFIG_PI),m)
+  M_OBJS += pi2.o
+  endif
 endif
 
 ifeq ($(CONFIG_PT),y)
 L_OBJS += pt.o
+else
+  ifeq ($(CONFIG_PT),m)
+  M_OBJS += pt.o
+  endif
+endif
+
+ifeq ($(CONFIG_BPQETHER),y)
+L_OBJS += bpqether.o
+else
+  ifeq ($(CONFIG_BPQETHER),m)
+  M_OBJS += bpqether.o
+  endif
 endif
 
 # If anything built-in uses slhc, then build it into the kernel also.
diff -u --recursive --new-file v2.1.8/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.8/linux/drivers/net/Space.c	Thu Oct 10 19:10:55 1996
+++ linux/drivers/net/Space.c	Sun Nov 10 19:12:56 1996
@@ -227,17 +227,24 @@
 #   define NEXT_DEV	(&sdla0_dev)
 #endif
 
+#ifdef CONFIG_AX25
 #ifdef CONFIG_NETROM
 	extern int nr_init(struct device *);
-	
 	static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, };
 	static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, };
 	static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, };
 	static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, };
-
 #   undef NEXT_DEV
 #   define	NEXT_DEV	(&nr0_dev)
 #endif
+#ifdef CONFIG_ROSE
+	extern int rose_init(struct device *);
+	static struct device rose1_dev = { "rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV,   rose_init, };
+	static struct device rose0_dev = { "rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &rose1_dev, rose_init, };
+#   undef NEXT_DEV
+#   define	NEXT_DEV	(&rose0_dev)
+#endif
+#endif
 
 /* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
 #ifdef CONFIG_ATP		/* AT-LAN-TEC (RealTek) pocket adaptor. */
@@ -308,6 +315,16 @@
 #undef NEXT_DEV
 #define NEXT_DEV (&slip_bootstrap)
 #endif	/* SLIP */
+  
+#if defined(CONFIG_MKISS)
+	/* To be exact, this node just hooks the initialization
+	   routines to the device structures.			*/
+extern int mkiss_init_ctrl_dev(struct device *);
+static struct device mkiss_bootstrap = {
+  "mkiss_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, mkiss_init_ctrl_dev, };
+#undef NEXT_DEV
+#define NEXT_DEV (&mkiss_bootstrap)
+#endif	/* MKISS */
   
 #if defined(CONFIG_STRIP)
 extern int strip_init_ctrl_dev(struct device *);
diff -u --recursive --new-file v2.1.8/linux/drivers/net/bpqether.c linux/drivers/net/bpqether.c
--- v2.1.8/linux/drivers/net/bpqether.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/bpqether.c	Sun Nov 10 19:12:56 1996
@@ -0,0 +1,690 @@
+/*
+ *	G8BPQ compatible "AX.25 via ethernet" driver release 003
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly 
+ *	fail to work with new releases, misbehave and/or generally screw up. 
+ *	It might even work. 
+ *
+ *	This code REQUIRES 2.0.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	This is a "pseudo" network driver to allow AX.25 over Ethernet
+ *	using G8BPQ encapsulation. It has been extracted from the protocol
+ *	implementation because 
+ *
+ *		- things got unreadable within the protocol stack
+ *		- to cure the protocol stack from "feature-ism"
+ *		- a protocol implementation shouldn't need to know on 
+ *		  which hardware it is running
+ *		- user-level programs like the AX.25 utilities shouldn't
+ *		  need to know about the hardware.
+ *		- IP over ethernet encapsulated AX.25 was impossible
+ *		- rxecho.c did not work
+ *		- to have room for extensions
+ *		- it just deserves to "live" as an own driver
+ *
+ *	This driver can use any ethernet destination address, and can be 
+ *	limited to accept frames from one dedicated ethernet card only.
+ *
+ *	Note that the driver sets up the BPQ devices automagically on
+ *	startup or (if started before the "insmod" of an ethernet device)
+ *	on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing
+ *	the ethernet device (in fact: as soon as another ethernet or bpq
+ *	device gets "ifconfig"ured).
+ *
+ *	I have heard that several people are thinking of experiments
+ *	with highspeed packet radio using existing ethernet cards.
+ *	Well, this driver is prepared for this purpose, just add
+ *	your tx key control and a txdelay / tailtime algorithm,
+ *	probably some buffering, and /voila/...
+ *
+ *	History
+ *	BPQ   001	Joerg(DL1BKE)		Extracted BPQ code from AX.25 
+ *						protocol stack and added my own
+ *						yet existing patches
+ *	BPQ   002	Joerg(DL1BKE)		Scan network device list on
+ *						startup.
+ *	BPQ   003	Joerg(DL1BKE)		Ethernet destination address
+ *						and accepted source address
+ *						can be configured by an ioctl()
+ *						call.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/module.h>
+#include <linux/net_alias.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <linux/bpqether.h>
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+	{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_defaddr[AX25_ADDR_LEN] =
+	{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+
+static char bpq_eth_addr[6];
+
+static int bpq_rcv(struct sk_buff *, struct device *, struct packet_type *);
+static int bpq_device_event(struct notifier_block *, unsigned long, void *);
+static char *bpq_print_ethaddr(unsigned char *);
+
+static struct packet_type bpq_packet_type = {
+	0,		/* ntohs(ETH_P_BPQ),*/
+	0,		/* copy */
+	bpq_rcv,
+	NULL,
+	NULL,
+};
+
+static struct notifier_block bpq_dev_notifier = {
+	bpq_device_event,
+	0
+};
+
+
+#define MAXBPQDEV 100
+
+static struct bpqdev {
+	struct bpqdev *next;
+	char   ethname[14];		/* ether device name */
+	struct device *ethdev;		/* link to ethernet device */
+	struct device axdev;		/* bpq device (bpq#) */
+	struct enet_statistics stats;	/* some statistics */
+	char   dest_addr[6];		/* ether destination address */
+	char   acpt_addr[6];		/* accept ether frames from this address only */
+} *bpq_devices = NULL;
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Get the ethernet device for a BPQ device
+ */
+static __inline__ struct device *bpq_get_ether_dev(struct device *dev)
+{
+	struct bpqdev *bpq;
+	
+	bpq = (struct bpqdev *)dev->priv;
+
+	return (bpq != NULL) ? bpq->ethdev : NULL;
+}
+
+/*
+ *	Get the BPQ device for the ethernet device
+ */
+static __inline__ struct device *bpq_get_ax25_dev(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+		if (bpq->ethdev == dev)
+			return &bpq->axdev;
+
+	return NULL;
+}
+
+static __inline__ int dev_is_ethdev(struct device *dev)
+{
+	return (
+			dev->type == ARPHRD_ETHER
+			&& strncmp(dev->name, "dummy", 5)
+#ifdef CONFIG_NET_ALIAS
+			&& !net_alias_is(dev)
+#endif
+	);
+}
+
+/*
+ *	Sanity check: remove all devices that ceased to exists and
+ *	return '1' if the given BPQ device was affected.
+ */
+static int bpq_check_devices(struct device *dev)
+{
+	struct bpqdev *bpq, *bpq_prev;
+	int result = 0;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	bpq_prev = NULL;
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) {
+		if (!dev_get(bpq->ethname)) {
+			if (bpq_prev)
+				bpq_prev->next = bpq->next;
+			else
+				bpq_devices = bpq->next;
+				
+			if (&bpq->axdev == dev)
+				result = 1;
+
+			unregister_netdev(&bpq->axdev);
+			kfree(bpq);
+		}
+
+		bpq_prev = bpq;
+	}
+	
+	restore_flags(flags);
+	
+	return result;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Receive an AX.25 frame via an ethernet interface.
+ */
+static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+	int len;
+	char * ptr;
+	struct ethhdr *eth = (struct ethhdr *)skb->mac.raw;
+	struct bpqdev *bpq;
+
+	skb->sk = NULL;		/* Initially we don't know who it's for */
+	
+	dev = bpq_get_ax25_dev(dev);
+
+	if (dev == NULL || dev->start == 0) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	/*
+	 * if we want to accept frames from just one ethernet device
+	 * we check the source address of the sender.
+	 */
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	if (!(bpq->acpt_addr[0] & 0x01) && memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN)) {
+		printk(KERN_DEBUG "bpqether: wrong dest %s\n", bpq_print_ethaddr(eth->h_source));
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	((struct bpqdev *)dev->priv)->stats.rx_packets++;
+
+	len = skb->data[0] + skb->data[1] * 256 - 5;
+
+	skb_pull(skb, 2);	/* Remove the length bytes */
+	skb_trim(skb, len);	/* Set the length of the data */
+
+	ptr = skb_push(skb, 1);
+	*ptr = 0;
+	
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+
+	return 0;
+}
+
+/*
+ * 	Send an AX.25 frame via an ethernet interface
+ */
+static int bpq_xmit(struct sk_buff *skb, struct device *dev)
+{
+	unsigned char *ptr;
+	struct bpqdev *bpq;
+	int size;
+	
+	/*
+	 * Just to be *really* sure not to send anything if the interface
+	 * is down, the ethernet device may have gone.
+	 */
+	if (!dev->start) {
+		bpq_check_devices(dev);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -ENODEV;
+	}
+
+	skb_pull(skb, 1);
+	size = skb->len;
+	
+	/* 
+	 * The AX.25 code leaves enough room for the ethernet header, but 
+	 * sendto() does not.
+	 */
+	if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {	/* Ough! */
+		struct sk_buff *newskb = alloc_skb(skb->len + AX25_BPQ_HEADER_LEN, GFP_ATOMIC);
+
+		if (newskb == NULL) {			/* Argh! */
+			printk(KERN_WARNING "bpq_xmit: not enough space to add BPQ Ether header\n");
+			dev_kfree_skb(skb, FREE_WRITE);
+			return -ENOMEM;
+		}
+
+		newskb->free = 1;
+		newskb->arp  = 1;
+		newskb->sk   = skb->sk;
+
+		skb_reserve(newskb, AX25_BPQ_HEADER_LEN);
+		memcpy(skb_put(newskb, size), skb->data, size);
+		dev_kfree_skb(skb, FREE_WRITE);
+		skb = newskb;
+
+		if (skb->sk != NULL)
+			atomic_add(skb->truesize, &skb->sk->wmem_alloc);
+	}
+	
+	skb->protocol = htons(ETH_P_AX25);
+
+	ptr = skb_push(skb, 2);
+
+	*ptr++ = (size + 5) % 256;
+	*ptr++ = (size + 5) / 256;
+	
+	bpq = (struct bpqdev *)dev->priv;
+	bpq->stats.tx_packets++;
+	
+	if ((dev = bpq_get_ether_dev(dev)) == NULL) {
+		bpq->stats.tx_dropped++;
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -ENODEV;
+	}
+
+	skb->dev = dev;
+	dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
+
+	return dev->hard_start_xmit(skb, dev);
+}
+
+/*
+ *	Statistics
+ */
+static struct enet_statistics * bpq_get_stats(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	return &bpq->stats;
+}
+
+
+/*
+ *	Rebuild header...
+ */
+static int bpq_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+	return ax25_rebuild_header((unsigned char *)buff, dev, raddr, skb);
+}
+
+/*
+ *	Set AX.25 callsign
+ */
+static int bpq_set_mac_address(struct device *dev, void *addr)
+{
+    struct sockaddr *sa = (struct sockaddr *)addr;
+
+    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+    return 0;
+}
+
+/*	Ioctl commands
+ *
+ *		SIOCSBPQETHOPT		reserved for enhancements
+ *		SIOCSBPQETHADDR		set the destination and accepted
+ *					source ethernet address (broadcast
+ *					or multicast: accept all)
+ */
+static int bpq_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	int err;
+	struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data;
+	struct bpqdev *bpq = dev->priv;
+	struct bpq_req req;
+
+	if (!suser())
+		return -EPERM;
+	
+	if (bpq == NULL)		/* woops! */
+		return -ENODEV;
+	
+	switch (cmd) {
+		case SIOCSBPQETHOPT:
+			if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0)
+				return err;
+			copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req));
+			switch (req.cmd) {
+				case SIOCGBPQETHPARAM:
+				case SIOCSBPQETHPARAM:
+				default:
+					return -EINVAL;
+			}
+			
+			break;
+			
+		case SIOCSBPQETHADDR:
+			if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0)
+				return err;
+			copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN);
+			copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * open/close a device
+ */
+static int bpq_open(struct device *dev)
+{
+	if (bpq_check_devices(dev))
+		return -ENODEV;		/* oops, it's gone */
+	
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int bpq_close(struct device *dev)
+{
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ * currently unused
+ */
+static int bpq_dev_init(struct device *dev)
+{
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Proc filesystem
+ */
+static char * bpq_print_ethaddr(unsigned char *e)
+{
+	static char buf[18];
+	
+	sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", 
+		e[0], e[1], e[2], e[3], e[4], e[5]);
+		
+	return buf;
+}
+
+int bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct bpqdev *bpqdev;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+  
+	cli();
+
+	len += sprintf(buffer, "dev   ether      destination        accept from\n");
+
+	for (bpqdev = bpq_devices; bpqdev != NULL; bpqdev = bpqdev->next) {
+		len += sprintf(buffer + len, "%-5s %-10s %s  ",
+			bpqdev->axdev.name, bpqdev->ethname,
+			bpq_print_ethaddr(bpqdev->dest_addr));
+
+		len += sprintf(buffer + len, "%s\n",
+			(bpqdev->acpt_addr[0] & 0x01) ? "*" : bpq_print_ethaddr(bpqdev->acpt_addr));
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+		
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Setup a new device.
+ */
+static int bpq_new_device(struct device *dev)
+{
+	int k;
+	unsigned char *buf;
+	struct bpqdev *bpq, *bpq2;
+	
+	if ((bpq = (struct bpqdev *)kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+		
+	memset(bpq, 0, sizeof(struct bpqdev));
+	
+	bpq->ethdev = dev;
+
+	bpq->ethname[sizeof(bpq->ethname)-1] = '\0';
+	strncpy(bpq->ethname, dev->name, sizeof(bpq->ethname)-1);
+
+	memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr));
+	memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
+
+	dev = &bpq->axdev;
+	buf = (unsigned char *)kmalloc(14, GFP_KERNEL);
+
+	for (k = 0; k < MAXBPQDEV; k++) {
+		struct device *odev;
+
+		sprintf(buf, "bpq%d", k);
+
+		if ((odev = dev_get(buf)) == NULL || bpq_check_devices(odev))
+			break;
+	}
+
+	if (k == MAXBPQDEV) {
+		kfree(bpq);
+		return -ENODEV;
+	}
+	
+	dev->priv = (void *)bpq;	/* pointer back */
+	dev->name = buf;
+	dev->init = bpq_dev_init;
+
+	if (register_netdev(dev) != 0) {
+		kfree(bpq);
+                return -EIO;
+        }
+
+	for (k=0; k < DEV_NUMBUFFS; k++)
+		skb_queue_head_init(&dev->buffs[k]);
+
+	dev->hard_start_xmit = bpq_xmit;
+	dev->hard_header     = ax25_encapsulate;
+	dev->rebuild_header  = bpq_rebuild_header;
+	dev->open	     = bpq_open;
+	dev->stop	     = bpq_close;
+	dev->set_mac_address = bpq_set_mac_address;
+	dev->get_stats	     = bpq_get_stats;
+	dev->do_ioctl	     = bpq_ioctl;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_defaddr, AX25_ADDR_LEN);
+
+	/* preset with reasonable values */
+
+#if CONFIG_INET
+	dev->flags      = 0;
+	dev->family     = AF_INET;
+	dev->pa_addr    = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask    = 0;
+	dev->pa_alen    = 4;
+#endif
+
+	dev->type            = ARPHRD_AX25;
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu             = AX25_DEF_PACLEN;
+	dev->addr_len        = AX25_ADDR_LEN;
+	
+	cli();
+
+	if (bpq_devices == NULL) {
+		bpq_devices = bpq;
+	} else {
+		for (bpq2 = bpq_devices; bpq2->next != NULL; bpq2 = bpq2->next);
+		bpq2->next = bpq;
+	}
+	
+	sti();
+
+	return 0;
+}
+
+
+/*
+ *	Handle device status changes.
+ */
+static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+	
+	if (!dev_is_ethdev(dev))
+		return NOTIFY_DONE;
+
+	bpq_check_devices(NULL);
+
+	switch (event) {
+		case NETDEV_UP:		/* new ethernet device -> new BPQ interface */
+			if (bpq_get_ax25_dev(dev) == NULL)
+				bpq_new_device(dev);
+			break;
+
+		case NETDEV_DOWN:	/* ethernet device closed -> close BPQ interface */
+			if ((dev = bpq_get_ax25_dev(dev)) != NULL)
+				dev_close(dev);
+			break;
+
+		default:
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Initialize driver. To be called from af_ax25 if not compiled as a
+ * module
+ */
+int bpq_init(void)
+{
+	struct device *dev;
+
+	bpq_packet_type.type  = htons(ETH_P_BPQ);
+	dev_add_pack(&bpq_packet_type);
+
+	register_netdevice_notifier(&bpq_dev_notifier);
+
+	printk(KERN_INFO "AX.25 ethernet driver version 0.01\n");
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_AX25_BPQETHER, 8, "bpqether",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		bpq_get_info
+	});
+#endif
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (dev_is_ethdev(dev))
+			bpq_new_device(dev);
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	register_symtab(NULL);
+
+	return bpq_init();
+}
+
+void cleanup_module(void)
+{
+	struct bpqdev *bpq;
+
+	dev_remove_pack(&bpq_packet_type);
+
+	unregister_netdevice_notifier(&bpq_dev_notifier);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_AX25_BPQETHER);
+#endif	
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+		unregister_netdev(&bpq->axdev);
+}
+#endif
diff -u --recursive --new-file v2.1.8/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.8/linux/drivers/net/de4x5.c	Tue Oct 29 19:58:11 1996
+++ linux/drivers/net/de4x5.c	Sat Nov  9 19:31:15 1996
@@ -29,8 +29,7 @@
     measurement. Their error is +/-20k on a quiet (private) network and also
     depend on what load the CPU has.
 
-    The author may    be reached  at  davies@wanton.lkg.dec.com  or  Digital
-    Equipment Corporation, 550 King Street, Littleton MA 01460.
+    The author may be reached at davies@maniac.ultranet.com.
 
     =========================================================================
     This driver has been written  substantially  from scratch, although  its
@@ -212,11 +211,13 @@
 			  and <michael@compurex.com>
       0.441   9-Sep-96    Change dc21041_autoconf() to probe quiet BNC media
                            with a loopback packet.
+      0.442   9-Sep-96    Include AUI in dc21041 media printout. Bug reported
+                           by <bhat@mundook.cs.mu.OZ.AU>
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.441 96/9/9 davies@wanton.lkg.dec.com\n";
+static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -915,7 +916,7 @@
 
     }
     
-    if (de4x5_debug > 0) {
+    if (de4x5_debug > 1) {
 	printk(version);
     }
     
@@ -3760,10 +3761,11 @@
 			(lp->media == TP  ? "TP." :
 			 (lp->media == ANS ? "TP/Nway." :
 			  (lp->media == BNC ? "BNC." : 
-			   (lp->media == BNC_AUI ? "BNC/AUI." : 
-			    (lp->media == EXT_SIA ? "EXT SIA." : 
-			     "???."
-			     )))))));
+			   (lp->media == AUI ? "AUI." : 
+			    (lp->media == BNC_AUI ? "BNC/AUI." : 
+			     (lp->media == EXT_SIA ? "EXT SIA." : 
+			      "???."
+			      ))))))));
 	    } else {
 		printk("%s: mode is %s\n", dev->name,
 		    (lp->media == NC ? "link down or incompatible connection.":
diff -u --recursive --new-file v2.1.8/linux/drivers/net/depca.c linux/drivers/net/depca.c
--- v2.1.8/linux/drivers/net/depca.c	Tue Oct 29 19:58:11 1996
+++ linux/drivers/net/depca.c	Sat Nov  9 19:31:15 1996
@@ -37,9 +37,7 @@
     I have benchmarked the driver with a  DE100 at 595kB/s to (542kB/s from)
     a DECstation 5000/200.
 
-    The   author   may    be   reached  at    davies@wanton.lkg.dec.com   or
-    davies@maniac.ultranet.com  or Digital  Equipment Corporation, 550  King
-    Street, Littleton MA 01460.
+    The author may be reached at davies@maniac.ultranet.com
 
     =========================================================================
 
@@ -208,7 +206,7 @@
     =========================================================================
 */
 
-static const char *version = "depca.c:v0.43 96/8/16 davies@wanton.lkg.dec.com\n";
+static const char *version = "depca.c:v0.43 96/8/16 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -650,7 +648,7 @@
       status = -ENXIO;
     }
     if (!status) {
-      if (depca_debug > 0) {
+      if (depca_debug > 1) {
 	printk(version);
       }
 
diff -u --recursive --new-file v2.1.8/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- v2.1.8/linux/drivers/net/ewrk3.c	Tue Oct 29 19:58:12 1996
+++ linux/drivers/net/ewrk3.c	Sat Nov  9 19:31:16 1996
@@ -18,9 +18,7 @@
     card and benchmarked with 'ttcp': it transferred 16M  of data at 975kB/s
     (7.8Mb/s) to a DECstation 5000/200.
 
-    The     author  may  be    reached    at  davies@wanton.lkg.dec.com   or
-    davies@maniac.ultranet.com or  Digital  Equipment Corporation,  550 King
-    Street, Littleton MA 01460.
+    The author may be reached at davies@maniac.ultranet.com.
 
     =========================================================================
     This driver has been written  substantially  from scratch, although  its
@@ -138,7 +136,7 @@
     =========================================================================
 */
 
-static const char *version = "ewrk3.c:v0.43 96/8/16 davies@wanton.lkg.dec.com\n";
+static const char *version = "ewrk3.c:v0.43 96/8/16 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -590,7 +588,7 @@
     }
 
     if (!status) {
-      if (ewrk3_debug > 0) {
+      if (ewrk3_debug > 1) {
 	printk(version);
       }
       
diff -u --recursive --new-file v2.1.8/linux/drivers/net/mkiss.c linux/drivers/net/mkiss.c
--- v2.1.8/linux/drivers/net/mkiss.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/mkiss.c	Sun Nov 10 19:12:56 1996
@@ -0,0 +1,1123 @@
+/*
+ *	MKISS Driver
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * 		This module implements the AX.25 protocol for kernel-based
+ *		devices like TTYs. It interfaces between a raw TTY, and the
+ *		kernel's AX.25 protocol layers, just like slip.c.
+ *		AX.25 needs to be seperated from slip.c while slip.c is no
+ *		longer a static kernel device since it is a module.
+ *		This method clears the way to implement other kiss protocols
+ *		like mkiss smack g8bpq ..... so far only mkiss is implemented.
+ *
+ * Hans Alblas Hansa@cuci.nl
+ *
+ *	History
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/major.h>
+
+#include <linux/timer.h>
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#include <net/ax25.h>
+
+#include "mkiss.h"
+
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#endif
+
+#ifdef MODULE
+#define AX25_VERSION    "AX25-MODULAR-NET3.019-NEWTTY"
+#define	min(a,b)	(a < b ? a : b)
+#else
+#define	AX25_VERSION	"AX25-NET3.019-NEWTTY"
+#endif
+
+#define NR_MKISS 4
+#define MKISS_SERIAL_TYPE_NORMAL 1
+
+struct mkiss_channel {
+	int magic;		/* magic word */
+	int init;		/* channel exists? */
+	struct tty_struct *tty; /* link to tty control structure */
+};
+
+typedef struct ax25_ctrl {
+	char if_name[8];	/* "ax0\0" .. "ax99999\0"	*/
+	struct ax_disp ctrl;	/* 				*/
+	struct device  dev;	/* the device			*/
+} ax25_ctrl_t;
+
+static ax25_ctrl_t **ax25_ctrls = NULL;
+int ax25_maxdev = AX25_MAXDEV;		/* Can be overridden with insmod! */
+
+static struct tty_ldisc	ax_ldisc;
+static struct tty_driver mkiss_driver;
+static int mkiss_refcount;
+static struct tty_struct *mkiss_table[NR_MKISS];
+static struct termios *mkiss_termios[NR_MKISS];
+static struct termios *mkiss_termios_locked[NR_MKISS];
+struct mkiss_channel MKISS_Info[NR_MKISS];
+
+static int ax25_init(struct device *);
+static int mkiss_init(void);
+static int mkiss_write(struct tty_struct *, int, const unsigned char *, int);
+static int kiss_esc(unsigned char *, unsigned char *, int);
+static void kiss_unesc(struct ax_disp *, unsigned char);
+
+/* Find a free channel, and link in this `tty' line. */
+static inline struct ax_disp *ax_alloc(void)
+{
+	ax25_ctrl_t *axp;
+	int i;
+
+	if (ax25_ctrls == NULL)		/* Master array missing ! */
+		return NULL;
+
+	for (i = 0; i < ax25_maxdev; i++) {
+		axp = ax25_ctrls[i];
+		
+		/* Not allocated ? */
+		if (axp == NULL)
+			break;
+			
+		/* Not in use ? */
+		if (!set_bit(AXF_INUSE, &axp->ctrl.flags))
+			break;
+	}
+
+	/* Sorry, too many, all slots in use */
+	if (i >= ax25_maxdev)
+		return NULL;
+
+	/* If no channels are available, allocate one */
+	if (axp == NULL && (ax25_ctrls[i] = (ax25_ctrl_t *)kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
+		axp = ax25_ctrls[i];
+		memset(axp, 0, sizeof(ax25_ctrl_t));
+
+		/* Initialize channel control data */
+		set_bit(AXF_INUSE, &axp->ctrl.flags);
+		sprintf(axp->if_name, "ax%d", i++);
+		axp->ctrl.tty      = NULL;
+		axp->dev.name      = axp->if_name;
+		axp->dev.base_addr = i;
+		axp->dev.priv      = (void *)&axp->ctrl;
+		axp->dev.next      = NULL;
+		axp->dev.init      = ax25_init;
+	}
+
+	if (axp != NULL) {
+		/*
+		 * register device so that it can be ifconfig'ed
+		 * ax25_init() will be called as a side-effect
+		 * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl !
+		 */
+		if (register_netdev(&axp->dev) == 0) {
+			/* (Re-)Set the INUSE bit.   Very Important! */
+			set_bit(AXF_INUSE, &axp->ctrl.flags);
+			axp->ctrl.dev = &axp->dev;
+			axp->dev.priv = (void *)&axp->ctrl;
+
+			return &axp->ctrl;
+		} else {
+			clear_bit(AXF_INUSE,&axp->ctrl.flags);
+			printk(KERN_ERR "ax_alloc() - register_netdev() failure.\n");
+		}
+	}
+
+	return NULL;
+}
+
+/* Free an AX25 channel. */
+static inline void ax_free(struct ax_disp *ax)
+{
+	/* Free all AX25 frame buffers. */
+	if (ax->rbuff)
+		kfree(ax->rbuff);
+	ax->rbuff = NULL;
+	if (ax->xbuff)
+		kfree(ax->xbuff);
+	ax->xbuff = NULL;
+	if (!clear_bit(AXF_INUSE, &ax->flags))
+		printk(KERN_ERR "%s: ax_free for already free unit.\n", ax->dev->name);
+}
+
+static void ax_changedmtu(struct ax_disp *ax)
+{
+	struct device *dev = ax->dev;
+	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+	int len;
+	unsigned long flags;
+
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	xbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC);
+	rbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC);
+
+	if (xbuff == NULL || rbuff == NULL)  {
+		printk(KERN_ERR "%s: unable to grow ax25 buffers, MTU change cancelled.\n",
+		       ax->dev->name);
+		dev->mtu = ax->mtu;
+		if (xbuff != NULL)
+			kfree(xbuff);
+		if (rbuff != NULL)
+			kfree(rbuff);
+		return;
+	}
+
+	save_flags(flags);
+	cli();
+
+	oxbuff    = ax->xbuff;
+	ax->xbuff = xbuff;
+	orbuff    = ax->rbuff;
+	ax->rbuff = rbuff;
+
+	if (ax->xleft) {
+		if (ax->xleft <= len) {
+			memcpy(ax->xbuff, ax->xhead, ax->xleft);
+		} else  {
+			ax->xleft = 0;
+			ax->tx_dropped++;
+		}
+	}
+
+	ax->xhead = ax->xbuff;
+
+	if (ax->rcount) {
+		if (ax->rcount <= len) {
+			memcpy(ax->rbuff, orbuff, ax->rcount);
+		} else  {
+			ax->rcount = 0;
+			ax->rx_over_errors++;
+			set_bit(AXF_ERROR, &ax->flags);
+		}
+	}
+
+	ax->mtu      = dev->mtu + 73;
+	ax->buffsize = len;
+
+	restore_flags(flags);
+
+	if (oxbuff != NULL)
+		kfree(oxbuff);
+	if (orbuff != NULL)
+		kfree(orbuff);
+}
+
+
+/* Set the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void ax_lock(struct ax_disp *ax)
+{
+	if (set_bit(0, (void *)&ax->dev->tbusy))
+		printk(KERN_ERR "%s: trying to lock already locked device!\n", ax->dev->name);
+}
+
+
+/* Clear the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void ax_unlock(struct ax_disp *ax)
+{
+	if (!clear_bit(0, (void *)&ax->dev->tbusy))
+		printk(KERN_ERR "%s: trying to unlock already unlocked device!\n", ax->dev->name);
+}
+
+/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
+static void ax_bump(struct ax_disp *ax)
+{
+	struct ax_disp *tmp_ax;
+	struct sk_buff *skb;
+	struct mkiss_channel *mkiss;
+	int count;
+	
+        tmp_ax = ax;
+                
+	if (ax->rbuff[0] > 0x0f) {
+		if (ax->mkiss != NULL) {
+			mkiss= ax->mkiss->tty->driver_data;
+			if (mkiss->magic == MKISS_DRIVER_MAGIC)
+				tmp_ax = ax->mkiss;
+		}
+ 	}
+ 	
+	count = ax->rcount;
+
+	if ((skb = dev_alloc_skb(count)) == NULL) {
+		printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", ax->dev->name);
+		ax->rx_dropped++;
+		return;
+	}
+
+	skb->dev      = tmp_ax->dev;
+	memcpy(skb_put(skb,count), ax->rbuff, count);
+	skb->mac.raw  = skb->data; 
+	skb->protocol = htons(ETH_P_AX25);
+	netif_rx(skb);
+	tmp_ax->rx_packets++;
+}
+
+/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
+static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len)
+{
+	unsigned char *p;
+	int actual, count;
+	struct mkiss_channel *mkiss = ax->tty->driver_data;
+
+	if (ax->mtu != ax->dev->mtu + 73)	/* Someone has been ifconfigging */
+		ax_changedmtu(ax);
+
+	if (len > ax->mtu) {		/* Sigh, shouldn't occur BUT ... */
+		len = ax->mtu;
+		printk(KERN_ERR "%s: truncating oversized transmit packet!\n", ax->dev->name);
+		ax->tx_dropped++;
+		ax_unlock(ax);
+		return;
+	}
+
+	p = icp;
+	
+	if (mkiss->magic  != MKISS_DRIVER_MAGIC) {
+		count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
+		ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count);
+		ax->tx_packets++;
+		ax->dev->trans_start = jiffies;
+		ax->xleft = count - actual;
+		ax->xhead = ax->xbuff + actual;
+	} else {
+		count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len);
+		ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count);
+		ax->tx_packets++; 
+		ax->mkiss->dev->trans_start = jiffies;
+		ax->mkiss->xleft = count - actual;
+		ax->mkiss->xhead = ax->mkiss->xbuff + actual;
+	}
+}
+
+/*
+ * Called by the driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+static void ax25_write_wakeup(struct tty_struct *tty)
+{
+	int actual;
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	struct mkiss_channel *mkiss;
+	
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+		return;
+	if (ax->xleft <= 0)  {
+		/* Now serial buffer is almost free & we can start
+		 * transmission of another packet
+		 */
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+		if (ax->mkiss != NULL) {
+			mkiss= ax->mkiss->tty->driver_data;
+	        	if (mkiss->magic  == MKISS_DRIVER_MAGIC)
+				ax_unlock(ax->mkiss);
+	        }
+
+		ax_unlock(ax);
+		mark_bh(NET_BH);
+		return;
+	}
+
+	actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft);
+	ax->xleft -= actual;
+	ax->xhead += actual;
+}
+
+/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
+static int ax_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	struct mkiss_channel *mkiss = ax->tty->driver_data;
+	struct ax_disp *tmp_ax;
+	
+	tmp_ax = NULL;
+	
+	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
+		if (skb->data[0] < 0x10)
+			skb->data[0] = skb->data[0] + 0x10;
+		tmp_ax = ax->mkiss;
+	}                
+
+	if (!dev->start)  {
+		printk(KERN_ERR "%s: xmit call when iface is down\n", dev->name);
+		return 1;
+	}
+
+	if (tmp_ax != NULL)
+		if (tmp_ax->dev->tbusy)
+			return 1;
+	     
+	if (tmp_ax != NULL)     
+		if (dev->tbusy) {
+			printk(KERN_ERR "mkiss: dev busy while serial dev is free\n");
+			ax_unlock(ax);
+	        }
+	        
+	if (dev->tbusy) {
+		/*
+		 * May be we must check transmitter timeout here ?
+		 *      14 Oct 1994 Dmitry Gorodchanin.
+		 */
+		if (jiffies - dev->trans_start  < 20 * HZ) {
+			/* 20 sec timeout not reached */
+			return 1;
+		}
+
+		printk(KERN_ERR "%s: transmit timed out, %s?\n", dev->name,
+		       (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ?
+		       "bad line quality" : "driver error");
+
+		ax->xleft = 0;
+		ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		ax_unlock(ax);
+	}
+	
+	/* We were not busy, so we are now... :-) */
+	if (skb != NULL) {
+		ax_lock(ax);
+		if (tmp_ax != NULL)
+			ax_lock(tmp_ax);
+		ax_encaps(ax, skb->data, skb->len);
+		dev_kfree_skb(skb, FREE_WRITE);
+	}
+
+	return 0;
+}
+
+
+/* Return the frame type ID */
+static int ax_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+	  void *daddr, void *saddr, unsigned len)
+{
+#ifdef CONFIG_INET
+	if (type != htons(ETH_P_AX25))
+		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+#endif
+	return 0;
+}
+
+
+static int ax_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+	return ax25_rebuild_header(buff, dev, raddr, skb);
+#endif
+	return 0;
+}
+
+
+/* Open the low-level part of the AX25 channel. Easy! */
+static int ax_open(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	unsigned long len;
+
+	if (ax->tty == NULL)
+		return -ENODEV;
+
+	/*
+	 * Allocate the frame buffers:
+	 *
+	 * rbuff	Receive buffer.
+	 * xbuff	Transmit buffer.
+	 * cbuff        Temporary compression buffer.
+	 */
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	if ((ax->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto norbuff;
+
+	if ((ax->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto noxbuff;
+
+	ax->mtu	     = dev->mtu + 73;
+	ax->buffsize = len;
+	ax->rcount   = 0;
+	ax->xleft    = 0;
+
+	ax->flags   &= (1 << AXF_INUSE);      /* Clear ESCAPE & ERROR flags */
+	/* Needed because address '0' is special */
+	if (dev->pa_addr == 0)
+		dev->pa_addr = ntohl(0xC0A80001);
+	dev->tbusy  = 0;
+	dev->start  = 1;
+
+	return 0;
+
+	/* Cleanup */
+	kfree(ax->xbuff);
+
+noxbuff:
+	kfree(ax->rbuff);
+
+norbuff:
+	return -ENOMEM;
+}
+
+
+/* Close the low-level part of the AX25 channel. Easy! */
+static int ax_close(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	if (ax->tty == NULL)
+		return -EBUSY;
+
+	ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	return 0;
+}
+
+static int ax25_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of data has been received, which can now be decapsulated
+ * and sent on to the AX.25 layer for further processing.
+ */
+static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+
+	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+		return;
+
+	/*
+	 * Argh! mtu change time! - costs us the packet part received
+	 * at the change
+	 */
+	if (ax->mtu != ax->dev->mtu + 73)
+		ax_changedmtu(ax);
+
+	/* Read the characters out of the buffer */
+	while (count--) {
+		if (fp != NULL && *fp++) {
+			if (!set_bit(AXF_ERROR, &ax->flags))
+				ax->rx_errors++;
+			cp++;
+			continue;
+		}
+
+		kiss_unesc(ax, *cp++);
+	}
+}
+
+static int ax25_open(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	struct ax_disp *tmp_ax;
+	struct mkiss_channel *mkiss;
+	int err, cnt;
+
+	/* First make sure we're not already connected. */
+	if (ax && ax->magic == AX25_MAGIC)
+		return -EEXIST;
+
+	/* OK.  Find a free AX25 channel to use. */
+	if ((ax = ax_alloc()) == NULL)
+		return -ENFILE;
+
+	ax->tty = tty;
+	tty->disc_data = ax;
+
+	ax->mkiss = NULL;
+	tmp_ax    = NULL;
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	/* Restore default settings */	
+	ax->dev->type = ARPHRD_AX25;
+
+	/* Perform the low-level AX25 initialization. */
+	if ((err = ax_open(ax->dev)))
+		return err;
+
+	mkiss= ax->tty->driver_data;
+
+	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
+		for (cnt = 1; cnt < ax25_maxdev; cnt++) {
+			if (ax25_ctrls[cnt]) {
+				if (ax25_ctrls[cnt]->dev.start) {
+					if (ax == &ax25_ctrls[cnt]->ctrl) {
+						cnt--;
+						tmp_ax = &ax25_ctrls[cnt]->ctrl;
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	if (tmp_ax != NULL) {
+		ax->mkiss     = tmp_ax;
+		tmp_ax->mkiss = ax;
+	}
+	
+	MOD_INC_USE_COUNT;
+
+	/* Done.  We have linked the TTY line to a channel. */
+	return ax->dev->base_addr;
+}
+
+static void ax25_close(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	int mkiss ;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return;
+
+	mkiss = ax->mode;
+	dev_close(ax->dev);
+	
+	tty->disc_data = 0;
+	ax->tty        = NULL;
+
+	/* VSV = very important to remove timers */
+	ax_free(ax);
+	unregister_netdev(ax->dev);
+
+	MOD_DEC_USE_COUNT;
+}
+
+
+static struct enet_statistics *ax_get_stats(struct device *dev)
+{
+	static struct enet_statistics stats;
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	memset(&stats, 0, sizeof(struct enet_statistics));
+
+	stats.rx_packets     = ax->rx_packets;
+	stats.tx_packets     = ax->tx_packets;
+	stats.rx_dropped     = ax->rx_dropped;
+	stats.tx_dropped     = ax->tx_dropped;
+	stats.tx_errors      = ax->tx_errors;
+	stats.rx_errors      = ax->rx_errors;
+	stats.rx_over_errors = ax->rx_over_errors;
+
+	return &stats;
+}
+
+
+/************************************************************************
+ *			   STANDARD ENCAPSULATION	        	 *
+ ************************************************************************/
+
+int kiss_esc(unsigned char *s, unsigned char *d, int len)
+{
+	unsigned char *ptr = d;
+	unsigned char c;
+
+	/*
+	 * Send an initial END character to flush out any
+	 * data that may have accumulated in the receiver
+	 * due to line noise.
+	 */
+
+	*ptr++ = END;
+
+	while (len-- > 0) {
+		switch (c = *s++) {
+			case END:
+				*ptr++ = ESC;
+				*ptr++ = ESC_END;
+				break;
+			case ESC:
+				*ptr++ = ESC;
+				*ptr++ = ESC_ESC;
+				break;
+			default:
+				*ptr++ = c;
+				break;
+		}
+	}
+
+	*ptr++ = END;
+
+	return ptr - d;
+}
+
+static void kiss_unesc(struct ax_disp *ax, unsigned char s)
+{
+	switch (s) {
+		case END:
+			/* drop keeptest bit = VSV */
+			if (test_bit(AXF_KEEPTEST, &ax->flags))
+				clear_bit(AXF_KEEPTEST, &ax->flags);
+
+			if (!clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
+				ax_bump(ax);
+
+			clear_bit(AXF_ESCAPE, &ax->flags);
+			ax->rcount = 0;
+			return;
+
+		case ESC:
+			set_bit(AXF_ESCAPE, &ax->flags);
+			return;
+		case ESC_ESC:
+			if (clear_bit(AXF_ESCAPE, &ax->flags))
+				s = ESC;
+			break;
+		case ESC_END:
+			if (clear_bit(AXF_ESCAPE, &ax->flags))
+				s = END;
+			break;
+	}
+
+	if (!test_bit(AXF_ERROR, &ax->flags)) {
+		if (ax->rcount < ax->buffsize) {
+			ax->rbuff[ax->rcount++] = s;
+			return;
+		}
+
+		ax->rx_over_errors++;
+		set_bit(AXF_ERROR, &ax->flags);
+	}
+}
+
+
+int ax_set_mac_address(struct device *dev, void *addr)
+{
+	int err;
+
+	if ((err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN)) != 0)
+		return err;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN);
+
+	return 0;
+}
+
+static int ax_set_dev_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
+
+	return 0;
+}
+
+
+/* Perform I/O control on an active ax25 channel. */
+static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	int err;
+	unsigned int tmp;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return -EINVAL;
+
+	switch (cmd) {
+	 	case SIOCGIFNAME:
+			if ((err = verify_area(VERIFY_WRITE, arg, strlen(ax->dev->name) + 1)) != 0)
+				return err;
+			copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1);
+			return 0;
+
+		case SIOCGIFENCAP:
+			if ((err = verify_area(VERIFY_WRITE, arg, sizeof(int))) != 0)
+				return err;
+			put_user(4, (int *)arg); 
+			return 0;
+
+		case SIOCSIFENCAP:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0)
+				return err;
+			get_user(tmp, (int *)arg);
+	 		ax->mode = tmp;
+			ax->dev->addr_len        = AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
+			ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
+			ax->dev->type            = ARPHRD_AX25;
+			return 0;
+
+		 case SIOCSIFHWADDR:
+			return ax_set_mac_address(ax->dev, arg);
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int ax_open_dev(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	if (ax->tty==NULL)
+		return -ENODEV;
+
+	return 0;
+}
+
+/* Initialize AX25 control device -- register AX25 line discipline */
+int mkiss_init_ctrl_dev(void)
+{
+	int status;
+
+	if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */
+	
+	if ((ax25_ctrls = (ax25_ctrl_t **)kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array !  No mkiss available\n");
+		return -ENOMEM;
+	}
+	
+	/* Clear the pointer array, we allocate devices when we need them */
+	memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */
+
+	/* Fill in our line protocol discipline, and register it */
+	memset(&ax_ldisc, 0, sizeof(ax_ldisc));
+	ax_ldisc.magic  = TTY_LDISC_MAGIC;
+	ax_ldisc.flags  = 0;
+	ax_ldisc.open   = ax25_open;
+	ax_ldisc.close  = ax25_close;
+	ax_ldisc.read   = NULL;
+	ax_ldisc.write  = NULL;
+	ax_ldisc.ioctl  = (int (*)(struct tty_struct *, struct file *, unsigned int, unsigned long))ax25_disp_ioctl;
+	ax_ldisc.select = NULL;
+
+	ax_ldisc.receive_buf  = ax25_receive_buf;
+	ax_ldisc.receive_room = ax25_receive_room;
+	ax_ldisc.write_wakeup = ax25_write_wakeup;
+
+	if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0)
+		printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status);
+
+	mkiss_init();
+
+#ifdef MODULE
+	return status;
+#else
+	/*
+	 * Return "not found", so that dev_init() will unlink
+	 * the placeholder device entry for us.
+	 */
+	return ENODEV;
+#endif
+}
+
+
+/* Initialize the driver.  Called by DDI. */
+static int ax25_init(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	int i;
+
+	static char ax25_bcast[AX25_ADDR_LEN] =
+		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+	static char ax25_test[AX25_ADDR_LEN] =
+		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+
+	if (ax == NULL)		/* Allocation failed ?? */
+		return -ENODEV;
+
+	/* Set up the "AX25 Control Block". (And clear statistics) */
+	memset(ax, 0, sizeof (struct ax_disp));
+	ax->magic  = AX25_MAGIC;
+	ax->dev	   = dev;
+	
+	/* Finish setting up the DEVICE info. */
+	dev->mtu             = AX_MTU;
+	dev->hard_start_xmit = ax_xmit;
+	dev->open            = ax_open_dev;
+	dev->stop            = ax_close;
+	dev->hard_header     = ax_header;
+	dev->get_stats	     = ax_get_stats;
+#ifdef HAVE_SET_MAC_ADDR
+	dev->set_mac_address = ax_set_dev_mac_address;
+#endif
+	dev->hard_header_len = 0;
+	dev->addr_len        = 0;
+	dev->type            = ARPHRD_AX25;
+	dev->tx_queue_len    = 10;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_test,  AX25_ADDR_LEN);
+
+	dev->rebuild_header  = ax_rebuild_header;
+
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	/* New-style flags. */
+	dev->flags      = 0;
+	dev->family     = AF_INET;
+	dev->pa_addr    = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask    = 0;
+	dev->pa_alen    = 4;
+
+	return 0;
+}
+
+static int mkiss_open(struct tty_struct *tty, struct file *filp)
+{
+	struct mkiss_channel *mkiss;
+	int chan;	 	 
+
+	chan = MINOR(tty->device) - tty->driver.minor_start;
+
+	if (chan < 0 || chan >= NR_MKISS)
+		return -ENODEV;
+
+	mkiss = &MKISS_Info[chan];
+
+	mkiss->magic =  MKISS_DRIVER_MAGIC;
+	mkiss->init  = 1;
+	mkiss->tty   = tty;
+
+	tty->driver_data = mkiss;
+
+	tty->termios->c_iflag  = IGNBRK | IGNPAR;
+	tty->termios->c_cflag  = B9600 | CS8 | CLOCAL;
+	tty->termios->c_cflag &= ~CBAUD;
+
+	return 0;
+}
+
+static void mkiss_close(struct tty_struct *tty, struct file * filp)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (mkiss == NULL || mkiss->magic != MKISS_DRIVER_MAGIC)
+                return;
+
+	mkiss->tty   = NULL;
+	mkiss->init  = 0;
+	tty->stopped = 0;
+}
+
+static int mkiss_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+	return 0;
+}
+
+static int mkiss_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	/* Ignore serial ioctl's */
+	switch (cmd) {
+		case TCSBRK:
+		case TIOCMGET:
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:		
+		case TCSETS:
+		case TCSETSF:		/* should flush first, but... */
+		case TCSETSW:		/* should wait until flush, but... */
+			return 0;		
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+
+static void mkiss_dummy(struct tty_struct *tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+	
+	if (tty == NULL)
+		return;
+
+	if (mkiss == NULL)
+		return;
+}
+
+static void mkiss_dummy2(struct tty_struct *tty, unsigned char ch)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+	
+	if (tty == NULL)
+		return;
+
+	if (mkiss == NULL)
+		return;
+}
+
+
+static int mkiss_write_room(struct tty_struct * tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return 0;
+
+	if (mkiss == NULL)
+		return 0;
+
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+
+static int mkiss_chars_in_buffer(struct tty_struct *tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return 0;
+
+	if (mkiss == NULL)
+		return 0;
+
+	return 0;
+}
+
+
+static void mkiss_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	/* we don't do termios */
+}
+
+/* ******************************************************************** */
+/* * 			Init MKISS driver 			      * */
+/* ******************************************************************** */
+
+static int mkiss_init(void)
+{
+	memset(&mkiss_driver, 0, sizeof(struct tty_driver));
+
+	mkiss_driver.magic       = MKISS_DRIVER_MAGIC;
+	mkiss_driver.name        = "mkiss";
+	mkiss_driver.major       = MKISS_MAJOR;		
+	mkiss_driver.minor_start = 0;
+	mkiss_driver.num         = NR_MKISS;
+	mkiss_driver.type        = TTY_DRIVER_TYPE_SERIAL;
+	mkiss_driver.subtype     = MKISS_SERIAL_TYPE_NORMAL;	/* not needed */
+
+	mkiss_driver.init_termios         = tty_std_termios;
+	mkiss_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
+	mkiss_driver.init_termios.c_cflag = B9600 | CS8 | CLOCAL;
+
+	mkiss_driver.flags           = TTY_DRIVER_REAL_RAW;
+	mkiss_driver.refcount        = &mkiss_refcount;
+	mkiss_driver.table           = mkiss_table;
+	mkiss_driver.termios         = (struct termios **)mkiss_termios;
+	mkiss_driver.termios_locked  = (struct termios **)mkiss_termios_locked;
+
+	mkiss_driver.ioctl           = mkiss_ioctl;
+	mkiss_driver.open            = mkiss_open;
+	mkiss_driver.close           = mkiss_close;
+	mkiss_driver.write           = mkiss_write;
+	mkiss_driver.write_room      = mkiss_write_room;
+	mkiss_driver.chars_in_buffer = mkiss_chars_in_buffer;
+	mkiss_driver.set_termios     = mkiss_set_termios;
+
+	/* some unused functions */
+	mkiss_driver.flush_buffer = mkiss_dummy;
+	mkiss_driver.throttle     = mkiss_dummy;
+	mkiss_driver.unthrottle   = mkiss_dummy;
+	mkiss_driver.stop         = mkiss_dummy;
+	mkiss_driver.start        = mkiss_dummy;
+	mkiss_driver.hangup       = mkiss_dummy;
+	mkiss_driver.flush_chars  = mkiss_dummy;        
+	mkiss_driver.put_char     = mkiss_dummy2;        
+       
+	if (tty_register_driver(&mkiss_driver)) {
+		printk(KERN_ERR "Couldn't register Mkiss device\n");
+		return -EIO;		           
+	}                                
+
+	printk(KERN_INFO "AX.25 Multikiss device enabled\n");
+
+	return 0;		
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	register_symtab(NULL);
+
+	return mkiss_init_ctrl_dev();
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+	if (ax25_ctrls != NULL) {
+		for (i = 0; i < ax25_maxdev; i++) {
+			if (ax25_ctrls[i]) {
+				/*
+				 * VSV = if dev->start==0, then device
+				 * unregistred while close proc.
+				 */ 
+				if (ax25_ctrls[i]->dev.start)
+					unregister_netdev(&(ax25_ctrls[i]->dev));
+
+				kfree(ax25_ctrls[i]);
+				ax25_ctrls[i] = NULL;
+			}
+		}
+
+		kfree(ax25_ctrls);
+		ax25_ctrls = NULL;
+	}
+
+	if ((i = tty_register_ldisc(N_AX25, NULL)))  
+		printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i);
+
+	if (tty_unregister_driver(&mkiss_driver))	/* remove devive */
+		printk(KERN_ERR "mkiss: can't unregister MKISS device\n");
+}
+ 
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.8/linux/drivers/net/mkiss.h linux/drivers/net/mkiss.h
--- v2.1.8/linux/drivers/net/mkiss.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/mkiss.h	Sun Nov 10 19:12:56 1996
@@ -0,0 +1,56 @@
+/****************************************************************************
+ *	Defines for the Multi-KISS driver.
+ ****************************************************************************/
+
+#define AX25_MAXDEV	16		/* MAX number of AX25 channels;
+					   This can be overridden with
+					   insmod -oax25_maxdev=nnn	*/
+#define AX_MTU		236	
+
+/* SLIP/KISS protocol characters. */
+#define END             0300		/* indicates end of frame	*/
+#define ESC             0333		/* indicates byte stuffing	*/
+#define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
+#define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
+
+struct ax_disp {
+	int                magic;
+
+	/* Various fields. */
+	struct tty_struct  *tty;		/* ptr to TTY structure		*/
+	struct device      *dev;		/* easy for intr handling	*/
+	struct ax_disp     *mkiss;		/* mkiss txport if mkiss channel*/
+
+	/* These are pointers to the malloc()ed frame buffers. */
+	unsigned char      *rbuff;		/* receiver buffer		*/
+	int                rcount;		/* received chars counter       */
+	unsigned char      *xbuff;		/* transmitter buffer		*/
+	unsigned char      *xhead;		/* pointer to next byte to XMIT */
+	int                xleft;		/* bytes left in XMIT queue     */
+
+	/* SLIP interface statistics. */
+	unsigned long      rx_packets;		/* inbound frames counter	*/
+	unsigned long      tx_packets;		/* outbound frames counter      */
+	unsigned long      rx_errors;		/* Parity, etc. errors          */
+	unsigned long      tx_errors;		/* Planned stuff                */
+	unsigned long      rx_dropped;		/* No memory for skb            */
+	unsigned long      tx_dropped;		/* When MTU change              */
+	unsigned long      rx_over_errors;	/* Frame bigger then SLIP buf.  */
+
+	/* Detailed SLIP statistics. */
+	int                 mtu;		/* Our mtu (to spot changes!)   */
+	int                 buffsize;		/* Max buffers sizes            */
+
+
+	unsigned char       flags;		/* Flag values/ mode etc	*/
+#define AXF_INUSE	0		/* Channel in use               */
+#define AXF_ESCAPE	1               /* ESC received                 */
+#define AXF_ERROR	2               /* Parity, etc. error           */
+#define AXF_KEEPTEST	3		/* Keepalive test flag		*/
+#define AXF_OUTWAIT	4		/* is outpacket was flag	*/
+
+	int                 mode;
+};
+
+#define AX25_MAGIC		0x5316
+#define MKISS_DRIVER_MAGIC	1215
diff -u --recursive --new-file v2.1.8/linux/drivers/net/pi2.c linux/drivers/net/pi2.c
--- v2.1.8/linux/drivers/net/pi2.c	Tue Oct 29 19:58:12 1996
+++ linux/drivers/net/pi2.c	Sun Nov 10 19:12:57 1996
@@ -53,6 +53,7 @@
    Oct  29, 1995 (ac)  A couple of minor fixes before this, and this release changes
    		       to the proper set_mac_address semantics which will break 
    		       a few programs I suspect.
+   Aug  18, 1996 (jsn) Converted to be used as a module.
 */
 
 /* The following #define invokes a hack that will improve performance (baud)
@@ -81,19 +82,11 @@
 #define DEF_B_SQUELDELAY 3	/* 30 mS */
 #define DEF_B_CLOCKMODE 0	/* Normal clock mode */
 
-static const char *version =
-"PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n";
-
 /* The following #define is only really required for the PI card, not
    the PI2 - but it's safer to leave it in. */
 #define REALLY_SLOW_IO 1
 
-#define PI2_MODULE 0
-
-#if PI2_MODULE > 0
 #include <linux/module.h>
-#endif
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
@@ -120,7 +113,6 @@
 #include "z8530.h"
 #include <net/ax25.h>
 
-
 struct mbuf {
     struct mbuf *next;
     int cnt;
@@ -553,7 +545,7 @@
 
 	    skb = dev_alloc_skb(sksize);
 	    if (skb == NULL) {
-		printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+		printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
 		lp->stats.rx_dropped++;
 		restore_flags(flags);
 		return;
@@ -642,7 +634,7 @@
 		sksize = pkt_len;
 		skb = dev_alloc_skb(sksize);
 		if (skb == NULL) {
-		    printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+		    printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
 		    lp->stats.rx_dropped++;
 		    restore_flags(flags);
 		    return;
@@ -1214,27 +1206,27 @@
     int ports[] =
     {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0};
 
-    printk(version);
+    printk(KERN_INFO "PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n");
 
     /* Only one card supported for now */
     for (port = &ports[0]; *port && !card_type; port++) {
 	ioaddr = *port;
 
 	if (check_region(ioaddr, PI_TOTAL_SIZE) == 0) {
-	    printk("PI: Probing for card at address %#3x\n",ioaddr);
+	    printk(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr);
 	    card_type = hw_probe(ioaddr);
 	}
     }
 
     switch (card_type) {
     case 1:
-	printk("PI: Found a PI card at address %#3x\n", ioaddr);
+	printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr);
 	break;
     case 2:
-	printk("PI: Found a PI2 card at address %#3x\n", ioaddr);
+	printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr);
 	break;
     default:
-	printk("PI: ERROR: No card found\n");
+	printk(KERN_ERR "PI: ERROR: No card found\n");
 	return -EIO;
     }
 
@@ -1256,6 +1248,7 @@
     register_netdev(&pi0b);
     pi0b.base_addr = ioaddr;
     pi0b.irq = 0;
+
     pi0b.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
 
     /* Now initialize them */
@@ -1275,8 +1268,9 @@
 	return 0;
 }
 
-static int pi_set_mac_address(struct device *dev, struct sockaddr *sa)
+static int pi_set_mac_address(struct device *dev, void *addr)
 {
+    struct sockaddr *sa = (struct sockaddr *)addr;
     memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);	/* addr is an AX.25 shifted ASCII */
     return 0;						/* mac address */
 }
@@ -1364,7 +1358,7 @@
 
 	lp->dmachan = dev->dma;
 	if (lp->dmachan < 1 || lp->dmachan > 3)
-	    printk("PI: DMA channel %d out of range\n", lp->dmachan);
+	    printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan);
 
 	/* chipset_init() was already called */
 
@@ -1381,7 +1375,7 @@
 	    /* 20 "jiffies" should be plenty of time... */
 	    dev->irq = autoirq_report(20);
 	    if (!dev->irq) {
-		printk(". Failed to detect IRQ line.\n");
+		printk(KERN_ERR "PI: Failed to detect IRQ line.\n");
 	    }
 	    save_flags(flags);
 	    cli();
@@ -1391,7 +1385,7 @@
 	    restore_flags(flags);
 	}
 
-	printk("PI: Autodetected IRQ %d, assuming DMA %d.\n",
+	printk(KERN_INFO "PI: Autodetected IRQ %d, assuming DMA %d.\n",
 	       dev->irq, dev->dma);
 
 	/* This board has jumpered interrupts. Snarf the interrupt vector
@@ -1400,7 +1394,7 @@
 	{
 	    int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", NULL);
 	    if (irqval) {
-		printk("PI: unable to get IRQ %d (irqval=%d).\n",
+		printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n",
 		       dev->irq, irqval);
 		return EAGAIN;
 	    }
@@ -1487,6 +1481,9 @@
     dev->interrupt = 0;
     dev->start = 1;
     first_time = 0;
+
+    MOD_INC_USE_COUNT;
+
     return 0;
 }
 
@@ -1520,7 +1517,7 @@
 
 #if 0
     if (dev_a == NULL) {
-	printk("PI: pi_interrupt(): irq %d for unknown device.\n", irq);
+	printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq);
 	return;
     }
 #endif    
@@ -1589,6 +1586,9 @@
 	free_p(ptr);
 
     restore_flags(flags);
+
+    MOD_DEC_USE_COUNT;
+
     return 0;
 }
 
@@ -1681,6 +1681,28 @@
     return &lp->stats;
 }
 
+#ifdef MODULE
+int init_module(void)
+{
+    register_symtab(NULL);
+    return pi_init();
+}
+
+void cleanup_module(void)
+{
+    free_irq(pi0a.irq, NULL);	/* IRQs and IO Ports are shared */
+    release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE);
+    irq2dev_map[pi0a.irq] = NULL;
+
+    kfree(pi0a.priv);
+    pi0a.priv = NULL;
+    unregister_netdev(&pi0a);
+
+    kfree(pi0b.priv);
+    pi0b.priv = NULL;
+    unregister_netdev(&pi0b);
+}
+#endif
 
 /*
  * Local variables:
diff -u --recursive --new-file v2.1.8/linux/drivers/net/pt.c linux/drivers/net/pt.c
--- v2.1.8/linux/drivers/net/pt.c	Tue Oct 29 19:58:12 1996
+++ linux/drivers/net/pt.c	Sun Nov 10 19:12:57 1996
@@ -32,6 +32,8 @@
  * 07/10/95 cs  Fixed for 1.3.30 (hopefully)
  * 26/11/95 cs  Fixed for 1.3.43, ala 29/10 for pi2.c by ac
  * 21/12/95 cs  Got rid of those nasty warnings when compiling, for 1.3.48
+ * 08/08/96 jsn Convert to use as a module. Removed send_kiss, empty_scc and
+ *		pt_loopback functions - they were unused.
  */
  
 /* 
@@ -65,6 +67,7 @@
 #define	PARAM_RETURN	255
 
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -90,10 +93,6 @@
 #include "z8530.h"
 #include <net/ax25.h>
 
-static char *version =
-"PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n";
-
-
 struct mbuf {
     struct mbuf *next;
     int cnt;
@@ -133,9 +132,7 @@
 static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
 static int hw_probe(int ioaddr);
 static void tdelay(struct pt_local *lp, int time);
-static void empty_scc(struct pt_local *lp);
 static void chipset_init(struct device *dev);
-static void send_kiss(struct device *dev, unsigned char arg, unsigned char val);
 
 static char ax25_bcast[7] =
 {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
@@ -185,7 +182,9 @@
 	ptr = skb->data;
 	if (ptr[0] != 0 && skb->len >= 2)
 	{
-		printk("Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
+#ifdef PT_DEBUG
+		printk(KERN_DEBUG "PT: Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
+#endif
 		/* Kludge to get device */
 		if ((struct pt_local*)(&pt0b.priv) == lp)
 			dev = &pt0b;
@@ -197,21 +196,17 @@
 			case PARAM_TXDELAY:
 				/*TxDelay is in 10mS increments */
 				lp->txdelay = ptr[1] * 10;
-				send_kiss(dev, PARAM_TXDELAY, (u_char)(lp->txdelay/10));
 				break;
 			case PARAM_PERSIST:
 				lp->persist = ptr[1];
-				send_kiss(dev, PARAM_PERSIST, (u_char)(lp->persist));
 				break;
 			case PARAM_SLOTTIME:
 				lp->slotime = ptr[1];
-				send_kiss(dev, PARAM_SLOTTIME, (u_char)(lp->slotime/10));
 				break;
 			case PARAM_FULLDUP:
 				/* Yeah right, you wish!  Fullduplex is a little while to
 				 * go folks, but this is how you fire it up
 				 */
-				send_kiss(dev, PARAM_FULLDUP, 0);
 				break;
 			/* Perhaps we should have txtail here?? */
 		} /*switch */
@@ -226,7 +221,7 @@
     restore_flags(flags);
 
 #ifdef PT_DEBUG
-	printk("PTd hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
 #endif	
     skb_queue_tail(&lp->sndq, skb);
     if (kickflag) {
@@ -320,21 +315,6 @@
     dev_kfree_skb(skb, FREE_WRITE);
 }
 
-static void pt_loopback(struct pt_local *lp, int onoff)
-{
-    if (lp->base & CHANA) {
-        if (onoff == ON)
-            outb_p(pt_sercfg |= PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
-        else
-            outb_p(pt_sercfg &= ~PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
-    } else {    /* it's channel B */
-        if (onoff == ON)
-            outb_p(pt_sercfg |= PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
-        else
-            outb_p(pt_sercfg &= ~PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
-    }
-} /*pt_loopback */
-
 /* Fill in the MAC-level header */
 static int pt_header (struct sk_buff *skb, struct device *dev, unsigned short type,
 		void *daddr, void *saddr, unsigned len)
@@ -366,7 +346,7 @@
     int tc, br;
 
 #ifdef PT_DEBUG
-	printk("PTd scc_init(): (%d).\n", lp->base & CHANA);
+	printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA);
 #endif	
     save_flags(flags);
     cli();
@@ -486,8 +466,8 @@
 
 	struct pt_local *lp = (struct pt_local*) dev->priv;
 #ifdef PT_DEBUG
-	printk("PTd chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
-	printk("PTd chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);	
+	printk(KERN_DEBUG "PT: chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
+	printk(KERN_DEBUG "PT: chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);	
 #endif
 	/* Reset SCC if both channels are to be canned */
 	if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) ||
@@ -498,7 +478,7 @@
 	        outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG);
 	        outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG);
 #ifdef PT_DEBUG
-		printk("PTd chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
+		printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
 #endif			        
 	}
 	/* Reset individual channel */
@@ -522,20 +502,20 @@
     { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
       0x2b0, 0x300, 0x330, 0x3f0,  0};
 
-    printk(version);
+    printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n");
 
     for (port = &ports[0]; *port && !card_type; port++) {
         ioaddr = *port;
 
         if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) {
-            printk("PT: Probing for card at address %#3x\n", ioaddr);
+            printk(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr);
             card_type = hw_probe(ioaddr);
         }
     }
     if (card_type) {
-        printk("PT: Found a PT at address %#3x\n",ioaddr);
+        printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr);
     } else {
-        printk("PT: ERROR: No card found.\n");
+        printk(KERN_ERR "PT: ERROR: No card found.\n");
         return -EIO;
     }
 
@@ -641,7 +621,7 @@
 	long br;
 	int cmd = lp->base + CTL;
 #ifdef PT_DEBUG
-	printk("PTd pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
 #endif			
 	if (x == ON) {
 	    /* Ex ints off to avoid int */
@@ -738,8 +718,9 @@
         return 0;
 }
 
-static int pt_set_mac_address(struct device *dev, struct sockaddr *sa)
+static int pt_set_mac_address(struct device *dev, void *addr)
 {
+	struct sockaddr *sa = (struct sockaddr *)addr;
 	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);		/* addr is an AX.25 shifted ASCII */
 	return 0;		/* mac address */
 }
@@ -857,11 +838,11 @@
             restore_flags(flags);
 
             if (!dev->irq) {
-                printk("PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
+                printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
             }
         }
 
-        printk("PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
+        printk(KERN_INFO "PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
 
         /* This board has jumpered interrupts. Snarf the interrupt vector
          * now.  There is no point in waiting since no other device can use
@@ -870,7 +851,7 @@
         {
             int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", NULL);
             if (irqval) {
-                printk("PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
+                printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
                     dev->irq, irqval);
                 return EAGAIN;
             }
@@ -965,6 +946,8 @@
     dev->interrupt = 0;
     dev->start = 1;
     first_time = 0;
+
+    MOD_INC_USE_COUNT;
 	
     return 0;
 } /* pt_open() */
@@ -974,7 +957,7 @@
 	struct pt_local *lp = (struct pt_local *) dev->priv;
 
 #ifdef PT_DEBUG
-	printk("PTd pt_send_packet(): (%d)\n", lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA);
 #endif			
 	/* If some higher layer thinks we've missed an tx-done interrupt
 	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
@@ -1019,8 +1002,10 @@
 	restore_flags(flags);
 	
 #ifdef PT_DEBUG
-	printk("PTd pt_close(): Closing down channel (%d).\n", lp->base & CHANA);	
+	printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA);	
 #endif	
+
+	MOD_DEC_USE_COUNT;
 	
 	return 0;
 } /* pt_close() */
@@ -1153,7 +1138,7 @@
 	cmd = lp->base + CTL;
 
 #ifdef PT_DEBUG
-	printk("PTd pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
 #endif	
 	
 	switch (lp->tstate) 
@@ -1253,7 +1238,7 @@
 	   restore_flags(flags);
 	   return;
 	default:
-		printk("PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
+		printk(KERN_ERR "PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
 		pt_rts(lp, OFF);
 		lp->tstate = IDLE;
 		break;		
@@ -1270,7 +1255,7 @@
     char rse;
     struct sk_buff *skb;
     int sksize, pkt_len;
-    struct mbuf *cur_buf;
+    struct mbuf *cur_buf = NULL;
     unsigned char *cfix;
 
     save_flags(flags);
@@ -1280,7 +1265,7 @@
     rse = rdscc(lp->cardbase, cmd, R1);
     
 #ifdef PT_DEBUG
-    printk("PTd pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
+    printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
 #endif        
 
 	if (lp->dmachan && (rse & Rx_OVR))
@@ -1333,7 +1318,7 @@
      if (rse & END_FR)
      {
 #ifdef PT_DEBUG
-	printk("PTd pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
+	printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
 #endif	     
 		if (lp->dmachan)
 		{
@@ -1371,7 +1356,7 @@
         	         
         	     }
 #ifdef PT_DEBUG
-	printk("PTd pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
+	printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
 #endif                 
              } else {
                  /* We have a valid frame */
@@ -1392,7 +1377,7 @@
                  skb = dev_alloc_skb(sksize);
                  if (skb == NULL)
                  {
-                     printk("PT: %s: Memory squeeze, dropping packet.\n", dev->name);
+                     printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name);
                      lp->stats.rx_dropped++;
                      restore_flags(flags);
                      return;
@@ -1428,15 +1413,6 @@
      restore_flags(flags);
 } /* pt_rxisr() */
 
-/* Read the SCC channel till no more data in receiver */
-static void empty_scc(struct pt_local *lp)
-{
-    while( rdscc(lp->cardbase, lp->base + CTL, R0) & Rx_CH_AV) {
-        /* Get data from Rx buffer and toss it */
-        (void) inb_p(lp->base + DATA);
-    }
-} /* empty_scc()*/
-
 /*
  * This handles the two timer interrupts.
  * This is a real bugger, cause you have to rip it out of the pi's
@@ -1447,7 +1423,7 @@
     unsigned long flags;
 
 #ifdef PT_DEBUG
-	printk("PTd pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
 #endif		
 	
     save_flags(flags);
@@ -1467,9 +1443,9 @@
       
     default:
 	if (lp->base & CHANA)
- 	    printk("PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
+ 	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
 	else
-	    printk("PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
+	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
 	break;		
     } /* end switch */
     restore_flags(flags);
@@ -1498,11 +1474,11 @@
         {
         	/* Read interrupt vector from R2, channel B */
 #ifdef PT_DEBUG
-		printk("PTd pt_interrupt(): R3 = %#3x", st);
+		printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st);
 #endif		        
 /*        	st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/
 #ifdef PT_DEBUG
-		printk(" R2 = %#3x.\n", st);
+		printk(KERN_DEBUG "PI: R2 = %#3x.\n", st);
 #endif	
 			if (st & CHARxIP) {
 			    /* Channel A Rx */
@@ -1573,7 +1549,7 @@
     st = rdscc(lp->cardbase, cmd, R0);
 
 #ifdef PT_DEBUG
-	printk("PTd exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
 #endif	
     /* Reset external status latch */
     wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
@@ -1588,7 +1564,7 @@
     {
     case ACTIVE:		/* Unexpected underrun */
 #ifdef PT_DEBUG
-	printk("PTd exisr(): unexpected underrun detected.\n");
+	printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n");
 #endif	    
         free_p(lp->sndbuf);
         lp->sndbuf = NULL;
@@ -1746,7 +1722,7 @@
     if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) )
     {
 #ifdef PT_DEBUG
-	printk("PTd exisr(): abort detected.\n");
+	printk(KERN_DEBUG "PT: exisr(): abort detected.\n");
 #endif        
   		/* read and dump all of SCC Rx FIFO */
         (void) rdscc(lp->cardbase, cmd, R8);
@@ -1765,7 +1741,7 @@
     if ( (st & DCD) != (lp->saved_RR0 & DCD))
     {
 #ifdef PT_DEBUG    
-        printk("PTd: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
+        printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
 #endif
 		if (st & DCD)
 		{
@@ -1773,7 +1749,7 @@
 			if (lp->rcvbuf->cnt > 0)
 			{
 #ifdef PT_DEBUG
-				printk("PTd pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
+				printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
 #endif					
 				/* wind back buffers */
 				lp->rcp = lp->rcvbuf->data;
@@ -1801,30 +1777,25 @@
 
 } /* pt_exisr() */
 
-/* This function is used to send the KISS params back to the kernel itself,
- * just like the TNCs do (I think)
- * It's a (bit of a) kludge
- */
-static void send_kiss(struct device *dev, unsigned char arg, unsigned char val)
+#ifdef MODULE
+int init_module(void)
 {
-	struct sk_buff *skb;
-	unsigned char *cfix;
-/*	struct pt_local *lp = (struct pt_local*)dev->priv;*/
-	
-	
-	skb = dev_alloc_skb(2);
-	if (skb == NULL)
-	{
-		printk("PT: send_kiss(): Memory squeeze, dropping KISS reply.\n");
-		return;
-	}
-	skb->dev = dev;
-	cfix = skb_put(skb, 2);
-	cfix[0]=arg;
-	cfix[1]=val;
-	skb->protocol=htons(ETH_P_AX25);
-	skb->mac.raw=skb->data;
-	IS_SKB(skb);
-	netif_rx(skb);
+    register_symtab(NULL);
+    return pt_init();
 }
-	
+
+void cleanup_module(void)
+{
+    free_irq(pt0a.irq, NULL);	/* IRQs and IO Ports are shared */
+    release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE);
+    irq2dev_map[pt0a.irq] = NULL;
+
+    kfree(pt0a.priv);
+    pt0a.priv = NULL;
+    unregister_netdev(&pt0a);
+    
+    kfree(pt0b.priv);
+    pt0b.priv = NULL;
+    unregister_netdev(&pt0b);
+}
+#endif
diff -u --recursive --new-file v2.1.8/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.8/linux/drivers/net/slip.c	Tue Oct 29 19:58:12 1996
+++ linux/drivers/net/slip.c	Sun Nov 10 19:12:57 1996
@@ -66,10 +66,6 @@
 #include <linux/tty.h>
 #include <linux/errno.h>
 #include <linux/netdevice.h>
-#ifdef CONFIG_AX25
-#include <linux/timer.h>
-#include <net/ax25.h>
-#endif
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
@@ -294,11 +290,8 @@
 			set_bit(SLF_ERROR, &sl->flags);
 		}
 	}
-#ifdef CONFIG_AX25
-	sl->mtu      = dev->mtu + 73;
-#else
 	sl->mtu      = dev->mtu;
-#endif
+	
 	sl->buffsize = len;
 
 	restore_flags(flags);
@@ -386,10 +379,7 @@
 	skb->dev = sl->dev;
 	memcpy(skb_put(skb,count), sl->rbuff, count);
 	skb->mac.raw=skb->data;
-	if(sl->mode & SL_MODE_AX25)
-		skb->protocol=htons(ETH_P_AX25);
-	else
-		skb->protocol=htons(ETH_P_IP);
+	skb->protocol=htons(ETH_P_IP);
 	netif_rx(skb);
 	sl->rx_packets++;
 }
@@ -402,11 +392,8 @@
 	int actual, count;
 
 
-#ifdef CONFIG_AX25
-	if (sl->mtu != sl->dev->mtu + 73) {	/* Someone has been ifconfigging */
-#else
 	if (sl->mtu != sl->dev->mtu) {	/* Someone has been ifconfigging */
-#endif
+
 		sl_changedmtu(sl);
 	}
 
@@ -535,15 +522,6 @@
 sl_header(struct sk_buff *skb, struct device *dev, unsigned short type,
 	  void *daddr, void *saddr, unsigned len)
 {
-#ifdef CONFIG_AX25
-#ifdef CONFIG_INET
-	struct slip *sl = (struct slip*)(dev->priv);
-
-	if (sl->mode & SL_MODE_AX25 && type != htons(ETH_P_AX25))  {
-		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
-	}
-#endif
-#endif
 	return 0;
 }
 
@@ -553,15 +531,6 @@
 sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
 		  struct sk_buff *skb)
 {
-#ifdef CONFIG_AX25
-#ifdef CONFIG_INET
-	struct slip *sl = (struct slip*)(dev->priv);
-
-	if (sl->mode & SL_MODE_AX25) {
-		return ax25_rebuild_header(buff, dev, raddr, skb);
-	}
-#endif
-#endif
 	return 0;
 }
 
@@ -611,12 +580,7 @@
 		goto noslcomp;
 	}
 #endif
-
-#ifdef CONFIG_AX25
-	sl->mtu	     = dev->mtu + 73;
-#else
 	sl->mtu	     = dev->mtu;
-#endif
 	sl->buffsize = len;
 	sl->rcount   = 0;
 	sl->xleft    = 0;
@@ -701,11 +665,8 @@
 	 * Argh! mtu change time! - costs us the packet part received
 	 * at the change
 	 */
-#ifdef CONFIG_AX25
-	if (sl->mtu != sl->dev->mtu + 73)  {
-#else
 	if (sl->mtu != sl->dev->mtu)  {
-#endif
+
 		sl_changedmtu(sl);
 	}
 
@@ -762,11 +723,6 @@
 	/* Restore default settings */
 	sl->mode      = SL_MODE_DEFAULT;
 	sl->dev->type = ARPHRD_SLIP + sl->mode;
-#ifdef CONFIG_AX25	
-	if (sl->dev->type == 260) {		/* KISS */
-		sl->dev->type = ARPHRD_AX25;
-	}
-#endif	
 	/* Perform the low-level SLIP initialization. */
 	if ((err = sl_open(sl->dev)))  {
 		return err;
@@ -1008,32 +964,6 @@
 }
 #endif /* CONFIG_SLIP_MODE_SLIP6 */
 
-#ifdef CONFIG_AX25
-int
-sl_set_mac_address(struct device *dev, void *addr)
-{
-	int err;
-
-	err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN);
-	if (err)  {
-		return err;
-	}
-
-	copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN);	/* addr is an AX.25 shifted ASCII mac address */
-
-	return 0;
-}
-
-static int
-sl_set_dev_mac_address(struct device *dev, void *addr)
-{
-	struct sockaddr *sa=addr;
-	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
-	return 0;
-}
-#endif /* CONFIG_AX25 */
-
-
 /* Perform I/O control on an active SLIP channel. */
 static int
 slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
@@ -1086,34 +1016,12 @@
 			return -EINVAL;
 		}
 #endif
-#ifndef CONFIG_AX25
-		if (tmp & SL_MODE_AX25) {
-			return -EINVAL;
-		}
-#else
-		if (tmp & SL_MODE_AX25) {
-			sl->dev->addr_len=AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
-			sl->dev->hard_header_len=AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
-		} else	{
-			sl->dev->addr_len=0;	/* No mac addr in slip mode */
-			sl->dev->hard_header_len=0;
-		}
-#endif
 		sl->mode = tmp;
 		sl->dev->type = ARPHRD_SLIP+sl->mode;
-#ifdef CONFIG_AX25		
-		if (sl->dev->type == 260)  {
-			sl->dev->type = ARPHRD_AX25;
-		}
-#endif		
 		return 0;
 
 	 case SIOCSIFHWADDR:
-#ifdef CONFIG_AX25
-		return sl_set_mac_address(sl->dev, arg);
-#else
 		return -EINVAL;
-#endif
 
 #ifdef CONFIG_SLIP_SMART
 	/* VSV changes start here */
@@ -1207,9 +1115,6 @@
 #if defined(SL_INCLUDE_CSLIP) && !defined(MODULE)
 	printk("CSLIP: code copyright 1989 Regents of the University of California.\n");
 #endif
-#ifdef CONFIG_AX25
-	printk(KERN_INFO "AX25: KISS encapsulation enabled.\n");
-#endif
 #ifdef CONFIG_SLIP_SMART
 	printk(KERN_INFO "SLIP linefill/keepalive option.\n");
 #endif	
@@ -1260,12 +1165,6 @@
 {
 	struct slip *sl = (struct slip*)(dev->priv);
 	int i;
-#ifdef CONFIG_AX25
-	static char ax25_bcast[AX25_ADDR_LEN] =
-		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
-	static char ax25_test[AX25_ADDR_LEN] =
-		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
-#endif
 
 	if (sl == NULL)		/* Allocation failed ?? */
 	  return -ENODEV;
@@ -1283,22 +1182,10 @@
 	dev->stop		= sl_close;
 	dev->hard_header	= sl_header;
 	dev->get_stats	        = sl_get_stats;
-#ifdef HAVE_SET_MAC_ADDR
-#ifdef CONFIG_AX25
-	dev->set_mac_address    = sl_set_dev_mac_address;
-#endif
-#endif
 	dev->hard_header_len	= 0;
 	dev->addr_len		= 0;
 	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
 	dev->tx_queue_len	= 10;
-#ifdef CONFIG_AX25
-	if (sl->dev->type == 260) {
-		sl->dev->type = ARPHRD_AX25;
-	}
-	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);	/* Only activated in AX.25 mode */
-	memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);	/*    ""      ""       ""    "" */
-#endif
 	dev->rebuild_header	= sl_rebuild_header;
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)  {
diff -u --recursive --new-file v2.1.8/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.8/linux/drivers/pci/pci.c	Sun Nov 10 20:12:11 1996
+++ linux/drivers/pci/pci.c	Tue Nov 12 10:35:24 1996
@@ -69,8 +69,8 @@
 	DEVICE( DEC,		DEC_TULIP_FAST,	"DC21140"),
 	DEVICE( DEC,		DEC_FDDI,	"DEFPA"),
 	DEVICE( DEC,		DEC_TULIP_PLUS,	"DC21041"),
-	DEVICE( DEC,		DEC_21052_AB,	"DC21052-AB"),
-	DEVICE( DEC,		DEC_21152_AA,	"DC21152-AA"),
+	DEVICE( DEC,		DEC_21052,	"DC21052"),
+	DEVICE( DEC,		DEC_21152,	"DC21152"),
 	DEVICE( CIRRUS,		CIRRUS_5430,	"GD 5430"),
 	DEVICE( CIRRUS,		CIRRUS_5434_4,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_5434_8,	"GD 5434"),
@@ -87,7 +87,7 @@
 	DEVICE( TRIDENT,	TRIDENT_9660,	"TG 9660"),
 	DEVICE( AI,		AI_M1435,	"M1435"),
 	DEVICE( MATROX,		MATROX_MGA_2,	"Atlas PX2085"),
-	DEVICE( MATROX,		MATROX_MIL     ,"Millenium"),
+	DEVICE( MATROX,		MATROX_MIL     ,"Millennium"),
 	DEVICE( MATROX,		MATROX_MGA_IMP,	"MGA Impression"),
 	DEVICE( CT,		CT_65545,	"65545"),
 	DEVICE( CT,		CT_65548,	"65548"),
diff -u --recursive --new-file v2.1.8/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.1.8/linux/drivers/scsi/ChangeLog.ncr53c8xx	Sun Nov 10 20:12:12 1996
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Sun Nov 10 14:07:53 1996
@@ -1,3 +1,20 @@
+Mon Nov 4 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c ncr53c8xx.h - revision 1.14c
+	Severall control command improvements:
+
+	- Allow to specify "all" to commands that apply to #target.
+	  For example: "setsync all 255" sets asynchronous data 
+	               transfers for all targets on a bus.
+
+	- Allow to control disconnection privilege per device, as follow:
+	  "setflag #target no_sync" disables disconnection for #target.
+	  "setflag #target" with no flag specified reenables it.
+
+	  Obviously #target may be specified as "all" in order to control 
+	  disconnection for all targets with a single control command.
+
+	- README file updated and some hints about SCSI problems solving added.
+
 Sun Oct 27 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
 	* ncr53c8xx.c ncr53c8xx.h - revision 1.14b
 	Add the following config parameters:
diff -u --recursive --new-file v2.1.8/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.8/linux/drivers/scsi/README.ncr53c8xx	Sun Nov 10 20:12:12 1996
+++ linux/drivers/scsi/README.ncr53c8xx	Sun Nov 10 14:07:52 1996
@@ -4,7 +4,7 @@
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-28 October 1996
+05 November 1996
 ===============================================================================
 
 1.  Introduction
@@ -21,14 +21,15 @@
       8.4  Set order type for tagged command
       8.5  Set debug mode
       8.6  Clear profile counters
+      8.7  Set flag (no_sync)
 9.  Configuration parameters
 10. Some constants and flags of the ncr53c8xx.h header files
 11. Provided files
-12. Installation procedure for Linux version 1
-13. Installation procedure for Linux version 2
-14. Control commands under linux-1.2.13
-15. Known problems
-      15.1 Tagged commands with Iomega Jaz device
+12. Installation procedure
+13. Control commands under linux-1.2.13
+14. Known problems
+      14.1 Tagged commands with Iomega Jaz device
+15. SCSI problems solving
 
 ===============================================================================
 
@@ -274,6 +275,9 @@
       echo "<verb> <parameters>" >/proc/scsi/ncr53c8xx/0
       (assumes controller number is 0)
 
+Using "all" for "<target>" parameter with the commands below will apply to 
+all targets of the scsi chain (except the controller).
+
 Available commands:
 
 8.1 Set minimum synchronous period
@@ -328,6 +332,8 @@
 	nego:    print information about SCSI negotiations
 	phase:   print information on script interruptions
 
+    Use "setdebug" with no argument to reset debug flags.
+
 
 8.6 Clear profile counters
 
@@ -338,6 +344,21 @@
     The "clearprof" command allows you to clear these counters at any time.
 
 
+8.7 Set flag (no_sync)
+ 
+    setflag <target> <flag>
+
+    target:    target number
+
+    For the moment, only one flag is available:
+        no_sync:   not allow target to disconnect.
+
+    Do not specify any flag in order to reset the flag. For example:
+    - setflag 4
+      will reset no_sync flag for target 4, so will allow it disconnections.
+    - setflag all
+      will allow disconnection for all devices on the scsi bus.
+
 9. Configuration parameters
 
 If the firmware of all your devices is perfect enough, all the features 
@@ -469,80 +490,38 @@
 
 	README.ncr53c8xx	: this file
 	ChangeLog.ncr53c8xx	: change log
+	ConfigHelp.ncr53c8xx	: Part of Configure.help about the driver
 	ncr53c8xx.h		: definitions
 	ncr53c8xx.c		: the driver code
 	scsitag.c		: command tool to enable tagged queue
 	conf.modules		: sample of /etc/conf.modules
 
-Installation procedure 1 replacing the NCR53c7,8xx driver:
-
-	Install1.ncr53c8xx	: installation script
-	Uninstall1.ncr53c8xx	: uninstallation script
-	53c7,8xx.h		: included by hosts.c, override the standard driver
+	Install.ncr53c8xx	: installation script
 
 	Patch-1.2.13.ncr53c8xx     : patch for linux-1.2.13
-	Patch-1.3.45-49.ncr53c8xx  : patch for linux-1.3.45 to linux-1.3.49
-	Patch-1.3.50-100.ncr53c8xx : patch for linux-1.3.50 to linux-1.3.100
-
-Installation procedure 2 replacing the previous version of the driver:
-
-	Install2.ncr53c8xx	   : installation script
+	Patch-2.0.22-25.ncr53c8xx  : patch for linux-2.0.22 to linux-2.0.25
 
 Prior to installing the driver, you must untar the distribution, as follow:
 
 	mkdir ncrB2L
 	cd ncrB2L
-	tar zxvf ncrBsd2Linux-1.14b-src.tar.gz
+	tar zxvf ncrBsd2Linux-1.14c-src.tar.gz
 
 
-12. Installation procedure for Linux version 1
+12. Installation procedure
 
-This install script only supports linux-1.2.13 and linux-1.3.45 to 1.3.100.
+This install script has been tested with linux-1.2.13 and linux-2.0.22 to 
+2.0.25. It will probably work with linux 2.0.X (X>25).
 
-This procedure just moves the standard driver files to SAVE_53 sub-directory 
-of linux/drivers/scsi, copies the drivers file to that directory and patches 
-the SCSI Makefile.
-The standard driver can be restored with Uninstall.ncr53c8xx
+This procedure copies the new driver files to the kernel tree and applies 
+a patch to some files of the kernel tree.
 
 	If your linux directory is at the standard location
 	"/usr/src/linux", just enter:
-		Install1.ncr53c8xx
+		Install.ncr53c8xx
 
 	Else enter:
-		Install1.ncr53c8xx <your_linux_directory>
-
-	Make the kernel:
-		Change to linux source directory
-		Configure with 53c7,8xx support (Y or m)  for Install.ncr53c8xx
-		Make dependancies
-		Make the kernel (use make zdisk first)
-		Make and install modules if you have configured with 'm'
-
-	Notes:
-		Since release 1.3.90, additionnal configuation parameters 
-		have been added for the standard NCR driver.
-		Just reply Y or N as you want to these questions;
-		The NCR53C8XX driver ignores those parameters.
-
-	If you prefer the standard NCR driver of Linux:
-	just enter:
-		Uninstall1.ncr53c8xx
-	or
-		Uninstall1.ncr53c8xx <your_linux_directory>
-	if your linux root is not /usr/src/linux.
-
-
-13. Installation procedure for Linux version 2
-
-This install script only supports linux-2.0.3 and above.
-This procedure copy the new driver files to the kernel tree.
-
-	If your linux directory is at the standard location
-	"/usr/src/linux", just enter:
-		Install2.ncr53c8xx
-
-	Else enter:
-		Install2.ncr53c8xx <your_linux_directory>
+		Install.ncr53c8xx <your_linux_directory>
 
 	Make the kernel:
 		Change to linux source directory
@@ -553,7 +532,7 @@
 		Make and install modules if you have configured with 'm'
 
 
-14. Control commands under linux-1.2.13
+13. Control commands under linux-1.2.13
 
 Profiling data and control commands using the proc SCSI file system are not 
 available for linux-1.2.13.
@@ -568,9 +547,9 @@
 Use "cc -o scsitag scsitag.c" to create the "scsitag" executable.
 
 
-15. Known problems
+14. Known problems
 
-15.1 Tagged commands with Iomega Jaz device
+14.1 Tagged commands with Iomega Jaz device
 
 I have not tried this device, however it has been reported to me the following:
 This device is capable of Tagged command queuing. However while spinning up,
@@ -581,6 +560,54 @@
 The other problem that may appear is timeouts. The only way to avoid timeouts 
 seems to edit linux/drivers/scsi/sd.c and to increase the current timeout 
 values.
+
+
+15. SCSI problems solving
+
+Most scsi problems are due to a non conformant scsi bus or to buggy devices.
+If infortunately you have scsi problems, you can check the following things:
+
+- scsi bus cables
+- terminations at both end of the scsi chain
+- linux syslog messages (some of them may help you)
+
+If you donnot find the source of problems, you can configure the driver with 
+no feature enabled.
+
+- only asynchronous data transfers
+- tagged commands disabled
+- disconnections not allowed
+
+Now, if your scsi bus is ok, your system have every chance to work with this 
+safe configuration but performances will not be optimal.
+
+If it still fails, then you can send your problem description to appropriate 
+mailing lists or news-groups.
+Send me a copy in order to be sure I will receive it.
+Obviously, a bug in the driver code is possible.
+
+     My email address: Gerard Roudier <groudier@club-internet.fr>
+
+Allowing disconnections is important if you use severall devices on your 
+scsi bus but often causes problems with buggy devices.
+Synchronous data transfers increases throughput of fast devices as hard disks.
+Good scsi hard disks with a large cache gain advantage of tagged commands 
+queuing.
+
+Try to enable one feature at a time with control commands. For example:
+
+- echo "setsync all 25" >/proc/scsi/ncr53c8xx/0
+  Will enable fast synchronous data transfer negotiation for all targets.
+
+- echo "setflag 3" >/proc/scsi/ncr53c8xx/0
+  Will reset flags (no_sync) for target 3, and so will allow it to disconnect 
+  the scsi bus.
+
+- echo "settags 3 4" >/proc/scsi/ncr53c8xx/0
+  Will enable tagged command queuing for target 3 if that device supports it.
+
+Once you have found the device and the feature that cause problems, just 
+donnot enable the feature for that device only.
 
 ===============================================================================
 End of NCR53C8XX driver README file
diff -u --recursive --new-file v2.1.8/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.8/linux/drivers/scsi/ncr53c8xx.c	Sun Nov 10 20:12:13 1996
+++ linux/drivers/scsi/ncr53c8xx.c	Sun Nov 10 14:07:52 1996
@@ -742,6 +742,7 @@
 #define UC_CLEARPROF	16
 
 #define	UF_TRACE	(0x01)
+#define	UF_NODISC	(0x02)
 
 /*---------------------------------------
 **
@@ -3667,6 +3668,18 @@
 	np->irq = irq;
 
 	/*
+	**	Not allow disconnections for all targets if asked by config
+	*/
+
+#ifdef	SCSI_NCR_NO_DISCONNECT
+	{
+		int i;
+		for (i = 0 ; i < MAX_TARGET ; i++)
+			np->target[i].usrflag |= UF_NODISC;
+	}
+#endif
+
+	/*
 	**	After SCSI devices have been opened, we cannot
 	**	reset the bus safely, so we do it here.
 	**	Interrupt handler does the real work.
@@ -3684,9 +3697,7 @@
 	ncr_exception (np);
 	restore_flags(flags);
 
-#ifndef SCSI_NCR_NO_DISCONNECT
 	np->disc = 1;
-#endif
 
 	/*
 	**	The middle-level SCSI driver does not
@@ -3993,7 +4004,7 @@
 
 	idmsg = M_IDENTIFY | cmd->lun;
 
-	if ((cp!=&np->ccb) && (np->disc))
+	if (cp != &np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
 		idmsg |= 0x40;
 
 	msgptr = cp->scsi_smsg;
@@ -4276,9 +4287,7 @@
 	DELAY (1000);
 	ncr_init(np, "scsi bus reset", HS_RESET);
 
-#ifndef SCSI_NCR_NO_DISCONNECT
 	np->disc = 1;
-#endif
 
 	restore_flags(flags);
 
@@ -5344,7 +5353,6 @@
 			if (!((np->user.target>>t)&1)) continue;
 			ncr_setmaxtags (np, &np->target[t], np->user.data);
 		};
-		np->disc = 1;
 		break;
 
 	case UC_SETDEBUG:
@@ -5482,9 +5490,7 @@
 				OUTB (nc_scntl1, CRST);
 			DELAY (1000);
 			ncr_init (np, "ncr dead ?", HS_TIMEOUT);
-#ifndef SCSI_NCR_NO_DISCONNECT
 			np->disc = 1;
-#endif
 			np->heartbeat = thistime;
 		}
 #endif /* undef */
@@ -5907,9 +5913,7 @@
 	*/
 
 	ncr_init (np, "fatal error", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
 	np->disc = 1;
-#endif
 }
 
 /*==========================================================
@@ -5963,9 +5967,7 @@
 		return;
 	};
 	ncr_init (np, "selection timeout", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
 	np->disc = 1;
-#endif
 }
 
 /*==========================================================
@@ -8335,13 +8337,16 @@
 	case UC_SETWIDE:
 	case UC_SETFLAG:
 		SKIP_SPACES(1);
-		GET_INT_ARG(target);
+		if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
+			ptr += arg_len; len -= arg_len;
+			uc->target = ~0;
+		} else {
+			GET_INT_ARG(target);
+			uc->target = (1<<target);
 #ifdef DEBUG_PROC_INFO
 printf("ncr_user_command: target=%ld\n", target);
 #endif
-		if (target > MAX_TARGET)
-			return -EINVAL;
-		uc->target = (1<<target);
+		}
 		break;
 	}
 
@@ -8404,6 +8409,8 @@
 			SKIP_SPACES(1);
 			if	((arg_len = is_keyword(ptr, len, "trace")))
 				uc->data |= UF_TRACE;
+			else if	((arg_len = is_keyword(ptr, len, "no_disc")))
+				uc->data |= UF_NODISC;
 			else
 				return -EINVAL;
 			ptr += arg_len; len -= arg_len;
diff -u --recursive --new-file v2.1.8/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.8/linux/drivers/scsi/ncr53c8xx.h	Sun Nov 10 20:12:13 1996
+++ linux/drivers/scsi/ncr53c8xx.h	Sun Nov 10 14:07:52 1996
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.14b"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.14c"
  
 /*
 **	If SCSI_NCR_SPECIAL_FEATURES is defined,
diff -u --recursive --new-file v2.1.8/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h
--- v2.1.8/linux/drivers/sound/sound_config.h	Tue Oct 29 19:58:19 1996
+++ linux/drivers/sound/sound_config.h	Sun Nov 10 12:50:02 1996
@@ -32,7 +32,7 @@
  * Use always 64k buffer size. There is no reason to use shorter.
  */
 #undef DSP_BUFFSIZE
-#define DSP_BUFFSIZE		(16*1024)
+#define DSP_BUFFSIZE		(64*1024)
 
 #ifndef DSP_BUFFCOUNT
 #define DSP_BUFFCOUNT		1	/* 1 is recommended. */
diff -u --recursive --new-file v2.1.8/linux/fs/Config.in linux/fs/Config.in
--- v2.1.8/linux/fs/Config.in	Wed Oct 16 10:48:24 1996
+++ linux/fs/Config.in	Sun Nov 10 14:05:48 1996
@@ -37,11 +37,9 @@
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  tristate 'Amiga FFS filesystem support (EXPERIMENTAL)' CONFIG_AFFS_FS
-  if [ "$CONFIG_AFFS_FS" != "n" ]; then
-    define_bool CONFIG_AMIGA_PARTITION y
-  fi
+tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS
+if [ "$CONFIG_AFFS_FS" != "n" ]; then
+  define_bool CONFIG_AMIGA_PARTITION y
 fi
 tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS
 if [ "$CONFIG_UFS_FS" != "n" ]; then
diff -u --recursive --new-file v2.1.8/linux/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- v2.1.8/linux/fs/affs/bitmap.c	Sun May 19 15:22:19 1996
+++ linux/fs/affs/bitmap.c	Sun Nov 10 14:05:48 1996
@@ -149,7 +149,7 @@
 	/* prealloc as much as possible within this word, but not in header zone */
 
 	if (zone_no) {
-		while (inode->u.affs_i.i_pa_cnt < MAX_PREALLOC && ++fb < 32) {
+		while (inode->u.affs_i.i_pa_cnt < AFFS_MAX_PREALLOC && ++fb < 32) {
 			fb = find_next_zero_bit(&w,32,fb);
 			if (fb > 31)
 				break;
@@ -158,7 +158,7 @@
 				break;
 			}
 			inode->u.affs_i.i_data[inode->u.affs_i.i_pa_last++] = fwb + fb;
-			inode->u.affs_i.i_pa_last &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_last &= AFFS_MAX_PREALLOC - 1;
 			inode->u.affs_i.i_pa_cnt++;
 			az->az_free--;
 		}
@@ -309,7 +309,7 @@
 		inode->u.affs_i.i_pa_cnt--;
 		unlock_super(sb);
 		block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
-		inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+		inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
 		goto init_block;
 	}
 	unlock_super(sb);
diff -u --recursive --new-file v2.1.8/linux/fs/affs/dir.c linux/fs/affs/dir.c
--- v2.1.8/linux/fs/affs/dir.c	Tue Oct 29 19:58:19 1996
+++ linux/fs/affs/dir.c	Sun Nov 10 14:05:48 1996
@@ -24,7 +24,8 @@
 #include <linux/amigaffs.h>
 
 static int affs_readdir(struct inode *, struct file *, void *, filldir_t);
-static int affs_dir_read(struct inode * inode, struct file * filp, char * buf, int count);
+static long affs_dir_read(struct inode * inode, struct file * filp, char * buf,
+			  unsigned long count);
 
 static struct file_operations affs_dir_operations = {
 	NULL,			/* lseek - default */
@@ -62,8 +63,8 @@
 	NULL			/* permissions */
 };
 
-static int
-affs_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long
+affs_dir_read(struct inode * inode, struct file * filp, char * buf, unsigned long count)
 {
 	return -EISDIR;
 }
diff -u --recursive --new-file v2.1.8/linux/fs/affs/file.c linux/fs/affs/file.c
--- v2.1.8/linux/fs/affs/file.c	Tue Oct 29 19:58:19 1996
+++ linux/fs/affs/file.c	Sun Nov 10 14:05:48 1996
@@ -19,6 +19,7 @@
 #include <linux/fcntl.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
+#include <linux/malloc.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
 #include <linux/dirent.h>
@@ -30,9 +31,17 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
-static int affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count);
-static int affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count);
-static int affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count);
+#if PAGE_SIZE < 4096
+#error PAGE_SIZE must be at least 4096
+#endif
+
+static long affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf,
+			       unsigned long count);
+static long affs_file_write(struct inode *inode, struct file *filp, const char *buf,
+			    unsigned long count);
+static long affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf,
+				unsigned long count);
+static int affs_open_file(struct inode *inode, struct file *filp);
 static void affs_release_file(struct inode *inode, struct file *filp);
 
 static struct file_operations affs_file_operations = {
@@ -43,7 +52,7 @@
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	generic_file_mmap,	/* mmap */
-	NULL,			/* no special open is needed */
+	affs_open_file,		/* special open is needed */
 	affs_release_file,	/* release */
 	file_fsync		/* brute force, but works */
 };
@@ -77,7 +86,7 @@
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	NULL,			/* mmap */
-	NULL,			/* no special open is needed */
+	affs_open_file,		/* special open is needed */
 	affs_release_file,	/* release */
 	file_fsync		/* brute force, but works */
 };
@@ -103,12 +112,133 @@
 	NULL			/* smap */
 };
 
+#define AFFS_ISINDEX(x)	((x < 129) ||				\
+			 (x < 512 && (x & 1) == 0) ||		\
+			 (x < 1024 && (x & 3) == 0) ||		\
+			 (x < 2048 && (x & 15) == 0) ||		\
+			 (x < 4096 && (x & 63) == 0) ||		\
+			 (x < 20480 && (x & 255) == 0) ||	\
+			 (x < 36864 && (x & 511) == 0))
+
+/* The keys of the extension blocks are stored in a 512-entry
+ * deep cache. In order to save memory, not every key of later
+ * extension blocks is stored - the larger the file gets, the
+ * bigger the holes inbetween.
+ */
+
+static int
+seqnum_to_index(int seqnum)
+{
+	/* All of the first 127 keys are stored */
+	if (seqnum < 128)
+		return seqnum;
+	seqnum -= 128;
+
+	/* Of the next 384 keys, every 2nd is kept */
+	if (seqnum < (192 * 2))
+		return 128 + (seqnum >> 1);
+	seqnum -= 192 * 2;
+	
+	/* Every 4th of the next 512 */
+	if (seqnum < (128 * 4))
+		return 128 + 192 + (seqnum >> 2);
+	seqnum -= 128 * 4;
+
+	/* Every 16th of the next 1024 */
+	if (seqnum < (64 * 16))
+		return 128 + 192 + 128 + (seqnum >> 4);
+	seqnum -= 64 * 16;
+
+	/* Every 64th of the next 2048 */
+	if (seqnum < (32 * 64))
+		return 128 + 192 + 128 + 64 + (seqnum >> 6);
+	seqnum -= 32 * 64;
+
+	/* Every 256th of the next 16384 */
+	if (seqnum < (64 * 256))
+		return 128 + 192 + 128 + 64 + 32 + (seqnum >> 8);
+	seqnum -= 64 * 256;
+
+	/* Every 512th upto 36479 (1.3 GB with 512 byte blocks).
+	 * Seeking to positions behind this will get slower
+	 * than dead snails nailed to the ground. But if
+	 * someone uses files that large with 512-byte blocks,
+	 * he or she deserves no better.
+	 */
+	
+	if (seqnum > (31 * 512))
+		seqnum = 31 * 512;
+	return 128 + 192 + 128 + 64 + 32 + 64 + (seqnum >> 9);
+}
+
+/* Now the other way round: Calculate the sequence
+ * number of a extension block of a key at the
+ * given index in the cache.
+ */
+
+static int
+index_to_seqnum(int index)
+{
+	if (index < 128)
+		return index;
+	index -= 128;
+	if (index < 192)
+		return 128 + (index << 1);
+	index -= 192;
+	if (index < 128)
+		return 128 + 192 * 2 + (index << 2);
+	index -= 128;
+	if (index < 64)
+		return 128 + 192 * 2 + 128 * 4 + (index << 4);
+	index -= 64;
+	if (index < 32)
+		return 128 + 192 * 2 + 128 * 4 + 64 * 16 + (index << 6);
+	index -= 32;
+	if (index < 64)
+		return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + (index << 8);
+	index -= 64;
+	return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + 64 * 256 + (index << 9);
+}
+
+static int __inline__
+calc_key(struct inode *inode, int *ext)
+{
+	int		  index;
+	struct key_cache *kc;
+
+	for (index = 0; index < 4; index++) {
+		kc = &inode->u.affs_i.i_ec->kc[index];
+		if (*ext == kc->kc_this_seq) {
+			return kc->kc_this_key;
+		} else if (*ext == kc->kc_this_seq + 1) {
+			if (kc->kc_next_key)
+				return kc->kc_next_key;
+			else {
+				(*ext)--;
+				return kc->kc_this_key;
+			}
+		}
+	}
+	index = seqnum_to_index(*ext);
+	if (index > inode->u.affs_i.i_ec->max_ext)
+		index = inode->u.affs_i.i_ec->max_ext;
+	*ext = index_to_seqnum(index);
+	return inode->u.affs_i.i_ec->ec[index];
+}
+
 int
 affs_bmap(struct inode *inode, int block)
 {
 	struct buffer_head	*bh;
-	int			 ext, key;
+	int			 ext, key, nkey;
 	int			 ptype, stype;
+	int			 index;
+	int			 keycount;
+	struct key_cache	*kc;
+	struct key_cache	*tkc;
+	struct timeval		 tv;
+	__s32			*keyp;
+	int			 i;
 
 	pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
 
@@ -116,246 +246,197 @@
 		printk("affs_bmap: block < 0\n");
 		return 0;
 	}
-
-	/* If this is a hard link, quietly exchange the inode with the original */
-
-	key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
-
-	ext = block / AFFS_I2HSIZE(inode);
-	if (ext) {
-		if (ext > inode->u.affs_i.i_max_ext)
-			ext = inode->u.affs_i.i_max_ext;
-		if (ext)
-			key = inode->u.affs_i.i_ext[ext -  1];
-		block -= ext * AFFS_I2HSIZE(inode);
+	if (!inode->u.affs_i.i_ec) {
+		printk("affs_bmap(): No ext_cache!?\n");
+		return 0;
 	}
 
+	/* Try to find the requested key in the cache.
+	 * In order to speed this up as much as possible,
+	 * the cache line lookup is done in a seperate
+	 * step.
+	 */
+
+	for (i = 0; i < 4; i++) {
+		tkc = &inode->u.affs_i.i_ec->kc[i];
+		/* Look in any cache if the key is there */
+		if (block <= tkc->kc_last && block >= tkc->kc_first) {
+			return tkc->kc_keys[block - tkc->kc_first];
+		}
+	}
+	kc = NULL;
+	tv = xtime;
+	for (i = 0; i < 4; i++) {
+		tkc = &inode->u.affs_i.i_ec->kc[i];
+		if (tkc->kc_lru_time.tv_sec > tv.tv_sec)
+			continue;
+		if (tkc->kc_lru_time.tv_sec < tv.tv_sec ||
+		    tkc->kc_lru_time.tv_usec < tv.tv_usec) {
+			kc = tkc;
+			tv = tkc->kc_lru_time;
+		}
+	}
+	if (!kc)	/* Really shouldn't happen */
+		kc = tkc;
+	kc->kc_lru_time = xtime;
+	keyp            = kc->kc_keys;
+	kc->kc_first    = block;
+	kc->kc_last     = -1;
+	keycount        = AFFS_KCSIZE;
+
+	/* Calculate sequence number of the extension block where the
+	 * number of the requested block is stored. 0 means it's in
+	 * the file header.
+	 */
+
+	ext    = block / AFFS_I2HSIZE(inode);
+	key    = calc_key(inode,&ext);
+	block -= ext * AFFS_I2HSIZE(inode);
+
 	for (;;) {
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
 			return 0;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
-		    (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE) {
+		index = seqnum_to_index(ext);
+		if (index > inode->u.affs_i.i_ec->max_ext &&
+		    (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
+		     (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE)) {
 			affs_brelse(bh);
 			return 0;
 		}
-		if (block < AFFS_I2HSIZE(inode))
-			break;
-		block -= AFFS_I2HSIZE(inode);
-		key    = htonl(FILE_END(bh->b_data,inode)->extension);
-		affs_brelse(bh);
-		if (ext < EXT_CACHE_SIZE - 1) {
-			inode->u.affs_i.i_ext[ext] = key;
-			inode->u.affs_i.i_max_ext  = ++ext;
-		}
-	}
-	key = AFFS_GET_HASHENTRY(bh->b_data,(AFFS_I2HSIZE(inode) - 1) - block);
-	affs_brelse(bh);
-	return key;
-}
-
-struct buffer_head *
-affs_getblock(struct inode *inode, int block)
-{
-	struct buffer_head	*bh;
-	struct buffer_head	*ebh;
-	int			 key;
-	int			 ext;
-	int			 cnt, j, pt;
-
-	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
-
-	if (block < 0)
-		return NULL;
-	key = inode->i_ino;
-	pt  = T_SHORT;
-
-	ext = block / AFFS_I2HSIZE(inode);
-	if (ext) {
-		if (ext > inode->u.affs_i.i_max_ext)
-			ext = inode->u.affs_i.i_max_ext;
-		if (ext) {
-			key    = inode->u.affs_i.i_ext[ext - 1];
-			block -= ext * AFFS_I2HSIZE(inode);
-			pt     = T_LIST;
-		}
-	}
-
-	for (;;) {
-		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-		if (!bh)
-			return NULL;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cnt,&j) ||
-		    cnt != pt || j != ST_FILE) {
-		    	printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
-			       pt == T_SHORT ? "file header" : "extension block");
-			affs_brelse(bh);
-			return NULL;
-		}
-		j = htonl(((struct file_front *)bh->b_data)->block_count);
-		while (j < AFFS_I2HSIZE(inode) && j <= block) {
-			key = affs_new_data(inode);
-			if (!key)
-				break;
-			lock_super(inode->i_sb);
-			if (AFFS_BLOCK(bh->b_data,inode,j)) {
-				unlock_super(inode->i_sb);
-				printk("AFFS: getblock(): block already allocated\n");
-				affs_free_block(inode->i_sb,key);
-				j++;
-				continue;
+		nkey = htonl(FILE_END(bh->b_data,inode)->extension);
+		if (block < AFFS_I2HSIZE(inode)) {
+			/* Fill cache as much as possible */
+			if (keycount) {
+				kc->kc_first = ext * AFFS_I2HSIZE(inode) + block;
+				keycount     = keycount < AFFS_I2HSIZE(inode) - block ? keycount :
+						AFFS_I2HSIZE(inode) - block;
+				for (i = 0; i < keycount; i++)
+					kc->kc_keys[i] = htonl(AFFS_BLOCK(bh->b_data,inode,block + i));
+				kc->kc_last = kc->kc_first + i - 1;
 			}
-			unlock_super(inode->i_sb);
-			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(key);
-			j++;
-		}
-		if (pt == T_SHORT)
-			((struct file_front *)bh->b_data)->first_data =
-								AFFS_BLOCK(bh->b_data,inode,0);
-		((struct file_front *)bh->b_data)->block_count = ntohl(j);
-		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-		mark_buffer_dirty(bh,1);
-
-		if (block < j)
 			break;
-		if (j < AFFS_I2HSIZE(inode)) {
-			affs_brelse(bh);
-			return NULL;
 		}
-
 		block -= AFFS_I2HSIZE(inode);
-		key    = htonl(FILE_END(bh->b_data,inode)->extension);
-		if (!key) {
-			key = affs_new_header(inode);
-			if (!key) {
-				affs_brelse(bh);
-				return NULL;
-			}
-			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-			if (!ebh) {
-				affs_free_block(inode->i_sb,key);
-				return NULL;
-			}
-			((struct file_front *)ebh->b_data)->primary_type = ntohl(T_LIST);
-			((struct file_front *)ebh->b_data)->own_key      = ntohl(key);
-			FILE_END(ebh->b_data,inode)->secondary_type      = ntohl(ST_FILE);
-			FILE_END(ebh->b_data,inode)->parent              = ntohl(inode->i_ino);
-			affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
-			FILE_END(bh->b_data,inode)->extension = ntohl(key);
-			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-			mark_buffer_dirty(bh,1);
-			affs_brelse(bh);
-			bh = ebh;
-		}
 		affs_brelse(bh);
-		pt = T_LIST;
-		if (ext < EXT_CACHE_SIZE - 1) {
-			inode->u.affs_i.i_ext[ext] = key;
-			inode->u.affs_i.i_max_ext  = ++ext;
-		}
-	}
+		ext++;
+		if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) {
+			inode->u.affs_i.i_ec->ec[index] = nkey;
+			inode->u.affs_i.i_ec->max_ext   = index;
+		}
+		key = nkey;
+	}
+	kc->kc_this_key = key;
+	kc->kc_this_seq = ext;
+	kc->kc_next_key = nkey;
 	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
-	if (!key)
-		return NULL;
-	
-	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+	return key;
 }
 
 struct buffer_head *
-affs_getblock_ofs(struct inode *inode, int block, int *blk_key)
+affs_getblock(struct inode *inode, int block)
 {
 	struct buffer_head	*bh;
-	struct buffer_head	*pbh;
 	struct buffer_head	*ebh;
-	int			 key;
+	struct buffer_head	*pbh;
+	struct key_cache	*kc;
+	int			 key, nkey;
 	int			 ext;
-	int			 cnt, j, pt;
+	int			 cf, j, pt;
+	int			 index;
+	int			 ofs;
 
-	pr_debug("AFFS: getblock_ofs(%lu,%d)\n",inode->i_ino,block);
+	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
 
 	if (block < 0)
 		return NULL;
-	key = inode->i_ino;
-	pt  = T_SHORT;
 
-	ext = block / AFFS_I2HSIZE(inode);
-	if (ext) {
-		if (ext > inode->u.affs_i.i_max_ext)
-			ext = inode->u.affs_i.i_max_ext;
-		if (ext) {
-			key    = inode->u.affs_i.i_ext[ext - 1];
-			block -= ext * AFFS_I2HSIZE(inode);
-			pt     = T_LIST;
-		}
-	}
+	/* Writers always use cache line 3. In almost all cases, files
+	 * will be written by only one process at the same time, and
+	 * they also will be written in strict sequential order. Thus
+	 * there is not much sense in looking whether the key of the
+	 * requested block is available - it won't be there.
+	 */
+	kc     = &inode->u.affs_i.i_ec->kc[3];
+	ofs    = inode->i_sb->u.affs_sb.s_flags & SF_OFS;
+	ext    = block / AFFS_I2HSIZE(inode);
+	key    = calc_key(inode,&ext);
+	block -= ext * AFFS_I2HSIZE(inode);
+	pt     = ext ? T_LIST : T_SHORT;
+	pbh    = NULL;
 
-	pbh = NULL;
 	for (;;) {
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
 			return NULL;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cnt,&j) ||
-		    cnt != pt || j != ST_FILE) {
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j) ||
+		    cf != pt || j != ST_FILE) {
 		    	printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
 			       pt == T_SHORT ? "file header" : "extension block");
 			affs_brelse(bh);
 			return NULL;
 		}
-		j = htonl(((struct file_front *)bh->b_data)->block_count);
+		j  = htonl(((struct file_front *)bh->b_data)->block_count);
+		cf = 0;
 		while (j < AFFS_I2HSIZE(inode) && j <= block) {
-			if (!pbh && inode->u.affs_i.i_lastblock >= 0) {
+			if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
 				if (j > 0)
 					pbh = affs_bread(inode->i_dev,ntohl(AFFS_BLOCK(bh->b_data,inode,j - 1)),
 							 AFFS_I2BSIZE(inode));
 				else
-					pbh = affs_getblock_ofs(inode,inode->u.affs_i.i_lastblock,&key);
+					pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
 				if (!pbh) {
 					printk("AFFS: getblock(): cannot get last block in file\n");
 					break;
 				}
 			}
-			key = affs_new_data(inode);
-			if (!key)
+			nkey = affs_new_data(inode);
+			if (!nkey)
 				break;
 			lock_super(inode->i_sb);
 			if (AFFS_BLOCK(bh->b_data,inode,j)) {
 				unlock_super(inode->i_sb);
 				printk("AFFS: getblock(): block already allocated\n");
-				affs_free_block(inode->i_sb,key);
+				affs_free_block(inode->i_sb,nkey);
 				j++;
 				continue;
 			}
-			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(key);
 			unlock_super(inode->i_sb);
-			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-			if (!ebh) {
-				printk("AFFS: getblock(): cannot get block %d\n",key);
-				affs_free_block(inode->i_sb,key);
-				AFFS_BLOCK(bh->b_data,inode,j) = 0;
-				break;
-			}
-			inode->u.affs_i.i_lastblock++;
-			DATA_FRONT(ebh)->primary_type    = ntohl(T_DATA);
-			DATA_FRONT(ebh)->header_key      = ntohl(inode->i_ino);
-			DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
-			if (pbh) {
-				DATA_FRONT(pbh)->data_size = ntohl(AFFS_I2BSIZE(inode) - 24);
-				DATA_FRONT(pbh)->next_data = ntohl(key);
-				affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
-				mark_buffer_dirty(pbh,0);
-				mark_buffer_dirty(ebh,0);
-				affs_brelse(pbh);
+			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(nkey);
+			if (ofs) {
+				ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
+				if (!ebh) {
+					printk("AFFS: getblock(): cannot get block %d\n",nkey);
+					affs_free_block(inode->i_sb,nkey);
+					AFFS_BLOCK(bh->b_data,inode,j) = 0;
+					break;
+				}
+				inode->u.affs_i.i_lastblock++;
+				DATA_FRONT(ebh)->primary_type    = ntohl(T_DATA);
+				DATA_FRONT(ebh)->header_key      = ntohl(inode->i_ino);
+				DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
+				if (pbh) {
+					DATA_FRONT(pbh)->data_size = ntohl(AFFS_I2BSIZE(inode) - 24);
+					DATA_FRONT(pbh)->next_data = ntohl(nkey);
+					affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
+					mark_buffer_dirty(pbh,0);
+					mark_buffer_dirty(ebh,0);
+					affs_brelse(pbh);
+				}
+				pbh = ebh;
 			}
-			pbh = ebh;
 			j++;
+			cf = 1;
 		}
-		if (pt == T_SHORT)
-			((struct file_front *)bh->b_data)->first_data =
+		if (cf) {
+			if (pt == T_SHORT)
+				((struct file_front *)bh->b_data)->first_data =
 								AFFS_BLOCK(bh->b_data,inode,0);
-		((struct file_front *)bh->b_data)->block_count = ntohl(j);
-		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-		mark_buffer_dirty(bh,1);
+			((struct file_front *)bh->b_data)->block_count = ntohl(j);
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+			mark_buffer_dirty(bh,1);
+		}
 
 		if (block < j) {
 			if (pbh)
@@ -393,17 +474,21 @@
 		}
 		affs_brelse(bh);
 		pt = T_LIST;
-		if (ext < EXT_CACHE_SIZE - 1) {
-			inode->u.affs_i.i_ext[ext] = key;
-			inode->u.affs_i.i_max_ext  = ++ext;
+		ext++;
+		if ((index = seqnum_to_index(ext)) > inode->u.affs_i.i_ec->max_ext &&
+		    AFFS_ISINDEX(ext) && inode->u.affs_i.i_ec) {
+			inode->u.affs_i.i_ec->ec[index] = key;
+			inode->u.affs_i.i_ec->max_ext   = index;
 		}
 	}
+	kc->kc_this_key = key;
+	kc->kc_this_seq = ext;
+	kc->kc_next_key = htonl(FILE_END(bh->b_data,inode)->extension);
 	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
 	if (!key)
 		return NULL;
-	*blk_key = key;
-
+	
 	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 }
 
@@ -411,8 +496,8 @@
  * You cannot directly read affs directories.
  */
 
-static int
-affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count)
+static long
+affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, unsigned long count)
 {
 	char *start;
 	int left, offset, size, sector;
@@ -458,8 +543,8 @@
 	return buf - start;
 }
 
-static int
-affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count)
+static long
+affs_file_write(struct inode *inode, struct file *filp, const char *buf, unsigned long count)
 {
 	off_t			 pos;
 	int			 written;
@@ -537,13 +622,12 @@
 	return written;
 }
 
-static int
-affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count)
+static long
+affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, unsigned long count)
 {
 	off_t			 pos;
 	int			 written;
 	int			 c;
-	int			 key;
 	int			 blocksize;
 	struct buffer_head	*bh;
 	struct inode		*ino;
@@ -580,7 +664,7 @@
 	blocksize = AFFS_I2BSIZE(inode) - 24;
 	written   = 0;
 	while (written < count) {
-		bh = affs_getblock_ofs(inode,pos / blocksize,&key);
+		bh = affs_getblock(inode,pos / blocksize);
 		if (!bh) {
 			if (!written)
 				written = -ENOSPC;
@@ -654,15 +738,12 @@
 	blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
 	first = (inode->i_size + blocksize - 1) / blocksize;
 	if (inode->u.affs_i.i_lastblock < first - 1) {
-		if (inode->i_sb->u.affs_sb.s_flags & SF_OFS)
-			bh = affs_getblock_ofs(inode,first - 1,&ekey);
-		else
 			bh = affs_getblock(inode,first - 1);
 
 		while (inode->u.affs_i.i_pa_cnt) {		/* Free any preallocated blocks */
 			affs_free_block(inode->i_sb,
 					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
 			inode->u.affs_i.i_pa_cnt--;
 		}
 		if (inode->u.affs_i.i_zone) {
@@ -755,10 +836,53 @@
 		ekey = key;
 	}
 	inode->u.affs_i.i_lastblock = ((inode->i_size + blocksize - 1) / blocksize) - 1;
-	inode->u.affs_i.i_max_ext   = 0;
+
+	/* Invalidate cache */
+	if (inode->u.affs_i.i_ec) {
+		inode->u.affs_i.i_ec->max_ext = 0;
+		for (key = 0; key < 3; key++) {
+			inode->u.affs_i.i_ec->kc[key].kc_next_key = 0;
+			inode->u.affs_i.i_ec->kc[key].kc_last     = -1;
+		}
+	}
+
 	iput(ino);
 }
 
+static int
+affs_open_file(struct inode *inode, struct file *filp)
+{
+	int	 error;
+	int	 key;
+	int	 i;
+
+	pr_debug("AFFS: open_file(ino=%lu)\n",inode->i_ino);
+
+	error = 0;
+	inode->u.affs_i.i_cache_users++;
+	lock_super(inode->i_sb);
+	if (!inode->u.affs_i.i_ec) {
+		inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
+		if (!inode->u.affs_i.i_ec) {
+			printk("AFFS: cache allocation failed\n");
+			error = ENOMEM;
+		} else {
+			/* We only have to initialize non-zero values.
+			 * get_free_page() zeroed the page already.
+			 */
+			key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+			inode->u.affs_i.i_ec->ec[0] = key;
+			for (i = 0; i < 4; i++) {
+				inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
+				inode->u.affs_i.i_ec->kc[i].kc_last     = -1;
+			}
+		}
+	}
+	unlock_super(inode->i_sb);
+
+	return error;
+}
+
 static void
 affs_release_file(struct inode *inode, struct file *filp)
 {
@@ -770,7 +894,7 @@
 		while (inode->u.affs_i.i_pa_cnt) {
 			affs_free_block(inode->i_sb,
 					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
 			inode->u.affs_i.i_pa_cnt--;
 		}
 		if (inode->u.affs_i.i_zone) {
@@ -781,4 +905,12 @@
 			unlock_super(inode->i_sb);
 		}
 	}
+	lock_super(inode->i_sb);
+	if (--inode->u.affs_i.i_cache_users == 0) {
+		if (inode->u.affs_i.i_ec) {
+			free_page((unsigned long)inode->u.affs_i.i_ec);
+			inode->u.affs_i.i_ec = NULL;
+		}
+	}
+	unlock_super(inode->i_sb);
 }
diff -u --recursive --new-file v2.1.8/linux/fs/affs/inode.c linux/fs/affs/inode.c
--- v2.1.8/linux/fs/affs/inode.c	Tue Oct 29 19:58:19 1996
+++ linux/fs/affs/inode.c	Sun Nov 10 14:05:48 1996
@@ -333,35 +333,53 @@
 
 	s->u.affs_sb.s_hashsize = 0;
 	if (blocksize > 0) {
-		chksum = blocksize;
-		num_bm = blocksize;
+		i = blocksize;
+		j = blocksize;
 	} else {
-		chksum = 512;
-		num_bm = 4096;
+		i = 512;
+		j = 4096;
 	}
-	for (blocksize = chksum; blocksize <= num_bm; blocksize <<= 1, size >>= 1) {
+	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
 		if (root_block < 0)
 			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
 		else
 			s->u.affs_sb.s_root_block = root_block;
-		pr_debug("Trying bs=%d bytes, root at %d, size=%d blocks (%d reserved)\n",
-			 blocksize,s->u.affs_sb.s_root_block,size,reserved);
 		set_blocksize(dev,blocksize);
-		bh = affs_bread(dev,s->u.affs_sb.s_root_block,blocksize);
-		if (!bh) {
-			printk("AFFS: unable to read root block\n");
-			goto out;
+
+		/* The root block location that was calculated above is not
+		 * correct if the partition size is an odd number of 512-
+		 * byte blocks, which will be rounded down to a number of
+		 * 1024-byte blocks, and if there were an even number of
+		 * reserved blocks. Ideally, all partition checkers should
+		 * report the real number of blocks of the real blocksize,
+		 * but since this just cannot be done, we have to try to
+		 * find the root block anyways. In the above case, it is one
+		 * block behind the calculated one. So we check this one, too.
+		 */
+		for (num_bm = 0; num_bm < 2; num_bm++) {
+			pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %d, "
+				 "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
+				 s->u.affs_sb.s_root_block + num_bm,size,reserved);
+			bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
+			if (!bh) {
+				printk("AFFS: unable to read root block\n");
+				goto out;
+			}
+			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
+			    ptype == T_SHORT && stype == ST_ROOT) {
+				s->s_blocksize             = blocksize;
+				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
+				s->u.affs_sb.s_root_block += num_bm;
+				key                        = 1;
+				break;
+			}
 		}
-		if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
-		    ptype == T_SHORT && stype == ST_ROOT) {
-			s->s_blocksize          = blocksize;
-			s->u.affs_sb.s_hashsize = blocksize / 4 - 56;
+		if (key)
 			break;
-		}
 		affs_brelse(bh);
 		bh = NULL;
 	}
-	if (!s->u.affs_sb.s_hashsize) {
+	if (!key) {
 		affs_brelse(bh);
 		if (!silent)
 			printk("AFFS: Can't find a valid root block on device %s\n",kdevname(dev));
@@ -512,8 +530,7 @@
 					}
 					ptype = (size + 31) & ~0x1F;
 					size  = 0;
-					if (!(s->s_flags & MS_RDONLY))
-						s->u.affs_sb.s_flags |= SF_BM_VALID;
+					s->u.affs_sb.s_flags |= SF_BM_VALID;
 				} else {
 					ptype = s->s_blocksize * 8 - 32;
 					size -= ptype;
@@ -629,6 +646,7 @@
 	unsigned short		 id;
 
 	pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
+
 	lbh   = NULL;
 	block = inode->i_ino;
 	if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
@@ -646,19 +664,19 @@
 	file_end   = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
 	prot       = (htonl(file_end->protect) & ~0x10) ^ FIBF_OWNER;
 
-	inode->u.affs_i.i_protect   = prot;
-	inode->u.affs_i.i_parent    = htonl(file_end->parent);
-	inode->u.affs_i.i_original  = 0;	
-	inode->u.affs_i.i_zone      = 0;
-	inode->u.affs_i.i_hlink     = 0;
-	inode->u.affs_i.i_pa_cnt    = 0;
-	inode->u.affs_i.i_pa_next   = 0;
-	inode->u.affs_i.i_pa_last   = 0;
-	inode->u.affs_i.i_ext[0]    = 0;
-	inode->u.affs_i.i_max_ext   = 0;
-	inode->u.affs_i.i_lastblock = -1;
-	inode->i_nlink              = 1;
-	inode->i_mode               = 0;
+	inode->u.affs_i.i_protect      = prot;
+	inode->u.affs_i.i_parent       = htonl(file_end->parent);
+	inode->u.affs_i.i_original     = 0;	
+	inode->u.affs_i.i_zone         = 0;
+	inode->u.affs_i.i_hlink        = 0;
+	inode->u.affs_i.i_pa_cnt       = 0;
+	inode->u.affs_i.i_pa_next      = 0;
+	inode->u.affs_i.i_pa_last      = 0;
+	inode->u.affs_i.i_ec           = NULL;
+	inode->u.affs_i.i_cache_users  = 0;
+	inode->u.affs_i.i_lastblock    = -1;
+	inode->i_nlink                 = 1;
+	inode->i_mode                  = 0;
 
 	if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE)
 		inode->i_mode = inode->i_sb->u.affs_sb.s_mode;
@@ -876,16 +894,16 @@
 	inode->i_blksize = 0;
 	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 
-	inode->u.affs_i.i_original  = 0;
-	inode->u.affs_i.i_parent    = dir->i_ino;
-	inode->u.affs_i.i_zone      = 0;
-	inode->u.affs_i.i_hlink     = 0;
-	inode->u.affs_i.i_pa_cnt    = 0;
-	inode->u.affs_i.i_pa_next   = 0;
-	inode->u.affs_i.i_pa_last   = 0;
-	inode->u.affs_i.i_ext[0]    = 0;
-	inode->u.affs_i.i_max_ext   = 0;
-	inode->u.affs_i.i_lastblock = -1;
+	inode->u.affs_i.i_original     = 0;
+	inode->u.affs_i.i_parent       = dir->i_ino;
+	inode->u.affs_i.i_zone         = 0;
+	inode->u.affs_i.i_hlink        = 0;
+	inode->u.affs_i.i_pa_cnt       = 0;
+	inode->u.affs_i.i_pa_next      = 0;
+	inode->u.affs_i.i_pa_last      = 0;
+	inode->u.affs_i.i_ec           = NULL;
+	inode->u.affs_i.i_cache_users  = 0;
+	inode->u.affs_i.i_lastblock    = -1;
 
 	insert_inode_hash(inode);
 
diff -u --recursive --new-file v2.1.8/linux/fs/ext2/file.c linux/fs/ext2/file.c
--- v2.1.8/linux/fs/ext2/file.c	Tue Oct 29 19:58:42 1996
+++ linux/fs/ext2/file.c	Tue Nov 12 15:09:34 1996
@@ -36,6 +36,7 @@
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
 
+static long long ext2_file_lseek(struct inode *, struct file *, long long, int);
 static long ext2_file_write (struct inode *, struct file *, const char *, unsigned long);
 static void ext2_release_file (struct inode *, struct file *);
 
@@ -44,7 +45,7 @@
  * the ext2 filesystem.
  */
 static struct file_operations ext2_file_operations = {
-	NULL,			/* lseek - default */
+	ext2_file_lseek,	/* lseek */
 	generic_file_read,	/* read */
 	ext2_file_write,	/* write */
 	NULL,			/* readdir - bad */
@@ -80,6 +81,36 @@
 	NULL			/* smap */
 };
 
+/*
+ * Make sure the offset never goes beyond the 32-bit mark..
+ */
+static long long ext2_file_lseek(struct inode *inode,
+	struct file *file,
+	long long offset,
+	int origin)
+{
+	long long retval;
+
+	switch (origin) {
+		case 2:
+			offset += inode->i_size;
+			break;
+		case 1:
+			offset += file->f_pos;
+	}
+	retval = -EINVAL;
+	/* make sure the offset fits in 32 bits */
+	if (((unsigned long long) offset >> 32) == 0) {
+		if (offset != file->f_pos) {
+			file->f_pos = offset;
+			file->f_reada = 0;
+			file->f_version = ++event;
+		}
+		retval = offset;
+	}
+	return retval;
+}
+
 static inline void remove_suid(struct inode *inode)
 {
 	unsigned int mode;
@@ -98,9 +129,7 @@
 static long ext2_file_write (struct inode * inode, struct file * filp,
 			    const char * buf, unsigned long count)
 {
-	const loff_t two_gb = 2147483647;
-	loff_t pos;
-	off_t pos2;
+	__u32 pos;
 	long block;
 	int offset;
 	int written, c;
@@ -109,6 +138,9 @@
 	int err;
 	int i,buffercount,write_error;
 
+	/* POSIX: mtime/ctime may not change for 0 count */
+	if (!count)
+		return 0;
 	write_error = buffercount = 0;
 	if (!inode) {
 		printk("ext2_file_write: inode = NULL\n");
@@ -127,11 +159,18 @@
 		return -EINVAL;
 	}
 	remove_suid(inode);
+
 	if (filp->f_flags & O_APPEND)
 		pos = inode->i_size;
 	else
 		pos = filp->f_pos;
-	pos2 = (off_t) pos;
+	/* Check for overflow.. */
+	if (pos > (__u32) (pos + count)) {
+		count = ~pos; /* == 0xFFFFFFFF - pos */
+		if (!count)
+			return -EFBIG;
+	}
+
 	/*
 	 * If a file has been opened in synchronous mode, we have to ensure
 	 * that meta-data will also be written synchronously.  Thus, we
@@ -140,16 +179,11 @@
 	 */
 	if (filp->f_flags & O_SYNC)
 		inode->u.ext2_i.i_osync++;
-	block = pos2 >> EXT2_BLOCK_SIZE_BITS(sb);
-	offset = pos2 & (sb->s_blocksize - 1);
+	block = pos >> EXT2_BLOCK_SIZE_BITS(sb);
+	offset = pos & (sb->s_blocksize - 1);
 	c = sb->s_blocksize - offset;
 	written = 0;
-	while (count > 0) {
-		if (pos > two_gb) {
-			if (!written)
-				written = -EFBIG;
-			break;
-		}
+	do {
 		bh = ext2_getblk (inode, block, 1, &err);
 		if (!bh) {
 			if (!written)
@@ -158,7 +192,6 @@
 		}
 		if (c > count)
 			c = count;
-		count -= c;
 		if (c != sb->s_blocksize && !buffer_uptodate(bh)) {
 			ll_rw_block (READ, 1, &bh);
 			wait_on_buffer (bh);
@@ -177,10 +210,10 @@
 			break;
 		}
 		update_vm_cache(inode, pos, bh->b_data + offset, c);
-		pos2 += c;
 		pos += c;
 		written += c;
 		buf += c;
+		count -= c;
 		mark_buffer_uptodate(bh, 1);
 		mark_buffer_dirty(bh, 0);
 		if (filp->f_flags & O_SYNC)
@@ -202,7 +235,7 @@
 		block++;
 		offset = 0;
 		c = sb->s_blocksize;
-	}
+	} while (count);
 	if ( buffercount ){
 		ll_rw_block(WRITE, buffercount, bufferlist);
 		for(i=0; i<buffercount; i++){
diff -u --recursive --new-file v2.1.8/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v2.1.8/linux/fs/nfs/nfsroot.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/nfs/nfsroot.c	Sun Nov 10 19:12:57 1996
@@ -79,7 +79,7 @@
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>	/* For AX25_P_IP */
 #endif
 #include <linux/skbuff.h>
@@ -306,7 +306,7 @@
 
 	/* If it's not ethernet or AX25, delete it. */
 	if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 	   (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
 #endif
 	    rarp->ar_pln != 4) {
diff -u --recursive --new-file v2.1.8/linux/include/asm-alpha/checksum.h linux/include/asm-alpha/checksum.h
--- v2.1.8/linux/include/asm-alpha/checksum.h	Tue Oct  3 12:18:54 1995
+++ linux/include/asm-alpha/checksum.h	Sun Nov 10 19:29:34 1996
@@ -66,4 +66,11 @@
 	return ~sum;
 }
 
+#define _HAVE_ARCH_IPV6_CSUM
+extern unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+                                          struct in6_addr *daddr,
+                                          __u16 len,
+                                          unsigned short proto,
+                                          unsigned int sum);
+
 #endif
diff -u --recursive --new-file v2.1.8/linux/include/asm-alpha/termios.h linux/include/asm-alpha/termios.h
--- v2.1.8/linux/include/asm-alpha/termios.h	Sun Mar 24 12:09:37 1996
+++ linux/include/asm-alpha/termios.h	Sun Nov 10 19:12:57 1996
@@ -71,6 +71,7 @@
 #define N_SLIP		1
 #define N_MOUSE		2
 #define N_PPP		3
+#define N_AX25		5
 
 #ifdef __KERNEL__
 /*	eof=^D		eol=\0		eol2=\0		erase=del
diff -u --recursive --new-file v2.1.8/linux/include/asm-i386/termios.h linux/include/asm-i386/termios.h
--- v2.1.8/linux/include/asm-i386/termios.h	Fri Sep 20 15:20:45 1996
+++ linux/include/asm-i386/termios.h	Sun Nov 10 19:12:57 1996
@@ -52,6 +52,7 @@
 #define N_MOUSE		2
 #define N_PPP		3
 #define N_STRIP		4
+#define N_AX25		5
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-m68k/termios.h linux/include/asm-m68k/termios.h
--- v2.1.8/linux/include/asm-m68k/termios.h	Fri Apr  5 14:52:09 1996
+++ linux/include/asm-m68k/termios.h	Sun Nov 10 19:12:57 1996
@@ -52,6 +52,7 @@
 #define N_MOUSE		2
 #define N_PPP		3
 #define N_STRIP		4
+#define N_AX25		5
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-ppc/termios.h linux/include/asm-ppc/termios.h
--- v2.1.8/linux/include/asm-ppc/termios.h	Mon May 27 12:00:59 1996
+++ linux/include/asm-ppc/termios.h	Sun Nov 10 19:12:57 1996
@@ -343,6 +343,7 @@
 #define N_SLIP		1
 #define N_MOUSE		2
 #define N_PPP		3
+#define N_AX25		5
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/a.out.h linux/include/asm-sparc/a.out.h
--- v2.1.8/linux/include/asm-sparc/a.out.h	Sat Nov 25 04:31:10 1995
+++ linux/include/asm-sparc/a.out.h	Sat Nov  9 10:29:01 1996
@@ -1,4 +1,4 @@
-/* $Id: a.out.h,v 1.8 1995/11/25 02:31:09 davem Exp $ */
+/* $Id: a.out.h,v 1.9 1996/05/29 13:44:54 ecd Exp $ */
 #ifndef __SPARC_A_OUT_H__
 #define __SPARC_A_OUT_H__
 
@@ -40,6 +40,52 @@
 #define N_TRSIZE(a)	((a).a_trsize)
 #define N_DRSIZE(a)	((a).a_drsize)
 #define N_SYMSIZE(a)	((a).a_syms)
+
+/*
+ * Sparc relocation types
+ */
+enum reloc_type
+{
+	RELOC_8,
+	RELOC_16,
+	RELOC_32,	/* simplest relocs */
+	RELOC_DISP8,
+	RELOC_DISP16,
+	RELOC_DISP32,	/* Disp's (pc-rel) */
+	RELOC_WDISP30,
+	RELOC_WDISP22,  /* SR word disp's */
+	RELOC_HI22,
+	RELOC_22,	/* SR 22-bit relocs */
+	RELOC_13,
+	RELOC_LO10,	/* SR 13&10-bit relocs */
+	RELOC_SFA_BASE,
+	RELOC_SFA_OFF13, /* SR S.F.A. relocs */
+	RELOC_BASE10,
+	RELOC_BASE13,
+	RELOC_BASE22,	/* base_relative pic */
+	RELOC_PC10,
+	RELOC_PC22,	/* special pc-rel pic */
+	RELOC_JMP_TBL,	/* jmp_tbl_rel in pic */
+	RELOC_SEGOFF16,	/* ShLib offset-in-seg */
+	RELOC_GLOB_DAT,
+	RELOC_JMP_SLOT,
+	RELOC_RELATIVE 	/* rtld relocs */
+};
+
+/*
+ * Format of a relocation datum.
+ */
+struct relocation_info /* used when header.a_machtype == M_SPARC */
+{
+        unsigned long   r_address;  /* relocation addr */
+        unsigned int    r_index:24; /* segment index or symbol index */
+        unsigned int    r_extern:1; /* if F, r_index==SEG#; if T, SYM idx */
+        int             r_pad:2;    /* <unused> */
+        enum reloc_type r_type:5;   /* type of relocation to perform */
+        long            r_addend;   /* addend for relocation value */
+};
+
+#define N_RELOCATION_INFO_DECLARED 1
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/asmmacro.h linux/include/asm-sparc/asmmacro.h
--- v2.1.8/linux/include/asm-sparc/asmmacro.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/asmmacro.h	Sat Nov  9 10:29:03 1996
@@ -68,9 +68,7 @@
  * have to appropriate code to actually release the kernel
  * entry lock.
  */
-#define RESTORE_ALL \
-	b	ret_trap_entry; \
-	 nop;
+#define RESTORE_ALL b ret_trap_entry; clr %l6;
 
 #ifndef __SMP__
 
@@ -96,10 +94,6 @@
 	/* This is so complicated I suggest you don't look at it. */
 #define ENTER_MASK(mask) \
 	GET_PROCESSOR_OFFSET(l4) \
-	set	C_LABEL(smp_spinning), %l6; \
-	add	%l6, %l4, %l6; \
-	mov	1, %l5; \
-	st	%l5, [%l6]; \
 	set	C_LABEL(smp_proc_in_lock), %l5; \
 	ld	[%l5 + %l4], %l6; \
 	or	%l6, mask, %l6; \
@@ -157,9 +151,6 @@
 	ld	[%l5], %l5; \
 	st	%l4, [%l5]; \
 4: \
-	GET_PROCESSOR_OFFSET(l4) \
-	set	C_LABEL(smp_spinning), %l6; \
-	st	%g0, [%l6 + %l4];
 
 #define ENTER_SYSCALL \
 	ENTER_MASK(SMP_FROM_SYSCALL) \
@@ -198,9 +189,7 @@
 	INCREMENT_COUNTER(syscall_count, l6, l5)
 
 
-#define RESTORE_ALL_FASTIRQ \
-	b	ret_irq_entry; \
-	 nop;
+#define RESTORE_ALL_FASTIRQ b,a	ret_irq_entry;
 
 #endif /* !(__SMP__) */
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/atomic.h linux/include/asm-sparc/atomic.h
--- v2.1.8/linux/include/asm-sparc/atomic.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/atomic.h	Sat Nov  9 21:39:32 1996
@@ -6,55 +6,110 @@
 #ifndef __ARCH_SPARC_ATOMIC__
 #define __ARCH_SPARC_ATOMIC__
 
-#ifdef __SMP__
-#include <asm/smp.h>
-#include <asm/smp_lock.h>
-#endif
-
 typedef int atomic_t;
 
+#ifdef __KERNEL__
+#include <asm/system.h>
+#include <asm/psr.h>
+
+/*
+ * Make sure gcc doesn't try to be clever and move things around
+ * on us. We need to use _exactly_ the address the user gave us,
+ * not some alias that contains the same information.
+ */
+#define __atomic_fool_gcc(x) ((struct { int a[100]; } *)x)
+
 static __inline__ void atomic_add(atomic_t i, atomic_t *v)
 {
-	unsigned long flags;
-
-	save_flags(flags); cli();
-	*v += i;
-	restore_flags(flags);
+	__asm__ __volatile__("
+	rd	%%psr, %%g2
+	andcc	%%g2, %2, %%g0
+	be,a	1f
+	 wr	%%g2, %2, %%psr
+1:	ld	[%0], %%g3
+	add	%%g3, %1, %%g3
+	andcc	%%g2, %2, %%g0
+	st	%%g3, [%0]
+	be,a	1f
+	 wr	%%g2, 0x0, %%psr
+1:	nop; nop;
+        "
+        : : "r" (__atomic_fool_gcc(v)), "r" (i), "i" (PSR_PIL)
+        : "g2", "g3");
 }
 
 static __inline__ void atomic_sub(atomic_t i, atomic_t *v)
 {
-	unsigned long flags;
-
-	save_flags(flags); cli();
-	*v -= i;
-	restore_flags(flags);
-}
+	__asm__ __volatile__("
+	rd	%%psr, %%g2
+	andcc	%%g2, %2, %%g0
+	be,a	1f
+	 wr	%%g2, %2, %%psr
+1:	ld	[%0], %%g3
+	sub	%%g3, %1, %%g3
+	andcc	%%g2, %2, %%g0
+	st	%%g3, [%0]
+	be,a	1f
+	 wr	%%g2, 0x0, %%psr
+1:       nop; nop;
+        "
+        : : "r" (__atomic_fool_gcc(v)), "r" (i), "i" (PSR_PIL)
+        : "g2", "g3");
+}
+
+static __inline__ int atomic_add_return(atomic_t i, atomic_t *v)
+{
+	__asm__ __volatile__("
+	rd	%%psr, %%g2
+	andcc	%%g2, %3, %%g0
+	be,a	1f
+	 wr	%%g2, %3, %%psr
+1:	ld	[%1], %%g3
+	add	%%g3, %2, %0
+	andcc	%%g2, %3, %%g0
+	st	%0, [%1]
+	be,a	1f
+	 wr	%%g2, 0x0, %%psr
+1:	nop; nop;
+        "
+        : "=&r" (i)
+        : "r" (__atomic_fool_gcc(v)), "0" (i), "i" (PSR_PIL)
+        : "g2", "g3");
+
+	return i;
+}
+
+static __inline__ int atomic_sub_return(atomic_t i, atomic_t *v)
+{
+	__asm__ __volatile__("
+	rd	%%psr, %%g2
+	andcc	%%g2, %3, %%g0
+	be,a	1f
+	 wr	%%g2, %3, %%psr
+1:	ld	[%1], %%g3
+	sub	%%g3, %2, %0
+	andcc	%%g2, %3, %%g0
+	st	%0, [%1]
+	be,a	1f
+	 wr	%%g2, 0x0, %%psr
+1:	nop; nop;
+        "
+        : "=&r" (i)
+        : "r" (__atomic_fool_gcc(v)), "0" (i), "i" (PSR_PIL)
+        : "g2", "g3");
+
+	return i;
+}
+
+#define atomic_dec_return(v) atomic_sub_return(1,(v))
+#define atomic_inc_return(v) atomic_add_return(1,(v))
 
-static __inline__ int atomic_sub_and_test(atomic_t i, atomic_t *v)
-{
-	unsigned long flags, result;
+#define atomic_sub_and_test(i, v) (atomic_sub_return((i), (v)) == 0)
+#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
 
-	save_flags(flags); cli();
-	*v -= i;
-	result = (*v == 0);
-	restore_flags(flags);
-	return result;
-}
+#define atomic_inc(v) atomic_add(1,(v))
+#define atomic_dec(v) atomic_sub(1,(v))
 
-static __inline__ void atomic_inc(atomic_t *v)
-{
-	atomic_add(1, v);
-}
-
-static __inline__ void atomic_dec(atomic_t *v)
-{
-	atomic_sub(1, v);
-}
-
-static __inline__ int atomic_dec_and_test(atomic_t *v)
-{
-	return atomic_sub_and_test(1, v);
-}
+#endif /* !(__KERNEL__) */
 
 #endif /* !(__ARCH_SPARC_ATOMIC__) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/atops.h linux/include/asm-sparc/atops.h
--- v2.1.8/linux/include/asm-sparc/atops.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/atops.h	Sat Nov  9 10:29:04 1996
@@ -7,7 +7,7 @@
 
 #ifdef __SMP__
 
-extern __inline volatile unsigned char ldstub(volatile unsigned char *lock)
+extern __inline__ __volatile__ unsigned char ldstub(volatile unsigned char *lock)
 {
 	volatile unsigned char retval;
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/auxio.h linux/include/asm-sparc/auxio.h
--- v2.1.8/linux/include/asm-sparc/auxio.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/auxio.h	Sat Nov  9 21:43:35 1996
@@ -1,4 +1,4 @@
-/* $Id: auxio.h,v 1.11 1996/04/25 06:12:45 davem Exp $
+/* $Id: auxio.h,v 1.14 1996/10/31 06:29:10 davem Exp $
  * auxio.h:  Definitions and code for the Auxiliary I/O register.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -22,6 +22,7 @@
 #define AUXIO_FLPY_DCHG   0x10    /* A disk change occurred.  Read only. */
 #define AUXIO_EDGE_ON     0x10    /* sun4m - On means Jumper block is in. */
 #define AUXIO_FLPY_DSEL   0x08    /* Drive select/start-motor. Write only. */
+#define AUXIO_LINK_TEST   0x08    /* sun4m - On means TPE Carrier detect. */
 
 /* Set the following to one, then zero, after doing a pseudo DMA transfer. */
 #define AUXIO_FLPY_TCNT   0x04    /* Floppy terminal count. Write only. */
@@ -32,16 +33,17 @@
 
 #define AUXREG   ((volatile unsigned char *)(auxio_register))
 
-#define TURN_ON_LED   *AUXREG = (*AUXREG | AUXIO_ORMEIN | AUXIO_LED)
-#define TURN_OFF_LED  *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_LED))
-#define FLIP_LED      *AUXREG = ((*AUXREG | AUXIO_ORMEIN) ^ AUXIO_LED)
-#define FLPY_MOTORON  *AUXREG = ((*AUXREG | AUXIO_ORMEIN) | AUXIO_FLPY_DSEL)
-#define FLPY_MOTOROFF *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_FLPY_DSEL))
-#define FLPY_TCNTON   *AUXREG = ((*AUXREG | AUXIO_ORMEIN) | AUXIO_FLPY_TCNT)
-#define FLPY_TCNTOFF  *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_FLPY_TCNT))
+/* These are available on sun4c */
+#define TURN_ON_LED   if (AUXREG) *AUXREG = (*AUXREG | AUXIO_ORMEIN | AUXIO_LED)
+#define TURN_OFF_LED  if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_LED))
+#define FLIP_LED      if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) ^ AUXIO_LED)
+#define FLPY_MOTORON  if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) | AUXIO_FLPY_DSEL)
+#define FLPY_MOTOROFF if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_FLPY_DSEL))
+#define FLPY_TCNTON   if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) | AUXIO_FLPY_TCNT)
+#define FLPY_TCNTOFF  if (AUXREG) *AUXREG = ((*AUXREG | AUXIO_ORMEIN) & (~AUXIO_FLPY_TCNT))
 
 #ifndef __ASSEMBLY__
-extern inline void set_auxio(unsigned char bits_on, unsigned char bits_off)
+extern __inline__ void set_auxio(unsigned char bits_on, unsigned char bits_off)
 {
 	unsigned char regval;
 	unsigned long flags;
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/bitops.h linux/include/asm-sparc/bitops.h
--- v2.1.8/linux/include/asm-sparc/bitops.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/bitops.h	Sat Nov  9 21:39:34 1996
@@ -1,7 +1,8 @@
-/* $Id: bitops.h,v 1.23 1996/04/20 07:54:35 davem Exp $
+/* $Id: bitops.h,v 1.36 1996/09/29 22:57:21 davem Exp $
  * bitops.h: Bit string operations on the Sparc.
  *
- * Copyright 1995, David S. Miller (davem@caip.rutgers.edu).
+ * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright 1996 Eddie C. Dost   (ecd@skynet.be)
  */
 
 #ifndef _SPARC_BITOPS_H
@@ -9,18 +10,83 @@
 
 #include <linux/kernel.h>
 
-#ifdef __KERNEL__
-#include <asm/system.h>
-#endif
+#ifndef __KERNEL__
 
-#ifdef __SMP__
+/* User mode bitops, defined here for convenience. Note: these are not
+ * atomic, so packages like nthreads should do some locking around these
+ * themself.
+ */
 
-#define SMPVOL volatile
+#define __SMPVOL
 
-#else
+extern __inline__ unsigned long set_bit(unsigned long nr, void *addr)
+{
+	int mask;
+	unsigned long *ADDR = (unsigned long *) addr;
+
+	ADDR += nr >> 5;
+	mask = 1 << (nr & 31);
+	__asm__ __volatile__("
+	ld	[%0], %%g3
+	or	%%g3, %2, %%g2
+	st	%%g2, [%0]
+	and	%%g3, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask)
+	: "g2", "g3");
+
+	return (unsigned long) ADDR;
+}
+
+extern __inline__ unsigned long clear_bit(unsigned long nr, void *addr)
+{
+	int mask;
+	unsigned long *ADDR = (unsigned long *) addr;
+
+	ADDR += nr >> 5;
+	mask = 1 << (nr & 31);
+	__asm__ __volatile__("
+	ld	[%0], %%g3
+	andn	%%g3, %2, %%g2
+	st	%%g2, [%0]
+	and	%%g3, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask)
+	: "g2", "g3");
+
+	return (unsigned long) ADDR;
+}
+
+extern __inline__ unsigned long change_bit(unsigned long nr, void *addr)
+{
+	int mask;
+	unsigned long *ADDR = (unsigned long *) addr;
+
+	ADDR += nr >> 5;
+	mask = 1 << (nr & 31);
+	__asm__ __volatile__("
+	ld	[%0], %%g3
+	xor	%%g3, %2, %%g2
+	st	%%g2, [%0]
+	and	%%g3, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask)
+	: "g2", "g3");
+
+	return (unsigned long) ADDR;
+}
 
-#define SMPVOL
+#else /* __KERNEL__ */
 
+#include <asm/system.h>
+
+#ifdef __SMP__
+#define __SMPVOL volatile
+#else
+#define __SMPVOL
 #endif
 
 /* Set bit 'nr' in 32-bit quantity at address 'addr' where bit '0'
@@ -29,55 +95,96 @@
  * all bit-ops return 0 if bit was previously clear and != 0 otherwise.
  */
 
-extern __inline__ unsigned long set_bit(unsigned long nr, SMPVOL void *addr)
+extern __inline__ unsigned long set_bit(unsigned long nr, __SMPVOL void *addr)
 {
-	int mask, flags;
+	int mask;
 	unsigned long *ADDR = (unsigned long *) addr;
-	unsigned long oldbit;
 
 	ADDR += nr >> 5;
 	mask = 1 << (nr & 31);
-	save_flags(flags); cli();
-	oldbit = (mask & *ADDR);
-	*ADDR |= mask;
-	restore_flags(flags);
-	return oldbit != 0;
+	__asm__ __volatile__("
+	rd	%%psr, %%g3
+	andcc	%%g3, %3, %%g0
+	be,a	1f
+	 wr	%%g3, %3, %%psr
+1:	ld	[%0], %%g4
+	or	%%g4, %2, %%g2
+	andcc	%%g3, %3, %%g0
+	st	%%g2, [%0]
+	be,a	1f
+	 wr	%%g3, 0x0, %%psr
+1:	nop
+	and	%%g4, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask), "i" (PSR_PIL)
+	: "g2", "g3", "g4");
+
+	return (unsigned long) ADDR;
 }
 
-extern __inline__ unsigned long clear_bit(unsigned long nr, SMPVOL void *addr)
+extern __inline__ unsigned long clear_bit(unsigned long nr, __SMPVOL void *addr)
 {
-	int mask, flags;
+	int mask;
 	unsigned long *ADDR = (unsigned long *) addr;
-	unsigned long oldbit;
 
 	ADDR += nr >> 5;
 	mask = 1 << (nr & 31);
-	save_flags(flags); cli();
-	oldbit = (mask & *ADDR);
-	*ADDR &= ~mask;
-	restore_flags(flags);
-	return oldbit != 0;
+	__asm__ __volatile__("
+	rd	%%psr, %%g3
+	andcc	%%g3, %3, %%g0
+	be,a	1f
+	 wr	%%g3, %3, %%psr
+1:	ld	[%0], %%g4
+	andn	%%g4, %2, %%g2
+	andcc	%%g3, %3, %%g0
+	st	%%g2, [%0]
+	be,a	1f
+	wr	%%g3, 0x0, %%psr
+1:	nop
+	and	%%g4, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask), "i" (PSR_PIL)
+	: "g2", "g3", "g4");
+
+	return (unsigned long) ADDR;
 }
 
-extern __inline__ unsigned long change_bit(unsigned long nr, SMPVOL void *addr)
+extern __inline__ unsigned long change_bit(unsigned long nr, __SMPVOL void *addr)
 {
-	int mask, flags;
+	int mask;
 	unsigned long *ADDR = (unsigned long *) addr;
-	unsigned long oldbit;
 
 	ADDR += nr >> 5;
 	mask = 1 << (nr & 31);
-	save_flags(flags); cli();
-	oldbit = (mask & *ADDR);
-	*ADDR ^= mask;
-	restore_flags(flags);
-	return oldbit != 0;
+	__asm__ __volatile__("
+	rd	%%psr, %%g3
+	andcc	%%g3, %3, %%g0
+	be,a	1f
+	 wr	%%g3, %3, %%psr
+1:	ld	[%0], %%g4
+	xor	%%g4, %2, %%g2
+	andcc	%%g3, %3, %%g0
+	st	%%g2, [%0]
+	be,a	1f
+	 wr	%%g3, 0x0, %%psr
+1:	nop
+	and	%%g4, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask), "i" (PSR_PIL)
+	: "g2", "g3", "g4");
+
+	return (unsigned long) ADDR;
 }
 
+#endif /* __KERNEL__ */
+
 /* The following routine need not be atomic. */
-extern __inline__ unsigned long test_bit(int nr, const SMPVOL void *addr)
+extern __inline__ unsigned long test_bit(int nr, __const__ __SMPVOL void *addr)
 {
-	return ((1UL << (nr & 31)) & (((const unsigned int *) addr)[nr >> 5])) != 0;
+	return 1UL & (((__const__ unsigned int *) addr)[nr >> 5] >> (nr & 31));
 }
 
 /* The easy/cheese version for now. */
@@ -128,7 +235,7 @@
 	tmp = *p;
 
 found_first:
-	tmp |= ~0UL >> size;
+	tmp |= ~0UL << size;
 found_middle:
 	return result + ffz(tmp);
 }
@@ -140,50 +247,135 @@
 #define find_first_zero_bit(addr, size) \
         find_next_zero_bit((addr), (size), 0)
 
+#ifndef __KERNEL__
+
+extern __inline__ int __ext2_set_bit(int nr, void *addr)
+{
+	int		mask;
+	unsigned char	*ADDR = (unsigned char *) addr;
+
+	ADDR += nr >> 3;
+	mask = 1 << (nr & 0x07);
+	__asm__ __volatile__("
+	ldub	[%0], %%g3
+	or	%%g3, %2, %%g2
+	stb	%%g2, [%0]
+	and	%%g3, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask)
+	: "g2", "g3");
+
+	return (int) ADDR;
+}
+
+extern __inline__ int __ext2_clear_bit(int nr, void *addr)
+{
+	int		mask;
+	unsigned char	*ADDR = (unsigned char *) addr;
+
+	ADDR += nr >> 3;
+	mask = 1 << (nr & 0x07);
+	__asm__ __volatile__("
+	ldub	[%0], %%g3
+	andn	%%g3, %2, %%g2
+	stb	%%g2, [%0]
+	and	%%g3, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask)
+	: "g2", "g3");
+
+	return (int) ADDR;
+}
+
+#else /* __KERNEL__ */
+
 /* Now for the ext2 filesystem bit operations and helper routines. */
 
-extern __inline__ int ext2_set_bit(int nr,void * addr)
+extern __inline__ int __ext2_set_bit(int nr,void * addr)
 {
-	int		mask, retval, flags;
+	int		mask;
 	unsigned char	*ADDR = (unsigned char *) addr;
 
 	ADDR += nr >> 3;
 	mask = 1 << (nr & 0x07);
-	save_flags(flags); cli();
-	retval = (mask & *ADDR) != 0;
-	*ADDR |= mask;
-	restore_flags(flags);
-	return retval;
+	__asm__ __volatile__("
+	rd	%%psr, %%g3
+	andcc	%%g3, %3, %%g0
+	be,a	1f
+	 wr	%%g3, %3, %%psr
+1:	ldub	[%0], %%g4
+	or	%%g4, %2, %%g2
+	andcc	%%g3, %3, %%g0
+	stb	%%g2, [%0]
+	be,a	1f
+	 wr	%%g3, 0x0, %%psr
+1:	nop
+	and	%%g4, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask), "i" (PSR_PIL)
+	: "g2", "g3", "g4");
+
+	return (int) ADDR;
 }
 
-extern __inline__ int ext2_clear_bit(int nr, void * addr)
+extern __inline__ int __ext2_clear_bit(int nr, void * addr)
 {
-	int		mask, retval, flags;
+	int		mask;
 	unsigned char	*ADDR = (unsigned char *) addr;
 
 	ADDR += nr >> 3;
 	mask = 1 << (nr & 0x07);
-	save_flags(flags); cli();
-	retval = (mask & *ADDR) != 0;
-	*ADDR &= ~mask;
-	restore_flags(flags);
-	return retval;
+	__asm__ __volatile__("
+	rd	%%psr, %%g3
+	andcc	%%g3, %3, %%g0
+	be,a	1f
+	 wr	%%g3, %3, %%psr
+1:	ldub	[%0], %%g4
+	andn	%%g4, %2, %%g2
+	andcc	%%g3, %3, %%g0
+	stb	%%g2, [%0]
+	be,a	1f
+	wr	%%g3, 0x0, %%psr
+1:	nop
+	and	%%g4, %2, %0
+	"
+	: "=&r" (ADDR)
+	: "0" (ADDR), "r" (mask), "i" (PSR_PIL)
+	: "g2", "g3", "g4");
+
+	return (int) ADDR;
 }
 
-extern __inline__ int ext2_test_bit(int nr, const void * addr)
+#endif /* __KERNEL__ */
+
+extern __inline__ int __ext2_test_bit(int nr, __const__ void * addr)
 {
 	int			mask;
-	const unsigned char	*ADDR = (const unsigned char *) addr;
+	__const__ unsigned char	*ADDR = (__const__ unsigned char *) addr;
 
 	ADDR += nr >> 3;
 	mask = 1 << (nr & 0x07);
 	return ((mask & *ADDR) != 0);
 }
 
-#define ext2_find_first_zero_bit(addr, size) \
-        ext2_find_next_zero_bit((addr), (size), 0)
+extern __inline__ unsigned short __swab16(unsigned short value)
+{
+        return ((value >> 8) | (value << 8));
+}     
+
+extern __inline__ unsigned long __swab32(unsigned long value)
+{
+        return ((value >> 24) | ((value >> 8) & 0xff00) |
+               ((value << 8) & 0xff0000) | (value << 24));
+}     
+
+#define __ext2_find_first_zero_bit(addr, size) \
+        __ext2_find_next_zero_bit((addr), (size), 0)
 
-extern __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)
+extern __inline__ unsigned long __ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)
 {
 	unsigned long *p = ((unsigned long *) addr) + (offset >> 5);
 	unsigned long result = offset & ~31UL;
@@ -195,7 +387,7 @@
 	offset &= 31UL;
 	if(offset) {
 		tmp = *(p++);
-		tmp |= ~0UL << (32-offset);
+		tmp |= __swab32(~0UL >> (32-offset));
 		if(size < 32)
 			goto found_first;
 		if(~tmp)
@@ -214,10 +406,9 @@
 	tmp = *p;
 
 found_first:
-	tmp |= ~0UL << size;
+	return result + ffz(__swab32(tmp) | (~0UL << size));
 found_middle:
-	tmp = ((tmp>>24) | ((tmp>>8)&0xff00) | ((tmp<<8)&0xff0000) | (tmp<<24));
-	return result + ffz(tmp);
+	return result + ffz(__swab32(tmp));
 }
 
 #endif /* defined(_SPARC_BITOPS_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/byteorder.h linux/include/asm-sparc/byteorder.h
--- v2.1.8/linux/include/asm-sparc/byteorder.h	Sat Nov 25 04:31:21 1995
+++ linux/include/asm-sparc/byteorder.h	Sat Nov  9 10:29:09 1996
@@ -1,4 +1,4 @@
-/* $Id: byteorder.h,v 1.6 1995/11/25 02:31:20 davem Exp $ */
+/* $Id: byteorder.h,v 1.9 1996/08/30 05:21:34 davem Exp $ */
 #ifndef _SPARC_BYTEORDER_H
 #define _SPARC_BYTEORDER_H
 
@@ -7,9 +7,18 @@
 #define htonl(x) x
 #define htons(x) x
 
-#ifdef __KERNEL__
-#define __BIG_ENDIAN
+/* Some programs depend upon these being around. */
+#define __constant_ntohl(x) x
+#define __constant_ntohs(x) x
+#define __constant_htonl(x) x
+#define __constant_htons(x) x
+
+#ifndef __BIG_ENDIAN
+#define __BIG_ENDIAN 4321
 #endif
+
+#ifndef __BIG_ENDIAN_BITFIELD
 #define __BIG_ENDIAN_BITFIELD
+#endif
 
 #endif /* !(_SPARC_BYTEORDER_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/cache.h linux/include/asm-sparc/cache.h
--- v2.1.8/linux/include/asm-sparc/cache.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/cache.h	Sat Nov  9 10:29:12 1996
@@ -1,4 +1,4 @@
-/* $Id: cache.h,v 1.4 1996/04/25 06:12:49 davem Exp $
+/* $Id: cache.h,v 1.5 1996/08/29 09:48:06 davem Exp $
  * cache.h:  Cache specific code for the Sparc.  These include flushing
  *           and direct tag/data line access.
  *
@@ -20,7 +20,7 @@
  */
 
 /* First, cache-tag access. */
-extern inline unsigned int get_icache_tag(int setnum, int tagnum)
+extern __inline__ unsigned int get_icache_tag(int setnum, int tagnum)
 {
 	unsigned int vaddr, retval;
 
@@ -31,7 +31,7 @@
 	return retval;
 }
 
-extern inline void put_icache_tag(int setnum, int tagnum, unsigned int entry)
+extern __inline__ void put_icache_tag(int setnum, int tagnum, unsigned int entry)
 {
 	unsigned int vaddr;
 
@@ -44,8 +44,8 @@
 /* Second cache-data access.  The data is returned two-32bit quantities
  * at a time.
  */
-extern inline void get_icache_data(int setnum, int tagnum, int subblock,
-			      unsigned int *data)
+extern __inline__ void get_icache_data(int setnum, int tagnum, int subblock,
+				       unsigned int *data)
 {
 	unsigned int value1, value2, vaddr;
 
@@ -60,8 +60,8 @@
 	data[0] = value1; data[1] = value2;
 }
 
-extern inline void put_icache_data(int setnum, int tagnum, int subblock,
-			      unsigned int *data)
+extern __inline__ void put_icache_data(int setnum, int tagnum, int subblock,
+				       unsigned int *data)
 {
 	unsigned int value1, value2, vaddr;
 
@@ -85,35 +85,35 @@
  */
 
 /* Flushes which clear out both the on-chip and external caches */
-extern inline void flush_ei_page(unsigned int addr)
+extern __inline__ void flush_ei_page(unsigned int addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_PAGE) :
 			     "memory");
 }
 
-extern inline void flush_ei_seg(unsigned int addr)
+extern __inline__ void flush_ei_seg(unsigned int addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_SEG) :
 			     "memory");
 }
 
-extern inline void flush_ei_region(unsigned int addr)
+extern __inline__ void flush_ei_region(unsigned int addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_REGION) :
 			     "memory");
 }
 
-extern inline void flush_ei_ctx(unsigned int addr)
+extern __inline__ void flush_ei_ctx(unsigned int addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_CTX) :
 			     "memory");
 }
 
-extern inline void flush_ei_user(unsigned int addr)
+extern __inline__ void flush_ei_user(unsigned int addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_USER) :
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/checksum.h linux/include/asm-sparc/checksum.h
--- v2.1.8/linux/include/asm-sparc/checksum.h	Sun Nov 10 20:12:14 1996
+++ linux/include/asm-sparc/checksum.h	Sun Nov 10 23:28:33 1996
@@ -1,4 +1,4 @@
-/* $Id: checksum.h,v 1.13 1996/04/18 03:30:19 davem Exp $ */
+/* $Id: checksum.h,v 1.22 1996/11/10 21:28:25 davem Exp $ */
 #ifndef __SPARC_CHECKSUM_H
 #define __SPARC_CHECKSUM_H
 
@@ -7,6 +7,7 @@
  *  Copyright(C) 1995 Linus Torvalds
  *  Copyright(C) 1995 Miguel de Icaza
  *  Copyright(C) 1996 David S. Miller
+ *  Copyright(C) 1996 Eddie C. Dost
  *
  * derived from:
  *	Alpha checksum c-code
@@ -14,8 +15,7 @@
  *      RFC1071 Computing the Internet Checksum
  */
 
-/*
- * computes the checksum of a memory block at buff, length len,
+/* computes the checksum of a memory block at buff, length len,
  * and adds in "sum" (32-bit)
  *
  * returns a 32-bit number suitable for feeding into itself
@@ -28,8 +28,7 @@
  */
 extern unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum);
 
-/*
- * the same as csum_partial, but copies from fs:src while it
+/* the same as csum_partial, but copies from fs:src while it
  * checksums
  *
  * here even more important to align src and dst on a 32-bit (or even
@@ -40,85 +39,81 @@
 #define csum_partial_copy_fromuser(s, d, l, w)  \
                        csum_partial_copy((char *) (s), (d), (l), (w))
 
-/* ihl is always 5 or greater, almost always is 5, iph is always word
- * aligned but can fail to be dword aligned very often.
+/* ihl is always 5 or greater, almost always is 5, and iph is word aligned
+ * the majority of the time.
  */
-extern inline unsigned short ip_fast_csum(const unsigned char *iph, unsigned int ihl)
+extern __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph,
+					      unsigned int ihl)
 {
-	unsigned long tmp1, tmp2;
 	unsigned short sum;
 
-	__asm__ __volatile__("
-		ld	[%1 + 0x00], %0
-		ld	[%1 + 0x04], %3
-		sub	%2, 4, %2
-		addcc	%3, %0, %0
-		ld	[%1 + 0x08], %4
-		addxcc	%4, %0, %0
-		ld	[%1 + 0x0c], %3
-		addxcc	%3, %0, %0
-		ld	[%1 + 0x10], %4
-		addx	%0, %%g0, %0
-	1:
-		addcc	%4, %0, %0
-		add	%1, 4, %1
-		addxcc	%0, %%g0, %0
-		subcc	%2, 1, %2
-		be,a	2f
-		 sll	%0, 16, %3
-
-		b	1b
-		 ld	[%1 + 0x10], %4
-	2:
-		addcc	%0, %3, %3
-		srl	%3, 16, %0
-		addx	%0, %%g0, %0
-		xnor	%%g0, %0, %0
-	" : "=r" (sum), "=&r" (iph), "=&r" (ihl), "=r" (tmp1), "=r" (tmp2)
-	  : "1" (iph), "2" (ihl));
-
+	/* Note: We must read %2 before we touch %0 for the first time,
+	 *       because GCC can legitimately use the same register for
+	 *       both operands.
+	 */
+	__asm__ __volatile__("sub\t%2, 4, %%g4\n\t"
+			     "ld\t[%1 + 0x00], %0\n\t"
+			     "ld\t[%1 + 0x04], %%g2\n\t"
+			     "ld\t[%1 + 0x08], %%g3\n\t"
+			     "addcc\t%%g2, %0, %0\n\t"
+			     "addxcc\t%%g3, %0, %0\n\t"
+			     "ld\t[%1 + 0x0c], %%g2\n\t"
+			     "ld\t[%1 + 0x10], %%g3\n\t"
+			     "addxcc\t%%g2, %0, %0\n\t"
+			     "addx\t%0, %%g0, %0\n"
+			     "1:\taddcc\t%%g3, %0, %0\n\t"
+			     "add\t%1, 4, %1\n\t"
+			     "addxcc\t%0, %%g0, %0\n\t"
+			     "subcc\t%%g4, 1, %%g4\n\t"
+			     "be,a\t2f\n\t"
+			     "sll\t%0, 16, %%g2\n\t"
+			     "b\t1b\n\t"
+			     "ld\t[%1 + 0x10], %%g3\n"
+			     "2:\taddcc\t%0, %%g2, %%g2\n\t"
+			     "srl\t%%g2, 16, %0\n\t"
+			     "addx\t%0, %%g0, %0\n\t"
+			     "xnor\t%%g0, %0, %0"
+			     : "=r" (sum), "=&r" (iph)
+			     : "r" (ihl), "1" (iph)
+			     : "g2", "g3", "g4");
 	return sum;
 }
 
-/*
- * computes the checksum of the TCP/UDP pseudo-header
+/* computes the checksum of the TCP/UDP pseudo-header
  * returns a 16-bit checksum, already complemented
  */
-extern inline unsigned short csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
-					       unsigned int len, unsigned short proto,
-					       unsigned int sum)
+extern __inline__ unsigned short csum_tcpudp_magic(unsigned long saddr,
+						   unsigned long daddr,
+						   unsigned int len,
+						   unsigned short proto,
+						   unsigned int sum)
 {
-	__asm__ __volatile__("
-		addcc	%1, %0, %0
-		addxcc	%2, %0, %0
-		addxcc	%3, %0, %0
-		addx	%0, %%g0, %0
-		sll	%0, 16, %1
-		addcc	%1, %0, %0
-		srl	%0, 16, %0
-		addx	%0, %%g0, %0
-		xnor	%%g0, %0, %0
-	" : "=r" (sum), "=r" (saddr)
-	  : "r" (daddr), "r" ((proto<<16)+len), "0" (sum), "1" (saddr));
-
+	__asm__ __volatile__("addcc\t%1, %0, %0\n\t"
+			     "addxcc\t%2, %0, %0\n\t"
+			     "addxcc\t%3, %0, %0\n\t"
+			     "addx\t%0, %%g0, %0\n\t"
+			     "sll\t%0, 16, %1\n\t"
+			     "addcc\t%1, %0, %0\n\t"
+			     "srl\t%0, 16, %0\n\t"
+			     "addx\t%0, %%g0, %0\n\t"
+			     "xnor\t%%g0, %0, %0"
+			     : "=r" (sum), "=r" (saddr)
+			     : "r" (daddr), "r" ((proto<<16)+len), "0" (sum),
+			     "1" (saddr));
 	return sum;
 }
 
-/*
- *	Fold a partial checksum without adding pseudo headers
- */
-extern inline unsigned int csum_fold(unsigned int sum)
+/* Fold a partial checksum without adding pseudo headers. */
+extern __inline__ unsigned int csum_fold(unsigned int sum)
 {
 	unsigned int tmp;
 
-	__asm__ __volatile__("
-		addcc	%0, %1, %1
-		srl	%1, 16, %1
-		addx	%1, %%g0, %1
-		xnor	%%g0, %1, %0
-	" : "=&r" (sum), "=r" (tmp)
-	  : "0" (sum), "1" (sum<<16));
-
+	__asm__ __volatile__("addcc\t%0, %1, %1\n\t"
+			     "srl\t%1, 16, %1\n\t"
+			     "addx\t%1, %%g0, %1\n\t"
+			     "xnor\t%%g0, %1, %0"
+			     : "=&r" (sum), "=r" (tmp)
+			     : "0" (sum), "1" (sum<<16));
 	return sum;
 }
 
@@ -159,11 +154,8 @@
 	return csum_fold(sum);
 }
 
-/*
- * this routine is used for miscellaneous IP-like checksums, mainly
- * in icmp.c
- */
-extern inline unsigned short ip_compute_csum(unsigned char * buff, int len)
+/* this routine is used for miscellaneous IP-like checksums, mainly in icmp.c */
+extern __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len)
 {
 	return csum_fold(csum_partial(buff, len, 0));
 }
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/cprefix.h linux/include/asm-sparc/cprefix.h
--- v2.1.8/linux/include/asm-sparc/cprefix.h	Tue Nov 21 12:31:53 1995
+++ linux/include/asm-sparc/cprefix.h	Sat Nov  9 10:29:15 1996
@@ -9,10 +9,10 @@
 #ifndef __SPARC_CPREFIX_H
 #define __SPARC_CPREFIX_H
 
-#ifndef __svr4__
-#define C_LABEL_PREFIX _
-#else
+#if defined(__svr4__) || defined(__ELF__)
 #define C_LABEL_PREFIX
+#else
+#define C_LABEL_PREFIX _
 #endif
 
 #define CONCAT(a, b) CONCAT2(a, b)
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/cypress.h linux/include/asm-sparc/cypress.h
--- v2.1.8/linux/include/asm-sparc/cypress.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/cypress.h	Sat Nov  9 10:29:16 1996
@@ -1,4 +1,4 @@
-/* $Id: cypress.h,v 1.5 1996/04/25 06:12:51 davem Exp $
+/* $Id: cypress.h,v 1.6 1996/08/29 09:48:09 davem Exp $
  * cypress.h: Cypress module specific definitions and defines.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -48,25 +48,25 @@
 #define CYPRESS_NFAULT    0x00000002
 #define CYPRESS_MENABLE   0x00000001
 
-extern inline void cypress_flush_page(unsigned long page)
+extern __inline__ void cypress_flush_page(unsigned long page)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (page), "i" (ASI_M_FLUSH_PAGE));
 }
 
-extern inline void cypress_flush_segment(unsigned long addr)
+extern __inline__ void cypress_flush_segment(unsigned long addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_SEG));
 }
 
-extern inline void cypress_flush_region(unsigned long addr)
+extern __inline__ void cypress_flush_region(unsigned long addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_REGION));
 }
 
-extern inline void cypress_flush_context(void)
+extern __inline__ void cypress_flush_context(void)
 {
 	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
 			     "i" (ASI_M_FLUSH_CTX));
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/dma.h linux/include/asm-sparc/dma.h
--- v2.1.8/linux/include/asm-sparc/dma.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/dma.h	Sat Nov  9 21:40:36 1996
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.16 1996/04/25 06:12:54 davem Exp $
+/* $Id: dma.h,v 1.22 1996/10/17 05:29:01 davem Exp $
  * include/asm-sparc/dma.h
  *
  * Copyright 1995 (C) David S. Miller (davem@caip.rutgers.edu)
@@ -28,10 +28,10 @@
 
 /* Structure to describe the current status of DMA registers on the Sparc */
 struct sparc_dma_registers {
-  volatile unsigned long cond_reg;   /* DMA condition register */
-  volatile char * st_addr;           /* Start address of this transfer */
-  volatile unsigned long cnt;        /* How many bytes to transfer */
-  volatile unsigned long dma_test;   /* DMA test register */
+  __volatile__ unsigned long cond_reg;   /* DMA condition register */
+  __volatile__ char * st_addr;           /* Start address of this transfer */
+  __volatile__ unsigned long cnt;        /* How many bytes to transfer */
+  __volatile__ unsigned long dma_test;   /* DMA test register */
 };
 
 /* DVMA chip revisions */
@@ -41,7 +41,8 @@
 	dvmarev1,
 	dvmarev2,
 	dvmarev3,
-	dvmarevplus
+	dvmarevplus,
+	dvmahme
 };
 
 #define DMA_HASCOUNT(rev)  ((rev)==dvmaesc1)
@@ -83,6 +84,7 @@
 #define DMA_ESCV1        0x40000000        /* DMA ESC Version 1 */
 #define DMA_VERS1        0x80000000        /* DMA rev 1 */
 #define DMA_VERS2        0xa0000000        /* DMA rev 2 */
+#define DMA_VERHME       0xb0000000        /* DMA hme gate array */
 #define DMA_VERSPLUS     0x90000000        /* DMA rev 1 PLUS */
 
 #define DMA_HNDL_INTR    0x00000001        /* An IRQ needs to be handled */
@@ -97,6 +99,8 @@
 #define DMA_ST_WRITE     0x00000100        /* write from device to memory */
 #define DMA_ENABLE       0x00000200        /* Fire up DMA, handle requests */
 #define DMA_PEND_READ    0x00000400        /* DMA_VERS1/0/PLUS Pending Read */
+#define DMA_ESC_BURST    0x00000800        /* 1=16byte 0=32byte */
+#define DMA_READ_AHEAD   0x00001800        /* DMA read ahead partial longword */
 #define DMA_DSBL_RD_DRN  0x00001000        /* No EC drain on slave reads */
 #define DMA_BCNT_ENAB    0x00002000        /* If on, use the byte counter */
 #define DMA_TERM_CNTR    0x00004000        /* Terminal counter */
@@ -106,6 +110,10 @@
 #define DMA_ADD_ENABLE   0x00040000        /* Special ESC DVMA optimization */
 #define DMA_E_BURST8	 0x00040000	   /* ENET: SBUS r/w burst size */
 #define DMA_BRST_SZ      0x000c0000        /* SCSI: SBUS r/w burst size */
+#define DMA_BRST64       0x00080000        /* SCSI: 64byte bursts (HME on UltraSparc only) */
+#define DMA_BRST32       0x00040000        /* SCSI: 32byte bursts */
+#define DMA_BRST16       0x00000000        /* SCSI: 16byte bursts */
+#define DMA_BRST0        0x00080000        /* SCSI: no bursts (non-HME gate arrays) */
 #define DMA_ADDR_DISAB   0x00100000        /* No FIFO drains during addr */
 #define DMA_2CLKS        0x00200000        /* Each transfer = 2 clock ticks */
 #define DMA_3CLKS        0x00400000        /* Each transfer = 3 clock ticks */
@@ -113,6 +121,7 @@
 #define DMA_CNTR_DISAB   0x00800000        /* No IRQ when DMA_TERM_CNTR set */
 #define DMA_AUTO_NADDR   0x01000000        /* Use "auto nxt addr" feature */
 #define DMA_SCSI_ON      0x02000000        /* Enable SCSI dma */
+#define DMA_PARITY_OFF   0x02000000        /* HME: disable parity checking */
 #define DMA_LOADED_ADDR  0x04000000        /* Address has been loaded */
 #define DMA_LOADED_NADDR 0x08000000        /* Next address has been loaded */
 
@@ -131,7 +140,7 @@
 
 /* Yes, I hack a lot of elisp in my spare time... */
 #define DMA_ERROR_P(regs)  ((((regs)->cond_reg) & DMA_HNDL_ERROR))
-#define DMA_IRQ_P(regs)    ((((regs)->cond_reg) & DMA_HNDL_INTR))
+#define DMA_IRQ_P(regs)    ((((regs)->cond_reg) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)))
 #define DMA_WRITE_P(regs)  ((((regs)->cond_reg) & DMA_ST_WRITE))
 #define DMA_OFF(regs)      ((((regs)->cond_reg) &= (~DMA_ENABLE)))
 #define DMA_INTSOFF(regs)  ((((regs)->cond_reg) &= (~DMA_INT_ENAB)))
@@ -159,8 +168,8 @@
 /* Pause until counter runs out or BIT isn't set in the DMA condition
  * register.
  */
-extern inline void sparc_dma_pause(struct sparc_dma_registers *regs,
-				   unsigned long bit)
+extern __inline__ void sparc_dma_pause(struct sparc_dma_registers *regs,
+				       unsigned long bit)
 {
 	int ctr = 50000;   /* Let's find some bugs ;) */
 
@@ -194,7 +203,7 @@
         for((dma) = dma_chain; (dma); (dma) = (dma)->next)
 
 extern int get_dma_list(char *);
-extern int request_dma(unsigned int, const char *);
+extern int request_dma(unsigned int, __const__ char *);
 extern void free_dma(unsigned int);
 
 #endif /* !(_ASM_SPARC_DMA_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/elf.h linux/include/asm-sparc/elf.h
--- v2.1.8/linux/include/asm-sparc/elf.h	Sun Aug  4 14:12:40 1996
+++ linux/include/asm-sparc/elf.h	Sat Nov  9 22:16:59 1996
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.3 1996/04/22 15:48:48 miguel Exp $ */
+/* $Id: elf.h,v 1.5 1996/08/08 00:06:13 ecd Exp $ */
 #ifndef __ASMSPARC_ELF_H
 #define __ASMSPARC_ELF_H
 
@@ -23,11 +23,12 @@
 /*
  * These are used to set parameters in the core dumps.
  */
-#define ELF_ARCH EM_SPARC
+#define ELF_ARCH	EM_SPARC
 #define ELF_CLASS	ELFCLASS32
 #define ELF_DATA	ELFDATA2MSB;
 
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	4096
 
-#endif
+
+#endif /* !(__ASMSPARC_ELF_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/errno.h linux/include/asm-sparc/errno.h
--- v2.1.8/linux/include/asm-sparc/errno.h	Sat Nov 25 04:31:42 1995
+++ linux/include/asm-sparc/errno.h	Sat Nov  9 10:29:22 1996
@@ -1,4 +1,4 @@
-/* $Id: errno.h,v 1.4 1995/11/25 02:31:41 davem Exp $ */
+/* $Id: errno.h,v 1.5 1996/07/13 02:05:13 tridge Exp $ */
 #ifndef _SPARC_ERRNO_H
 #define _SPARC_ERRNO_H
 
@@ -37,7 +37,7 @@
 #define	EPIPE		32	/* Broken pipe */
 #define	EDOM		33	/* Math argument out of domain of func */
 #define	ERANGE		34	/* Math result not representable */
-#define	EWOULDBLOCK	35	/* Operation would block */
+#define	EWOULDBLOCK	EAGAIN	/* Operation would block */
 #define	EINPROGRESS	36	/* Operation now in progress */
 #define	EALREADY	37	/* Operation already in progress */
 #define	ENOTSOCK	38	/* Socket operation on non-socket */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/fbio.h linux/include/asm-sparc/fbio.h
--- v2.1.8/linux/include/asm-sparc/fbio.h	Tue Apr 23 12:31:34 1996
+++ linux/include/asm-sparc/fbio.h	Sat Nov  9 10:29:23 1996
@@ -1,7 +1,8 @@
 #ifndef __LINUX_FBIO_H
 #define __LINUX_FBIO_H
 
-/* Constants used for fbio SunOS compatibility -miguel */
+/* Constants used for fbio SunOS compatibility */
+/* (C) 1996 Miguel de Icaza */
 
 /* Frame buffer types */
 #define FBTYPE_NOTYPE           -1
@@ -28,7 +29,9 @@
 #define FBTYPE_SUNGT            18
 #define FBTYPE_SUNLEO           19      /* zx Leo card */
 #define FBTYPE_MDICOLOR         20      /* cg14 */
-#define FBTYPE_LASTPLUSONE      21
+#define FBTYPE_TCXCOLOR		21	/* SUNW,tcx card */
+
+#define FBTYPE_LASTPLUSONE      21	/* This is not last + 1 in fact... */
 
 /* fbio ioctls */
 /* Returned by FBIOGTYPE */
@@ -52,6 +55,7 @@
 };
 
 #define FBIOPUTCMAP _IOW('F', 3, struct fbcmap)
+#define FBIOGETCMAP _IOW('F', 4, struct fbcmap)
 
 /* # of device specific values */
 #define FB_ATTR_NDEVSPECIFIC    8
@@ -115,7 +119,82 @@
  
 /* get max cursor size */
 #define FBIOGCURMAX     _IOR('F', 28, struct fbcurpos)
- 
+
+/* wid manipulation */
+struct fb_wid_alloc {
+#define FB_WID_SHARED_8		0
+#define FB_WID_SHARED_24	1
+#define FB_WID_DBL_8		2
+#define FB_WID_DBL_24		3
+	__u32	wa_type;
+	__s32	wa_index;	/* Set on return */
+	__u32	wa_count;	
+};
+struct fb_wid_item {
+	__u32	wi_type;
+	__s32	wi_index;
+	__u32	wi_attrs;
+	__u32	wi_values[32];
+};
+struct fb_wid_list {
+	__u32	wl_flags;
+	__u32	wl_count;
+	struct fb_wid_item	*wl_list;
+};
+
+#define FBIO_WID_ALLOC	_IOWR('F', 30, struct fb_wid_alloc)
+#define FBIO_WID_FREE	_IOW('F', 31, struct fb_wid_alloc)
+#define FBIO_WID_PUT	_IOW('F', 32, struct fb_wid_list)
+#define FBIO_WID_GET	_IOWR('F', 33, struct fb_wid_list)
+
+/* Cg14 ioctls */
+#define MDI_IOCTL          ('M'<<8)
+#define MDI_RESET          (MDI_IOCTL|1)
+#define MDI_GET_CFGINFO    (MDI_IOCTL|2)
+#define MDI_SET_PIXELMODE  (MDI_IOCTL|3)
+#    define MDI_32_PIX     32
+#    define MDI_16_PIX     16
+#    define MDI_8_PIX      8
+
+struct mdi_cfginfo {
+	int     mdi_ncluts;     /* Number of implemented CLUTs in this MDI */
+        int     mdi_type;       /* FBTYPE name */
+        int     mdi_height;     /* height */
+        int     mdi_width;      /* widht */
+        int     mdi_size;       /* available ram */
+        int     mdi_mode;       /* 8bpp, 16bpp or 32bpp */
+        int     mdi_pixfreq;    /* pixel clock (from PROM) */
+};
+
+/* SparcLinux specific ioctl for the MDI, should be replaced for
+ * the SET_XLUT/SET_CLUTn ioctls instead
+ */
+#define MDI_CLEAR_XLUT       (MDI_IOCTL|9)
+
+/* leo ioctls */
+struct leo_clut_alloc {
+	__u32	clutid;	/* Set on return */
+ 	__u32	flag;
+ 	__u32	index;
+};
+
+struct leo_clut {
+#define LEO_CLUT_WAIT	0x00000001	/* Not yet implemented */
+ 	__u32	flag;
+ 	__u32	clutid;
+ 	__u32	offset;
+ 	__u32	count;
+ 	char *	red;
+ 	char *	green;
+ 	char *	blue;
+};
+#define LEO_CLUTALLOC	_IOWR('L', 53, struct leo_clut_alloc)
+#define LEO_CLUTFREE	_IOW('L', 54, struct leo_clut_alloc)
+#define LEO_CLUTREAD	_IOW('L', 55, struct leo_clut)
+#define LEO_CLUTPOST	_IOW('L', 56, struct leo_clut)
+#define LEO_SETGAMMA	_IOW('L', 68, int) /* Not yet implemented */
+#define LEO_GETGAMMA	_IOR('L', 69, int) /* Not yet implemented */
+
 #ifdef __KERNEL__
 /* Addresses on the fd of a cgsix that are mappable */
 #define CG6_FBC    0x70000000
@@ -128,6 +207,73 @@
 #define CG6_DHC    0x80000000
 
 #define CG3_MMAP_OFFSET 0x4000000
+
+/* Addresses on the fd of a tcx that are mappable */
+#define TCX_RAM8BIT   		0x00000000
+#define TCX_RAM24BIT   		0x01000000
+#define TCX_UNK3   		0x10000000
+#define TCX_UNK4   		0x20000000
+#define TCX_CONTROLPLANE   	0x28000000
+#define TCX_UNK6   		0x30000000
+#define TCX_UNK7   		0x38000000
+#define TCX_TEC    		0x70000000
+#define TCX_BTREGS 		0x70002000
+#define TCX_THC    		0x70004000
+#define TCX_DHC    		0x70008000
+#define TCX_ALT	   		0x7000a000
+#define TCX_SYNC   		0x7000e000
+#define TCX_UNK2    		0x70010000
+
+/* CG14 definitions */
+
+/* Offsets into the OBIO space: */
+#define CG14_REGS        0       /* registers */
+#define CG14_CURSORREGS  0x1000  /* cursor registers */
+#define CG14_DACREGS     0x2000  /* DAC registers */
+#define CG14_XLUT        0x3000  /* X Look Up Table -- ??? */
+#define CG14_CLUT1       0x4000  /* Color Look Up Table */
+#define CG14_CLUT2       0x5000  /* Color Look Up Table */
+#define CG14_CLUT3       0x6000  /* Color Look Up Table */
+#define CG14_AUTO	 0xf000
+
 #endif /* KERNEL */
+
+/* These are exported to userland for applications to use */
+/* Mappable offsets for the cg14: control registers */
+#define MDI_DIRECT_MAP 0x10000000
+#define MDI_CTLREG_MAP 0x20000000
+#define MDI_CURSOR_MAP 0x30000000
+#define MDI_SHDW_VRT_MAP 0x40000000
+
+/* Mappable offsets for the cg14: frame buffer resolutions */
+/* 32 bits */
+#define MDI_CHUNKY_XBGR_MAP 0x50000000
+#define MDI_CHUNKY_BGR_MAP 0x60000000
+
+/* 16 bits */
+#define MDI_PLANAR_X16_MAP 0x70000000
+#define MDI_PLANAR_C16_MAP 0x80000000
+
+/* 8 bit is done as CG3 MMAP offset */
+/* 32 bits, planar */
+#define MDI_PLANAR_X32_MAP 0x90000000
+#define MDI_PLANAR_B32_MAP 0xa0000000
+#define MDI_PLANAR_G32_MAP 0xb0000000
+#define MDI_PLANAR_R32_MAP 0xc0000000
+
+/* Mappable offsets on leo */
+#define LEO_SS0_MAP            0x00000000
+#define LEO_LC_SS0_USR_MAP     0x00800000
+#define LEO_LD_SS0_MAP         0x00801000
+#define LEO_LX_CURSOR_MAP      0x00802000
+#define LEO_SS1_MAP            0x00803000
+#define LEO_LC_SS1_USR_MAP     0x01003000
+#define LEO_LD_SS1_MAP         0x01004000
+#define LEO_UNK_MAP            0x01005000
+#define LEO_LX_KRN_MAP         0x01006000
+#define LEO_LC_SS0_KRN_MAP     0x01007000
+#define LEO_LC_SS1_KRN_MAP     0x01008000
+#define LEO_LD_GBL_MAP         0x01009000
+#define LEO_UNK2_MAP           0x0100a000
 
 #endif /* __LINUX_FBIO_H */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/fcntl.h linux/include/asm-sparc/fcntl.h
--- v2.1.8/linux/include/asm-sparc/fcntl.h	Sun Sep 22 09:41:32 1996
+++ linux/include/asm-sparc/fcntl.h	Sat Nov  9 10:29:24 1996
@@ -1,4 +1,4 @@
-/* $Id: fcntl.h,v 1.6 1996/01/28 02:09:23 davem Exp $ */
+/* $Id: fcntl.h,v 1.8 1996/10/27 08:55:26 davem Exp $ */
 #ifndef _SPARC_FCNTL_H
 #define _SPARC_FCNTL_H
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/floppy.h linux/include/asm-sparc/floppy.h
--- v2.1.8/linux/include/asm-sparc/floppy.h	Thu May 30 15:56:07 1996
+++ linux/include/asm-sparc/floppy.h	Mon Nov 11 00:28:45 1996
@@ -211,12 +211,12 @@
 unsigned long pdma_areasize;
 
 /* Common routines to all controller types on the Sparc. */
-static inline void virtual_dma_init(void)
+static __inline__ void virtual_dma_init(void)
 {
 	/* nothing... */
 }
 
-static inline void sun_fd_disable_dma(void)
+static __inline__ void sun_fd_disable_dma(void)
 {
 	doing_pdma = 0;
 	if (pdma_base) {
@@ -225,7 +225,7 @@
 	}
 }
 
-static inline void sun_fd_set_dma_mode(int mode)
+static __inline__ void sun_fd_set_dma_mode(int mode)
 {
 	switch(mode) {
 	case DMA_MODE_READ:
@@ -240,17 +240,17 @@
 	}
 }
 
-static inline void sun_fd_set_dma_addr(char *buffer)
+static __inline__ void sun_fd_set_dma_addr(char *buffer)
 {
 	pdma_vaddr = buffer;
 }
 
-static inline void sun_fd_set_dma_count(int length)
+static __inline__ void sun_fd_set_dma_count(int length)
 {
 	pdma_size = length;
 }
 
-static inline void sun_fd_enable_dma(void)
+static __inline__ void sun_fd_enable_dma(void)
 {
 	pdma_vaddr = mmu_lockarea(pdma_vaddr, pdma_size);
 	pdma_base = pdma_vaddr;
@@ -347,9 +347,9 @@
 
 static int sparc_eject(void)
 {
-	set_dor(0, ~0, 0x90);
+	set_dor(0x00, 0xff, 0x90);
 	udelay(500);
-	set_dor(0, ~0x90, 0);
+	set_dor(0x00, 0x6f, 0x00);
 	udelay(500);
 	return 0;
 }
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/head.h linux/include/asm-sparc/head.h
--- v2.1.8/linux/include/asm-sparc/head.h	Mon May  6 12:26:16 1996
+++ linux/include/asm-sparc/head.h	Sat Nov  9 10:29:26 1996
@@ -1,4 +1,4 @@
-/* $Id: head.h,v 1.27 1996/04/25 06:13:06 davem Exp $ */
+/* $Id: head.h,v 1.30 1996/07/29 21:00:28 miguel Exp $ */
 #ifndef __SPARC_HEAD_H
 #define __SPARC_HEAD_H
 
@@ -58,6 +58,18 @@
         or %l7, %lo(C_LABEL(sys_call_table)), %l7; \
         b solaris_syscall; \
         rd %psr, %l0;
+
+#define INDIRECT_SOLARIS_SYSCALL(x) \
+        sethi %hi(C_LABEL(sys_call_table)), %l7; \
+	or %g0,%lo(x),%g1; \
+	b solaris_indirect_syscall; \
+        rd %psr, %l0; 
+
+#define BREAKPOINT_TRAP \
+	b breakpoint_trap; \
+	rd %psr,%l0; \
+	nop; \
+	nop;
 
 /* Software trap for Sparc-netbsd system calls. */
 #define NETBSD_SYSCALL_TRAP \
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/idprom.h linux/include/asm-sparc/idprom.h
--- v2.1.8/linux/include/asm-sparc/idprom.h	Sat Nov 25 04:31:49 1995
+++ linux/include/asm-sparc/idprom.h	Sat Nov  9 10:29:27 1996
@@ -1,4 +1,4 @@
-/* $Id: idprom.h,v 1.5 1995/11/25 02:31:49 davem Exp $
+/* $Id: idprom.h,v 1.6 1996/08/04 10:35:07 ecd Exp $
  * idprom.h: Macros and defines for idprom routines
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -7,8 +7,6 @@
 #ifndef _SPARC_IDPROM_H
 #define _SPARC_IDPROM_H
 
-extern struct linux_romvec *romvec;
-
 /* Offset into the EEPROM where the id PROM is located on the 4c */
 #define IDPROM_OFFSET  0x7d8
 
@@ -16,19 +14,20 @@
 /* MicroSPARC(-II) does not decode 31rd bit, but it works. */
 #define IDPROM_OFFSET_M  0xfd8
 
-struct idp_struct
+struct idprom
 {
-  unsigned char id_f_id;      /* format identifier */
-  unsigned char id_machtype;  /* Machine type */
-  unsigned char id_eaddr[6];  /* hardware ethernet address */
-  long id_domf;               /* Date when this machine was manufactured */
-  unsigned int id_sernum:24;  /* Unique serial number */
-  unsigned char id_cksum;     /* XXX */
-  unsigned char dummy[16];    /* XXX */
+	unsigned char	id_format;	/* Format identifier (always 0x01) */
+	unsigned char	id_machtype;	/* Machine type */
+	unsigned char	id_ethaddr[6];	/* Hardware ethernet address */
+	long		id_date;	/* Date of manufacture */
+	unsigned int	id_sernum:24;	/* Unique serial number */
+	unsigned char	id_cksum;	/* Checksum - xor of the data bytes */
+	unsigned char	reserved[16];
 };
 
-extern struct idp_struct *idprom;
+extern struct idprom *idprom;
+extern void idprom_init(void);
 
-#define IDPROM_SIZE  (sizeof(struct idp_struct))
+#define IDPROM_SIZE  (sizeof(struct idprom))
 
 #endif /* !(_SPARC_IDPROM_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/io.h linux/include/asm-sparc/io.h
--- v2.1.8/linux/include/asm-sparc/io.h	Sat Nov 25 04:31:51 1995
+++ linux/include/asm-sparc/io.h	Sat Nov  9 21:39:31 1996
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.8 1995/11/25 02:31:50 davem Exp $ */
+/* $Id: io.h,v 1.10 1996/08/29 09:48:14 davem Exp $ */
 #ifndef __SPARC_IO_H
 #define __SPARC_IO_H
 
@@ -14,42 +14,42 @@
  * space only works on sun4's
  */
 
-extern inline unsigned long inb_local(unsigned long addr)
+extern __inline__ unsigned long inb_local(unsigned long addr)
 {
 	return 0;
 }
 
-extern inline void outb_local(unsigned char b, unsigned long addr)
+extern __inline__ void outb_local(unsigned char b, unsigned long addr)
 {
 	return;
 }
 
-extern inline unsigned long inb(unsigned long addr)
+extern __inline__ unsigned long inb(unsigned long addr)
 {
 	return 0;
 }
 
-extern inline unsigned long inw(unsigned long addr)
+extern __inline__ unsigned long inw(unsigned long addr)
 {
 	return 0;
 }
 
-extern inline unsigned long inl(unsigned long addr)
+extern __inline__ unsigned long inl(unsigned long addr)
 {
 	return 0;
 }
 
-extern inline void outb(unsigned char b, unsigned long addr)
+extern __inline__ void outb(unsigned char b, unsigned long addr)
 {
 	return;
 }
 
-extern inline void outw(unsigned short b, unsigned long addr)
+extern __inline__ void outw(unsigned short b, unsigned long addr)
 {
 	return;
 }
 
-extern inline void outl(unsigned int b, unsigned long addr)
+extern __inline__ void outl(unsigned int b, unsigned long addr)
 {
 	return;
 }
@@ -57,32 +57,32 @@
 /*
  * Memory functions
  */
-extern inline unsigned long readb(unsigned long addr)
+extern __inline__ unsigned long readb(unsigned long addr)
 {
 	return 0;
 }
 
-extern inline unsigned long readw(unsigned long addr)
+extern __inline__ unsigned long readw(unsigned long addr)
 {
 	return 0;
 }
 
-extern inline unsigned long readl(unsigned long addr)
+extern __inline__ unsigned long readl(unsigned long addr)
 {
 	return 0;
 }
 
-extern inline void writeb(unsigned short b, unsigned long addr)
+extern __inline__ void writeb(unsigned short b, unsigned long addr)
 {
 	return;
 }
 
-extern inline void writew(unsigned short b, unsigned long addr)
+extern __inline__ void writew(unsigned short b, unsigned long addr)
 {
 	return;
 }
 
-extern inline void writel(unsigned int b, unsigned long addr)
+extern __inline__ void writel(unsigned int b, unsigned long addr)
 {
 	return;
 }
@@ -93,8 +93,8 @@
 extern void sun4c_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
 extern void srmmu_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
 
-extern inline void mapioaddr(unsigned long physaddr, unsigned long virt_addr,
-			     int bus, int rdonly)
+extern __inline__ void mapioaddr(unsigned long physaddr, unsigned long virt_addr,
+				 int bus, int rdonly)
 {
 	switch(sparc_cpu_model) {
 	case sun4c:
@@ -114,7 +114,29 @@
 	return;
 }
 
+extern void srmmu_unmapioaddr(unsigned long virt);
+extern void sun4c_unmapioaddr(unsigned long virt);
+
+extern __inline__ void unmapioaddr(unsigned long virt_addr)
+{
+	switch(sparc_cpu_model) {
+	case sun4c:
+		sun4c_unmapioaddr(virt_addr);
+		break;
+	case sun4m:
+	case sun4d:
+	case sun4e:
+		srmmu_unmapioaddr(virt_addr);
+		break;
+	default:
+		printk("unmapioaddr: sparc_cpu_model = %d, halt...\n", sparc_cpu_model);
+		halt();
+	};
+	return;
+}
+
 extern void *sparc_alloc_io (void *, void *, int, char *, int, int);
+extern void sparc_free_io (void *, int);
 extern void *sparc_dvma_malloc (int, char *);
 
 #endif /* !(__SPARC_IO_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/ioctl.h linux/include/asm-sparc/ioctl.h
--- v2.1.8/linux/include/asm-sparc/ioctl.h	Sat Nov 25 04:31:53 1995
+++ linux/include/asm-sparc/ioctl.h	Sat Nov  9 10:29:28 1996
@@ -1,4 +1,4 @@
-/* $Id: ioctl.h,v 1.4 1995/11/25 02:31:52 davem Exp $ */
+/* $Id: ioctl.h,v 1.5 1996/05/17 03:31:09 davem Exp $ */
 #ifndef _SPARC_IOCTL_H
 #define _SPARC_IOCTL_H
 
@@ -33,7 +33,7 @@
 #define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
 #define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
 #define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
-#define _IORW(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
+#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
 
 #define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
 #define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/ioctls.h linux/include/asm-sparc/ioctls.h
--- v2.1.8/linux/include/asm-sparc/ioctls.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/ioctls.h	Sat Nov  9 10:29:30 1996
@@ -16,63 +16,61 @@
 #define TCSETSW		_IOW('T', 10, struct termios)
 #define TCSETSF		_IOW('T', 11, struct termios)
 
-/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
- * someday.  This is completely bogus, I know...
+/* Note that all the ioctls that are not available in Linux have a 
+ * double underscore on the front to: a) avoid some programs to
+ * thing we support some ioctls under Linux (autoconfiguration stuff)
  */
-#define TCGETSTAT       _IO('T', 200) /* Rutgers specific */
-#define TCSETSTAT       _IO('T', 201) /* Rutgers specific */
-
 /* Little t */
 #define TIOCGETD	_IOR('t', 0, int)
 #define TIOCSETD	_IOW('t', 1, int)
-#define TIOCHPCL        _IO('t', 2) /* SunOS Specific */
-#define TIOCMODG        _IOR('t', 3, int) /* SunOS Specific */
-#define TIOCMODS        _IOW('t', 4, int) /* SunOS Specific */
-#define TIOCGETP        _IOR('t', 8, struct sgttyb) /* SunOS Specific */
-#define TIOCSETP        _IOW('t', 9, struct sgttyb) /* SunOS Specific */
-#define TIOCSETN        _IOW('t', 10, struct sgttyb) /* SunOS Specific */
+#define __TIOCHPCL        _IO('t', 2) /* SunOS Specific */
+#define __TIOCMODG        _IOR('t', 3, int) /* SunOS Specific */
+#define __TIOCMODS        _IOW('t', 4, int) /* SunOS Specific */
+#define __TIOCGETP        _IOR('t', 8, struct sgttyb) /* SunOS Specific */
+#define __TIOCSETP        _IOW('t', 9, struct sgttyb) /* SunOS Specific */
+#define __TIOCSETN        _IOW('t', 10, struct sgttyb) /* SunOS Specific */
 #define TIOCEXCL	_IO('t', 13)
 #define TIOCNXCL	_IO('t', 14)
-#define TIOCFLUSH       _IOW('t', 16, int) /* SunOS Specific */
-#define TIOCSETC        _IOW('t', 17, struct tchars) /* SunOS Specific */
-#define TIOCGETC        _IOR('t', 18, struct tchars) /* SunOS Specific */
-#define TIOCTCNTL       _IOW('t', 32, int) /* SunOS Specific */
-#define TIOCSIGNAL      _IOW('t', 33, int) /* SunOS Specific */
-#define TIOCSETX        _IOW('t', 34, int) /* SunOS Specific */
-#define TIOCGETX        _IOR('t', 35, int) /* SunOS Specific */
+#define __TIOCFLUSH       _IOW('t', 16, int) /* SunOS Specific */
+#define __TIOCSETC        _IOW('t', 17, struct tchars) /* SunOS Specific */
+#define __TIOCGETC        _IOR('t', 18, struct tchars) /* SunOS Specific */
+#define __TIOCTCNTL       _IOW('t', 32, int) /* SunOS Specific */
+#define __TIOCSIGNAL      _IOW('t', 33, int) /* SunOS Specific */
+#define __TIOCSETX        _IOW('t', 34, int) /* SunOS Specific */
+#define __TIOCGETX        _IOR('t', 35, int) /* SunOS Specific */
 #define TIOCCONS	_IO('t', 36)
-#define TIOCSSIZE       _IOW('t', 37, struct sunos_ttysize) /* SunOS Specific */
-#define TIOCGSIZE       _IOR('t', 38, struct sunos_ttysize) /* SunOS Specific */
+#define __TIOCSSIZE     _IOW('t', 37, struct sunos_ttysize) /* SunOS Specific */
+#define __TIOCGSIZE     _IOR('t', 38, struct sunos_ttysize) /* SunOS Specific */
 #define TIOCGSOFTCAR	_IOR('t', 100, int)
 #define TIOCSSOFTCAR	_IOW('t', 101, int)
-#define TIOCUCNTL       _IOW('t', 102, int) /* SunOS Specific */
+#define __TIOCUCNTL       _IOW('t', 102, int) /* SunOS Specific */
 #define TIOCSWINSZ	_IOW('t', 103, struct winsize)
 #define TIOCGWINSZ	_IOR('t', 104, struct winsize)
-#define TIOCREMOTE      _IOW('t', 105, int) /* SunOS Specific */
+#define __TIOCREMOTE      _IOW('t', 105, int) /* SunOS Specific */
 #define TIOCMGET	_IOR('t', 106, int)
 #define TIOCMBIC	_IOW('t', 107, int)
 #define TIOCMBIS	_IOW('t', 108, int)
 #define TIOCMSET	_IOW('t', 109, int)
-#define TIOCSTART       _IO('t', 110) /* SunOS Specific */
-#define TIOCSTOP        _IO('t', 111) /* SunOS Specific */
+#define __TIOCSTART       _IO('t', 110) /* SunOS Specific */
+#define __TIOCSTOP        _IO('t', 111) /* SunOS Specific */
 #define TIOCPKT		_IOW('t', 112, int)
 #define TIOCNOTTY	_IO('t', 113)
 #define TIOCSTI		_IOW('t', 114, char)
 #define TIOCOUTQ	_IOR('t', 115, int)
-#define TIOCGLTC        _IOR('t', 116, struct ltchars) /* SunOS Specific */
-#define TIOCSLTC        _IOW('t', 117, struct ltchars) /* SunOS Specific */
+#define __TIOCGLTC        _IOR('t', 116, struct ltchars) /* SunOS Specific */
+#define __TIOCSLTC        _IOW('t', 117, struct ltchars) /* SunOS Specific */
 /* 118 is the non-posix setpgrp tty ioctl */
 /* 119 is the non-posix getpgrp tty ioctl */
-#define TIOCCDTR        _IO('t', 120) /* SunOS Specific */
-#define TIOCSDTR        _IO('t', 121) /* SunOS Specific */
-#define TIOCCBRK        _IO('t', 122) /* SunOS Specific */
-#define TIOCSBRK        _IO('t', 123) /* SunOS Specific */
-#define TIOCLGET        _IOW('t', 124, int) /* SunOS Specific */
-#define TIOCLSET        _IOW('t', 125, int) /* SunOS Specific */
-#define TIOCLBIC        _IOW('t', 126, int) /* SunOS Specific */
-#define TIOCLBIS        _IOW('t', 127, int) /* SunOS Specific */
-#define TIOCISPACE      _IOR('t', 128, int) /* SunOS Specific */
-#define TIOCISIZE       _IOR('t', 129, int) /* SunOS Specific */
+#define __TIOCCDTR        _IO('t', 120) /* SunOS Specific */
+#define __TIOCSDTR        _IO('t', 121) /* SunOS Specific */
+#define __TIOCCBRK        _IO('t', 122) /* SunOS Specific */
+#define __TIOCSBRK        _IO('t', 123) /* SunOS Specific */
+#define __TIOCLGET        _IOW('t', 124, int) /* SunOS Specific */
+#define __TIOCLSET        _IOW('t', 125, int) /* SunOS Specific */
+#define __TIOCLBIC        _IOW('t', 126, int) /* SunOS Specific */
+#define __TIOCLBIS        _IOW('t', 127, int) /* SunOS Specific */
+#define __TIOCISPACE      _IOR('t', 128, int) /* SunOS Specific */
+#define __TIOCISIZE       _IOR('t', 129, int) /* SunOS Specific */
 #define TIOCSPGRP	_IOW('t', 130, int)
 #define TIOCGPGRP	_IOR('t', 131, int)
 #define TIOCSCTTY	_IO('t', 132)
@@ -85,6 +83,12 @@
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
 
+/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
+ * someday.  This is completely bogus, I know...
+ */
+#define __TCGETSTAT       _IO('T', 200) /* Rutgers specific */
+#define __TCSETSTAT       _IO('T', 201) /* Rutgers specific */
+
 /* Linux specific, no SunOS equivalent. */
 #define TIOCLINUX	0x541C
 #define TIOCGSERIAL	0x541E
@@ -100,6 +104,17 @@
 #define TIOCSERGETLSR   0x5459 /* Get line status register */
 #define TIOCSERGETMULTI 0x545A /* Get multiport config  */
 #define TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+/* Kernel definitions */
+#ifdef __KERNEL__
+#define TIOCGETC __TIOCGETC
+#define TIOCGETP __TIOCGETP
+#define TIOCGLTC __TIOCGLTC
+#define TIOCSLTC __TIOCSLTC
+#define TIOCSETP __TIOCSETP
+#define TIOCSETN __TIOCSETN
+#define TIOCSETC __TIOCSETC
+#endif
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/iommu.h linux/include/asm-sparc/iommu.h
--- v2.1.8/linux/include/asm-sparc/iommu.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/iommu.h	Sat Nov  9 21:40:09 1996
@@ -107,12 +107,12 @@
 	unsigned long end;   /* Last managed virtual address */
 };
 
-extern inline void iommu_invalidate(struct iommu_regs *regs)
+extern __inline__ void iommu_invalidate(struct iommu_regs *regs)
 {
 	regs->tlbflush = 0;
 }
 
-extern inline void iommu_invalidate_page(struct iommu_regs *regs, unsigned long page)
+extern __inline__ void iommu_invalidate_page(struct iommu_regs *regs, unsigned long page)
 {
 	regs->pageflush = (page & PAGE_MASK);
 }
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/irq.h linux/include/asm-sparc/irq.h
--- v2.1.8/linux/include/asm-sparc/irq.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/irq.h	Sat Nov  9 21:40:09 1996
@@ -1,4 +1,4 @@
-/* $Id: irq.h,v 1.13 1996/04/25 06:13:09 davem Exp $
+/* $Id: irq.h,v 1.14 1996/08/29 09:48:18 davem Exp $
  * irq.h: IRQ registers on the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -34,7 +34,7 @@
 extern void (*set_irq_udt)(int);
 #endif
 
-extern int request_fast_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname);
+extern int request_fast_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, __const__ char *devname);
 
 /* On the sun4m, just like the timers, we have both per-cpu and master
  * interrupt registers.
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/kbio.h linux/include/asm-sparc/kbio.h
--- v2.1.8/linux/include/asm-sparc/kbio.h	Mon Mar  4 08:50:00 1996
+++ linux/include/asm-sparc/kbio.h	Sat Nov  9 10:29:32 1996
@@ -26,6 +26,12 @@
 /* Set routing of keystrokes to /dev/kbd */
 #define KIOCSDIRECT _IOW('k', 10, int)
 
+/* Set keyboard leds */
+#define KIOCSLED    _IOW('k', 14, unsigned char)
+
+/* Get keyboard leds */
+#define KIOCGLED    _IOR('k', 15, unsigned char)
+
 /* Top bit records if the key is up or down */
 #define KBD_UP      0x80
 
@@ -34,4 +40,5 @@
 
 /* All keys up */
 #define KBD_IDLE    0x75
+
 #endif /* __LINUX_KBIO_H */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/kdebug.h linux/include/asm-sparc/kdebug.h
--- v2.1.8/linux/include/asm-sparc/kdebug.h	Mon May  6 12:26:16 1996
+++ linux/include/asm-sparc/kdebug.h	Sat Nov  9 21:54:14 1996
@@ -1,4 +1,4 @@
-/* $Id: kdebug.h,v 1.8 1995/12/25 23:31:38 davem Exp $
+/* $Id: kdebug.h,v 1.9 1996/05/10 19:52:35 davem Exp $
  * kdebug.h:  Defines and definitions for debugging the Linux kernel
  *            under various kernel debuggers.
  *
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/mbus.h linux/include/asm-sparc/mbus.h
--- v2.1.8/linux/include/asm-sparc/mbus.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/mbus.h	Sat Nov  9 22:37:10 1996
@@ -1,4 +1,4 @@
-/* $Id: mbus.h,v 1.7 1996/04/25 06:13:12 davem Exp $
+/* $Id: mbus.h,v 1.8 1996/08/29 09:48:21 davem Exp $
  * mbus.h:  Various defines for MBUS modules.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -81,7 +81,7 @@
  */
 #define TBR_ID_SHIFT            20
 
-extern inline int get_cpuid(void)
+extern __inline__ int get_cpuid(void)
 {
 	register int retval;
 	__asm__ __volatile__("rd %%tbr, %0\n\t"
@@ -91,7 +91,7 @@
 	return (retval & 3);
 }
 
-extern inline int get_modid(void)
+extern __inline__ int get_modid(void)
 {
 	return (get_cpuid() | 0x8);
 }
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/memreg.h linux/include/asm-sparc/memreg.h
--- v2.1.8/linux/include/asm-sparc/memreg.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/memreg.h	Sat Nov  9 10:29:39 1996
@@ -1,4 +1,4 @@
-/* $Id: memreg.h,v 1.6 1996/04/25 06:13:13 davem Exp $ */
+/* $Id: memreg.h,v 1.8 1996/08/29 09:48:23 davem Exp $ */
 #ifndef _SPARC_MEMREG_H
 #define _SPARC_MEMREG_H
 /* memreg.h:  Definitions of the values found in the synchronous
@@ -33,5 +33,20 @@
 #define SUN4C_ASYNC_BADDVMA 0x0010  /* error during DVMA access */
 #define SUN4C_ASYNC_NOMEM   0x0020  /* write back pointed to bad phys addr */
 #define SUN4C_ASYNC_BADWB   0x0080  /* write back points to non-present page */
+
+/* Memory parity error register with associated bit constants. */
+#ifndef __ASSEMBLY__
+extern __volatile__ unsigned long *sun4c_memerr_reg;
+#endif
+
+#define	SUN4C_MPE_ERROR	0x80	/* Parity error detected. (ro) */
+#define	SUN4C_MPE_MULTI	0x40	/* Multiple parity errors detected. (ro) */
+#define	SUN4C_MPE_TEST	0x20	/* Write inverse parity. (rw) */
+#define	SUN4C_MPE_CHECK	0x10	/* Enable parity checking. (rw) */
+#define	SUN4C_MPE_ERR00	0x08	/* Parity error in bits 0-7. (ro) */
+#define	SUN4C_MPE_ERR08	0x04	/* Parity error in bits 8-15. (ro) */
+#define	SUN4C_MPE_ERR16	0x02	/* Parity error in bits 16-23. (ro) */
+#define	SUN4C_MPE_ERR24	0x01	/* Parity error in bits 24-31. (ro) */
+#define	SUN4C_MPE_ERRS	0x0F	/* Bit mask for the error bits. (ro) */
 
 #endif /* !(_SPARC_MEMREG_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/mman.h linux/include/asm-sparc/mman.h
--- v2.1.8/linux/include/asm-sparc/mman.h	Wed Oct  9 08:55:23 1996
+++ linux/include/asm-sparc/mman.h	Sat Nov  9 10:29:41 1996
@@ -1,4 +1,4 @@
-/* $Id: mman.h,v 1.7 1996/04/25 06:13:15 davem Exp $ */
+/* $Id: mman.h,v 1.8 1996/10/27 08:55:28 davem Exp $ */
 #ifndef __SPARC_MMAN_H__
 #define __SPARC_MMAN_H__
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/mostek.h linux/include/asm-sparc/mostek.h
--- v2.1.8/linux/include/asm-sparc/mostek.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/mostek.h	Sat Nov  9 10:29:44 1996
@@ -1,67 +1,112 @@
-/* $Id: mostek.h,v 1.5 1996/04/25 06:13:17 davem Exp $
+/* $Id: mostek.h,v 1.8 1996/11/04 00:45:30 ecd Exp $
  * mostek.h:  Describes the various Mostek time of day clock registers.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
  */
+
 #ifndef _SPARC_MOSTEK_H
 #define _SPARC_MOSTEK_H
 
 #include <asm/idprom.h>
 
-/* First the Mostek 48t02 clock chip.  The registers other than the
- * control register are in binary coded decimal.
+/*       M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ)
+ *
+ *                             Data
+ * Address                                                 Function
+ *        Bit 7 Bit 6 Bit 5 Bit 4Bit 3 Bit 2 Bit 1 Bit 0
+ *   7ff  -     -     -     -    -     -     -     -       Year 00-99
+ *   7fe  0     0     0     -    -     -     -     -      Month 01-12
+ *   7fd  0     0     -     -    -     -     -     -       Date 01-31
+ *   7fc  0     FT    0     0    0     -     -     -        Day 01-07
+ *   7fb  KS    0     -     -    -     -     -     -      Hours 00-23
+ *   7fa  0     -     -     -    -     -     -     -    Minutes 00-59
+ *   7f9  ST    -     -     -    -     -     -     -    Seconds 00-59
+ *   7f8  W     R     S     -    -     -     -     -    Control
+ *
+ *   * ST is STOP BIT
+ *   * W is WRITE BIT
+ *   * R is READ BIT
+ *   * S is SIGN BIT
+ *   * FT is FREQ TEST BIT
+ *   * KS is KICK START BIT
  */
+
+/* The Mostek 48t02 real time clock and NVRAM chip. The registers
+ * other than the control register are in binary coded decimal. Some
+ * control bits also live outside the control register.
+ */
+
 struct mostek48t02 {
-	char eeprom[2008];            /* This is the eeprom, don't touch! */
-	struct idp_struct idprom;     /* The idprom lives here. */
-	volatile unsigned char creg;  /* Control register */
-	volatile unsigned char sec;   /* Seconds (0-59) */
-	volatile unsigned char min;   /* Minutes (0-59) */
-	volatile unsigned char hour;  /* Hour (0-23) */
-	volatile unsigned char dow;   /* Day of the week (1-7) */
-	volatile unsigned char dom;   /* Day of the month (1-31) */
-	volatile unsigned char mnth;  /* Month of year (1-12) */
-	volatile unsigned char yr;    /* Year (0-99) */
+	volatile char eeprom[2008];	/* This is the eeprom, don't touch! */
+	struct idprom idprom;		/* The idprom lives here. */
+	volatile unsigned char creg;	/* Control register */
+	volatile unsigned char sec;	/* Seconds (0-59) */
+	volatile unsigned char min;	/* Minutes (0-59) */
+	volatile unsigned char hour;	/* Hour (0-23) */
+	volatile unsigned char dow;	/* Day of the week (1-7) */
+	volatile unsigned char dom;	/* Day of the month (1-31) */
+	volatile unsigned char month;	/* Month of year (1-12) */
+	volatile unsigned char year;	/* Year (0-99) */
 };
 
 extern struct mostek48t02 *mstk48t02_regs;
 
 /* Control register values. */
-#define MSTK_CREG_WRITE    0x80   /* Must set this before placing values. */
-#define MSTK_CREG_READ     0x40   /* Stop the clock, I want to fetch values. */
-#define MSTK_CREG_SIGN     0x20   /* Grrr... what's this??? */
-
-#define MSTK_YR_ZERO       1968   /* If year reg has zero, it is 1968 */
-#define MSTK_CVT_YEAR(yr)  ((yr) + MSTK_YR_ZERO)
-
-/* Fun with masks. */
-#define MSTK_SEC_MASK      0x7f
-#define MSTK_MIN_MASK      0x7f
-#define MSTK_HOUR_MASK     0x3f
-#define MSTK_DOW_MASK      0x07
-#define MSTK_DOM_MASK      0x3f
-#define MSTK_MNTH_MASK     0x1f
-#define MSTK_YR_MASK       0xff
-
-/* Conversion routines. */
-#define MSTK_REGVAL_TO_DECIMAL(x)  (((x) & 0xf) + 0xa * ((x) >> 0x4))
-#define MSTK_DECIMAL_TO_REGVAL(x)  ((((x) / 0xa) << 0x4) + ((x) % 0xa))
+#define	MSTK_CREG_WRITE	0x80	/* Must set this before placing values. */
+#define	MSTK_CREG_READ	0x40	/* Stop updates to allow a clean read. */
+#define	MSTK_CREG_SIGN	0x20	/* Slow/speed clock in calibration mode. */
+
+/* Control bits that live in the other registers. */
+#define	MSTK_STOP	0x80	/* Stop the clock oscillator. (sec) */
+#define	MSTK_KICK_START	0x80	/* Kick start the clock chip. (hour) */
+#define MSTK_FREQ_TEST	0x40	/* Frequency test mode. (day) */
+
+#define MSTK_YEAR_ZERO       1968   /* If year reg has zero, it is 1968. */
+#define MSTK_CVT_YEAR(yr)  ((yr) + MSTK_YEAR_ZERO)
+
+/* Masks that define how much space each value takes up. */
+#define	MSTK_SEC_MASK	0x7f
+#define	MSTK_MIN_MASK	0x7f
+#define	MSTK_HOUR_MASK	0x3f
+#define	MSTK_DOW_MASK	0x07
+#define	MSTK_DOM_MASK	0x3f
+#define	MSTK_MONTH_MASK	0x1f
+#define	MSTK_YEAR_MASK	0xff
+
+/* Binary coded decimal conversion macros. */
+#define MSTK_REGVAL_TO_DECIMAL(x)  (((x) & 0x0F) + 0x0A * ((x) >> 0x04))
+#define MSTK_DECIMAL_TO_REGVAL(x)  ((((x) / 0x0A) << 0x04) + ((x) % 0x0A))
+
+/* Generic register set and get macros for internal use. */
+#define MSTK_GET(regs,var,mask) (MSTK_REGVAL_TO_DECIMAL(regs->var & MSTK_ ## mask ## _MASK))
+#define MSTK_SET(regs,var,value,mask) do { regs->var &= ~(MSTK_ ## mask ## _MASK); regs->var |= MSTK_DECIMAL_TO_REGVAL(value) & (MSTK_ ## mask ## _MASK); } while (0)
 
 /* Macros to make register access easier on our fingers. These give you
- * the decimal value of the register requested if applicable.  You pass
+ * the decimal value of the register requested if applicable. You pass
  * the a pointer to a 'struct mostek48t02'.
  */
-#define MSTK_REG_CREG(ptr)  (ptr->creg)
-#define MSTK_REG_SEC(ptr)   (MSTK_REGVAL_TO_DECIMAL((ptr->sec & MSTK_SEC_MASK)))
-#define MSTK_REG_MIN(ptr)   (MSTK_REGVAL_TO_DECIMAL((ptr->min & MSTK_MIN_MASK)))
-#define MSTK_REG_HOUR(ptr)  (MSTK_REGVAL_TO_DECIMAL((ptr->hour & MSTK_HOUR_MASK)))
-#define MSTK_REG_DOW(ptr)   (MSTK_REGVAL_TO_DECIMAL((ptr->dow & MSTK_DOW_MASK)))
-#define MSTK_REG_DOM(ptr)   (MSTK_REGVAL_TO_DECIMAL((ptr->dom & MSTK_DOM_MASK)))
-#define MSTK_REG_MNTH(ptr)  (MSTK_REGVAL_TO_DECIMAL((ptr->mnth & MSTK_MNTH_MASK)))
-#define MSTK_REG_YR(ptr)    (MSTK_REGVAL_TO_DECIMAL((ptr->yr & MSTK_YR_MASK)))
-
-/* The Mostek 48t02 clock chip.  Found on Sun4m's I think.  It has the
- * same (basically) layout of the 48t02 chip.
+#define	MSTK_REG_CREG(regs)	(regs->creg)
+#define	MSTK_REG_SEC(regs)	MSTK_GET(regs,sec,SEC)
+#define	MSTK_REG_MIN(regs)	MSTK_GET(regs,min,MIN)
+#define	MSTK_REG_HOUR(regs)	MSTK_GET(regs,hour,HOUR)
+#define	MSTK_REG_DOW(regs)	MSTK_GET(regs,dow,DOW)
+#define	MSTK_REG_DOM(regs)	MSTK_GET(regs,dom,DOM)
+#define	MSTK_REG_MONTH(regs)	MSTK_GET(regs,month,MONTH)
+#define	MSTK_REG_YEAR(regs)	MSTK_GET(regs,year,YEAR)
+
+#define	MSTK_SET_REG_SEC(regs,value)	MSTK_SET(regs,sec,value,SEC)
+#define	MSTK_SET_REG_MIN(regs,value)	MSTK_SET(regs,min,value,MIN)
+#define	MSTK_SET_REG_HOUR(regs,value)	MSTK_SET(regs,hour,value,HOUR)
+#define	MSTK_SET_REG_DOW(regs,value)	MSTK_SET(regs,dow,value,DOW)
+#define	MSTK_SET_REG_DOM(regs,value)	MSTK_SET(regs,dom,value,DOM)
+#define	MSTK_SET_REG_MONTH(regs,value)	MSTK_SET(regs,month,value,MONTH)
+#define	MSTK_SET_REG_YEAR(regs,value)	MSTK_SET(regs,year,value,YEAR)
+
+
+/* The Mostek 48t08 clock chip. Found on Sun4m's I think. It has the
+ * same (basically) layout of the 48t02 chip except for the extra
+ * NVRAM on board (8 KB against the 48t02's 2 KB).
  */
 struct mostek48t08 {
 	char offset[6*1024];         /* Magic things may be here, who knows? */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/msi.h linux/include/asm-sparc/msi.h
--- v2.1.8/linux/include/asm-sparc/msi.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/msi.h	Sat Nov  9 10:29:46 1996
@@ -1,8 +1,8 @@
-/* $Id: msi.h,v 1.1 1996/04/20 10:14:32 davem Exp $
+/* $Id: msi.h,v 1.3 1996/08/29 09:48:25 davem Exp $
  * msi.h:  Defines specific to the MBus - Sbus - Interface.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost   (ecd@pool.informatik.rwth-aachen.de)
+ * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
  */
 
 #ifndef _SPARC_MSI_H
@@ -19,7 +19,7 @@
 #define MSI_ASYNC_MODE  0x80000000	/* Operate the MSI asynchronously */
 
 
-extern inline void msi_set_sync(void)
+extern __inline__ void msi_set_sync(void)
 {
 	__asm__ __volatile__ ("lda [%0] %1, %%g3\n\t"
 			      "andn %%g3, %2, %%g3\n\t"
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/mxcc.h linux/include/asm-sparc/mxcc.h
--- v2.1.8/linux/include/asm-sparc/mxcc.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/mxcc.h	Sat Nov  9 10:29:47 1996
@@ -1,4 +1,4 @@
-/* $Id: mxcc.h,v 1.4 1996/04/25 06:13:21 davem Exp $
+/* $Id: mxcc.h,v 1.6 1996/08/29 09:48:27 davem Exp $
  * mxcc.h:  Definitions of the Viking MXCC registers
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -83,7 +83,7 @@
  * MID: The moduleID of the cpu your read this from.
  */
 
-extern inline void mxcc_set_stream_src(unsigned long *paddr)
+extern __inline__ void mxcc_set_stream_src(unsigned long *paddr)
 {
 	unsigned long data0 = paddr[0];
 	unsigned long data1 = paddr[1];
@@ -96,7 +96,7 @@
 			      "i" (ASI_M_MXCC) : "g2", "g3");
 }
 
-extern inline void mxcc_set_stream_dst(unsigned long *paddr)
+extern __inline__ void mxcc_set_stream_dst(unsigned long *paddr)
 {
 	unsigned long data0 = paddr[0];
 	unsigned long data1 = paddr[1];
@@ -107,6 +107,27 @@
 			      "r" (data0), "r" (data1),
 			      "r" (MXCC_DESSTREAM),
 			      "i" (ASI_M_MXCC) : "g2", "g3");
+}
+
+extern __inline__ unsigned long mxcc_get_creg(void)
+{
+	unsigned long mxcc_control;
+
+	__asm__ __volatile__("set -1, %%g2\n\t"
+			     "set -1, %%g3\n\t"
+			     "stda %%g2, [%1] %2\n\t"
+			     "lda [%3] %2, %0\n\t" :
+			     "=r" (mxcc_control) :
+			     "r" (MXCC_EREG), "i" (ASI_M_MXCC),
+			     "r" (MXCC_CREG) : "g2", "g3");
+	return mxcc_control;
+}
+
+extern __inline__ void mxcc_set_creg(unsigned long mxcc_control)
+{
+	__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+			     "r" (mxcc_control), "r" (MXCC_CREG),
+			     "i" (ASI_M_MXCC));
 }
 
 #endif /* !(_SPARC_MXCC_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/openprom.h linux/include/asm-sparc/openprom.h
--- v2.1.8/linux/include/asm-sparc/openprom.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/openprom.h	Sat Nov  9 10:29:48 1996
@@ -1,4 +1,4 @@
-/* $Id: openprom.h,v 1.16 1996/04/23 01:54:46 davem Exp $ */
+/* $Id: openprom.h,v 1.19 1996/09/25 03:51:08 davem Exp $ */
 #ifndef __SPARC_OPENPROM_H
 #define __SPARC_OPENPROM_H
 
@@ -42,7 +42,7 @@
 	void (*v2_dev_close)(int d);
 	int (*v2_dev_read)(int d, char *buf, int nbytes);
 	int (*v2_dev_write)(int d, char *buf, int nbytes);
-	void (*v2_dev_seek)(int d, int hi, int lo);
+	int (*v2_dev_seek)(int d, int hi, int lo);
 
 	/* Never issued (multistage load support) */
 	void (*v2_wheee2)(void);
@@ -117,9 +117,9 @@
 
 	/* Miscellany. */
 	void (*pv_reboot)(char *bootstr);
-	void (*pv_printf)(const char *fmt, ...);
+	void (*pv_printf)(__const__ char *fmt, ...);
 	void (*pv_abort)(void);
-	int *pv_ticks;
+	__volatile__ int *pv_ticks;
 	void (*pv_halt)(void);
 	void (**pv_synchook)(void);
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/openpromio.h linux/include/asm-sparc/openpromio.h
--- v2.1.8/linux/include/asm-sparc/openpromio.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/openpromio.h	Sat Nov  9 21:55:01 1996
@@ -0,0 +1,70 @@
+#ifndef	_SPARC_OPENPROMIO_H
+#define	_SPARC_OPENPROMIO_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/*
+ * SunOS and Solaris /dev/openprom definitions. The ioctl values
+ * were chosen to be exactly equal to the SunOS equivalents.
+ */
+
+struct openpromio
+{
+	u_int	oprom_size;		/* Actual size of the oprom_array. */
+	char	oprom_array[1];		/* Holds property names and values. */
+};
+
+#define	OPROMMAXPARAM	4096		/* Maximum size of oprom_array. */
+
+#define	OPROMGETOPT		0x20004F01
+#define	OPROMSETOPT		0x20004F02
+#define	OPROMNXTOPT		0x20004F03
+#define	OPROMSETOPT2		0x20004F04
+#define	OPROMNEXT		0x20004F05
+#define	OPROMCHILD		0x20004F06
+#define	OPROMGETPROP		0x20004F07
+#define	OPROMNXTPROP		0x20004F08
+#define	OPROMU2P		0x20004F09
+#define	OPROMGETCONS		0x20004F0A
+#define	OPROMGETFBNAME		0x20004F0B
+#define	OPROMGETBOOTARGS	0x20004F0C
+
+/*
+ * Return values from OPROMGETCONS:
+ */
+
+#define OPROMCONS_NOT_WSCONS    0
+#define OPROMCONS_STDIN_IS_KBD  0x1     /* stdin device is kbd */
+#define OPROMCONS_STDOUT_IS_FB  0x2     /* stdout is a framebuffer */
+#define OPROMCONS_OPENPROM      0x4     /* supports openboot */
+
+
+/*
+ *  NetBSD/OpenBSD /dev/openprom definitions.
+ */
+
+struct opiocdesc
+{
+	int	op_nodeid;		/* PROM Node ID (value-result) */
+	int	op_namelen;		/* Length of op_name. */
+	char	*op_name;		/* Pointer to the property name. */
+	int	op_buflen;		/* Length of op_buf (value-result) */
+	char	*op_buf;		/* Pointer to buffer. */
+};
+
+#define	OPIOCGET	_IOWR('O', 1, struct opiocdesc)
+#define	OPIOCSET	_IOW('O', 2, struct opiocdesc)
+#define	OPIOCNEXTPROP	_IOWR('O', 3, struct opiocdesc)
+#define	OPIOCGETOPTNODE	_IOR('O', 4, int)
+#define	OPIOCGETNEXT	_IOWR('O', 5, int)
+#define	OPIOCGETCHILD	_IOWR('O', 6, int)
+
+
+#ifdef __KERNEL__
+int openprom_init(void);
+#endif
+
+
+#endif /* _SPARC_OPENPROMIO_H */
+
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/oplib.h linux/include/asm-sparc/oplib.h
--- v2.1.8/linux/include/asm-sparc/oplib.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/oplib.h	Sat Nov  9 10:29:49 1996
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.8 1996/04/25 06:13:23 davem Exp $
+/* $Id: oplib.h,v 1.12 1996/10/31 06:29:13 davem Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -103,12 +103,12 @@
 /* Enter the prom, with possibility of continuation with the 'go'
  * command in newer proms.
  */
-extern void prom_halt(void);
+extern void prom_cmdline(void);
 
 /* Enter the prom, with no chance of continuation for the stand-alone
  * which calls this.
  */
-extern void prom_die(void);
+extern void prom_halt(void);
 
 /* Set the PROM 'sync' callback function to the passed function pointer.
  * When the user gives the 'sync' command at the prom prompt while the
@@ -123,7 +123,7 @@
  * gets passed a buffer where you would like it stuffed.  The return value
  * is the format type of this idprom or 0xff on error.
  */
-extern unsigned char prom_getidp(char *idp_buffer, int idpbuf_size);
+extern unsigned char prom_get_idprom(char *idp_buffer, int idpbuf_size);
 
 /* Get the prom major version. */
 extern int prom_version(void);
@@ -244,6 +244,11 @@
 /* Does the passed node have the given "name"? YES=1 NO=0 */
 extern int prom_nodematch(int thisnode, char *name);
 
+/* Puts in buffer a prom name in the form name@x,y or name (x for which_io 
+ * and y for first regs phys address
+ */
+extern int prom_getname(int node, char *buf, int buflen);
+
 /* Search all siblings starting at the passed node for "name" matching
  * the given string.  Returns the node on success, zero on failure.
  */
@@ -267,6 +272,9 @@
  */
 extern int prom_setprop(int node, char *prop_name, char *prop_value,
 			int value_size);
+			
+extern int prom_pathtoinode(char *path);
+extern int prom_inst2pkg(int);
 
 /* Dorking with Bus ranges... */
 
@@ -281,7 +289,9 @@
 /* Apply promlib probed OBIO ranges to registers. */
 extern void prom_apply_obio_ranges(struct linux_prom_registers *obioregs, int nregs);
 
-/* Apply promlib probed SBUS ranges to registers. */
-extern void prom_apply_sbus_ranges(struct linux_prom_registers *sbusregs, int nregs);
+/* Apply ranges of any prom node (and optionally parent node as well) to registers. */
+extern void prom_apply_generic_ranges(int node, int parent, 
+				      struct linux_prom_registers *sbusregs, int nregs);
+				   
 
 #endif /* !(__SPARC_OPLIB_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h
--- v2.1.8/linux/include/asm-sparc/page.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/page.h	Sat Nov  9 10:29:51 1996
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.27 1996/04/18 01:33:42 davem Exp $
+/* $Id: page.h,v 1.30 1996/10/27 08:55:30 davem Exp $
  * page.h:  Various defines and such for MMU operations on the Sparc for
  *          the Linux kernel.
  *
@@ -11,13 +11,26 @@
 #include <asm/head.h>       /* for KERNBASE */
 
 #define PAGE_SHIFT   12
-#define PAGE_OFFSET  KERNBASE
 #define PAGE_SIZE    (1 << PAGE_SHIFT)
 #define PAGE_MASK    (~(PAGE_SIZE-1))
 
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
 
+#define clear_page(page)	memset((void *)(page), 0, PAGE_SIZE)
+#define copy_page(to,from)	memcpy((void *)(to), (void *)(from), PAGE_SIZE)
+
+extern unsigned long page_offset;
+
+#define PAGE_OFFSET  (page_offset)
+
+/* translate between physical and virtual addresses */
+extern unsigned long (*mmu_v2p)(unsigned long);
+extern unsigned long (*mmu_p2v)(unsigned long);
+
+#define __pa(x)    mmu_v2p(x)
+#define __va(x)    mmu_p2v(x)
+
 /* The following structure is used to hold the physical
  * memory configuration of the machine.  This is filled in
  * probe_memory() and is later used by mem_init() to set up
@@ -105,11 +118,15 @@
 
 #endif
 
+extern unsigned long sparc_unmapped_base;
+
+#define TASK_UNMAPPED_BASE	(sparc_unmapped_base)
+
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)  (((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
-/* We now put the free page pool mapped contiguously in high memory above
- * the kernel.
+/* Now, to allow for very large physical memory configurations we
+ * place the page pool both above the kernel and below the kernel.
  */
 #define MAP_NR(addr) ((((unsigned long) (addr)) - PAGE_OFFSET) >> PAGE_SHIFT)
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/pgtable.h linux/include/asm-sparc/pgtable.h
--- v2.1.8/linux/include/asm-sparc/pgtable.h	Mon Apr 22 10:59:40 1996
+++ linux/include/asm-sparc/pgtable.h	Mon Nov 11 00:24:56 1996
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.46 1996/04/21 11:01:53 davem Exp $ */
+/* $Id: pgtable.h,v 1.51 1996/10/27 08:55:32 davem Exp $ */
 #ifndef _SPARC_PGTABLE_H
 #define _SPARC_PGTABLE_H
 
@@ -22,6 +22,10 @@
 
 extern void (*quick_kernel_fault)(unsigned long);
 
+/* Allocate a block of RAM which is aligned to its size.
+   This procedure can be used until the call to mem_init(). */
+extern void *sparc_init_alloc(unsigned long *kbrk, unsigned long size);
+
 /* mmu-specific process creation/cloning/etc hooks. */
 extern void (*mmu_exit_hook)(void);
 extern void (*mmu_flush_hook)(void);
@@ -36,16 +40,18 @@
 
 /* Routines for getting a dvma scsi buffer. */
 struct mmu_sglist {
-	/* ick, I know... */
 	char *addr;
-	char *alt_addr;
+	char *__dont_touch;
 	unsigned int len;
+	char *dvma_addr;
 };
 extern char *(*mmu_get_scsi_one)(char *, unsigned long, struct linux_sbus *sbus);
 extern void  (*mmu_get_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
 extern void  (*mmu_release_scsi_one)(char *, unsigned long, struct linux_sbus *sbus);
 extern void  (*mmu_release_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
 
+extern void  (*mmu_map_dma_area)(unsigned long addr, int len);
+
 extern unsigned int pmd_shift;
 extern unsigned int pmd_size;
 extern unsigned int pmd_mask;
@@ -166,8 +172,6 @@
 #define PAGE_PTR(address) \
 ((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK)
 
-extern unsigned long high_memory;
-
 extern int (*pte_none)(pte_t);
 extern int (*pte_present)(pte_t);
 extern void (*pte_clear)(pte_t *);
@@ -202,6 +206,7 @@
  * and a page entry and page directory to the page they refer to.
  */
 extern pte_t (*mk_pte)(unsigned long, pgprot_t);
+extern pte_t (*mk_pte_phys)(unsigned long, pgprot_t);
 extern pte_t (*mk_pte_io)(unsigned long, pgprot_t, int);
 
 extern void (*pgd_set)(pgd_t *, pmd_t *);
@@ -341,13 +346,13 @@
 
 #define NO_CONTEXT     -1
 
-extern inline void remove_from_ctx_list(struct ctx_list *entry)
+extern __inline__ void remove_from_ctx_list(struct ctx_list *entry)
 {
 	entry->next->prev = entry->prev;
 	entry->prev->next = entry->next;
 }
 
-extern inline void add_to_ctx_list(struct ctx_list *head, struct ctx_list *entry)
+extern __inline__ void add_to_ctx_list(struct ctx_list *head, struct ctx_list *entry)
 {
 	entry->next = head;
 	(entry->prev = head->prev)->next = entry;
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/pgtsrmmu.h linux/include/asm-sparc/pgtsrmmu.h
--- v2.1.8/linux/include/asm-sparc/pgtsrmmu.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/pgtsrmmu.h	Mon Nov 11 00:24:56 1996
@@ -1,4 +1,4 @@
-/* $Id: pgtsrmmu.h,v 1.17 1996/04/25 06:13:26 davem Exp $
+/* $Id: pgtsrmmu.h,v 1.24 1996/10/07 03:03:06 davem Exp $
  * pgtsrmmu.h:  SRMMU page table defines and code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -10,6 +10,10 @@
 #include <linux/config.h>
 #include <asm/page.h>
 
+#if CONFIG_AP1000
+#include <asm/ap1000/apreg.h>
+#endif
+
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
 #define SRMMU_PMD_SHIFT         18
 #define SRMMU_PMD_SIZE          (1UL << SRMMU_PMD_SHIFT)
@@ -30,7 +34,7 @@
 #define SRMMU_PMD_TABLE_SIZE    0x100 /* 64 entries, 4 bytes a piece */
 #define SRMMU_PGD_TABLE_SIZE    0x400 /* 256 entries, 4 bytes a piece */
 
-#define SRMMU_VMALLOC_START   (0xfe200000)
+#define SRMMU_VMALLOC_START   (0xfe300000)
 
 /* Definition of the values in the ET field of PTD's and PTE's */
 #define SRMMU_ET_MASK         0x3
@@ -61,7 +65,7 @@
 #define SRMMU_PRIV         0x1c
 #define SRMMU_PRIV_RDONLY  0x18
 
-#define SRMMU_CHG_MASK    (SRMMU_REF | SRMMU_DIRTY | SRMMU_ET_PTE)
+#define SRMMU_CHG_MASK    (0xffffff00 | SRMMU_REF | SRMMU_DIRTY)
 
 /* Some day I will implement true fine grained access bits for
  * user pages because the SRMMU gives us the capabilities to
@@ -76,7 +80,8 @@
 				    SRMMU_EXEC | SRMMU_REF)
 #define SRMMU_PAGE_RDONLY  __pgprot(SRMMU_VALID | SRMMU_CACHE | \
 				    SRMMU_EXEC | SRMMU_REF)
-#define SRMMU_PAGE_KERNEL  __pgprot(SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV)
+#define SRMMU_PAGE_KERNEL  __pgprot(SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV | \
+				    SRMMU_DIRTY | SRMMU_REF)
 
 /* SRMMU Register addresses in ASI 0x4.  These are valid for all
  * current SRMMU implementations that exist.
@@ -87,8 +92,22 @@
 #define SRMMU_FAULT_STATUS       0x00000300
 #define SRMMU_FAULT_ADDR         0x00000400
 
+/*
+ * "normal" sun systems have their memory on bus 0. This means the top
+ * 4 bits of 36 bit physical addresses are 0. We use this define to
+ * determine if a piece of memory might be normal memory, or if its
+ * definately some sort of device memory.  
+ *
+ * On the AP+ normal memory is on bus 8. Why? Ask Fujitsu :-)
+*/
+#if CONFIG_AP1000
+#define MEM_BUS_SPACE 8
+#else
+#define MEM_BUS_SPACE 0
+#endif
+
 /* Accessing the MMU control register. */
-extern inline unsigned int srmmu_get_mmureg(void)
+extern __inline__ unsigned int srmmu_get_mmureg(void)
 {
         unsigned int retval;
 	__asm__ __volatile__("lda [%%g0] %1, %0\n\t" :
@@ -97,19 +116,18 @@
 	return retval;
 }
 
-extern inline void srmmu_set_mmureg(unsigned long regval)
+extern __inline__ void srmmu_set_mmureg(unsigned long regval)
 {
 	__asm__ __volatile__("sta %0, [%%g0] %1\n\t" : :
 			     "r" (regval), "i" (ASI_M_MMUREGS) : "memory");
 
 }
 
-extern inline void srmmu_set_ctable_ptr(unsigned long paddr)
+extern __inline__ void srmmu_set_ctable_ptr(unsigned long paddr)
 {
 	paddr = ((paddr >> 4) & SRMMU_CTX_PMASK);
-#if CONFIG_AP1000
-	/* weird memory system on the AP1000 */
-        paddr |= (0x8<<28);
+#if MEM_BUS_SPACE
+        paddr |= (MEM_BUS_SPACE<<28);
 #endif
 	__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
 			     "r" (paddr), "r" (SRMMU_CTXTBL_PTR),
@@ -117,7 +135,7 @@
 			     "memory");
 }
 
-extern inline unsigned long srmmu_get_ctable_ptr(void)
+extern __inline__ unsigned long srmmu_get_ctable_ptr(void)
 {
 	unsigned int retval;
 
@@ -128,14 +146,19 @@
 	return (retval & SRMMU_CTX_PMASK) << 4;
 }
 
-extern inline void srmmu_set_context(int context)
+extern __inline__ void srmmu_set_context(int context)
 {
 	__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
 			     "r" (context), "r" (SRMMU_CTX_REG),
 			     "i" (ASI_M_MMUREGS) : "memory");
+#if CONFIG_AP1000
+	/* The AP1000+ message controller also needs to know
+	   the current task's context. */
+	MSC_OUT(MSC_PID, context);
+#endif
 }
 
-extern inline int srmmu_get_context(void)
+extern __inline__ int srmmu_get_context(void)
 {
 	register int retval;
 	__asm__ __volatile__("lda [%1] %2, %0\n\t" :
@@ -145,7 +168,7 @@
 	return retval;
 }
 
-extern inline unsigned int srmmu_get_fstatus(void)
+extern __inline__ unsigned int srmmu_get_fstatus(void)
 {
 	unsigned int retval;
 
@@ -155,7 +178,7 @@
 	return retval;
 }
 
-extern inline unsigned int srmmu_get_faddr(void)
+extern __inline__ unsigned int srmmu_get_faddr(void)
 {
 	unsigned int retval;
 
@@ -166,8 +189,12 @@
 }
 
 /* This is guaranteed on all SRMMU's. */
-extern inline void srmmu_flush_whole_tlb(void)
+extern __inline__ void srmmu_flush_whole_tlb(void)
 {
+#if CONFIG_AP1000
+	extern void mc_tlb_flush_all(void);
+	mc_tlb_flush_all();
+#endif
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t": :
 			     "r" (0x400),        /* Flush entire TLB!! */
 			     "i" (ASI_M_FLUSH_PROBE) : "memory");
@@ -175,16 +202,22 @@
 }
 
 /* These flush types are not available on all chips... */
-extern inline void srmmu_flush_tlb_ctx(void)
+extern __inline__ void srmmu_flush_tlb_ctx(void)
 {
+#if CONFIG_AP1000
+	mc_tlb_flush_ctx();
+#endif
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t": :
 			     "r" (0x300),        /* Flush TLB ctx.. */
 			     "i" (ASI_M_FLUSH_PROBE) : "memory");
 
 }
 
-extern inline void srmmu_flush_tlb_region(unsigned long addr)
+extern __inline__ void srmmu_flush_tlb_region(unsigned long addr)
 {
+#if CONFIG_AP1000
+	mc_tlb_flush_region();
+#endif
 	addr &= SRMMU_PGDIR_MASK;
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t": :
 			     "r" (addr | 0x200), /* Flush TLB region.. */
@@ -193,8 +226,11 @@
 }
 
 
-extern inline void srmmu_flush_tlb_segment(unsigned long addr)
+extern __inline__ void srmmu_flush_tlb_segment(unsigned long addr)
 {
+#if CONFIG_AP1000
+	mc_tlb_flush_segment();
+#endif
 	addr &= SRMMU_PMD_MASK;
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t": :
 			     "r" (addr | 0x100), /* Flush TLB segment.. */
@@ -202,8 +238,11 @@
 
 }
 
-extern inline void srmmu_flush_tlb_page(unsigned long page)
+extern __inline__ void srmmu_flush_tlb_page(unsigned long page)
 {
+#if CONFIG_AP1000
+	mc_tlb_flush_page(page);
+#endif
 	page &= PAGE_MASK;
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t": :
 			     "r" (page),        /* Flush TLB page.. */
@@ -211,7 +250,7 @@
 
 }
 
-extern inline unsigned long srmmu_hwprobe(unsigned long vaddr)
+extern __inline__ unsigned long srmmu_hwprobe(unsigned long vaddr)
 {
 	unsigned long retval;
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/pgtsun4c.h linux/include/asm-sparc/pgtsun4c.h
--- v2.1.8/linux/include/asm-sparc/pgtsun4c.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/pgtsun4c.h	Sat Nov  9 10:29:56 1996
@@ -1,4 +1,4 @@
-/* $Id: pgtsun4c.h,v 1.24 1996/03/26 06:51:56 miguel Exp $
+/* $Id: pgtsun4c.h,v 1.27 1996/10/30 06:01:32 davem Exp $
  * pgtsun4c.h:  Sun4c specific pgtable.h defines and code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -6,6 +6,8 @@
 #ifndef _SPARC_PGTSUN4C_H
 #define _SPARC_PGTSUN4C_H
 
+#include <asm/contregs.h>
+
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
 #define SUN4C_PMD_SHIFT       22
 #define SUN4C_PMD_SIZE        (1UL << SUN4C_PMD_SHIFT)
@@ -37,7 +39,7 @@
  * translations at KERNBASE + 128MB for 1MB, then we begin the VMALLOC
  * area, makes sense.  This works out to the value below.
  */
-#define SUN4C_VMALLOC_START   (0xfe200000)
+#define SUN4C_VMALLOC_START   (0xfe300000)
 
 /*
  * Sparc SUN4C pte fields.
@@ -50,6 +52,7 @@
 #define _SUN4C_PAGE_IO        0x04000000   /* I/O page */
 #define _SUN4C_PAGE_REF       0x02000000   /* Page has been accessed/referenced */
 #define _SUN4C_PAGE_DIRTY     0x01000000   /* Page has been modified, is dirty */
+#define _SUN4C_PAGE_PRESENT   0x00400000   /* present (known) page */
 
 #define _SUN4C_PAGE_CHG_MASK  (0xffff | _SUN4C_PAGE_REF | _SUN4C_PAGE_DIRTY)
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/posix_types.h linux/include/asm-sparc/posix_types.h
--- v2.1.8/linux/include/asm-sparc/posix_types.h	Mon Jun  3 10:09:14 1996
+++ linux/include/asm-sparc/posix_types.h	Sat Nov  9 10:29:57 1996
@@ -8,17 +8,18 @@
  */
 
 /* When cross-compilation is no longer an issue, fix this. */
-#ifdef __svr4__
-typedef unsigned int           __kernel_size_t;      /* solaris sucks */
+#if defined(__svr4__) || defined(__ELF__)
+typedef unsigned int           __kernel_size_t;
 #else
-typedef long unsigned int      __kernel_size_t;      /* sunos is much better */
-#endif /* !(__svr4__) */
+typedef long unsigned int      __kernel_size_t;
+#endif /* !(__svr4__ || __ELF__) */
 
 typedef int                    __kernel_ssize_t;
 typedef long int               __kernel_ptrdiff_t;
 typedef long                   __kernel_time_t;
 typedef long                   __kernel_clock_t;
 typedef int                    __kernel_pid_t;
+typedef unsigned short         __kernel_ipc_pid_t;
 typedef unsigned short         __kernel_uid_t;
 typedef unsigned short         __kernel_gid_t;
 typedef unsigned short         __kernel_dev_t;
@@ -55,7 +56,7 @@
 }
 
 #undef __FD_ISSET
-static __inline__ int __FD_ISSET(unsigned long fd, __kernel_fd_set *p)
+static __inline__ int __FD_ISSET(unsigned long fd, __const__ __kernel_fd_set *p)
 { 
 	unsigned long _tmp = fd / __NFDBITS;
 	unsigned long _rem = fd % __NFDBITS;
@@ -63,27 +64,43 @@
 }
 
 /*
- * This will unroll the loop for the normal constant cases (4 or 8 longs,
- * for 256 and 512-bit fd_sets respectively)
+ * This will unroll the loop for the normal constant cases (8 or 32 longs,
+ * for 256 and 1024-bit fd_sets respectively)
  */
 #undef __FD_ZERO
 static __inline__ void __FD_ZERO(__kernel_fd_set *p)
 {
-	unsigned int *tmp = p->fds_bits;
+	unsigned long *tmp = p->fds_bits;
 	int i;
 
-	if (__builtin_constant_p(__FDSET_INTS)) {
-		switch (__FDSET_INTS) {
+	if (__builtin_constant_p(__FDSET_LONGS)) {
+		switch (__FDSET_LONGS) {
+			case 32:
+			  tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;
+			  tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0;
+			  tmp[ 8] = 0; tmp[ 9] = 0; tmp[10] = 0; tmp[11] = 0;
+			  tmp[12] = 0; tmp[13] = 0; tmp[14] = 0; tmp[15] = 0;
+			  tmp[16] = 0; tmp[17] = 0; tmp[18] = 0; tmp[19] = 0;
+			  tmp[20] = 0; tmp[21] = 0; tmp[22] = 0; tmp[23] = 0;
+			  tmp[24] = 0; tmp[25] = 0; tmp[26] = 0; tmp[27] = 0;
+			  tmp[28] = 0; tmp[29] = 0; tmp[30] = 0; tmp[31] = 0;
+			  return;
+			case 16:
+			  tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;
+			  tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0;
+			  tmp[ 8] = 0; tmp[ 9] = 0; tmp[10] = 0; tmp[11] = 0;
+			  tmp[12] = 0; tmp[13] = 0; tmp[14] = 0; tmp[15] = 0;
+			  return;
 			case 8:
-				tmp[0] = 0; tmp[1] = 0; tmp[2] = 0; tmp[3] = 0;
-				tmp[4] = 0; tmp[5] = 0; tmp[6] = 0; tmp[7] = 0;
-				return;
+			  tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;
+			  tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0;
+			  return;
 			case 4:
-				tmp[0] = 0; tmp[1] = 0; tmp[2] = 0; tmp[3] = 0;
-				return;
+			  tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;
+			  return;
 		}
 	}
-	i = __FDSET_INTS;
+	i = __FDSET_LONGS;
 	while (i) {
 		i--;
 		*tmp = 0;
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.1.8/linux/include/asm-sparc/processor.h	Sun Apr 21 12:30:33 1996
+++ linux/include/asm-sparc/processor.h	Sat Nov  9 21:39:40 1996
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.43 1996/03/23 02:40:05 davem Exp $
+/* $Id: processor.h,v 1.48 1996/10/27 08:55:36 davem Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -30,7 +30,15 @@
 #define wp_works_ok__is_a_macro /* for versions in ksyms.c */
 
 /* Whee, this is STACK_TOP and the lowest kernel address too... */
-#define TASK_SIZE	(KERNBASE)
+#define TASK_SIZE	(page_offset)
+
+/* Ok this is hot.  Sparc exception save area. */
+struct exception_struct {
+	unsigned long count;   /* Exception count */
+	unsigned long pc;      /* Callers PC for copy/clear user */
+	unsigned long expc;    /* Where to jump when exception signaled */
+	unsigned long address; /* Saved user base address for transfer */
+};
 
 /* The Sparc processor specific thread struct. */
 struct thread_struct {
@@ -69,11 +77,13 @@
 	} fpqueue[16];
 	struct sigstack sstk_info;
 	unsigned long flags;
+	struct exception_struct ex __attribute__ ((aligned (8)));
 	int current_ds;
 	struct exec core_exec;     /* just what it says. */
 };
 
 #define SPARC_FLAG_KTHREAD      0x1    /* task is a kernel thread */
+#define SPARC_FLAG_UNALIGNED    0x2    /* is allowed to do unaligned accesses */
 
 #define INIT_MMAP { &init_mm, (0), (0), \
 		    __pgprot(0x0) , VM_READ | VM_WRITE | VM_EXEC }
@@ -97,33 +107,43 @@
    0,          0,  { { 0, 0, }, }, \
 /* sstk_info */ \
 { 0, 0, }, \
-/* flags,              current_ds, */ \
-   SPARC_FLAG_KTHREAD, USER_DS, \
+/* flags,              ex,       current_ds, */ \
+   SPARC_FLAG_KTHREAD, { 0, }, USER_DS, \
 /* core_exec */ \
 { 0, }, \
 }
 
 /* Return saved PC of a blocked thread. */
-extern inline unsigned long thread_saved_pc(struct thread_struct *t)
+extern __inline__ unsigned long thread_saved_pc(struct thread_struct *t)
 {
-	return t->kregs->pc;
+	return t->kpc;
 }
 
-/*
- * Do necessary setup to start up a newly executed thread.
- */
-extern inline void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
+/* Do necessary setup to start up a newly executed thread. */
+extern __inline__ void start_thread(struct pt_regs * regs, unsigned long pc,
+				    unsigned long sp)
 {
-	unsigned long saved_psr = (regs->psr & (PSR_CWP)) | PSR_S;
-	int i;
+	register unsigned long zero asm("g1");
 
-	for(i = 0; i < 16; i++) regs->u_regs[i] = 0;
-	regs->y = 0;
+	regs->psr = (regs->psr & (PSR_CWP)) | PSR_S;
 	regs->pc = ((pc & (~3)) - 4);
 	regs->npc = regs->pc + 4;
-	regs->psr = saved_psr;
-	regs->u_regs[UREG_FP] = (sp - REGWIN_SZ);
+	regs->y = 0;
+	zero = 0;
+	__asm__ __volatile__("std\t%%g0, [%0 + %3 + 0x00]\n\t"
+			     "std\t%%g0, [%0 + %3 + 0x08]\n\t"
+			     "std\t%%g0, [%0 + %3 + 0x10]\n\t"
+			     "std\t%%g0, [%0 + %3 + 0x18]\n\t"
+			     "std\t%%g0, [%0 + %3 + 0x20]\n\t"
+			     "std\t%%g0, [%0 + %3 + 0x28]\n\t"
+			     "std\t%%g0, [%0 + %3 + 0x30]\n\t"
+			     "st\t%1, [%0 + %3 + 0x38]\n\t"
+			     "st\t%%g0, [%0 + %3 + 0x3c]"
+			     : : "r" (regs), "r" (sp - REGWIN_SZ), "r" (zero),
+			     "i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0])));
 }
+
+#define release_thread(tsk)		do { } while(0)
 
 #ifdef __KERNEL__
 extern unsigned long (*alloc_kernel_stack)(struct task_struct *tsk);
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/psr.h linux/include/asm-sparc/psr.h
--- v2.1.8/linux/include/asm-sparc/psr.h	Mon Mar  4 08:50:03 1996
+++ linux/include/asm-sparc/psr.h	Sat Nov  9 10:29:58 1996
@@ -1,4 +1,4 @@
-/* $Id: psr.h,v 1.10 1996/03/01 07:20:57 davem Exp $
+/* $Id: psr.h,v 1.12 1996/09/30 02:23:19 davem Exp $
  * psr.h: This file holds the macros for masking off various parts of
  *        the processor status register on the Sparc. This is valid
  *        for Version 8. On the V9 this is renamed to the PSTATE
@@ -38,18 +38,18 @@
 
 #ifndef __ASSEMBLY__
 /* Get the %psr register. */
-extern inline unsigned int get_psr(void)
+extern __inline__ unsigned int get_psr(void)
 {
 	unsigned int psr;
-	__asm__ __volatile__("rd %%psr, %0\n\t" :
+	__asm__ __volatile__("rd\t%%psr, %0" :
 			     "=r" (psr));
 	return psr;
 }
 
-extern inline void put_psr(unsigned int new_psr)
+extern __inline__ void put_psr(unsigned int new_psr)
 {
-	__asm__ __volatile__("wr %0, 0x0, %%psr\n\t"
-			     "nop; nop; nop;\n\t" : :
+	__asm__ __volatile__("wr\t%0, 0x0, %%psr\n\t"
+			     "nop; nop; nop;" : :
 			     "r" (new_psr));
 }
 
@@ -60,12 +60,12 @@
 
 extern unsigned int fsr_storage;
 
-extern inline unsigned int get_fsr(void)
+extern __inline__ unsigned int get_fsr(void)
 {
 	unsigned int fsr = 0;
 
-	__asm__ __volatile__("st %%fsr, %1\n\t"
-			     "ld %1, %0\n\t" :
+	__asm__ __volatile__("st\t%%fsr, %1\n\t"
+			     "ld\t%1, %0" :
 			     "=r" (fsr) :
 			     "m" (fsr_storage));
 	return fsr;
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/ptrace.h linux/include/asm-sparc/ptrace.h
--- v2.1.8/linux/include/asm-sparc/ptrace.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/ptrace.h	Sat Nov  9 10:29:59 1996
@@ -1,4 +1,4 @@
-/* $Id: ptrace.h,v 1.20 1996/04/24 09:10:02 davem Exp $ */
+/* $Id: ptrace.h,v 1.22 1996/10/27 08:55:38 davem Exp $ */
 #ifndef _SPARC_PTRACE_H
 #define _SPARC_PTRACE_H
 
@@ -45,7 +45,6 @@
 	unsigned long ins[8];
 };
 
-
 /* A Sparc stack frame */
 struct sparc_stackf {
 	unsigned long locals[8];
@@ -74,7 +73,7 @@
 #define REGWIN_SZ         0x40
 #endif
 
-/* First generic task_struct offsets. sizeof(task_struct)=1576 */
+/* First generic task_struct offsets. sizeof(task_struct)=1616 */
 #define TASK_STATE        0x000
 #define TASK_PRIORITY     0x008
 #define TASK_SIGNAL       0x00c
@@ -99,7 +98,13 @@
 #define THREAD_FLOAT_REGS 0x460
 #define THREAD_FSR        0x560
 #define THREAD_SIGSTK     0x5e8
-#define THREAD_MM     0x620
+#define THREAD_FLAGS      0x5f0
+#define THREAD_EX_COUNT   0x5f8
+#define THREAD_EX_PC      0x5fc
+#define THREAD_EX_EXPC    0x600
+#define THREAD_EX_ADDR    0x604
+#define THREAD_DS         0x608
+#define THREAD_MM         0x638
 #define THREAD_MM_CTX     0x008
 
 /* These are for pt_regs. */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/reg.h linux/include/asm-sparc/reg.h
--- v2.1.8/linux/include/asm-sparc/reg.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/reg.h	Sat Nov  9 10:29:59 1996
@@ -0,0 +1,79 @@
+/*
+ * linux/asm-sparc/reg.h
+ * Layout of the registers as expected by gdb on the Sparc
+ * we should replace the user.h definitions with those in
+ * this file, we don't even use the other 
+ * -miguel
+ *
+ * The names of the structures, constants and aliases in this file
+ * have the same names as the sunos ones, some programs rely on these
+ * names (gdb for example).
+ *
+ */
+
+#ifndef __SPARC_REG_H
+#define __SPARC_REG_H
+
+struct regs {
+	int     r_psr;
+#define r_ps r_psr
+        int     r_pc; 
+        int     r_npc;
+        int     r_y;  
+        int     r_g1; 
+        int     r_g2;
+        int     r_g3;
+        int     r_g4;
+        int     r_g5;
+        int     r_g6;
+        int     r_g7;
+        int     r_o0;
+        int     r_o1;
+        int     r_o2;
+        int     r_o3;
+        int     r_o4;
+        int     r_o5;
+        int     r_o6;
+        int     r_o7;
+};
+
+struct fpq {
+        unsigned long *addr;
+        unsigned long instr;
+};
+
+struct  fq {
+        union {
+                double  whole;
+                struct  fpq fpq;
+        } FQu;
+};
+
+#define FPU_REGS_TYPE unsigned int
+#define FPU_FSR_TYPE unsigned
+
+struct fp_status {
+        union {
+                FPU_REGS_TYPE Fpu_regs[32];
+                double  Fpu_dregs[16];
+        } fpu_fr;
+        FPU_FSR_TYPE Fpu_fsr;
+        unsigned Fpu_flags;
+        unsigned Fpu_extra;
+        unsigned Fpu_qcnt;
+        struct fq Fpu_q[16];
+};
+
+#define fpu_regs  f_fpstatus.fpu_fr.Fpu_regs
+#define fpu_dregs f_fpstatus.fpu_fr.Fpu_dregs
+#define fpu_fsr   f_fpstatus.Fpu_fsr
+#define fpu_flags f_fpstatus.Fpu_flags
+#define fpu_extra f_fpstatus.Fpu_extra
+#define fpu_q     f_fpstatus.Fpu_q
+#define fpu_qcnt  f_fpstatus.Fpu_qcnt
+
+struct fpu {
+        struct fp_status f_fpstatus;
+};
+
+#endif /* __SPARC_REG_H */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/ross.h linux/include/asm-sparc/ross.h
--- v2.1.8/linux/include/asm-sparc/ross.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/ross.h	Sat Nov  9 10:30:01 1996
@@ -1,4 +1,4 @@
-/* $Id: ross.h,v 1.9 1996/04/08 08:34:21 davem Exp $
+/* $Id: ross.h,v 1.11 1996/08/29 09:48:40 davem Exp $
  * ross.h: Ross module specific definitions and defines.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -93,7 +93,7 @@
 #define HYPERSPARC_ICCR_FTD     0x00000002
 #define HYPERSPARC_ICCR_ICE     0x00000001
 
-extern inline unsigned int get_ross_icr(void)
+extern __inline__ unsigned int get_ross_icr(void)
 {
 	unsigned int icreg;
 
@@ -105,7 +105,7 @@
 	return icreg;
 }
 
-extern inline void put_ross_icr(unsigned int icreg)
+extern __inline__ void put_ross_icr(unsigned int icreg)
 {
 	__asm__ __volatile__("or %%g0, %0, %%g1\n\t"
 			     ".word 0xbf806000\n\t" /* wr %g1, 0x0, %iccr */
@@ -121,44 +121,44 @@
 /* HyperSparc specific cache flushing. */
 
 /* This is for the on-chip instruction cache. */
-extern inline void hyper_flush_whole_icache(void)
+extern __inline__ void hyper_flush_whole_icache(void)
 {
 	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
 			     "i" (ASI_M_FLUSH_IWHOLE));
 	return;
 }
 
-extern int hyper_cache_size;
-extern int hyper_line_size;
+extern int vac_cache_size;
+extern int vac_line_size;
 
-extern inline void hyper_clear_all_tags(void)
+extern __inline__ void hyper_clear_all_tags(void)
 {
 	unsigned long addr;
 
-	for(addr = 0; addr < hyper_cache_size; addr += hyper_line_size)
+	for(addr = 0; addr < vac_cache_size; addr += vac_line_size)
 		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 				     "r" (addr), "i" (ASI_M_DATAC_TAG));
 }
 
-extern inline void hyper_flush_unconditional_combined(void)
+extern __inline__ void hyper_flush_unconditional_combined(void)
 {
 	unsigned long addr;
 
-	for(addr = 0; addr < hyper_cache_size; addr += hyper_line_size)
+	for(addr = 0; addr < vac_cache_size; addr += vac_line_size)
 		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 				     "r" (addr), "i" (ASI_M_FLUSH_CTX));
 }
 
-extern inline void hyper_flush_cache_user(void)
+extern __inline__ void hyper_flush_cache_user(void)
 {
 	unsigned long addr;
 
-	for(addr = 0; addr < hyper_cache_size; addr += hyper_line_size)
+	for(addr = 0; addr < vac_cache_size; addr += vac_line_size)
 		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 				     "r" (addr), "i" (ASI_M_FLUSH_USER));
 }
 
-extern inline void hyper_flush_cache_page(unsigned long page)
+extern __inline__ void hyper_flush_cache_page(unsigned long page)
 {
 	unsigned long end;
 
@@ -167,7 +167,7 @@
 	while(page < end) {
 		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 				     "r" (page), "i" (ASI_M_FLUSH_PAGE));
-		page += hyper_line_size;
+		page += vac_line_size;
 	}
 }
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/rtc.h linux/include/asm-sparc/rtc.h
--- v2.1.8/linux/include/asm-sparc/rtc.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/rtc.h	Sat Nov  9 21:55:17 1996
@@ -0,0 +1,27 @@
+/* $Id: rtc.h,v 1.2 1996/08/21 23:17:39 ecd Exp $
+ *
+ * rtc.h: Definitions for access to the Mostek real time clock
+ *
+ * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
+ */
+
+#ifndef _RTC_H
+#define _RTC_H
+
+#include <linux/ioctl.h>
+
+struct rtc_time
+{
+	int	sec;	/* Seconds (0-59) */
+	int	min;	/* Minutes (0-59) */
+	int	hour;	/* Hour (0-23) */
+	int	dow;	/* Day of the week (1-7) */
+	int	dom;	/* Day of the month (1-31) */
+	int	month;	/* Month of year (1-12) */
+	int	year;	/* Year (0-99) */
+};
+
+#define RTCGET _IOR('p', 20, struct rtc_time)
+#define RTCSET _IOW('p', 21, struct rtc_time)
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/sbus.h linux/include/asm-sparc/sbus.h
--- v2.1.8/linux/include/asm-sparc/sbus.h	Mon Mar  4 08:50:03 1996
+++ linux/include/asm-sparc/sbus.h	Sat Nov  9 21:40:09 1996
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.9 1996/02/15 09:13:03 davem Exp $
+/* $Id: sbus.h,v 1.11 1996/10/31 06:29:12 davem Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -24,17 +24,17 @@
  * numbers + offsets, and vice versa.
  */
 
-extern inline unsigned long sbus_devaddr(int slotnum, unsigned long offset)
+extern __inline__ unsigned long sbus_devaddr(int slotnum, unsigned long offset)
 {
   return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<25)+(offset));
 }
 
-extern inline int sbus_dev_slot(unsigned long dev_addr)
+extern __inline__ int sbus_dev_slot(unsigned long dev_addr)
 {
   return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>25);
 }
 
-extern inline unsigned long sbus_dev_offset(unsigned long dev_addr)
+extern __inline__ unsigned long sbus_dev_offset(unsigned long dev_addr)
 {
   return (unsigned long) (((dev_addr)-SUN_SBUS_BVADDR)&SBUS_OFF_MASK);
 }
@@ -69,13 +69,15 @@
 	struct linux_sbus_device *devices;   /* Link to devices on this SBus */
 	struct iommu_struct *iommu;          /* IOMMU for this sbus if applicable */
 	int prom_node;                       /* PROM device tree node for this SBus */
-	char prom_name[64];                  /* Usually "sbus" */
+	char prom_name[64];                  /* Usually "sbus" or "sbi" */
 	int clock_freq;
+	struct linux_prom_ranges sbus_ranges[PROMREG_MAX];
+	int num_sbus_ranges;
 };
 
 extern struct linux_sbus *SBus_chain;
 
-extern inline int
+extern __inline__ int
 sbus_is_slave(struct linux_sbus_device *dev)
 {
 	/* Have to write this for sun4c's */
@@ -88,5 +90,12 @@
 
 #define for_each_sbusdev(device, bus) \
         for((device) = (bus)->devices; (device); (device)=(device)->next)
+        
+#define for_all_sbusdev(device, bus) \
+	for((bus) = SBus_chain, (device) = (bus)->devices; (bus); (device)=((device)->next ? (device)->next : ((bus) = (bus)->next, (bus) ? (bus)->devices : 0)))
+
+/* Apply promlib probed SBUS ranges to registers. */
+extern void prom_apply_sbus_ranges(struct linux_sbus *sbus, 
+				   struct linux_prom_registers *sbusregs, int nregs);
 
 #endif /* !(_SPARC_SBUS_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/segment.h linux/include/asm-sparc/segment.h
--- v2.1.8/linux/include/asm-sparc/segment.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/segment.h	Sat Nov  9 10:30:03 1996
@@ -1,134 +1,6 @@
-/* $Id: segment.h,v 1.10 1996/03/08 01:19:38 miguel Exp $ */
-#ifndef _ASM_SEGMENT_H
-#define _ASM_SEGMENT_H
+#ifndef __SPARC_SEGMENT_H
+#define __SPARC_SEGMENT_H
 
-#ifdef __KERNEL__
-#include <asm/vac-ops.h>
-#endif
-
-#ifndef __ASSEMBLY__
-
-/*
- * Uh, these should become the main single-value transfer routines..
- * They automatically use the right size if we just have the right
- * pointer type..
- */
-#define put_user(x,ptr) __put_user((unsigned long)(x),(ptr),sizeof(*(ptr)))
-#define get_user(ptr) ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr))))
-
-/*
- * This is a silly but good way to make sure that
- * the __put_user function is indeed always optimized,
- * and that we use the correct sizes..
- */
-extern int bad_user_access_length(void);
-
-/* I should make this use unaligned transfers etc.. */
-static inline void __put_user(unsigned long x, void * y, int size)
-{
-	switch (size) {
-		case 1:
-			*(char *) y = x;
-			break;
-		case 2:
-			*(short *) y = x;
-			break;
-		case 4:
-			*(int *) y = x;
-			break;
-		default:
-			bad_user_access_length();
-	}
-}
-
-/* I should make this use unaligned transfers etc.. */
-static inline unsigned long __get_user(const void * y, int size)
-{
-	switch (size) {
-		case 1:
-			return *(unsigned char *) y;
-		case 2:
-			return *(unsigned short *) y;
-		case 4:
-			return *(unsigned int *) y;
-		default:
-			return bad_user_access_length();
-	}
-}
-
-/*
- * Deprecated routines
- */
-
-static inline unsigned char get_user_byte(const char * addr)
-{
-	return *addr;
-}
-
-#define get_fs_byte(addr) get_user_byte((char *)(addr))
-
-static inline unsigned short get_user_word(const short *addr)
-{
-	return *addr;
-}
-
-#define get_fs_word(addr) get_user_word((short *)(addr))
-
-static inline unsigned long get_user_long(const int *addr)
-{
-	return *addr;
-}
-
-#define get_fs_long(addr) get_user_long((int *)(addr))
-
-static inline void put_user_byte(char val,char *addr)
-{
-	*addr = val;
-}
+/* Only here because we have some old header files that expect it.. */
 
-#define put_fs_byte(x,addr) put_user_byte((x),(char *)(addr))
-
-static inline void put_user_word(short val,short * addr)
-{
-	*addr = val;
-}
-
-#define put_fs_word(x,addr) put_user_word((x),(short *)(addr))
-
-static inline void put_user_long(unsigned long val,int * addr)
-{
-	*addr = val;
-}
-
-#define put_fs_long(x,addr) put_user_long((x),(int *)(addr))
-
-#define memcpy_fromfs(to, from, n) memcpy((to),(from),(n))
-
-#define memcpy_tofs(to, from, n) memcpy((to),(from),(n))
-
-/* Sparc is not segmented, however we need to be able to fool verify_area()
- * when doing system calls from kernel mode legitimately.
- */
-#define KERNEL_DS   0
-#define USER_DS     1
-
-extern int active_ds;
-
-static inline int get_fs(void)
-{
-	return active_ds;
-}
-
-static inline int get_ds(void)
-{
-	return KERNEL_DS;
-}
-
-static inline void set_fs(int val)
-{
-	active_ds = val;
-}
-
-#endif  /* __ASSEMBLY__ */
-
-#endif /* _ASM_SEGMENT_H */
+#endif
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/semaphore.h linux/include/asm-sparc/semaphore.h
--- v2.1.8/linux/include/asm-sparc/semaphore.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/semaphore.h	Sat Nov  9 21:39:32 1996
@@ -0,0 +1,42 @@
+#ifndef _SPARC_SEMAPHORE_H
+#define _SPARC_SEMAPHORE_H
+
+/* Dinky, good for nothing, just barely irq safe, Sparc semaphores.
+ *
+ * I'll write better ones later.
+ */
+
+#include <asm/atomic.h>
+
+struct semaphore {
+	atomic_t count;
+	atomic_t waiting;
+	struct wait_queue * wait;
+};
+
+#define MUTEX ((struct semaphore) { 1, 0, NULL })
+#define MUTEX_LOCKED ((struct semaphore) { 0, 0, NULL })
+
+extern void __down(struct semaphore * sem);
+extern void __up(struct semaphore * sem);
+
+/*
+ * This isn't quite as clever as the x86 side, but the gp register
+ * makes things a bit more complicated on the alpha..
+ */
+extern inline void down(struct semaphore * sem)
+{
+	for (;;) {
+		if (atomic_dec_return(&sem->count) >= 0)
+			break;
+		__down(sem);
+	}
+}
+
+extern inline void up(struct semaphore * sem)
+{
+	if (atomic_inc_return(&sem->count) <= 0)
+		__up(sem);
+}	
+
+#endif /* !(_SPARC_SEMAPHORE_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/sigcontext.h linux/include/asm-sparc/sigcontext.h
--- v2.1.8/linux/include/asm-sparc/sigcontext.h	Mon Mar  4 08:50:03 1996
+++ linux/include/asm-sparc/sigcontext.h	Sat Nov  9 10:30:04 1996
@@ -1,4 +1,4 @@
-/* $Id: sigcontext.h,v 1.8 1996/03/01 07:20:59 davem Exp $ */
+/* $Id: sigcontext.h,v 1.9 1996/10/27 08:55:42 davem Exp $ */
 #ifndef _ASMsparc_SIGCONTEXT_H
 #define _ASMsparc_SIGCONTEXT_H
 
@@ -15,7 +15,7 @@
 };
 
 /* This is what SunOS does, so shall I. */
-struct sigcontext_struct {
+struct sigcontext {
 	int sigc_onstack;      /* state to restore */
 	int sigc_mask;         /* sigmask to restore */
 	int sigc_sp;           /* stack pointer */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/signal.h linux/include/asm-sparc/signal.h
--- v2.1.8/linux/include/asm-sparc/signal.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/signal.h	Sat Nov  9 21:39:39 1996
@@ -1,9 +1,15 @@
-/* $Id: signal.h,v 1.21 1996/04/25 06:13:28 davem Exp $ */
+/* $Id: signal.h,v 1.29 1996/10/27 08:55:45 davem Exp $ */
 #ifndef _ASMSPARC_SIGNAL_H
 #define _ASMSPARC_SIGNAL_H
 
 #include <asm/sigcontext.h>
 
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+#include <linux/personality.h>
+#endif
+#endif
+
 /* On the Sparc the signal handlers get passed a 'sub-signal' code
  * for certain signal types, which we document here.
  */
@@ -49,7 +55,8 @@
 #define    SUBSIG_PROTECTION    4
 #define    SUBSIG_SEGERROR      5
 
-#define SIGSYS          12
+#define SIGSYS		12
+
 #define SIGPIPE		13
 #define SIGALRM		14
 #define SIGTERM		15
@@ -138,7 +145,7 @@
 
 /* Type of a signal handler.  */
 #ifdef __KERNEL__
-typedef void (*__sighandler_t)(int, int, struct sigcontext_struct *, char *);
+typedef void (*__sighandler_t)(int, int, struct sigcontext *, char *);
 #else
 typedef void (*__sighandler_t)(int);
 #endif
@@ -151,7 +158,19 @@
 	__sighandler_t  sa_handler;
 	sigset_t        sa_mask;
 	unsigned long   sa_flags;
+	void (*sa_restorer) (void);     /* not used by Linux/SPARC yet */
 };
+
+#ifdef __KERNEL__
+
+/* use the following macro to get the size of a sigaction struct
+   when copying to/from userland */
+#define SIGACTION_SIZE(personality) (((personality) & PER_BSD)?\
+				     sizeof(struct sigaction)-sizeof(void *):\
+				     sizeof(struct sigaction))
+
+#endif /* __KERNEL__ */
+
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/smp.h linux/include/asm-sparc/smp.h
--- v2.1.8/linux/include/asm-sparc/smp.h	Mon May  6 12:26:16 1996
+++ linux/include/asm-sparc/smp.h	Sat Nov  9 10:30:06 1996
@@ -31,7 +31,7 @@
 
 extern struct cpuinfo_sparc cpu_data[NR_CPUS];
 
-typedef volatile unsigned char klock_t;
+typedef __volatile__ unsigned char klock_t;
 extern klock_t kernel_flag;
 
 #define KLOCK_HELD       0xff
@@ -44,13 +44,13 @@
 extern int smp_found_cpus;
 extern unsigned char boot_cpu_id;
 extern unsigned long cpu_present_map;
-extern volatile unsigned long smp_invalidate_needed[NR_CPUS];
-extern volatile unsigned long kernel_counter;
-extern volatile unsigned char active_kernel_processor;
+extern __volatile__ unsigned long smp_invalidate_needed[NR_CPUS];
+extern __volatile__ unsigned long kernel_counter;
+extern __volatile__ unsigned char active_kernel_processor;
 extern void smp_message_irq(void);
 extern unsigned long ipi_count;
-extern volatile unsigned long kernel_counter;
-extern volatile unsigned long syscall_count;
+extern __volatile__ unsigned long kernel_counter;
+extern __volatile__ unsigned long syscall_count;
 
 extern void print_lock_state(void);
 
@@ -69,25 +69,25 @@
 extern void smp_capture(void);
 extern void smp_release(void);
 
-extern inline void xc0(smpfunc_t func) { smp_cross_call(func, 0, 0, 0, 0, 0); }
-extern inline void xc1(smpfunc_t func, unsigned long arg1)
+extern __inline__ void xc0(smpfunc_t func) { smp_cross_call(func, 0, 0, 0, 0, 0); }
+extern __inline__ void xc1(smpfunc_t func, unsigned long arg1)
 { smp_cross_call(func, arg1, 0, 0, 0, 0); }
-extern inline void xc2(smpfunc_t func, unsigned long arg1, unsigned long arg2)
+extern __inline__ void xc2(smpfunc_t func, unsigned long arg1, unsigned long arg2)
 { smp_cross_call(func, arg1, arg2, 0, 0, 0); }
-extern inline void xc3(smpfunc_t func, unsigned long arg1, unsigned long arg2,
-		       unsigned long arg3)
+extern __inline__ void xc3(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+			   unsigned long arg3)
 { smp_cross_call(func, arg1, arg2, arg3, 0, 0); }
-extern inline void xc4(smpfunc_t func, unsigned long arg1, unsigned long arg2,
-		       unsigned long arg3, unsigned long arg4)
+extern __inline__ void xc4(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+			   unsigned long arg3, unsigned long arg4)
 { smp_cross_call(func, arg1, arg2, arg3, arg4, 0); }
-extern inline void xc5(smpfunc_t func, unsigned long arg1, unsigned long arg2,
-		       unsigned long arg3, unsigned long arg4, unsigned long arg5)
+extern __inline__ void xc5(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+			   unsigned long arg3, unsigned long arg4, unsigned long arg5)
 { smp_cross_call(func, arg1, arg2, arg3, arg4, arg5); }
 
-extern volatile int cpu_number_map[NR_CPUS];
-extern volatile int cpu_logical_map[NR_CPUS];
+extern __volatile__ int cpu_number_map[NR_CPUS];
+extern __volatile__ int cpu_logical_map[NR_CPUS];
 
-extern __inline int smp_processor_id(void)
+extern __inline__ int smp_processor_id(void)
 {
 	int cpuid;
 
@@ -99,10 +99,10 @@
 }
 
 
-extern volatile unsigned long smp_proc_in_lock[NR_CPUS]; /* for computing process time */
-extern volatile int smp_process_available;
+extern __volatile__ unsigned long smp_proc_in_lock[NR_CPUS]; /* for computing process time */
+extern __volatile__ int smp_process_available;
 
-extern inline int smp_swap(volatile int *addr, int value)
+extern __inline__ int smp_swap(volatile int *addr, int value)
 {
 	__asm__ __volatile__("swap [%2], %0\n\t" :
 			     "=&r" (value) :
@@ -110,30 +110,35 @@
 	return value;
 }
 
-extern inline volatile void inc_smp_counter(volatile int *ctr)
+extern __inline__ __volatile__ void inc_smp_counter(volatile int *ctr)
 {
 	int tmp;
 
 	while((tmp = smp_swap(ctr, -1)) == -1)
-		;
-	smp_swap(ctr, (tmp + 1));
+		while(*ctr == -1)
+			;
+
+	*ctr = (tmp + 1);
 }
 
-extern inline volatile void dec_smp_counter(volatile int *ctr)
+extern __inline__ __volatile__ void dec_smp_counter(volatile int *ctr)
 {
 	int tmp;
 
 	while((tmp = smp_swap(ctr, -1)) == -1)
-		;
-	smp_swap(ctr, (tmp - 1));
+		while(*ctr == -1)
+			;
+
+	*ctr = (tmp - 1);
 }
 
-extern inline volatile int read_smp_counter(volatile int *ctr)
+extern __inline__ __volatile__ int read_smp_counter(volatile int *ctr)
 {
 	int value;
 
 	while((value = *ctr) == -1)
 		;
+
 	return value;
 }
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/smp_lock.h linux/include/asm-sparc/smp_lock.h
--- v2.1.8/linux/include/asm-sparc/smp_lock.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/smp_lock.h	Mon Nov 11 00:48:52 1996
@@ -18,7 +18,7 @@
  */
 
 /* Knock knock... */
-extern __inline void lock_kernel(void)
+extern __inline__ void lock_kernel(void)
 {
 	unsigned long flags;
 	int proc = smp_processor_id();
@@ -40,7 +40,7 @@
 }
 
 /* I want out... */
-extern __inline void unlock_kernel(void)
+extern __inline__ void unlock_kernel(void)
 {
 	unsigned long flags;
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/smpprim.h linux/include/asm-sparc/smpprim.h
--- v2.1.8/linux/include/asm-sparc/smpprim.h	Sat Nov 25 04:32:48 1995
+++ linux/include/asm-sparc/smpprim.h	Sat Nov  9 10:30:07 1996
@@ -1,4 +1,4 @@
-/*  $Id: smpprim.h,v 1.4 1995/11/25 02:32:47 davem Exp $
+/*  $Id: smpprim.h,v 1.5 1996/08/29 09:48:49 davem Exp $
  *  smpprim.h:  SMP locking primitives on the Sparc
  *
  *  God knows we won't be actually using this code for some time
@@ -15,7 +15,7 @@
  * atomic.
  */
 
-extern inline volatile char test_and_set(void *addr)
+extern __inline__ __volatile__ char test_and_set(void *addr)
 {
 	char state = 0;
 
@@ -27,7 +27,7 @@
 }
 
 /* Initialize a spin-lock. */
-extern inline volatile smp_initlock(void *spinlock)
+extern __inline__ __volatile__ smp_initlock(void *spinlock)
 {
 	/* Unset the lock. */
 	*((unsigned char *) spinlock) = 0;
@@ -36,7 +36,7 @@
 }
 
 /* This routine spins until it acquires the lock at ADDR. */
-extern inline volatile smp_lock(void *addr)
+extern __inline__ __volatile__ smp_lock(void *addr)
 {
 	while(test_and_set(addr) == 0xff)
 		;
@@ -46,7 +46,7 @@
 }
 
 /* This routine releases the lock at ADDR. */
-extern inline volatile smp_unlock(void *addr)
+extern __inline__ __volatile__ smp_unlock(void *addr)
 {
 	*((unsigned char *) addr) = 0;
 }
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/stat.h linux/include/asm-sparc/stat.h
--- v2.1.8/linux/include/asm-sparc/stat.h	Sat Nov 25 04:32:53 1995
+++ linux/include/asm-sparc/stat.h	Sat Nov  9 21:39:35 1996
@@ -1,10 +1,10 @@
-/* $Id: stat.h,v 1.6 1995/11/25 02:32:53 davem Exp $ */
+/* $Id: stat.h,v 1.7 1996/10/27 08:55:48 davem Exp $ */
 #ifndef _SPARC_STAT_H
 #define _SPARC_STAT_H
 
 #include <linux/types.h>
 
-struct old_stat {
+struct __old_kernel_stat {
 	unsigned short st_dev;
 	unsigned short st_ino;
 	unsigned short st_mode;
@@ -18,7 +18,7 @@
 	unsigned long  st_ctime;
 };
 
-struct new_stat {
+struct stat {
 	dev_t   st_dev;
 	ino_t   st_ino;
 	mode_t  st_mode;
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/statfs.h linux/include/asm-sparc/statfs.h
--- v2.1.8/linux/include/asm-sparc/statfs.h	Mon Jun  3 10:09:14 1996
+++ linux/include/asm-sparc/statfs.h	Sat Nov  9 21:39:33 1996
@@ -1,4 +1,4 @@
-/* $Id: statfs.h,v 1.3 1995/11/25 02:32:54 davem Exp $ */
+/* $Id: statfs.h,v 1.4 1996/06/07 00:41:05 ecd Exp $ */
 #ifndef _SPARC_STATFS_H
 #define _SPARC_STATFS_H
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/string.h linux/include/asm-sparc/string.h
--- v2.1.8/linux/include/asm-sparc/string.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/string.h	Sat Nov  9 10:30:09 1996
@@ -1,4 +1,4 @@
-/* $Id: string.h,v 1.19 1996/03/23 02:40:10 davem Exp $
+/* $Id: string.h,v 1.27 1996/10/27 08:55:50 davem Exp $
  * string.h: External definitions for optimized assembly string
  *           routines for the Linux Kernel.
  *
@@ -8,10 +8,171 @@
 #ifndef __SPARC_STRING_H__
 #define __SPARC_STRING_H__
 
+/* Really, userland/ksyms should not see any of this stuff. */
+
+#if defined(__KERNEL__) && !defined(EXPORT_SYMTAB)
+
+/* First the mem*() things. */
 #define __HAVE_ARCH_BCOPY
 #define __HAVE_ARCH_MEMMOVE
 #define __HAVE_ARCH_MEMCPY
+extern void *__memcpy(void *,const void *,__kernel_size_t);
+
+extern inline void *__constant_memcpy(void *to, const void *from, __kernel_size_t n)
+{
+	extern void __copy_1page(void *, const void *);
+
+	if(n <= 32) {
+		__builtin_memcpy(to, from, n);
+	} else {
+		switch(n) {
+		case 4096:
+			__copy_1page(to, from);
+			break;
+		default:
+			__memcpy(to, from, n);
+			break;
+		}
+	}
+	return to;
+}
+
+extern inline void *__nonconstant_memcpy(void *to, const void *from, __kernel_size_t n)
+{
+	__memcpy(to, from, n);
+	return to;
+}
+
+#undef memcpy
+#define memcpy(t, f, n) \
+(__builtin_constant_p(n) ? \
+ __constant_memcpy((t),(f),(n)) : \
+ __nonconstant_memcpy((t),(f),(n)))
+
 #define __HAVE_ARCH_MEMSET
+extern void *__memset(void *,int,__kernel_size_t);
+
+extern inline void *__constant_c_and_count_memset(void *s, char c, size_t count)
+{
+	extern void *bzero_1page(void *);
+	extern void *__bzero(void *, size_t);
+
+	if(!c) {
+		if(count == 4096)
+			bzero_1page(s);
+		else
+			__bzero(s, count);
+	} else {
+		__memset(s, c, count);
+	}
+	return s;
+}
+
+extern inline void *__constant_c_memset(void *s, char c, size_t count)
+{
+	extern void *__bzero(void *, size_t);
+
+	if(!c)
+		__bzero(s, count);
+	else
+		__memset(s, c, count);
+	return s;
+}
+
+#undef memset
+#define memset(s, c, count) \
+(__builtin_constant_p(c) ? (__builtin_constant_p(count) ? \
+                            __constant_c_and_count_memset((s), (c), (count)) : \
+                            __constant_c_memset((s), (c), (count))) \
+                         : __memset((s), (c), (count)))
+
+#define __HAVE_ARCH_MEMSCAN
+
+#undef memscan
+#define memscan(__arg0, __char, __arg2)						\
+({										\
+	extern void *__memscan_zero(void *, size_t);				\
+	extern void *__memscan_generic(void *, int, size_t);			\
+	void *__retval, *__addr = (__arg0);					\
+	size_t __size = (__arg2);						\
+										\
+	if(__builtin_constant_p(__char) && !(__char))				\
+		__retval = __memscan_zero(__addr, __size);			\
+	else									\
+		__retval = __memscan_generic(__addr, (__char), __size);		\
+										\
+	__retval;								\
+})
+
+#define __HAVE_ARCH_MEMCMP
+
+/* Now the str*() stuff... */
 #define __HAVE_ARCH_STRLEN
+
+#define __HAVE_ARCH_STRNCMP
+
+extern int __strncmp(const char *, const char *, __kernel_size_t);
+
+extern inline int __constant_strncmp(const char *src, const char *dest, __kernel_size_t count)
+{
+	register int retval;
+	switch(count) {
+	case 0: return 0;
+	case 1: return (src[0] - dest[0]);
+	case 2: retval = (src[0] - dest[0]);
+		if(!retval && src[0])
+		  retval = (src[1] - dest[1]);
+		return retval;
+	case 3: retval = (src[0] - dest[0]);
+		if(!retval && src[0]) {
+		  retval = (src[1] - dest[1]);
+		  if(!retval && src[1])
+		    retval = (src[2] - dest[2]);
+		}
+		return retval;
+	case 4: retval = (src[0] - dest[0]);
+		if(!retval && src[0]) {
+		  retval = (src[1] - dest[1]);
+		  if(!retval && src[1]) {
+		    retval = (src[2] - dest[2]);
+		    if (!retval && src[2])
+		      retval = (src[3] - dest[3]);
+		  }
+		}
+		return retval;
+	case 5: retval = (src[0] - dest[0]);
+		if(!retval && src[0]) {
+		  retval = (src[1] - dest[1]);
+		  if(!retval && src[1]) {
+		    retval = (src[2] - dest[2]);
+		    if (!retval && src[2]) {
+		      retval = (src[3] - dest[3]);
+		      if (!retval && src[3])
+		        retval = (src[4] - dest[4]);
+		    }
+		  }
+		}
+		return retval;
+	default:
+		retval = (src[0] - dest[0]);
+		if(!retval && src[0]) {
+		  retval = (src[1] - dest[1]);
+		  if(!retval && src[1]) {
+		    retval = (src[2] - dest[2]);
+		    if(!retval && src[2])
+		      retval = __strncmp(src+3,dest+3,count-3);
+		  }
+		}
+		return retval;
+	}
+}
+
+#undef strncmp
+#define strncmp(__arg0, __arg1, __arg2)	\
+(__builtin_constant_p(__arg2) ?	\
+ __constant_strncmp(__arg0, __arg1, __arg2) : \
+ __strncmp(__arg0, __arg1, __arg2))
+
+#endif /* (__KERNEL__) && !(_KSYMS_INTERNEL) */
 
 #endif /* !(__SPARC_STRING_H__) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/svr4.h linux/include/asm-sparc/svr4.h
--- v2.1.8/linux/include/asm-sparc/svr4.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/svr4.h	Sat Nov  9 10:30:13 1996
@@ -0,0 +1,119 @@
+/* Solaris/SPARC constants and definitions -- 
+ * (C) 1996 Miguel de Icaza
+ *
+ * This file is not meant to be included by user level applications
+ * but the solaris syscall emulator
+ */
+
+#ifndef _SPARC_SVR4_H
+#define _SPARC_SVR4_H
+
+/* Signals as used by svr4 */
+typedef struct {                /* signal set type */
+	ulong sigbits[4];
+} svr4_sigset_t;
+
+/* Values for siginfo.code */
+#define SVR4_SINOINFO 32767
+/* Siginfo, sucker expects bunch of information on those parameters */
+typedef union {
+	char total_size [128];
+	struct {
+		int signo;
+		int code;
+		int error;
+		union {
+		} data; 
+	} siginfo;
+} svr4_siginfo_t;
+
+/* Context definition */
+
+/* Location of the user stored registers into a greg_t */
+enum {
+	SVR4_PSR, SVR4_PC, SVR4_NPC, SVR4_Y,
+	SVR4_G1,  SVR4_G2, SVR4_G3,  SVR4_G4,
+	SVR4_G5,  SVR4_G6, SVR4_G7,  SVR4_O0,
+	SVR4_O1,  SVR4_O2, SVR4_O3,  SVR4_O4,
+	SVR4_O5,  SVR4_O6, SVR4_O7
+};
+
+/* sizeof (regs) / sizeof (greg_t), defined in the ABI */
+#define SVR4_NREGS  19
+#define SVR4_MAXWIN 31
+
+typedef struct {
+	uint rwin_lo[8];
+	uint rwin_in[8];
+} svr4_rwindow_t;
+
+typedef struct {
+	int            count;
+	int            *winptr [SVR4_MAXWIN]; /* pointer to the windows */
+	svr4_rwindow_t win[SVR4_MAXWIN];      /* the windows */
+} svr4_gwindows_t;
+
+typedef int svr4_gregset_t[SVR4_NREGS];
+
+typedef struct {
+	double   fpu_regs[32];
+	void     *fp_q;
+	unsigned fp_fsr;
+	u_char   fp_nqel;
+	u_char   fp_nqsize;
+	u_char   inuse;		/* if fpu is in use */
+} svr4_fregset_t;
+
+typedef struct {
+	uint    id;		/* if this holds "xrs" string => ptr is valid */
+	caddr_t ptr;
+} svr4_xrs_t;
+
+/* Machine dependant context */
+typedef struct {
+	svr4_gregset_t   greg;	/* registers 0..19 (see top) */
+	svr4_gwindows_t  *gwin;	/* may point to register windows */
+	svr4_fregset_t   freg;	/* floating point registers */
+	svr4_xrs_t       xrs;	/* mhm? */
+	long             pad[19];
+} svr4_mcontext_t;
+
+/* flags for stack_t.flags */
+enum svr4_stack_flags {
+	SS_ONSTACK,
+	SVR4_SS_DISABLE,
+};
+
+/* signal stack exection place, unsupported */
+typedef struct svr4_stack_t {
+        char *sp;
+        int  size;
+        int  flags;
+} svr4_stack_t;
+
+/* Context used by getcontext and setcontext */
+typedef struct svr4_ucontext_t {
+	u_long               flags; /* context flags, indicate what is loaded */
+	struct svr4_ucontext *link;
+	svr4_sigset_t        sigmask;
+	svr4_stack_t         stack;
+	svr4_mcontext_t      mcontext;
+	long                 pad[23];
+} svr4_ucontext_t;                          
+
+/* windows hold the windows as they were at signal time,
+ * ucontext->mcontext holds a pointer to them.
+ * addresses for uc and si are passed as parameters to svr4 signal
+ * handler
+ */
+
+/* This is the signal frame that is passed to the signal handler */
+typedef struct {
+	svr4_gwindows_t gw;	/* windows */
+	svr4_ucontext_t uc;	/* machine context */
+	svr4_siginfo_t  si;	/* siginfo */
+} svr4_signal_frame_t;
+
+#define SVR4_SF_ALIGNED (((sizeof (svr4_signal_frame_t) + 7) & (~7)))
+
+#endif /* include control */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/swift.h linux/include/asm-sparc/swift.h
--- v2.1.8/linux/include/asm-sparc/swift.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/swift.h	Sat Nov  9 10:30:13 1996
@@ -26,64 +26,66 @@
 #define SWIFT_NF       0x00000002   /* No fault mode */
 #define SWIFT_EN       0x00000001   /* MMU enable */
 
-extern inline void swift_inv_insn_tag(unsigned long addr)
+/* Bits [13:5] select one of 512 instruction cache tags */
+extern __inline__ void swift_inv_insn_tag(unsigned long addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_TXTC_TAG));
 }
 
-extern inline void swift_inv_data_tag(unsigned long addr)
+/* Bits [12:4] select one of 512 data cache tags */
+extern __inline__ void swift_inv_data_tag(unsigned long addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_DATAC_TAG));
 }
 
-extern inline void swift_flush_dcache(void)
+extern __inline__ void swift_flush_dcache(void)
 {
 	unsigned long addr;
 
-	for(addr = 0; addr < (PAGE_SIZE << 2); addr += 16)
+	for(addr = 0; addr < 0x2000; addr += 0x10)
 		swift_inv_data_tag(addr);
 }
 
-extern inline void swift_flush_icache(void)
+extern __inline__ void swift_flush_icache(void)
 {
 	unsigned long addr;
 
-	for(addr = 0; addr < (PAGE_SIZE << 2); addr += 16)
+	for(addr = 0; addr < 0x4000; addr += 0x20)
 		swift_inv_insn_tag(addr);
 }
 
-extern inline void swift_idflash_clear(void)
+extern __inline__ void swift_idflash_clear(void)
 {
 	unsigned long addr;
 
-	for(addr = 0; addr < (PAGE_SIZE << 2); addr += 16) {
-		swift_inv_insn_tag(addr);
+	for(addr = 0; addr < 0x2000; addr += 0x10) {
+		swift_inv_insn_tag(addr<<1);
 		swift_inv_data_tag(addr);
 	}
 }
 
 /* Swift is so broken, it isn't even safe to use the following. */
-extern inline void swift_flush_page(unsigned long page)
+extern __inline__ void swift_flush_page(unsigned long page)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (page), "i" (ASI_M_FLUSH_PAGE));
 }
 
-extern inline void swift_flush_segment(unsigned long addr)
+extern __inline__ void swift_flush_segment(unsigned long addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_SEG));
 }
 
-extern inline void swift_flush_region(unsigned long addr)
+extern __inline__ void swift_flush_region(unsigned long addr)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (addr), "i" (ASI_M_FLUSH_REGION));
 }
 
-extern inline void swift_flush_context(void)
+extern __inline__ void swift_flush_context(void)
 {
 	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
 			     "i" (ASI_M_FLUSH_CTX));
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/system.h linux/include/asm-sparc/system.h
--- v2.1.8/linux/include/asm-sparc/system.h	Mon May  6 12:26:16 1996
+++ linux/include/asm-sparc/system.h	Sat Nov  9 21:39:31 1996
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.29 1996/04/03 02:17:52 davem Exp $ */
+/* $Id: system.h,v 1.42 1996/09/30 02:23:21 davem Exp $ */
 #ifndef __SPARC_SYSTEM_H
 #define __SPARC_SYSTEM_H
 
@@ -46,105 +46,183 @@
  */
 extern void flush_user_windows(void);
 extern void synchronize_user_stack(void);
-extern void sparc_switch_to(void *new_task);
-#ifndef __SMP__
-#define switch_to(prev, next) do { \
-			  flush_user_windows(); \
-		          switch_to_context(next); \
-			  prev->tss.current_ds = active_ds; \
-                          active_ds = next->tss.current_ds; \
-			  if(last_task_used_math != next) \
-				  next->tss.kregs->psr &= ~PSR_EF; \
-                          sparc_switch_to(next); \
-                     } while(0)
-#else
-
 extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
 		   void *fpqueue, unsigned long *fpqdepth);
 
-#define switch_to(prev, next) do { \
-			  cli(); \
-			  if(prev->flags & PF_USEDFPU) { \
-                          	fpsave(&prev->tss.float_regs[0], &prev->tss.fsr, \
-                          	       &prev->tss.fpqueue[0], &prev->tss.fpqdepth); \
-                          	prev->flags &= ~PF_USEDFPU; \
-                          	prev->tss.kregs->psr &= ~PSR_EF; \
-                          } \
-			  prev->lock_depth = syscall_count; \
-			  kernel_counter += (next->lock_depth - prev->lock_depth); \
-			  syscall_count = next->lock_depth; \
-			  flush_user_windows(); \
-		          switch_to_context(next); \
-			  prev->tss.current_ds = active_ds; \
-                          active_ds = next->tss.current_ds; \
-                          sparc_switch_to(next); \
-			  sti(); \
-                     } while(0)
+#ifdef __SMP__
+#define SWITCH_ENTER \
+	cli(); \
+	if(prev->flags & PF_USEDFPU) { \
+		fpsave(&prev->tss.float_regs[0], &prev->tss.fsr, \
+		       &prev->tss.fpqueue[0], &prev->tss.fpqdepth); \
+		prev->flags &= ~PF_USEDFPU; \
+		prev->tss.kregs->psr &= ~PSR_EF; \
+	} \
+	prev->lock_depth = syscall_count; \
+	kernel_counter += (next->lock_depth - prev->lock_depth); \
+	syscall_count = next->lock_depth;
+
+#define SWITCH_EXIT sti();
+#define SWITCH_DO_LAZY_FPU
+#else
+#define SWITCH_ENTER
+#define SWITCH_EXIT
+#define SWITCH_DO_LAZY_FPU if(last_task_used_math != next) next->tss.kregs->psr&=~PSR_EF;
 #endif
 
-/* Changing the IRQ level on the Sparc. */
-extern inline void setipl(int __new_ipl)
+	/* Much care has gone into this code, do not touch it. */
+#define switch_to(prev, next) do {							\
+	__label__ here;									\
+	register unsigned long task_pc asm("o7");					\
+	SWITCH_ENTER									\
+	SWITCH_DO_LAZY_FPU								\
+	__asm__ __volatile__(								\
+	".globl\tflush_patch_switch\nflush_patch_switch:\n\t"				\
+	"save %sp, -0x40, %sp; save %sp, -0x40, %sp; save %sp, -0x40, %sp\n\t"		\
+	"save %sp, -0x40, %sp; save %sp, -0x40, %sp; save %sp, -0x40, %sp\n\t"		\
+	"save %sp, -0x40, %sp\n\t"							\
+	"restore; restore; restore; restore; restore; restore; restore");		\
+	if(!(next->tss.flags & SPARC_FLAG_KTHREAD) &&					\
+	   !(next->flags & PF_EXITING))							\
+		switch_to_context(next);						\
+	task_pc = ((unsigned long) &&here) - 0x8;					\
+	__asm__ __volatile__(								\
+	"rd\t%%psr, %%g4\n\t"								\
+	"std\t%%sp, [%%g6 + %3]\n\t"							\
+	"rd\t%%wim, %%g5\n\t"								\
+	"wr\t%%g4, 0x20, %%psr\n\t"							\
+	"std\t%%g4, [%%g6 + %2]\n\t"							\
+	"mov\t%1, %%g6\n\t"								\
+	"ldd\t[%%g6 + %2], %%g4\n\t"							\
+	"st\t%1, [%0]\n\t"								\
+	"wr\t%%g4, 0x20, %%psr\n\t"							\
+	"ldd\t[%%g6 + %3], %%sp\n\t"							\
+	"wr\t%%g5, 0x0, %%wim\n\t"							\
+	"ldd\t[%%sp + 0x00], %%l0\n\t"							\
+	"ldd\t[%%sp + 0x08], %%l2\n\t"							\
+	"ldd\t[%%sp + 0x10], %%l4\n\t"							\
+	"ldd\t[%%sp + 0x18], %%l6\n\t"							\
+	"ldd\t[%%sp + 0x20], %%i0\n\t"							\
+	"ldd\t[%%sp + 0x28], %%i2\n\t"							\
+	"ldd\t[%%sp + 0x30], %%i4\n\t"							\
+	"ldd\t[%%sp + 0x38], %%i6\n\t"							\
+	"wr\t%%g4, 0x0, %%psr\n\t"							\
+	"nop\n\t"									\
+	"jmpl\t%%o7 + 0x8, %%g0\n\t"							\
+	" nop\n\t" : : "r" (&(current_set[smp_processor_id()])), "r" (next),		\
+	"i" ((const unsigned long)(&((struct task_struct *)0)->tss.kpsr)),		\
+	"i" ((const unsigned long)(&((struct task_struct *)0)->tss.ksp)),		\
+	"r" (task_pc) : "g4", "g5");							\
+here: SWITCH_EXIT } while(0)
+
+/* Changing the IRQ level on the Sparc.   We now avoid writing the psr
+ * whenever possible.
+ */
+extern __inline__ void setipl(unsigned long __orig_psr)
+{
+	__asm__ __volatile__("wr\t%0, 0x0, %%psr\n\t"
+			     "nop; nop; nop;" : : "r" (__orig_psr) : "memory");
+}
+
+extern __inline__ void cli(void)
+{
+	unsigned long tmp;
+
+	__asm__ __volatile__("rd\t%%psr, %0\n\t"
+			     "andcc\t%0, %1, %%g0\n\t"
+			     "be,a\t1f\n\t"
+			     " wr\t%0, %1, %%psr\n"
+			     "1:\tnop; nop"
+			     : "=r" (tmp)
+			     : "i" (PSR_PIL)
+			     : "memory");
+}
+
+extern __inline__ void sti(void)
 {
-	__asm__ __volatile__("rd %%psr, %%g1\n\t"
-			     "andn %%g1, %1, %%g1\n\t"
-			     "sll %0, 8, %%g2\n\t"
-			     "and %%g2, %1, %%g2\n\t"
-			     "or %%g1, %%g2, %%g1\n\t"
-			     "wr %%g1, 0x0, %%psr\n\t"
-			     "nop; nop; nop\n\t" : :
-			     "r" (__new_ipl), "i" (PSR_PIL) :
-			     "g1", "g2");
-}
-
-extern inline int getipl(void)
-{
-	int retval;
-
-	__asm__ __volatile__("rd %%psr, %0\n\t"
-			     "and %0, %1, %0\n\t"
-			     "srl %0, 8, %0\n\t" :
-			     "=r" (retval) :
-			     "i" (PSR_PIL));
+	unsigned long tmp;
+
+	__asm__ __volatile__("rd\t%%psr, %0\n\t"
+			     "andcc\t%0, %1, %%g0\n\t"
+			     "bne,a\t1f\n\t"
+			     " wr\t%0, %1, %%psr\n"
+			     "1:\tnop; nop"
+			     : "=r" (tmp)
+			     : "i" (PSR_PIL)
+			     : "memory");
+}
+
+extern __inline__ unsigned long getipl(void)
+{
+	unsigned long retval;
+
+	__asm__ __volatile__("rd\t%%psr, %0" : "=r" (retval));
+	return retval;
+}
+
+extern __inline__ unsigned long swap_pil(unsigned long __new_psr)
+{
+	unsigned long retval, tmp1, tmp2;
+
+	__asm__ __volatile__("rd\t%%psr, %0\n\t"
+			     "and\t%0, %4, %1\n\t"
+			     "and\t%3, %4, %2\n\t"
+			     "xorcc\t%1, %2, %%g0\n\t"
+			     "bne,a\t1f\n\t"
+			     " wr %0, %4, %%psr\n"
+			     "1:\tnop; nop"
+			     : "=r" (retval), "=r" (tmp1), "=r" (tmp2)
+			     : "r" (__new_psr), "i" (PSR_PIL)
+			     : "memory");
 	return retval;
 }
 
-extern inline int swpipl(int __new_ipl)
+extern __inline__ unsigned long read_psr_and_cli(void)
 {
-	int retval;
+	unsigned long retval;
 
-	__asm__ __volatile__("rd %%psr, %%g1\n\t"
-			     "srl %%g1, 8, %0\n\t"
-			     "and %0, 15, %0\n\t"
-			     "andn %%g1, %2, %%g1\n\t"
-			     "and %1, 15, %%g2\n\t"
-			     "sll %%g2, 8, %%g2\n\t"
-			     "or %%g1, %%g2, %%g1\n\t"
-			     "wr %%g1, 0x0, %%psr\n\t"
-			     "nop; nop; nop\n\t" :
-			     "=r" (retval) :
-			     "r" (__new_ipl), "i" (PSR_PIL) :
-			     "g1", "g2");
+	__asm__ __volatile__("rd\t%%psr, %0\n\t"
+			     "andcc\t%0, %1, %%g0\n\t"
+			     "be,a\t1f\n\t"
+			     " wr\t%0, %1, %%psr\n"
+			     "1:\tnop; nop"
+			     : "=r" (retval)
+			     : "i" (PSR_PIL)
+			     : "memory");
 	return retval;
 }
 
 extern char spdeb_buf[256];
 
-#define cli()			setipl(15)  /* 15 = no int's except nmi's */
-#define sti()			setipl(0)   /* I'm scared */
-#define save_flags(flags)	do { flags = getipl(); } while (0)
-#define restore_flags(flags)	setipl(flags)
+#define save_flags(flags)	((flags) = getipl())
+#define save_and_cli(flags)	((flags) = read_psr_and_cli())
+#define restore_flags(flags)	setipl((flags))
+
+/* XXX Change this if we ever use a PSO mode kernel. */
+#define mb()  __asm__ __volatile__ ("" : : : "memory")
 
 #define nop() __asm__ __volatile__ ("nop");
 
-extern inline unsigned long xchg_u32(volatile unsigned long *m, unsigned long val)
+extern __inline__ unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
 {
-	unsigned long flags, retval;
+	__asm__ __volatile__("
+	rd	%%psr, %%g3
+	andcc	%%g3, %3, %%g0
+	be,a	1f
+	 wr	%%g3, %3, %%psr
+1:	ld	[%1], %%g2
+	andcc	%%g3, %3, %%g0
+	st	%2, [%1]
+	be,a	1f
+	 wr	%%g3, 0x0, %%psr
+1:	nop
+	mov	%%g2, %0
+	"
+        : "=&r" (val)
+        : "r" (m), "0" (val), "i" (PSR_PIL)
+        : "g2", "g3");
 
-	save_flags(flags); cli();
-	retval = *m;
-	*m = val;
-	restore_flags(flags);
-	return retval;
+	return val;
 }
 
 #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
@@ -152,7 +230,7 @@
 
 extern void __xchg_called_with_bad_pointer(void);
 
-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
+static __inline__ unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int size)
 {
 	switch (size) {
 	case 4:
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/termbits.h linux/include/asm-sparc/termbits.h
--- v2.1.8/linux/include/asm-sparc/termbits.h	Wed May  8 10:32:41 1996
+++ linux/include/asm-sparc/termbits.h	Sat Nov  9 21:39:37 1996
@@ -25,6 +25,10 @@
 	tcflag_t c_lflag;		/* local mode flags */
 	cc_t c_line;			/* line discipline */
 	cc_t c_cc[NCCS];		/* control characters */
+#ifdef __KERNEL__
+#define SIZEOF_USER_TERMIOS sizeof (struct termios) - (2*sizeof (cc_t))
+	cc_t _x_cc[2];                  /* We need them to hold vmin/vtime */
+#endif
 };
 
 /* c_cc characters */
@@ -38,14 +42,26 @@
 #define VSWTC    7
 #define VSTART   8
 #define VSTOP    9
+
+
+
 #define VSUSP    10
 #define VDSUSP   11  /* SunOS POSIX nicety I do believe... */
 #define VREPRINT 12
 #define VDISCARD 13
 #define VWERASE  14
 #define VLNEXT   15
+
+/* Kernel keeps vmin/vtime separated, user apps assume vmin/vtime is
+ * shared with eof/eol
+ */
+#ifdef __KERNEL__
+#define VMIN     16
+#define VTIME    17
+#else
 #define VMIN     VEOF
 #define VTIME    VEOL
+#endif
 
 /* c_iflag bits */
 #define IGNBRK	0x00000001
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/termios.h linux/include/asm-sparc/termios.h
--- v2.1.8/linux/include/asm-sparc/termios.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/termios.h	Sat Nov  9 21:39:37 1996
@@ -1,10 +1,11 @@
-/* $Id: termios.h,v 1.13 1996/04/04 12:51:30 davem Exp $ */
+/* $Id: termios.h,v 1.20 1996/10/31 00:59:54 davem Exp $ */
 #ifndef _SPARC_TERMIOS_H
 #define _SPARC_TERMIOS_H
 
 #include <asm/ioctls.h>
 #include <asm/termbits.h>
 
+#if defined(__KERNEL__) || defined(__DEFINE_BSD_TERMIOS)
 struct sgttyb {
 	char	sg_ispeed;
 	char	sg_ospeed;
@@ -30,6 +31,7 @@
 	char	t_werasc;
 	char	t_lnextc;
 };
+#endif /* __KERNEL__ */
 
 struct sunos_ttysize {
 	int st_lines;   /* Lines on the terminal */
@@ -59,18 +61,34 @@
 #define N_PPP		3
 
 #ifdef __KERNEL__
+
+/*
+ * c_cc characters in the termio structure.  Oh, how I love being
+ * backwardly compatible.  Notice that character 4 and 5 are
+ * interpreted differently depending on whether ICANON is set in
+ * c_lflag.  If it's set, they are used as _VEOF and _VEOL, otherwise
+ * as _VMIN and V_TIME.  This is for compatibility with OSF/1 (which
+ * is compatible with sysV)...
+ */
+#define _VMIN	4
+#define _VTIME	5
+
+
+#include <linux/string.h>
+
 /*	intr=^C		quit=^\		erase=del	kill=^U
-	eof/vmin=\1	eol/vtime=\0	eol2=\0		sxtc=\0
+	eof=^D		eol=\0		eol2=\0		sxtc=\0
 	start=^Q	stop=^S		susp=^Z		dsusp=^Y
 	reprint=^R	discard=^U	werase=^W	lnext=^V
+	vmin=\1         vtime=\0
 */
-#define INIT_C_CC "\003\034\177\025\001\000\000\000\021\023\032\031\022\025\027\026"
+#define INIT_C_CC "\003\034\177\025\004\000\000\000\021\023\032\031\022\025\027\026\001\000"
 
 /*
  * Translate a "termio" structure into a "termios". Ugh.
  */
-extern inline void trans_from_termio(struct termio * termio,
-	struct termios * termios)
+extern __inline__ void trans_from_termio(struct termio * termio,
+					 struct termios * termios)
 {
 #define SET_LOW_BITS(x,y)	((x) = (0xffff0000 & (x)) | (y))
 	SET_LOW_BITS(termios->c_iflag, termio->c_iflag);
@@ -78,14 +96,16 @@
 	SET_LOW_BITS(termios->c_cflag, termio->c_cflag);
 	SET_LOW_BITS(termios->c_lflag, termio->c_lflag);
 #undef SET_LOW_BITS
-	memcpy(termios->c_cc, termio->c_cc, NCC);
+	memcpy (termios->c_cc, termio->c_cc, NCC);
 }
 
 /*
  * Translate a "termios" structure into a "termio". Ugh.
+ *
+ * Note the "fun" _VMIN overloading.
  */
-extern inline void trans_to_termio(struct termios * termios,
-	struct termio * termio)
+extern __inline__ void trans_to_termio(struct termios * termios,
+				       struct termio * termio)
 {
 	termio->c_iflag = termios->c_iflag;
 	termio->c_oflag = termios->c_oflag;
@@ -93,6 +113,54 @@
 	termio->c_lflag = termios->c_lflag;
 	termio->c_line	= termios->c_line;
 	memcpy(termio->c_cc, termios->c_cc, NCC);
+	if (!(termios->c_lflag & ICANON)) {
+		termio->c_cc[_VMIN]  = termios->c_cc[VMIN];
+		termio->c_cc[_VTIME] = termios->c_cc[VTIME];
+	}
+}
+
+/* Note that in this case DEST is a user buffer and thus the checking
+ * and this ugly macro to avoid header file problems.
+ */
+#define termios_to_userland(d, s) \
+do { \
+	struct termios *dest = (d); \
+	struct termios *source = (s); \
+	put_user(source->c_iflag, &dest->c_iflag); \
+	put_user(source->c_oflag, &dest->c_oflag); \
+	put_user(source->c_cflag, &dest->c_cflag); \
+	put_user(source->c_lflag, &dest->c_lflag); \
+	put_user(source->c_line, &dest->c_line); \
+	copy_to_user(dest->c_cc, source->c_cc, NCCS); \
+	if (!(source->c_lflag & ICANON)){ \
+		put_user(source->c_cc[VMIN], &dest->c_cc[_VMIN]); \
+		put_user(source->c_cc[VTIME], &dest->c_cc[_VTIME]); \
+	} else { \
+		put_user(source->c_cc[VEOF], &dest->c_cc[VEOF]); \
+		put_user(source->c_cc[VEOL], &dest->c_cc[VEOL]); \
+	} \
+} while(0)
+
+/* termios to termios handling SunOS overloading of eof,eol/vmin,vtime
+ * In this case we are only working with kernel buffers so direct
+ * accesses are ok.
+ */
+extern __inline__ void termios_from_userland(struct termios * source,
+					     struct termios * dest)
+{
+	dest->c_iflag = source->c_iflag;
+	dest->c_oflag = source->c_oflag;
+	dest->c_cflag = source->c_cflag;
+	dest->c_lflag = source->c_lflag;
+	dest->c_line  = source->c_line;
+	memcpy(dest->c_cc, source->c_cc, NCCS);
+	if (dest->c_lflag & ICANON){
+		dest->c_cc [VEOF] = source->c_cc [VEOF];
+		dest->c_cc [VEOL] = source->c_cc [VEOL];
+	} else {
+		dest->c_cc[VMIN]  = source->c_cc[_VMIN];
+		dest->c_cc[VTIME] = source->c_cc[_VTIME];
+	}
 }
 
 #endif	/* __KERNEL__ */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/timer.h linux/include/asm-sparc/timer.h
--- v2.1.8/linux/include/asm-sparc/timer.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/timer.h	Sat Nov  9 22:36:13 1996
@@ -1,4 +1,4 @@
-/* $Id: timer.h,v 1.12 1996/03/24 20:21:29 davem Exp $
+/* $Id: timer.h,v 1.13 1996/08/29 09:48:59 davem Exp $
  * timer.h:  Definitions for the timer chips on the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -24,10 +24,10 @@
  */
 
 struct sun4c_timer_info {
-  volatile unsigned int cur_count10;
-  volatile unsigned int timer_limit10;
-  volatile unsigned int cur_count14;
-  volatile unsigned int timer_limit14;
+  __volatile__ unsigned int cur_count10;
+  __volatile__ unsigned int timer_limit10;
+  __volatile__ unsigned int cur_count14;
+  __volatile__ unsigned int timer_limit14;
 };
 
 #define SUN4C_TIMER_PHYSADDR   0xf3000000
@@ -47,16 +47,16 @@
 #define SUN4M_PRM_CNT_LVALUE  0x7FFFFC00
 
 struct sun4m_timer_percpu_info {
-  volatile unsigned int l14_timer_limit;    /* Initial value is 0x009c4000 */
-  volatile unsigned int l14_cur_count;
+  __volatile__ unsigned int l14_timer_limit;    /* Initial value is 0x009c4000 */
+  __volatile__ unsigned int l14_cur_count;
 
   /* This register appears to be write only and/or inaccessible
    * on Uni-Processor sun4m machines.
    */
-  volatile unsigned int l14_limit_noclear;  /* Data access error is here */
+  __volatile__ unsigned int l14_limit_noclear;  /* Data access error is here */
 
-  volatile unsigned int cntrl;            /* =1 after POST on Aurora */
-  volatile unsigned char space[PAGE_SIZE - 16];
+  __volatile__ unsigned int cntrl;            /* =1 after POST on Aurora */
+  __volatile__ unsigned char space[PAGE_SIZE - 16];
 };
 
 struct sun4m_timer_regs {
@@ -76,7 +76,7 @@
 };
 
 extern struct sun4m_timer_regs *sun4m_timers;
-extern volatile unsigned int *master_l10_counter;
-extern volatile unsigned int *master_l10_limit;
+extern __volatile__ unsigned int *master_l10_counter;
+extern __volatile__ unsigned int *master_l10_limit;
 
 #endif /* !(_SPARC_TIMER_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/traps.h linux/include/asm-sparc/traps.h
--- v2.1.8/linux/include/asm-sparc/traps.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/traps.h	Sat Nov  9 10:30:18 1996
@@ -1,4 +1,4 @@
-/* $Id: traps.h,v 1.6 1996/04/25 06:13:33 davem Exp $
+/* $Id: traps.h,v 1.8 1996/05/17 10:38:53 davem Exp $
  * traps.h:  Format of entries for the Sparc trap table.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -9,6 +9,8 @@
 
 #define NUM_SPARC_TRAPS  255
 
+#ifndef __ASSEMBLY__
+
 /* This is for V8 and V9 compliant Sparc CPUS */
 struct tt_entry {
 	unsigned long inst_one;
@@ -47,6 +49,8 @@
 	return tbr;
 }
 
+#endif /* !(__ASSEMBLY__) */
+
 /* For patching the trap table at boot time, we need to know how to
  * form various common Sparc instructions.  Thus these macros...
  */
@@ -61,6 +65,7 @@
           (0x10800000 | (((dest_addr-inst_addr)>>2)&0x3fffff))
 
 #define SPARC_RD_PSR_L0  (0xa1480000)
+#define SPARC_RD_WIM_L3  (0xa7500000)
 #define SPARC_NOP (0x01000000)
 
 /* Various interesting trap levels. */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/tsunami.h linux/include/asm-sparc/tsunami.h
--- v2.1.8/linux/include/asm-sparc/tsunami.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/tsunami.h	Sat Nov  9 10:30:19 1996
@@ -1,4 +1,4 @@
-/* $Id: tsunami.h,v 1.4 1996/04/04 12:51:32 davem Exp $
+/* $Id: tsunami.h,v 1.5 1996/08/29 09:49:03 davem Exp $
  * tsunami.h:  Module specific definitions for Tsunami V8 Sparcs
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -45,13 +45,13 @@
 #define TSUNAMI_NF        0x00000002
 #define TSUNAMI_ME        0x00000001
 
-extern inline void tsunami_flush_icache(void)
+extern __inline__ void tsunami_flush_icache(void)
 {
 	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
 			     "i" (ASI_M_IC_FLCLEAR) : "memory");
 }
 
-extern inline void tsunami_flush_dcache(void)
+extern __inline__ void tsunami_flush_dcache(void)
 {
 	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
 			     "i" (ASI_M_DC_FLCLEAR) : "memory");
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/types.h linux/include/asm-sparc/types.h
--- v2.1.8/linux/include/asm-sparc/types.h	Sun Apr 21 12:30:34 1996
+++ linux/include/asm-sparc/types.h	Sat Nov  9 10:30:19 1996
@@ -1,4 +1,4 @@
-/* $Id: types.h,v 1.9 1996/04/04 12:51:34 davem Exp $ */
+/* $Id: types.h,v 1.10 1996/08/29 09:49:04 davem Exp $ */
 #ifndef _SPARC_TYPES_H
 #define _SPARC_TYPES_H
 
@@ -17,30 +17,30 @@
 
 typedef unsigned short umode_t;
 
-typedef signed char __s8;
+typedef __signed__ char __s8;
 typedef unsigned char __u8;
 
-typedef signed short __s16;
+typedef __signed__ short __s16;
 typedef unsigned short __u16;
 
-typedef signed int __s32;
+typedef __signed__ int __s32;
 typedef unsigned int __u32;
 
-typedef signed long long __s64;
+typedef __signed__ long long __s64;
 typedef unsigned long long __u64;
 
 #ifdef __KERNEL__
 
-typedef signed char s8;
+typedef __signed__ char s8;
 typedef unsigned char u8;
 
-typedef signed short s16;
+typedef __signed__ short s16;
 typedef unsigned short u16;
 
-typedef signed int s32;
+typedef __signed__ int s32;
 typedef unsigned int u32;
 
-typedef signed long long s64;
+typedef __signed__ long long s64;
 typedef unsigned long long u64;
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/uaccess.h linux/include/asm-sparc/uaccess.h
--- v2.1.8/linux/include/asm-sparc/uaccess.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/uaccess.h	Mon Nov 11 00:25:06 1996
@@ -0,0 +1,261 @@
+/* $Id: uaccess.h,v 1.2 1996/10/31 00:59:56 davem Exp $ */
+#ifndef _ASM_UACCESS_H
+#define _ASM_UACCESS_H
+
+/*
+ * User space memory access functions
+ */
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <asm/vac-ops.h>
+#endif
+
+#ifndef __ASSEMBLY__
+
+/* Sparc is not segmented, however we need to be able to fool verify_area()
+ * when doing system calls from kernel mode legitimately.
+ *
+ * "For historical reasons, these macros are grossly misnamed." -Linus
+ */
+#define KERNEL_DS   0
+#define USER_DS     1
+
+#define VERIFY_READ	0
+#define VERIFY_WRITE	1
+
+#define get_fs() (current->tss.current_ds)
+#define get_ds() (KERNEL_DS)
+#define set_fs(val) ((current->tss.current_ds) = (val))
+
+#define __user_ok(addr,size) (((size) <= page_offset)&&((addr) <= page_offset-(size)))
+#define __kernel_ok (get_fs() == KERNEL_DS)
+#define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size)))
+#define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size))
+
+extern inline int verify_area(int type, const void * addr, unsigned long size)
+{
+	return access_ok(type,addr,size)?0:-EFAULT;
+}
+
+/* Uh, these should become the main single-value transfer routines..
+ * They automatically use the right size if we just have the right
+ * pointer type..
+ *
+ * This gets kind of ugly. We want to return _two_ values in "get_user()"
+ * and yet we don't want to do any pointers, because that is too much
+ * of a performance impact. Thus we have a few rather ugly macros here,
+ * and hide all the uglyness from the user.
+ */
+#define put_user(x,ptr) ({ \
+unsigned long __pu_addr = (unsigned long)(ptr); \
+__put_user_check((__typeof__(*(ptr)))(x),__pu_addr,sizeof(*(ptr))); })
+
+#define get_user(x,ptr) ({ \
+unsigned long __gu_addr = (unsigned long)(ptr); \
+__get_user_check((x),__gu_addr,sizeof(*(ptr)),__typeof__(*(ptr))); })
+
+/*
+ * The "__xxx" versions do not do address space checking, useful when
+ * doing multiple accesses to the same area (the user has to do the
+ * checks by hand with "access_ok()")
+ */
+#define __put_user(x,ptr) __put_user_nocheck((x),(ptr),sizeof(*(ptr)))
+#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr)),__typeof__(*(ptr)))
+
+struct __large_struct { unsigned long buf[100]; };
+#define __m(x) ((struct __large_struct *)(x))
+
+#define __put_user_check(x,addr,size) ({ \
+register int __pu_ret asm("g1"); \
+__pu_ret = -EFAULT; \
+if (__access_ok(addr,size)) { \
+switch (size) { \
+case 1: __put_user_8(x,addr,__pu_ret); break; \
+case 2: __put_user_16(x,addr,__pu_ret); break; \
+case 4: __put_user_32(x,addr,__pu_ret); break; \
+default: __pu_ret = __put_user_bad(); break; \
+} } __pu_ret; })
+
+#define __put_user_nocheck(x,addr,size) ({ \
+register int __pu_ret asm("g1"); \
+__pu_ret = -EFAULT; \
+switch (size) { \
+case 1: __put_user_8(x,addr,__pu_ret); break; \
+case 2: __put_user_16(x,addr,__pu_ret); break; \
+case 4: __put_user_32(x,addr,__pu_ret); break; \
+default: __pu_ret = __put_user_bad(); break; \
+} __pu_ret; })
+
+#define __put_user_8(x,addr,ret)						\
+__asm__ __volatile(								\
+	"/* Put user 8, inline. */\n\t"						\
+	"ld	[%%g6 + %3], %%g2\n\t"						\
+	"set	1f, %0\n\t"							\
+	"add	%%g2, 1, %%g3\n\t"						\
+	"st	%0, [%%g6 + %4]\n\t"						\
+	"st	%%g3, [%%g6 + %3]\n\t"						\
+	"stb	%1, [%2]\n\t"							\
+	"mov	0, %0\n\t"							\
+	"st	%%g2, [%%g6 + %3]\n"						\
+"1:\n" : "=&r" (ret) : "r" (x), "r" (__m(addr)),				\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.count)),	\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.expc))	\
+       : "g2", "g3")
+
+#define __put_user_16(x,addr,ret)						\
+__asm__ __volatile(								\
+	"/* Put user 16, inline. */\n\t"					\
+	"ld	[%%g6 + %3], %%g2\n\t"						\
+	"set	1f, %0\n\t"							\
+	"add	%%g2, 1, %%g3\n\t"						\
+	"st	%0, [%%g6 + %4]\n\t"						\
+	"st	%%g3, [%%g6 + %3]\n\t"						\
+	"sth	%1, [%2]\n\t"							\
+	"mov	0, %0\n\t"							\
+	"st	%%g2, [%%g6 + %3]\n"						\
+"1:\n" : "=&r" (ret) : "r" (x), "r" (__m(addr)),				\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.count)),	\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.expc))	\
+       : "g2", "g3")
+
+#define __put_user_32(x,addr,ret)						\
+__asm__ __volatile(								\
+	"/* Put user 32, inline. */\n\t"					\
+	"ld	[%%g6 + %3], %%g2\n\t"						\
+	"set	1f, %0\n\t"							\
+	"add	%%g2, 1, %%g3\n\t"						\
+	"st	%0, [%%g6 + %4]\n\t"						\
+	"st	%%g3, [%%g6 + %3]\n\t"						\
+	"st	%1, [%2]\n\t"							\
+	"mov	0, %0\n\t"							\
+	"st	%%g2, [%%g6 + %3]\n"						\
+"1:\n" : "=&r" (ret) : "r" (x), "r" (__m(addr)),				\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.count)),	\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.expc))	\
+       : "g2", "g3")
+
+extern int __put_user_bad(void);
+
+#define __get_user_check(x,addr,size,type) ({ \
+register int __gu_ret asm("g1"); \
+register unsigned long __gu_val = 0; \
+__gu_ret = -EFAULT; \
+if (__access_ok(addr,size)) { \
+switch (size) { \
+case 1: __get_user_8(__gu_val,addr,__gu_ret); break; \
+case 2: __get_user_16(__gu_val,addr,__gu_ret); break; \
+case 4: __get_user_32(__gu_val,addr,__gu_ret); break; \
+default: __gu_ret = __get_user_bad(); break; \
+} } x = (type) __gu_val; __gu_ret; })
+
+#define __get_user_nocheck(x,addr,size,type) ({ \
+register int __gu_ret asm("g1"); \
+register unsigned long __gu_val = 0; \
+__gu_ret = -EFAULT; \
+switch (size) { \
+case 1: __get_user_8(__gu_val,addr,__gu_ret); break; \
+case 2: __get_user_16(__gu_val,addr,__gu_ret); break; \
+case 4: __get_user_32(__gu_val,addr,__gu_ret); break; \
+default: __gu_ret = __get_user_bad(); break; \
+} x = (type) __gu_val; __gu_ret; })
+
+#define __get_user_8(x,addr,ret)						\
+__asm__ __volatile__(								\
+	"/* Get user 8, inline. */\n\t"						\
+	"ld	[%%g6 + %3], %%g2\n\t"						\
+	"set	1f, %0\n\t"							\
+	"add	%%g2, 1, %%g3\n\t"						\
+	"st	%0, [%%g6 + %4]\n\t"						\
+	"st	%%g3, [%%g6 + %3]\n\t"						\
+	"ldub	[%2], %1\n\t"							\
+	"mov	0, %0\n\t"							\
+	"st	%%g2, [%%g6 + %3]\n"						\
+"1:\n" : "=&r" (ret), "=&r" (x) : "r" (__m(addr)),				\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.count)),	\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.expc))	\
+       : "g2", "g3")
+
+#define __get_user_16(x,addr,ret)						\
+__asm__ __volatile__(								\
+	"/* Get user 16, inline. */\n\t"					\
+	"ld	[%%g6 + %3], %%g2\n\t"						\
+	"set	1f, %0\n\t"							\
+	"add	%%g2, 1, %%g3\n\t"						\
+	"st	%0, [%%g6 + %4]\n\t"						\
+	"st	%%g3, [%%g6 + %3]\n\t"						\
+	"lduh	[%2], %1\n\t"							\
+	"mov	0, %0\n\t"							\
+	"st	%%g2, [%%g6 + %3]\n"						\
+"1:\n" : "=&r" (ret), "=&r" (x) : "r" (__m(addr)),				\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.count)),	\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.expc))	\
+       : "g2", "g3")
+
+#define __get_user_32(x,addr,ret)						\
+__asm__ __volatile__(								\
+	"/* Get user 32, inline. */\n\t"					\
+	"ld	[%%g6 + %3], %%g2\n\t"						\
+	"set	1f, %0\n\t"							\
+	"add	%%g2, 1, %%g3\n\t"						\
+	"st	%0, [%%g6 + %4]\n\t"						\
+	"st	%%g3, [%%g6 + %3]\n\t"						\
+	"ld	[%2], %1\n\t"							\
+	"mov	0, %0\n\t"							\
+	"st	%%g2, [%%g6 + %3]\n"						\
+"1:\n" : "=&r" (ret), "=&r" (x) : "r" (__m(addr)),				\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.count)),	\
+	 "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ex.expc))	\
+       : "g2", "g3")
+
+extern int __get_user_bad(void);
+
+extern int __copy_to_user(unsigned long to, unsigned long from, int size);
+extern int __copy_from_user(unsigned long to, unsigned long from, int size);
+
+#define copy_to_user(to,from,n) ({ \
+unsigned long __copy_to = (unsigned long) (to); \
+unsigned long __copy_size = (unsigned long) (n); \
+unsigned long __copy_res; \
+if(__copy_size && __access_ok(__copy_to, __copy_size)) { \
+__copy_res = __copy_to_user(__copy_to, (unsigned long) (from), __copy_size); \
+if(__copy_res) __copy_res = __copy_size - __copy_res; \
+} else __copy_res = __copy_size; \
+__copy_res; })
+
+#define copy_from_user(to,from,n) ({ \
+unsigned long __copy_from = (unsigned long) (from); \
+unsigned long __copy_size = (unsigned long) (n); \
+unsigned long __copy_res; \
+if(__copy_size && __access_ok(__copy_from, __copy_size)) { \
+__copy_res = __copy_from_user((unsigned long) (to), __copy_from, __copy_size); \
+if(__copy_res) __copy_res = __copy_size - __copy_res; \
+} else __copy_res = __copy_size; \
+__copy_res; })
+
+extern int __clear_user(unsigned long addr, int size);
+
+#define clear_user(addr,n) ({ \
+unsigned long __clear_addr = (unsigned long) (addr); \
+int __clear_size = (int) (n); \
+int __clear_res; \
+if(__clear_size && __access_ok(__clear_addr, __clear_size)) { \
+__clear_res = __clear_user(__clear_addr, __clear_size); \
+if(__clear_res) __clear_res = __clear_size - __clear_res; \
+} else __clear_res = __clear_size; \
+__clear_res; })
+
+extern int __strncpy_from_user(unsigned long dest, unsigned long src, int count);
+
+#define strncpy_from_user(dest,src,count) ({ \
+unsigned long __sfu_src = (unsigned long) (src); \
+int __sfu_count = (int) (count); \
+long __sfu_res = -EFAULT; \
+if(__access_ok(__sfu_src, __sfu_count)) { \
+__sfu_res = __strncpy_from_user((unsigned long) (dest), __sfu_src, __sfu_count); \
+} __sfu_res; })
+
+#endif  /* __ASSEMBLY__ */
+
+#endif /* _ASM_UACCESS_H */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/unaligned.h linux/include/asm-sparc/unaligned.h
--- v2.1.8/linux/include/asm-sparc/unaligned.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/unaligned.h	Sat Nov  9 10:30:22 1996
@@ -0,0 +1,19 @@
+#ifndef _ASM_SPARC_UNALIGNED_H_
+#define _ASM_SPARC_UNALIGNED_H_
+
+/* Sparc can't handle unaligned accesses. */
+
+#include <asm/string.h>
+
+
+/* Use memmove here, so gcc does not insert a __builtin_memcpy. */
+
+#define get_unaligned(ptr) \
+  ({ __typeof__(*(ptr)) __tmp; memmove(&__tmp, (ptr), sizeof(*(ptr))); __tmp; })
+
+#define put_unaligned(val, ptr)				\
+  ({ __typeof__(*(ptr)) __tmp = (val);			\
+     memmove((ptr), &__tmp, sizeof(*(ptr)));		\
+     (void)0; })
+
+#endif /* _ASM_SPARC_UNALIGNED_H */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h
--- v2.1.8/linux/include/asm-sparc/unistd.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/unistd.h	Sat Nov  9 10:30:23 1996
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.21 1996/04/25 06:13:35 davem Exp $ */
+/* $Id: unistd.h,v 1.24 1996/08/29 09:49:06 davem Exp $ */
 #ifndef _SPARC_UNISTD_H
 #define _SPARC_UNISTD_H
 
@@ -270,20 +270,21 @@
 #define __NR__sysctl            251
 #define __NR_getsid             252
 #define __NR_fdatasync          253
+#define __NR_nfsctl             254
 
 #define _syscall0(type,name) \
 type name(void) \
 { \
 long __res; \
-__asm__ volatile ("or %%g0, %0, %%g1\n\t" \
-		  "t 0x10\n\t" \
-		  "bcc 1f\n\t" \
-		  "or %%g0, %%o0, %0\n\t" \
-		  "sub %%g0, %%o0, %0\n\t" \
-		  "1:\n\t" \
-		  : "=r" (__res)\
-		  : "0" (__NR_##name) \
-		  : "g1", "o0"); \
+__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
+		      "t 0x10\n\t" \
+		      "bcc 1f\n\t" \
+		      "or %%g0, %%o0, %0\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "1:\n\t" \
+		      : "=r" (__res)\
+		      : "0" (__NR_##name) \
+		      : "g1", "o0"); \
 if (__res >= 0) \
     return (type) __res; \
 errno = -__res; \
@@ -294,16 +295,16 @@
 type name(type1 arg1) \
 { \
 long __res; \
-__asm__ volatile ("or %%g0, %0, %%g1\n\t" \
-		  "or %%g0, %1, %%o0\n\t" \
-		  "t 0x10\n\t" \
-		  "bcc 1f\n\t" \
-		  "or %%g0, %%o0, %0\n\t" \
-		  "sub %%g0, %%o0, %0\n\t" \
-		  "1:\n\t" \
-		  : "=r" (__res), "=r" ((long)(arg1)) \
-		  : "0" (__NR_##name),"1" ((long)(arg1)) \
-		  : "g1", "o0"); \
+__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
+		      "or %%g0, %1, %%o0\n\t" \
+		      "t 0x10\n\t" \
+		      "bcc 1f\n\t" \
+		      "or %%g0, %%o0, %0\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "1:\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)) \
+		      : "0" (__NR_##name),"1" ((long)(arg1)) \
+		      : "g1", "o0"); \
 if (__res >= 0) \
 	return (type) __res; \
 errno = -__res; \
@@ -314,17 +315,17 @@
 type name(type1 arg1,type2 arg2) \
 { \
 long __res; \
-__asm__ volatile ("or %%g0, %0, %%g1\n\t" \
-		  "or %%g0, %1, %%o0\n\t" \
-		  "or %%g0, %2, %%o1\n\t" \
-		  "t 0x10\n\t" \
-		  "bcc 1f\n\t" \
-		  "or %%g0, %%o0, %0\n\t" \
-		  "sub %%g0,%%o0,%0\n\t" \
-		  "1:\n\t" \
-		  : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)) \
-		  : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)) \
-		  : "g1", "o0", "o1"); \
+__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
+		      "or %%g0, %1, %%o0\n\t" \
+		      "or %%g0, %2, %%o1\n\t" \
+		      "t 0x10\n\t" \
+		      "bcc 1f\n\t" \
+		      "or %%g0, %%o0, %0\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "1:\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)) \
+		      : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)) \
+		      : "g1", "o0", "o1"); \
 if (__res >= 0) \
 	return (type) __res; \
 errno = -__res; \
@@ -335,20 +336,20 @@
 type name(type1 arg1,type2 arg2,type3 arg3) \
 { \
 long __res; \
-__asm__ volatile ("or %%g0, %0, %%g1\n\t" \
-		  "or %%g0, %1, %%o0\n\t" \
-		  "or %%g0, %2, %%o1\n\t" \
-		  "or %%g0, %3, %%o2\n\t" \
-		  "t 0x10\n\t" \
-		  "bcc 1f\n\t" \
-		  "or %%g0, %%o0, %0\n\t" \
-		  "sub %%g0, %%o0, %0\n\t" \
-		  "1:\n\t" \
-		  : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
-		    "=r" ((long)(arg3)) \
-		  : "0" (__NR_##name), "1" ((long)(arg1)), "2" ((long)(arg2)), \
-		    "3" ((long)(arg3)) \
-		  : "g1", "o0", "o1", "o2"); \
+__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
+		      "or %%g0, %1, %%o0\n\t" \
+		      "or %%g0, %2, %%o1\n\t" \
+		      "or %%g0, %3, %%o2\n\t" \
+		      "t 0x10\n\t" \
+		      "bcc 1f\n\t" \
+		      "or %%g0, %%o0, %0\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "1:\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
+		        "=r" ((long)(arg3)) \
+		      : "0" (__NR_##name), "1" ((long)(arg1)), "2" ((long)(arg2)), \
+		        "3" ((long)(arg3)) \
+		      : "g1", "o0", "o1", "o2"); \
 if (__res>=0) \
 	return (type) __res; \
 errno = -__res; \
@@ -359,49 +360,49 @@
 type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
 { \
 long __res; \
-__asm__ volatile ("or %%g0, %0, %%g1\n\t" \
-		  "or %%g0, %1, %%o0\n\t" \
-		  "or %%g0, %2, %%o1\n\t" \
-		  "or %%g0, %3, %%o2\n\t" \
-		  "or %%g0, %4, %%o3\n\t" \
-		  "t 0x10\n\t" \
-		  "bcc 1f\n\t" \
-		  "or %%g0, %%o0, %0\n\t" \
-		  "sub %%g0,%%o0, %0\n\t" \
-		  "1:\n\t" \
-		  : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
-		    "=r" ((long)(arg3)), "=r" ((long)(arg4)) \
-		  : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)), \
-		    "3" ((long)(arg3)),"4" ((long)(arg4)) \
-		  : "g1", "o0", "o1", "o2", "o3"); \
+__asm__ __volatile__ ("or %%g0, %0, %%g1\n\t" \
+		      "or %%g0, %1, %%o0\n\t" \
+		      "or %%g0, %2, %%o1\n\t" \
+		      "or %%g0, %3, %%o2\n\t" \
+		      "or %%g0, %4, %%o3\n\t" \
+		      "t 0x10\n\t" \
+		      "bcc 1f\n\t" \
+		      "or %%g0, %%o0, %0\n\t" \
+		      "sub %%g0,%%o0, %0\n\t" \
+		      "1:\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
+		        "=r" ((long)(arg3)), "=r" ((long)(arg4)) \
+		      : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)), \
+		        "3" ((long)(arg3)),"4" ((long)(arg4)) \
+		      : "g1", "o0", "o1", "o2", "o3"); \
 if (__res>=0) \
 	return (type) __res; \
 errno = -__res; \
 return -1; \
 } 
-#
+
 #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
 	  type5,arg5) \
 type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
 { \
       long __res; \
 \
-__asm__ volatile ("or %%g0, %1, %%o0\n\t" \
-		  "or %%g0, %2, %%o1\n\t" \
-		  "or %%g0, %3, %%o2\n\t" \
-		  "or %%g0, %4, %%o3\n\t" \
-		  "or %%g0, %5, %%o4\n\t" \
-		  "or %%g0, %6, %%g1\n\t" \
-		  "t 0x10\n\t" \
-		  "bcc 1f\n\t" \
-		  "or %%g0, %%o0, %0\n\t" \
-		  "sub %%g0, %%o0, %0\n\t" \
-		  "1:\n\t" \
-		  : "=r" (__res) \
-		  : "0" ((long)(arg1)),"1" ((long)(arg2)), \
-		    "2" ((long)(arg3)),"3" ((long)(arg4)),"4" ((long)(arg5)), \
-		    "i" (__NR_##name)  \
-		  : "g1", "o0", "o1", "o2", "o3", "o4"); \
+__asm__ __volatile__ ("or %%g0, %1, %%o0\n\t" \
+		      "or %%g0, %2, %%o1\n\t" \
+		      "or %%g0, %3, %%o2\n\t" \
+		      "or %%g0, %4, %%o3\n\t" \
+		      "or %%g0, %5, %%o4\n\t" \
+		      "or %%g0, %6, %%g1\n\t" \
+		      "t 0x10\n\t" \
+		      "bcc 1f\n\t" \
+		      "or %%g0, %%o0, %0\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "1:\n\t" \
+		      : "=r" (__res) \
+		      : "r" ((long)(arg1)),"r" ((long)(arg2)), \
+		        "r" ((long)(arg3)),"r" ((long)(arg4)),"r" ((long)(arg5)), \
+		        "i" (__NR_##name)  \
+		      : "g1", "o0", "o1", "o2", "o3", "o4"); \
 if (__res>=0) \
 	return (type) __res; \
 errno = -__res; \
@@ -422,22 +423,22 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,idle)
-static inline _syscall0(int,fork)
-static inline _syscall2(int,clone,unsigned long,flags,char *,ksp)
-static inline _syscall0(int,pause)
-static inline _syscall0(int,setup)
-static inline _syscall0(int,sync)
-static inline _syscall0(pid_t,setsid)
-static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
-static inline _syscall1(int,dup,int,fd)
-static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
-static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
-static inline _syscall1(int,close,int,fd)
-static inline _syscall1(int,_exit,int,exitcode)
-static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static __inline__ _syscall0(int,idle)
+static __inline__ _syscall0(int,fork)
+static __inline__ _syscall2(int,clone,unsigned long,flags,char *,ksp)
+static __inline__ _syscall0(int,pause)
+static __inline__ _syscall0(int,setup)
+static __inline__ _syscall0(int,sync)
+static __inline__ _syscall0(pid_t,setsid)
+static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
+static __inline__ _syscall1(int,dup,int,fd)
+static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp)
+static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode)
+static __inline__ _syscall1(int,close,int,fd)
+static __inline__ _syscall1(int,_exit,int,exitcode)
+static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
 
-static inline pid_t wait(int * wait_stat)
+static __inline__ pid_t wait(int * wait_stat)
 {
 	return waitpid(-1,wait_stat,0);
 }
@@ -450,7 +451,7 @@
  * a system call from a "real" process, but the process memory space will
  * not be free'd until both the parent and the child have exited.
  */
-static inline pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+static __inline__ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
 	long retval;
 
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/user.h linux/include/asm-sparc/user.h
--- v2.1.8/linux/include/asm-sparc/user.h	Mon Mar  4 08:50:04 1996
+++ linux/include/asm-sparc/user.h	Sat Nov  9 10:30:24 1996
@@ -1,11 +1,14 @@
-/* $Id: user.h,v 1.3 1995/12/29 21:48:03 davem Exp $
+/* $Id: user.h,v 1.4 1996/07/24 23:17:14 miguel Exp $
  * asm-sparc/user.h: Core file definitions for the Sparc.
  *
+ * Keep in sync with reg.h.  Actually, we could get rid of this
+ * one, since we won't a.out core dump that much anyways - miguel.
  * Copyright (C) 1995 (davem@caip.rutgers.edu)
  */
 #ifndef _SPARC_USER_H
 #define _SPARC_USER_H
 
+#include <asm/a.out.h>
 struct sunos_regs {
 	unsigned long psr, pc, npc, y;
 	unsigned long regs[15];
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/vac-ops.h linux/include/asm-sparc/vac-ops.h
--- v2.1.8/linux/include/asm-sparc/vac-ops.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/vac-ops.h	Sat Nov  9 10:30:25 1996
@@ -1,4 +1,4 @@
-/* $Id: vac-ops.h,v 1.11 1996/04/25 06:13:38 davem Exp $ */
+/* $Id: vac-ops.h,v 1.12 1996/07/08 15:12:30 ecd Exp $ */
 #ifndef _SPARC_VAC_OPS_H
 #define _SPARC_VAC_OPS_H
 
@@ -79,7 +79,7 @@
  * a 'bad alias'.
  */
 #define S4CVAC_BADALIAS(vaddr1, vaddr2) \
-        (((unsigned long) (vaddr1)) ^ ((unsigned long) (vaddr2)) & \
+        ((((unsigned long) (vaddr1)) ^ ((unsigned long) (vaddr2))) & \
 	 (S4CVAC_BADBITS))
 
 /* The following structure describes the characteristics of a sun4c
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/vaddrs.h linux/include/asm-sparc/vaddrs.h
--- v2.1.8/linux/include/asm-sparc/vaddrs.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/vaddrs.h	Sat Nov  9 10:30:26 1996
@@ -1,4 +1,4 @@
-/* $Id: vaddrs.h,v 1.20 1996/04/25 06:13:40 davem Exp $ */
+/* $Id: vaddrs.h,v 1.21 1996/10/07 03:03:02 davem Exp $ */
 #ifndef _SPARC_VADDRS_H
 #define _SPARC_VADDRS_H
 
@@ -17,8 +17,8 @@
  * a pointer and then the value in the assembly code
  */
 #define  IOBASE_VADDR   0xfe000000  /* Base for mapping pages */
-#define  IOBASE_LEN     0x00200000  /* Length of the IO area */
-#define  IOBASE_END     0xfe200000
+#define  IOBASE_LEN     0x00300000  /* Length of the IO area */
+#define  IOBASE_END     0xfe300000
 #define  DVMA_VADDR     0xfff00000  /* Base area of the DVMA on suns */
 #define  DVMA_LEN       0x00040000  /* Size of the DVMA address space */
 #define  DVMA_END       0xfff40000
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/viking.h linux/include/asm-sparc/viking.h
--- v2.1.8/linux/include/asm-sparc/viking.h	Thu Apr 25 13:23:18 1996
+++ linux/include/asm-sparc/viking.h	Sat Nov  9 10:30:27 1996
@@ -1,4 +1,4 @@
-/* $Id: viking.h,v 1.13 1996/04/25 06:13:43 davem Exp $
+/* $Id: viking.h,v 1.16 1996/08/29 09:49:10 davem Exp $
  * viking.h:  Defines specific to the GNU/Viking MBUS module.
  *            This is SRMMU stuff.
  *
@@ -7,6 +7,7 @@
 #ifndef _SPARC_VIKING_H
 #define _SPARC_VIKING_H
 
+#include <asm/asi.h>
 #include <asm/mxcc.h>
 
 /* Bits in the SRMMU control register for GNU/Viking modules.
@@ -92,6 +93,7 @@
 #define VIKING_SPENABLE     0x00004000   /* Enable bus cache snooping */
 #define VIKING_ACENABLE     0x00008000   /* Enable alternate caching */
 #define VIKING_TCENABLE     0x00010000   /* Enable table-walks to be cached */
+#define VIKING_DPENABLE     0x00040000   /* Enable the data prefetcher */
 
 /*
  * GNU/Viking Breakpoint Action Register fields.
@@ -105,38 +107,38 @@
 #define VIKING_PTAG_DIRTY   0x00010000   /* Block has been modified */
 #define VIKING_PTAG_SHARED  0x00000100   /* Shared with some other cache */
 
-extern inline void viking_flush_icache(void)
+extern __inline__ void viking_flush_icache(void)
 {
 	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
 			     "i" (ASI_M_IC_FLCLEAR));
 }
 
-extern inline void viking_flush_dcache(void)
+extern __inline__ void viking_flush_dcache(void)
 {
 	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
 			     "i" (ASI_M_DC_FLCLEAR));
 }
 
-extern inline void viking_unlock_icache(void)
+extern __inline__ void viking_unlock_icache(void)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (0x80000000), "i" (ASI_M_IC_FLCLEAR));
 }
 
-extern inline void viking_unlock_dcache(void)
+extern __inline__ void viking_unlock_dcache(void)
 {
 	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
 			     "r" (0x80000000), "i" (ASI_M_DC_FLCLEAR));
 }
 
-extern inline void viking_set_bpreg(unsigned long regval)
+extern __inline__ void viking_set_bpreg(unsigned long regval)
 {
 	__asm__ __volatile__("sta %0, [%%g0] %1\n\t" : :
 			     "r" (regval),
 			     "i" (ASI_M_ACTION));
 }
 
-extern inline unsigned long viking_get_bpreg(void)
+extern __inline__ unsigned long viking_get_bpreg(void)
 {
 	unsigned long regval;
 
@@ -146,8 +148,8 @@
 	return regval;
 }
 
-extern inline void viking_get_dcache_ptag(int set, int block,
-					  unsigned long *data)
+extern __inline__ void viking_get_dcache_ptag(int set, int block,
+					      unsigned long *data)
 {
 	unsigned long ptag = ((set & 0x7f) << 5) | ((block & 0x3) << 26) |
 			     0x80000000;
@@ -161,6 +163,36 @@
 			      "g2", "g3");
 	data[0] = info;
 	data[1] = page;
+}
+
+extern __inline__ void viking_mxcc_turn_off_parity(unsigned long *mregp,
+						   unsigned long *mxcc_cregp)
+{
+	unsigned long mreg = *mregp;
+	unsigned long mxcc_creg = *mxcc_cregp;
+
+	mreg &= ~(VIKING_PCENABLE);
+	mxcc_creg &= ~(MXCC_CTL_PARE);
+
+	__asm__ __volatile__ ("set 1f, %%g2\n\t"
+			      "andcc %%g2, 4, %%g0\n\t"
+			      "bne 2f\n\t"
+			      " nop\n"
+			      "1:\n\t"
+			      "sta %0, [%%g0] %3\n\t"
+			      "sta %1, [%2] %4\n\t"
+			      "b 1f\n\t"
+			      " nop\n\t"
+			      "nop\n"
+			      "2:\n\t"
+			      "sta %0, [%%g0] %3\n\t"
+			      "sta %1, [%2] %4\n"
+			      "1:\n\t" : :
+			      "r" (mreg), "r" (mxcc_creg),
+			      "r" (MXCC_CREG), "i" (ASI_M_MMUREGS),
+			      "i" (ASI_M_MXCC) : "g2");
+	*mregp = mreg;
+	*mxcc_cregp = mxcc_creg;
 }
 
 #endif /* !(_SPARC_VIKING_H) */
diff -u --recursive --new-file v2.1.8/linux/include/asm-sparc/winmacro.h linux/include/asm-sparc/winmacro.h
--- v2.1.8/linux/include/asm-sparc/winmacro.h	Sun Apr 21 12:30:36 1996
+++ linux/include/asm-sparc/winmacro.h	Sat Nov  9 10:30:28 1996
@@ -1,4 +1,4 @@
-/* $Id: winmacro.h,v 1.16 1996/03/27 02:43:18 davem Exp $
+/* $Id: winmacro.h,v 1.17 1996/09/19 20:27:44 davem Exp $
  * winmacro.h: Window loading-unloading macros.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -118,12 +118,12 @@
 	and      %idreg, 0xc, %idreg; \
 	sethi    %hi(C_LABEL(current_set)), %dest_reg; \
 	or       %dest_reg, %lo(C_LABEL(current_set)), %dest_reg; \
-	add      %dest_reg, %idreg, %dest_reg; \
-	ld       [%dest_reg], %dest_reg;
+	add      %dest_reg, %idreg, %idreg; \
+	ld       [%idreg], %dest_reg;
 #else
 #define LOAD_CURRENT(dest_reg, idreg) \
-        sethi    %hi(C_LABEL(current_set)), %dest_reg; \
-        ld       [%dest_reg + %lo(C_LABEL(current_set))], %dest_reg;
+        sethi    %hi(C_LABEL(current_set)), %idreg; \
+        ld       [%idreg + %lo(C_LABEL(current_set))], %dest_reg;
 #endif
 
 #endif /* !(_SPARC_WINMACRO_H) */
diff -u --recursive --new-file v2.1.8/linux/include/linux/affs_fs_i.h linux/include/linux/affs_fs_i.h
--- v2.1.8/linux/include/linux/affs_fs_i.h	Sun May 19 15:22:20 1996
+++ linux/include/linux/affs_fs_i.h	Tue Nov 12 15:14:14 1996
@@ -1,24 +1,45 @@
 #ifndef _AFFS_FS_I
 #define _AFFS_FS_I
 
-#define EXT_CACHE_SIZE	12
-#define MAX_PREALLOC	8	/* MUST be a power of 2 */
+#include <linux/a.out.h>
+#include <linux/time.h>
+
+#define AFFS_MAX_PREALLOC	16	/* MUST be a power of 2 */
+#define AFFS_KCSIZE		73	/* Allows for 1 extension block at 512 byte-blocks */
+
+struct key_cache {
+	struct timeval	 kc_lru_time;	/* Last time this cache was used */
+	int	 kc_first;		/* First cached key */
+	int	 kc_last;		/* Last cached key */
+	int	 kc_this_key;		/* Key of extension block this data block keys are from */
+	int	 kc_this_seq;		/* Sequence number of this extension block */
+	int	 kc_next_key;		/* Key of next extension block */
+	int	 kc_keys[AFFS_KCSIZE];	/* Key cache */
+};
+
+#define EC_SIZE	(PAGE_SIZE - 4 * sizeof(struct key_cache) - 4) / 4
+
+struct ext_cache {
+	struct key_cache  kc[4];	/* The 4 key caches */
+	__s32	 ec[EC_SIZE];		/* Keys of assorted extension blocks */
+	int	 max_ext;		/* Index of last known extension block */
+};
 
 /*
  * affs fs inode data in memory
  */
 struct affs_inode_info {
-	__u32	i_protect;		/* unused attribute bits */
-	__s32	i_parent;		/* parent ino */
-	__s32	i_original;		/* if != 0, this is the key of the original */
-	__s32	i_ext[EXT_CACHE_SIZE];	/* extension block numbers */
-	__s32	i_data[MAX_PREALLOC];	/* preallocated blocks */
-	int	i_lastblock;		/* last allocated block */
-	short	i_max_ext;		/* last known extension block */
-	short	i_pa_cnt;		/* number of preallocated blocks */
-	short	i_pa_next;		/* Index of next block in i_data[] */
-	short	i_pa_last;		/* Index of next free slot in i_data[] */
-	short	i_zone;			/* write zone */
+	__u32	 i_protect;		/* unused attribute bits */
+	__s32	 i_parent;		/* parent ino */
+	__s32	 i_original;		/* if != 0, this is the key of the original */
+	__s32	 i_data[AFFS_MAX_PREALLOC];	/* preallocated blocks */
+	struct ext_cache *i_ec;		/* Cache gets allocated dynamically */
+	int	 i_cache_users;		/* Cache cannot be freed while > 0 */
+	int	 i_lastblock;		/* last allocated block */
+	short	 i_pa_cnt;		/* number of preallocated blocks */
+	short	 i_pa_next;		/* Index of next block in i_data[] */
+	short	 i_pa_last;		/* Index of next free slot in i_data[] */
+	short	 i_zone;		/* write zone */
 	unsigned char i_hlink;		/* This is a fake */
 	unsigned char i_pad;
 };
diff -u --recursive --new-file v2.1.8/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v2.1.8/linux/include/linux/ax25.h	Wed Aug  7 08:41:57 1996
+++ linux/include/linux/ax25.h	Sun Nov 10 19:12:57 1996
@@ -20,46 +20,22 @@
 #define	AX25_HDRINCL	8
 #define AX25_IDLE	9
 #define AX25_PACLEN	10
-#define AX25_IPMAXQUEUE 11
+#define AX25_MAXQUEUE	11
 
 #define AX25_KILL	99
 
-#define SIOCAX25GETUID		(SIOCPROTOPRIVATE)
+#define SIOCAX25GETUID		(SIOCPROTOPRIVATE+0)
 #define SIOCAX25ADDUID		(SIOCPROTOPRIVATE+1)
 #define SIOCAX25DELUID		(SIOCPROTOPRIVATE+2)
 #define SIOCAX25NOUID		(SIOCPROTOPRIVATE+3)
-#define	SIOCAX25BPQADDR		(SIOCPROTOPRIVATE+4)
-#define	SIOCAX25GETPARMS	(SIOCPROTOPRIVATE+5)
-#define	SIOCAX25SETPARMS	(SIOCPROTOPRIVATE+6)
-#define SIOCAX25OPTRT		(SIOCPROTOPRIVATE+7)
-#define SIOCAX25CTLCON		(SIOCPROTOPRIVATE+8)
+#define SIOCAX25OPTRT		(SIOCPROTOPRIVATE+4)
+#define SIOCAX25CTLCON		(SIOCPROTOPRIVATE+5)
 
 #define AX25_SET_RT_IPMODE	2
 
 #define AX25_NOUID_DEFAULT	0
 #define AX25_NOUID_BLOCK	1
 
-#define	AX25_DIGI_INBAND	0x01	/* Allow digipeating within port **/
-#define	AX25_DIGI_XBAND		0x02	/* Allow digipeating across ports **/
-
-#define	AX25_VALUES_IPDEFMODE	0	/* 'D'=DG 'V'=VC */
-#define	AX25_VALUES_AXDEFMODE	1	/* 8=Normal 128=Extended Seq Nos */
-#define	AX25_VALUES_NETROM	2	/* Allow NET/ROM  - 0=No 1=Yes */
-#define	AX25_VALUES_TEXT	3	/* Allow PID=Text - 0=No 1=Yes */
-#define	AX25_VALUES_BACKOFF	4	/* 'E'=Exponential 'L'=Linear */
-#define	AX25_VALUES_CONMODE	5	/* Allow connected modes - 0=No 1=Yes */
-#define	AX25_VALUES_WINDOW	6	/* Default window size for standard AX.25 */
-#define	AX25_VALUES_EWINDOW	7	/* Default window size for extended AX.25 */
-#define	AX25_VALUES_T1		8	/* Default T1 timeout value */
-#define	AX25_VALUES_T2		9	/* Default T2 timeout value */
-#define	AX25_VALUES_T3		10	/* Default T3 timeout value */
-#define	AX25_VALUES_N2		11	/* Default N2 value */
-#define	AX25_VALUES_DIGI	12	/* Digipeat mode */
-#define AX25_VALUES_IDLE	13	/* mode vc idle timer */
-#define AX25_VALUES_PACLEN	14	/* AX.25 MTU */
-#define AX25_VALUES_IPMAXQUEUE  15	/* Maximum number of buffers enqueued */
-#define	AX25_MAX_VALUES		20
-
 typedef struct {
 	char ax25_call[7];	/* 6 call + SSID (shifted ascii!) */
 } ax25_address;
@@ -98,16 +74,6 @@
 	ax25_address dest_addr;
 	unsigned int cmd;
 	unsigned long arg;
-};
-
-struct ax25_bpqaddr_struct {
-	char dev[16];
-	ax25_address addr;
-};
-
-struct ax25_parms_struct {
-	ax25_address port_addr;
-	unsigned short values[AX25_MAX_VALUES];
 };
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/include/linux/bpqether.h linux/include/linux/bpqether.h
--- v2.1.8/linux/include/linux/bpqether.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/bpqether.h	Sun Nov 10 19:12:57 1996
@@ -0,0 +1,41 @@
+#ifndef	__BPQETHER_H
+#define	__BPQETHER_H
+
+/*
+ * 	Defines for the BPQETHER pseudo device driver
+ */
+
+#ifndef __LINUX_IF_ETHER_H
+#include <linux/if_ether.h>
+#endif
+
+#define SIOCSBPQETHOPT		(SIOCDEVPRIVATE+0)	/* reserved */
+#define SIOCSBPQETHADDR		(SIOCDEVPRIVATE+1)
+ 
+struct bpq_ethaddr {
+	unsigned char destination[ETH_ALEN];
+	unsigned char accept[ETH_ALEN];
+};
+
+/* 
+ * For SIOCSBPQETHOPT - this is compatible with PI2/PacketTwin card drivers,
+ * currently not implemented, though. If someone wants to hook a radio
+ * to his ethernet card he may find this useful... ;-)
+ */
+
+#define SIOCGBPQETHPARAM	0x5000  /* get Level 1 parameters */
+#define SIOCSBPQETHPARAM	0x5001  /* set */
+
+struct bpq_req  {
+    int cmd;
+    int speed;			/* unused */
+    int clockmode;		/* unused */
+    int txdelay;
+    unsigned char persist;	/* unused */
+    int slotime;		/* unused */
+    int squeldelay;
+    int dmachan;		/* unused */
+    int irq;			/* unused */
+};
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.1.8/linux/include/linux/cdrom.h	Wed Sep 18 11:13:53 1996
+++ linux/include/linux/cdrom.h	Tue Nov 12 10:32:49 1996
@@ -279,14 +279,6 @@
 
 
 /*
- * For controlling a changer.  (Used by ATAPI driver.)
- * This ioctl is depreciated in favor of CDROM_SELECT_DISC from
- * ucdrom.h.  It will probably be deleted during the 2.1 kernel series.
- */
-#define CDROMLOADFROMSLOT	0x531a	/* LOAD disk from slot*/
-
-
-/*
  * CD-ROM-specific SCSI command opcodes
  */
 
diff -u --recursive --new-file v2.1.8/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v2.1.8/linux/include/linux/if_arp.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/if_arp.h	Tue Nov 12 15:17:01 1996
@@ -44,6 +44,8 @@
 #define ARPHRD_CSLIP6	259
 #define ARPHRD_RSRVD	260		/* Notional KISS type 		*/
 #define ARPHRD_ADAPT	264
+#define ARPHRD_ROSE	270
+#define ARPHRD_X25	271		/* CCITT X.25			*/
 #define ARPHRD_PPP	512
 
 #define ARPHRD_TUNNEL	768		/* IPIP tunnel			*/
@@ -52,7 +54,7 @@
 #define ARPHRD_SKIP	771		/* SKIP vif			*/
 #define ARPHRD_LOOPBACK	772		/* Loopback device		*/
 #define ARPHRD_LOCALTLK 773		/* Localtalk device		*/
-#define ARPHRD_SIT	774		/* sit0 device - IPv6-in-IPv4	*/
+#define ARPHRD_SIT	776		/* sit0 device - IPv6-in-IPv4	*/
 
 /* ARP protocol opcodes. */
 #define	ARPOP_REQUEST	1		/* ARP request			*/
diff -u --recursive --new-file v2.1.8/linux/include/linux/in.h linux/include/linux/in.h
--- v2.1.8/linux/include/linux/in.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/in.h	Tue Nov 12 15:14:14 1996
@@ -118,11 +118,10 @@
 #define INADDR_ALLHOSTS_GROUP 	0xe0000001      /* 224.0.0.1   */
 #define INADDR_MAX_LOCAL_GROUP  0xe00000ff      /* 224.0.0.255 */
 
-#ifdef __KERNEL__
-
 /* <asm/byteorder.h> contains the htonl type stuff.. */
 #include <asm/byteorder.h> 
 
+#ifdef __KERNEL__
 /* Some random defines to make it easier in the kernel.. */
 #define LOOPBACK(x)	(((x) & htonl(0xff000000)) == htonl(0x7f000000))
 #define MULTICAST(x)	(((x) & htonl(0xf0000000)) == htonl(0xe0000000))
diff -u --recursive --new-file v2.1.8/linux/include/linux/in6.h linux/include/linux/in6.h
--- v2.1.8/linux/include/linux/in6.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/in6.h	Sun Nov 10 19:53:22 1996
@@ -27,11 +27,15 @@
 {
 	union 
 	{
-		unsigned char	u6_addr8[16];
+		__u8		u6_addr8[16];
 		__u32		u6_addr32[4];
+#if (~0UL) > 0xffffffff
+		__u64		u6_addr64[2];
+#endif
 	} in6_u;
-#define s6_addr32		in6_u.u6_addr32
 #define s6_addr			in6_u.u6_addr8
+#define s6_addr32		in6_u.u6_addr32
+#define s6_addr64		in6_u.u6_addr64
 };
 
 struct sockaddr_in6 {
@@ -47,7 +51,7 @@
 	struct in6_addr ipv6mr_multiaddr;
 
 	/* local IPv6 address of interface */
-	struct in6_addr ipv6mr_interface;
+	int		ipv6mr_ifindex;
 };
 
 /*
diff -u --recursive --new-file v2.1.8/linux/include/linux/ipv6.h linux/include/linux/ipv6.h
--- v2.1.8/linux/include/linux/ipv6.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/ipv6.h	Tue Nov 12 15:17:03 1996
@@ -42,8 +42,8 @@
 
 
 struct in6_pktinfo {
-	int		ipi6_ifindex;
 	struct in6_addr	ipi6_addr;
+	int		ipi6_ifindex;
 };
 
 #define IPV6_SRCRT_STRICT	0x01	/* this hop must be a neighbor	*/
diff -u --recursive --new-file v2.1.8/linux/include/linux/ipv6_route.h linux/include/linux/ipv6_route.h
--- v2.1.8/linux/include/linux/ipv6_route.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/ipv6_route.h	Sun Nov 10 19:53:22 1996
@@ -28,14 +28,14 @@
 #define DCF_INVALID	RTF_INVALID
 
 struct in6_rtmsg {
-	__u32			rtmsg_type;
 	struct in6_addr		rtmsg_dst;
 	struct in6_addr		rtmsg_gateway;
+	__u32			rtmsg_type;
 	__u16			rtmsg_prefixlen;
 	__u16			rtmsg_metric;
-	char			rtmsg_device[16];
-        __u16			rtmsg_flags;
 	unsigned long		rtmsg_info;
+        __u32			rtmsg_flags;
+	char			rtmsg_device[16];
 };
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/include/linux/major.h linux/include/linux/major.h
--- v2.1.8/linux/include/linux/major.h	Mon Sep  2 19:33:41 1996
+++ linux/include/linux/major.h	Sun Nov 10 19:12:57 1996
@@ -66,6 +66,7 @@
 #define Z2RAM_MAJOR	37
 #define RISCOM8_NORMAL_MAJOR 48
 #define RISCOM8_CALLOUT_MAJOR 49
+#define MKISS_MAJOR	55
 #define APBLOCK_MAJOR   60   /* AP1000 Block device */
 #define DDV_MAJOR       61   /* AP1000 DDV block device */
 
diff -u --recursive --new-file v2.1.8/linux/include/linux/msg.h linux/include/linux/msg.h
--- v2.1.8/linux/include/linux/msg.h	Wed Oct 16 10:48:29 1996
+++ linux/include/linux/msg.h	Sun Nov 10 19:47:25 1996
@@ -3,6 +3,10 @@
 
 #include <linux/ipc.h>
 
+/* ipcs ctl commands */
+#define MSG_STAT 11
+#define MSG_INFO 12
+
 /* msgrcv options */
 #define MSG_NOERROR     010000  /* no error if message is too big */
 #define MSG_EXCEPT      020000  /* recv any msg except of specified type.*/
@@ -64,10 +68,6 @@
 	time_t msg_stime;       /* msgsnd time */
 	short msg_ts;           /* message text size */
 };
-
-/* ipcs ctl commands */
-#define MSG_STAT 11
-#define MSG_INFO 12
 
 asmlinkage int sys_msgget (key_t key, int msgflg);
 asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
diff -u --recursive --new-file v2.1.8/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.8/linux/include/linux/netdevice.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/netdevice.h	Tue Nov 12 15:16:59 1996
@@ -32,10 +32,10 @@
 #define DEV_NUMBUFFS	3
 #define MAX_ADDR_LEN	7
 
-#if !defined(CONFIG_AX25) && !defined(CONFIG_TR)
+#if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR)
 #define LL_MAX_HEADER	32
 #else
-#if defined(CONFIG_AX25)
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #define LL_MAX_HEADER	96
 #else
 #define LL_MAX_HEADER	48
diff -u --recursive --new-file v2.1.8/linux/include/linux/netrom.h linux/include/linux/netrom.h
--- v2.1.8/linux/include/linux/netrom.h	Wed Aug  7 08:41:57 1996
+++ linux/include/linux/netrom.h	Sun Nov 10 19:12:57 1996
@@ -15,14 +15,13 @@
 #define NETROM_N2	3
 #define	NETROM_HDRINCL	4
 #define	NETROM_PACLEN	5
+#define	NETROM_T4	6
+#define	NETROM_IDLE	7
 
 #define	NETROM_KILL	99
 
-#define	SIOCNRGETPARMS		(SIOCPROTOPRIVATE+0)
-#define	SIOCNRSETPARMS		(SIOCPROTOPRIVATE+1)
-#define	SIOCNRDECOBS		(SIOCPROTOPRIVATE+2)
-#define	SIOCNRRTCTL		(SIOCPROTOPRIVATE+3)
-#define	SIOCNRCTLCON		(SIOCPROTOPRIVATE+4)
+#define	SIOCNRDECOBS		(SIOCPROTOPRIVATE+0)
+#define	SIOCNRCTLCON		(SIOCPROTOPRIVATE+1)
 
 struct nr_route_struct {
 #define	NETROM_NEIGH	0
@@ -34,18 +33,6 @@
 	char mnemonic[7];
 	ax25_address neighbour;
 	unsigned int obs_count;
-};
-
-struct nr_parms_struct {
-	unsigned int quality;
-	unsigned int obs_count;
-	unsigned int ttl;
-	unsigned int timeout;
-	unsigned int ack_delay;
-	unsigned int busy_delay;
-	unsigned int tries;
-	unsigned int window;
-	unsigned int paclen;
 };
 
 struct nr_ctl_struct {
diff -u --recursive --new-file v2.1.8/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.8/linux/include/linux/pci.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/pci.h	Tue Nov 12 10:35:24 1996
@@ -264,8 +264,8 @@
 #define PCI_DEVICE_ID_DEC_TULIP_FAST	0x0009
 #define PCI_DEVICE_ID_DEC_FDDI		0x000F
 #define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014
-#define PCI_DEVICE_ID_DEC_21052_AB	0x0021
-#define PCI_DEVICE_ID_DEC_21152_AA	0x0024
+#define PCI_DEVICE_ID_DEC_21052		0x0021
+#define PCI_DEVICE_ID_DEC_21152		0x0024
 
 #define PCI_VENDOR_ID_CIRRUS		0x1013
 #define PCI_DEVICE_ID_CIRRUS_5430	0x00a0
diff -u --recursive --new-file v2.1.8/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.8/linux/include/linux/proc_fs.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/proc_fs.h	Tue Nov 12 15:14:28 1996
@@ -107,6 +107,10 @@
 	PROC_NET_STRIP_STATUS,
 	PROC_NET_STRIP_TRACE,
 	PROC_NET_Z8530,
+	PROC_NET_RS_NODES,
+	PROC_NET_RS_NEIGH,
+	PROC_NET_RS_ROUTES,
+	PROC_NET_RS,
 	PROC_NET_LAST
 };
 
diff -u --recursive --new-file v2.1.8/linux/include/linux/rose.h linux/include/linux/rose.h
--- v2.1.8/linux/include/linux/rose.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/rose.h	Sun Nov 10 19:12:57 1996
@@ -0,0 +1,51 @@
+/*
+ * These are the public elements of the Linux kernel Rose implementation.
+ * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the
+ * definition of the ax25_address structure.
+ */
+ 
+#ifndef	ROSE_KERNEL_H
+#define	ROSE_KERNEL_H
+
+#define PF_ROSE		AF_ROSE
+#define ROSE_MTU	128
+
+#define ROSE_T0		1
+#define ROSE_T1		2
+#define	ROSE_T2		3
+#define	ROSE_T3		4
+#define	ROSE_IDLE	5
+#define	ROSE_HDRINCL	6
+
+#define	ROSE_KILL	99
+
+#define	SIOCRSCTLCON		(SIOCPROTOPRIVATE+0)
+
+typedef struct {
+	char rose_addr[5];
+} rose_address;
+
+struct sockaddr_rose {
+	short		srose_family;
+	rose_address	srose_addr;
+	ax25_address	srose_call;
+	int		srose_ndigis;
+	ax25_address	srose_digi;
+};
+
+struct rose_route_struct {
+	rose_address  address;
+	ax25_address  neighbour;
+	char          device[16];
+	unsigned char ndigis;
+	ax25_address  digipeaters[AX25_MAX_DIGIS];
+};
+
+struct rose_ctl_struct {
+	unsigned int  lci;
+	char          dev[20];
+	unsigned int  cmd;
+	unsigned long arg;
+};
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/include/linux/sem.h linux/include/linux/sem.h
--- v2.1.8/linux/include/linux/sem.h	Wed Oct  9 08:55:23 1996
+++ linux/include/linux/sem.h	Sun Nov 10 19:47:26 1996
@@ -15,6 +15,10 @@
 #define SETVAL  16       /* set semval */
 #define SETALL  17       /* set all semval's */
 
+/* ipcs ctl cmds */
+#define SEM_STAT 18
+#define SEM_INFO 19
+
 /* One semid data structure for each set of semaphores in the system. */
 struct semid_ds {
 	struct ipc_perm	sem_perm;		/* permissions .. see ipc.h */
@@ -76,10 +80,6 @@
 	int	semval;		/* current value */
 	int	sempid;		/* pid of last operation */
 };
-
-/* ipcs ctl cmds */
-#define SEM_STAT 18
-#define SEM_INFO 19
 
 /* One queue for each semaphore set in the system. */
 struct sem_queue {
diff -u --recursive --new-file v2.1.8/linux/include/linux/shm.h linux/include/linux/shm.h
--- v2.1.8/linux/include/linux/shm.h	Wed Oct 16 10:48:30 1996
+++ linux/include/linux/shm.h	Sun Nov 10 19:47:26 1996
@@ -33,6 +33,10 @@
 #define SHM_LOCK 	11
 #define SHM_UNLOCK 	12
 
+/* ipcs ctl commands */
+#define SHM_STAT 	13
+#define SHM_INFO 	14
+
 struct	shminfo {
 	int shmmax;
 	int shmmin;
@@ -41,16 +45,6 @@
 	int shmall;
 };
 
-#ifdef __KERNEL__
-
-/* shm_mode upper byte flags */
-#define	SHM_DEST	01000	/* segment will be destroyed on last detach */
-#define SHM_LOCKED      02000   /* segment will not be swapped */
-
-/* ipcs ctl commands */
-#define SHM_STAT 	13
-#define SHM_INFO 	14
-
 struct shm_info {
 	int used_ids;
 	unsigned long shm_tot;	/* total allocated shm */
@@ -59,6 +53,12 @@
 	unsigned long swap_attempts;
 	unsigned long swap_successes;
 };
+
+#ifdef __KERNEL__
+
+/* shm_mode upper byte flags */
+#define	SHM_DEST	01000	/* segment will be destroyed on last detach */
+#define SHM_LOCKED      02000   /* segment will not be swapped */
 
 asmlinkage int sys_shmget (key_t key, int size, int flag);
 asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, unsigned long *addr);
diff -u --recursive --new-file v2.1.8/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.1.8/linux/include/linux/socket.h	Sun Nov 10 20:12:15 1996
+++ linux/include/linux/socket.h	Sun Nov 10 19:59:14 1996
@@ -58,17 +58,17 @@
 extern __inline__ struct cmsghdr * cmsg_nxthdr(struct msghdr *mhdr,
 					       struct cmsghdr *cmsg)
 {
-	void * ptr;
+	unsigned char * ptr;
 
 	if (cmsg->cmsg_len < sizeof(struct cmsghdr))
 	{
 		return NULL;
 	}
 	ptr = ((unsigned char *) cmsg) +  cmsg->cmsg_len;
-	if (ptr >= mhdr->msg_control + mhdr->msg_controllen)
+	if (ptr >= (unsigned char *) mhdr->msg_control + mhdr->msg_controllen)
 		return NULL;
 
-	return ptr;
+	return (struct cmsghdr *) ptr;
 }
 
 /* Control Messages */
@@ -94,12 +94,13 @@
 #define AF_AX25		3	/* Amateur Radio AX.25 		*/
 #define AF_IPX		4	/* Novell IPX 			*/
 #define AF_APPLETALK	5	/* Appletalk DDP 		*/
-#define	AF_NETROM	6	/* Amateur radio NetROM 	*/
+#define AF_NETROM	6	/* Amateur Radio NET/ROM 	*/
 #define AF_BRIDGE	7	/* Multiprotocol bridge 	*/
 #define AF_AAL5		8	/* Reserved for Werner's ATM 	*/
 #define AF_X25		9	/* Reserved for X.25 project 	*/
 #define AF_INET6	10	/* IP version 6			*/
-#define AF_MAX		12	/* For now.. */
+#define AF_ROSE		11	/* Amateur Radio X.25 PLP	*/
+#define AF_MAX		13	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -113,6 +114,7 @@
 #define PF_AAL5		AF_AAL5
 #define PF_X25		AF_X25
 #define PF_INET6	AF_INET6
+#define PR_ROSE		AF_ROSE
 
 #define PF_MAX		AF_MAX
 
@@ -134,7 +136,8 @@
 #define SOL_IPX		256
 #define SOL_AX25	257
 #define SOL_ATALK	258
-#define	SOL_NETROM	259
+#define SOL_NETROM	259
+#define SOL_ROSE	260
 #define SOL_TCP		6
 #define SOL_UDP		17
 
diff -u --recursive --new-file v2.1.8/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.8/linux/include/linux/sysctl.h	Sun Nov 10 20:12:15 1996
+++ linux/include/linux/sysctl.h	Sun Nov 10 19:12:57 1996
@@ -81,6 +81,8 @@
 #define NET_AX25        9
 #define NET_BRIDGE	10
 #define NET_IPV6	11
+#define NET_ROSE	12
+#define NET_X25		13
 
 /* /proc/sys/net/core */
 
@@ -108,8 +110,30 @@
 /* /proc/sys/net/appletalk */
 
 /* /proc/sys/net/netrom */
+#define	NET_NETROM_DEFAULT_PATH_QUALITY			1
+#define	NET_NETROM_OBSOLESCENCE_COUNT_INITIALISER	2
+#define	NET_NETROM_NETWORK_TTL_INITIALISER		3
+#define	NET_NETROM_TRANSPORT_TIMEOUT			4
+#define	NET_NETROM_TRANSPORT_MAXIMUM_TRIES		5
+#define	NET_NETROM_TRANSPORT_ACKNOWLEDGE_DELAY		6
+#define	NET_NETROM_TRANSPORT_BUSY_DELAY			7
+#define	NET_NETROM_TRANSPORT_REQUESTED_WINDOW_SIZE	8
+#define	NET_NETROM_TRANSPORT_NO_ACTIVITY_TIMEOUT	9
+#define	NET_NETROM_TRANSPORT_PACKET_LENGTH		10
+#define	NET_NETROM_ROUTING_CONTROL			11
 
 /* /proc/sys/net/ax25 */
+/* Values are generated dynamically */
+
+/* /proc/sys/net/rose */
+#define	NET_ROSE_RESTART_REQUEST_TIMEOUT		1
+#define	NET_ROSE_CALL_REQUEST_TIMEOUT			2
+#define	NET_ROSE_RESET_REQUEST_TIMEOUT			3
+#define	NET_ROSE_CLEAR_REQUEST_TIMEOUT			4
+#define	NET_ROSE_NO_ACTIVITY_TIMEOUT			5
+#define	NET_ROSE_ROUTING_CONTROL			6
+
+/* /proc/sys/net/x25 */
 
 /* CTL_PROC names: */
 
diff -u --recursive --new-file v2.1.8/linux/include/linux/time.h linux/include/linux/time.h
--- v2.1.8/linux/include/linux/time.h	Tue Jul  2 19:08:43 1996
+++ linux/include/linux/time.h	Mon Nov 11 15:38:07 1996
@@ -24,6 +24,8 @@
 #ifdef __KERNEL__
 void do_gettimeofday(struct timeval *tv);
 void do_settimeofday(struct timeval *tv);
+void get_fast_time(struct timeval *tv);
+void (*do_get_fast_time)(struct timeval *);
 #endif
 
 #define FD_SETSIZE		__FD_SETSIZE
diff -u --recursive --new-file v2.1.8/linux/include/linux/ucdrom.h linux/include/linux/ucdrom.h
--- v2.1.8/linux/include/linux/ucdrom.h	Wed Aug 14 10:21:03 1996
+++ linux/include/linux/ucdrom.h	Tue Nov 12 10:32:49 1996
@@ -6,32 +6,46 @@
 #define LINUX_UCDROM_H
 
 #ifdef __KERNEL__
-struct cdrom_device_ops {
-/* routines */
-	int (*open) (kdev_t, int);
-	void (*release) (kdev_t);
-	int (*open_files) (kdev_t);      /* number of open files */
-	int (*drive_status) (kdev_t);
-	int (*disc_status) (kdev_t);
-	int (*media_changed) (kdev_t);
-	int (*tray_move) (kdev_t, int);
-	int (*lock_door) (kdev_t, int);
-	int (*select_speed) (kdev_t, int);
-	int (*select_disc) (kdev_t, int);
-	int (*get_last_session) (kdev_t, struct cdrom_multisession *);
-	int (*get_mcn) (kdev_t, struct cdrom_mcn *);
-	int (*reset) (kdev_t dev);       /* hard reset device */
-	int (*audio_ioctl) (kdev_t, unsigned int, void *); /* play stuff */
-	int (*dev_ioctl) (kdev_t, unsigned int, unsigned long); /* dev-specific */
+struct cdrom_device_info {
+	struct cdrom_device_ops  *ops;  /* link to device_ops */
+	struct cdrom_device_info *next; /* next device_info for this major */
+	void *handle;		        /* driver-dependent data */
 /* specifications */
-	const int capability;           /* capability flags */
+        kdev_t dev;	                /* device number */
 	int mask;                       /* mask of capability: disables them */
 	const int speed;                /* maximum speed for reading data */
-	const int minors;               /* number of minor devs supported */
 	const int capacity;             /* number of discs in jukebox */
 /* device-related storage */
-	int options;                    /* options flags */
-	long mc_flags;                  /* media change buffer flags (2*16) */
+	int options : 30;               /* options flags */
+	unsigned mc_flags : 2;          /* media change buffer flags */
+    	int use_count;                  /* number of times device opened */
+};
+
+struct cdrom_device_ops {
+/* routines */
+	int (*open) (struct cdrom_device_info *, int);
+	void (*release) (struct cdrom_device_info *);
+	int (*drive_status) (struct cdrom_device_info *, int);
+	int (*disc_status) (struct cdrom_device_info *);
+	int (*media_changed) (struct cdrom_device_info *, int);
+	int (*tray_move) (struct cdrom_device_info *, int);
+	int (*lock_door) (struct cdrom_device_info *, int);
+	int (*select_speed) (struct cdrom_device_info *, int);
+	int (*select_disc) (struct cdrom_device_info *, int);
+	int (*get_last_session) (struct cdrom_device_info *,
+				 struct cdrom_multisession *);
+	int (*get_mcn) (struct cdrom_device_info *,
+			struct cdrom_mcn *);
+	/* hard reset device */
+	int (*reset) (struct cdrom_device_info *);
+	/* play stuff */
+	int (*audio_ioctl) (struct cdrom_device_info *,unsigned int, void *);
+	/* dev-specific */
+ 	int (*dev_ioctl) (struct cdrom_device_info *,
+			  unsigned int, unsigned long);
+/* driver specifications */
+	const int capability;   /* capability flags */
+	int n_minors;           /* number of active minor devices */
 };
 #endif
 
@@ -67,6 +81,11 @@
 #define CDO_LOCK	0x8             /* lock tray on open files */
 #define CDO_CHECK_TYPE	0x10            /* check type on open for data */
 
+/* Special codes for specifying changer slots. */
+#include <limits.h>
+#define CDSL_NONE       INT_MAX-1
+#define CDSL_CURRENT    INT_MAX
+
 /* Some more ioctls to control these options */
 #define CDROM_SET_OPTIONS	0x5320
 #define CDROM_CLEAR_OPTIONS	0x5321
@@ -75,6 +94,7 @@
 #define CDROM_MEDIA_CHANGED	0x5325
 #define CDROM_DRIVE_STATUS	0x5326  /* tray position, etc. */
 #define CDROM_DISC_STATUS	0x5327  /* disc type etc. */
+#define CDROM_CHANGER_NSLOTS    0x5328
 
 /* Rename an old ioctl */
 #define CDROM_GET_MCN	CDROM_GET_UPC	/* medium catalog number */
@@ -83,9 +103,8 @@
 /* the general file operations structure: */
 extern struct file_operations cdrom_fops;
 
-extern int register_cdrom(int major, char *name,
-                          struct cdrom_device_ops *cdo);
-extern int unregister_cdrom(int major, char *name);
+extern int register_cdrom(struct cdrom_device_info *cdi, char *name);
+extern int unregister_cdrom(struct cdrom_device_info *cdi);
 #endif
 
 #endif	/* LINUX_UCDROM_H */
diff -u --recursive --new-file v2.1.8/linux/include/net/ax25.h linux/include/net/ax25.h
--- v2.1.8/linux/include/net/ax25.h	Mon Sep 30 11:21:33 1996
+++ linux/include/net/ax25.h	Tue Nov 12 15:17:03 1996
@@ -23,6 +23,7 @@
 #define	AX25_DIGI_HEADER_LEN	(AX25_MAX_DIGIS * AX25_ADDR_LEN)
 #define	AX25_MAX_HEADER_LEN	(AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN)
  
+#define AX25_P_ROSE	0x01
 #define AX25_P_IP	0xCC
 #define AX25_P_ARP	0xCD
 #define AX25_P_TEXT 	0xF0
@@ -109,25 +110,46 @@
 #define AX25_STATE_3	3
 #define AX25_STATE_4	4
 
-#define MODULUS 	8			/*  Standard AX.25 modulus */
-#define	EMODULUS	128			/*  Extended AX.25 modulus */
+#define	AX25_MAX_DEVICES	20	/* Max No of AX.25 devices */
 
-#define	AX25_DEF_IPDEFMODE	'D'
-#define	AX25_DEF_AXDEFMODE	8
-#define	AX25_DEF_NETROM		1
-#define	AX25_DEF_TEXT		1
-#define	AX25_DEF_BACKOFF	'E'
-#define	AX25_DEF_CONMODE	1
-#define	AX25_DEF_WINDOW		2
-#define	AX25_DEF_EWINDOW	32
-#define	AX25_DEF_T1		10
-#define	AX25_DEF_T2		3
-#define	AX25_DEF_T3		300
-#define	AX25_DEF_N2		10
-#define AX25_DEF_IDLE		20
-#define AX25_DEF_PACLEN		256
-#define AX25_DEF_IPMAXQUEUE	2		/* 1 * ax25->window */
-#define	AX25_DEF_DIGI		(AX25_DIGI_INBAND|AX25_DIGI_XBAND)
+#define MODULUS 		8	/*  Standard AX.25 modulus */
+#define	EMODULUS		128	/*  Extended AX.25 modulus */
+
+#define	AX25_DIGI_INBAND	0x01	/* Allow digipeating within port **/
+#define	AX25_DIGI_XBAND		0x02	/* Allow digipeating across ports **/
+
+#define	AX25_VALUES_IPDEFMODE	0	/* 0=DG 1=VC */
+#define	AX25_VALUES_AXDEFMODE	1	/* 0=Normal 1=Extended Seq Nos */
+#define	AX25_VALUES_TEXT	2	/* Allow PID=Text - 0=No 1=Yes */
+#define	AX25_VALUES_BACKOFF	3	/* 0=Linear 1=Exponential */
+#define	AX25_VALUES_CONMODE	4	/* Allow connected modes - 0=No 1=Yes */
+#define	AX25_VALUES_WINDOW	5	/* Default window size for standard AX.25 */
+#define	AX25_VALUES_EWINDOW	6	/* Default window size for extended AX.25 */
+#define	AX25_VALUES_T1		7	/* Default T1 timeout value */
+#define	AX25_VALUES_T2		8	/* Default T2 timeout value */
+#define	AX25_VALUES_T3		9	/* Default T3 timeout value */
+#define	AX25_VALUES_N2		10	/* Default N2 value */
+#define AX25_VALUES_IDLE	11	/* mode vc idle timer */
+#define AX25_VALUES_PACLEN	12	/* AX.25 MTU */
+#define AX25_VALUES_MAXQUEUE	13	/* Maximum number of buffers enqueued */
+#define	AX25_VALUES_DIGI	14	/* Digipeat mode */
+#define	AX25_MAX_VALUES		15
+
+#define	AX25_DEF_IPDEFMODE	0			/* Datagram */
+#define	AX25_DEF_AXDEFMODE	0			/* Normal */
+#define	AX25_DEF_TEXT		1			/* PID=Text allowed */
+#define	AX25_DEF_BACKOFF	1			/* Exponential backoff */
+#define	AX25_DEF_CONMODE	1			/* Connected mode allowed */
+#define	AX25_DEF_WINDOW		2			/* Window=2 */
+#define	AX25_DEF_EWINDOW	32			/* Module-128 Window=32 */
+#define	AX25_DEF_T1		(10 * PR_SLOWHZ)	/* T1=10s */
+#define	AX25_DEF_T2		(3 * PR_SLOWHZ)		/* T2=3s  */
+#define	AX25_DEF_T3		(300 * PR_SLOWHZ)	/* T3=300s */
+#define	AX25_DEF_N2		10			/* N2=10 */
+#define AX25_DEF_IDLE		(20 * 60 * PR_SLOWHZ)	/* Idle=20 mins */		
+#define AX25_DEF_PACLEN		256			/* Paclen=256 */
+#define AX25_DEF_MAXQUEUE	2			/* 1 * ax25->window */
+#define	AX25_DEF_DIGI		0x03			/* All digis alowed */
 
 typedef struct ax25_uid_assoc {
 	struct ax25_uid_assoc *next;
@@ -166,11 +188,19 @@
 	struct sock		*sk;		/* Backlink to socket */
 } ax25_cb;
 
+struct ax25_dev {
+	char name[20];
+	struct device *dev;
+	int values[AX25_MAX_VALUES];
+};
+
 /* af_ax25.c */
 extern ax25_address null_ax25_address;
 extern char *ax2asc(ax25_address *);
+extern ax25_address *asc2ax(char *);
 extern int  ax25cmp(ax25_address *, ax25_address *);
 extern int  ax25_send_frame(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern int  ax25_link_up(ax25_address *, ax25_address *, struct device *);
 extern void ax25_destroy_socket(ax25_cb *);
 extern struct device *ax25rtr_get_dev(ax25_address *);
 extern int  ax25_encapsulate(struct sk_buff *, struct device *, unsigned short,
@@ -203,6 +233,7 @@
 extern void dama_establish_data_link(ax25_cb *);
 
 /* ax25_route.c */
+extern struct ax25_dev ax25_device[];
 extern int  ax25_rt_get_info(char *, char **, off_t, int, int);
 extern int  ax25_cs_get_info(char *, char **, off_t, int, int);
 extern int  ax25_rt_autobind(ax25_cb *, ax25_address *);
@@ -211,13 +242,10 @@
 extern void ax25_rt_device_down(struct device *);
 extern int  ax25_rt_ioctl(unsigned int, void *);
 extern char ax25_ip_mode_get(ax25_address *, struct device *);
-extern unsigned short ax25_dev_get_value(struct device *, int);
+extern int  ax25_dev_get_value(struct device *, int);
 extern void ax25_dev_device_up(struct device *);
 extern void ax25_dev_device_down(struct device *);
-extern int  ax25_dev_ioctl(unsigned int, void *);
-extern int  ax25_bpq_get_info(char *, char **, off_t, int, int);
-extern ax25_address *ax25_bpq_get_addr(struct device *);
-extern int  ax25_bpq_ioctl(unsigned int, void *);
+extern void ax25_rt_free(void);
 
 /* ax25_subr.c */
 extern void ax25_clear_queues(ax25_cb *);
@@ -239,12 +267,27 @@
 extern void ax25_dama_on(ax25_cb *);	/* dl1bke 951121 */
 extern void ax25_dama_off(ax25_cb *);	/* dl1bke 951121 */
 
-/* ax25_timer */
+/* ax25_timer.c */
 extern void ax25_set_timer(ax25_cb *);
 extern void ax25_t1_timeout(ax25_cb *);
+extern void ax25_link_failed(ax25_address *, struct device *);
+extern int  (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
+extern int  ax25_listen_mine(ax25_address *, struct device *);
+
+/* sysctl_net_ax25.c */
+extern void ax25_register_sysctl(void);
+extern void ax25_unregister_sysctl(void);
 
 /* ... */
 
-extern ax25_cb * volatile ax25_list;
+extern ax25_cb *volatile ax25_list;
+
+/* support routines for modules that use AX.25, in ax25_timer.c */
+extern int  ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
+extern void ax25_protocol_release(unsigned int);
+extern int  ax25_linkfail_register(void (*)(ax25_address *, struct device *));
+extern void ax25_linkfail_release(void (*)(ax25_address *, struct device *));
+extern int  ax25_listen_register(ax25_address *, struct device *);
+extern void ax25_listen_release(ax25_address *, struct device *);
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/include/net/checksum.h linux/include/net/checksum.h
--- v2.1.8/linux/include/net/checksum.h	Sun Nov 10 20:12:15 1996
+++ linux/include/net/checksum.h	Tue Nov 12 15:20:20 1996
@@ -81,8 +81,8 @@
 	csum += carry;
 
 	uproto = htonl(proto);
-	csum += proto;
-	carry = (csum < proto);
+	csum += uproto;
+	carry = (csum < uproto);
 	csum += carry;
 
 	return csum_fold(csum);
diff -u --recursive --new-file v2.1.8/linux/include/net/ipv6_route.h linux/include/net/ipv6_route.h
--- v2.1.8/linux/include/net/ipv6_route.h	Sun Nov 10 20:12:15 1996
+++ linux/include/net/ipv6_route.h	Sat Nov  9 19:50:27 1996
@@ -114,6 +114,8 @@
 #define DC_TIME_RUN		(5*HZ)
 #define DC_TIME_RETRY		HZ
 
+#define RT6_FILTER_NONE		0
+#define RT6_FILTER_RTNODES	1
 /*
  *	Prototypes
  */
@@ -145,6 +147,8 @@
 
 extern void			ipv6_route_init(void);
 extern void			ipv6_route_cleanup(void);
+
+extern void			rt6_ifdown(struct device *dev);
 
 extern int			ipv6_route_add(struct in6_rtmsg *rt);
 
diff -u --recursive --new-file v2.1.8/linux/include/net/netrom.h linux/include/net/netrom.h
--- v2.1.8/linux/include/net/netrom.h	Wed Aug  7 08:41:57 1996
+++ linux/include/net/netrom.h	Sun Nov 10 19:12:57 1996
@@ -37,15 +37,16 @@
 
 #define NR_DEFAULT_T1		(120 * PR_SLOWHZ)	/* Outstanding frames - 120 seconds */
 #define NR_DEFAULT_T2		(5   * PR_SLOWHZ)	/* Response delay     - 5 seconds */
-#define NR_DEFAULT_N2		3			/* Number of Retries */
-#define	NR_DEFAULT_T4		(180 * PR_SLOWHZ)	/* Transport Busy Delay */
-#define	NR_DEFAULT_WINDOW	4			/* Default Window Size	*/
-#define	NR_DEFAULT_OBS		6			/* Default Obsolescence Count */
-#define	NR_DEFAULT_QUAL		10			/* Default Neighbour Quality */
-#define	NR_DEFAULT_TTL		16			/* Default Time To Live */
+#define NR_DEFAULT_N2		3			/* Number of Retries - 3 */
+#define	NR_DEFAULT_T4		(180 * PR_SLOWHZ)	/* Busy Delay - 180 seconds */
+#define	NR_DEFAULT_IDLE		(20* 60 * PR_SLOWHZ)	/* No Activuty Timeout - 900 seconds*/
+#define	NR_DEFAULT_WINDOW	4			/* Default Window Size - 4 */
+#define	NR_DEFAULT_OBS		6			/* Default Obsolescence Count - 6 */
+#define	NR_DEFAULT_QUAL		10			/* Default Neighbour Quality - 10 */
+#define	NR_DEFAULT_TTL		16			/* Default Time To Live - 16 */
 #define NR_MODULUS 		256
-#define NR_MAX_WINDOW_SIZE	127			/* Maximum Window Allowable */
-#define	NR_DEFAULT_PACLEN	236			/* Default Packet Length */
+#define NR_MAX_WINDOW_SIZE	127			/* Maximum Window Allowable - 127 */
+#define	NR_DEFAULT_PACLEN	236			/* Default Packet Length - 236 */
 
 typedef struct {
 	ax25_address		user_addr, source_addr, dest_addr;
@@ -55,8 +56,8 @@
 	unsigned char		state, condition, bpqext, hdrincl;
 	unsigned short		vs, vr, va, vl;
 	unsigned char		n2, n2count;
-	unsigned short		t1, t2, rtt;
-	unsigned short		t1timer, t2timer, t4timer;
+	unsigned short		t1, t2, t4, idle, rtt;
+	unsigned short		t1timer, t2timer, t4timer, idletimer;
 	unsigned short		fraglen, paclen;
 	struct sk_buff_head	ack_queue;
 	struct sk_buff_head	reseq_queue;
@@ -91,7 +92,17 @@
 };
 
 /* af_netrom.c */
-extern struct nr_parms_struct nr_default;
+extern int  sysctl_netrom_default_path_quality;
+extern int  sysctl_netrom_obsolescence_count_initialiser;
+extern int  sysctl_netrom_network_ttl_initialiser;
+extern int  sysctl_netrom_transport_timeout;
+extern int  sysctl_netrom_transport_maximum_tries;
+extern int  sysctl_netrom_transport_acknowledge_delay;
+extern int  sysctl_netrom_transport_busy_delay;
+extern int  sysctl_netrom_transport_requested_window_size;
+extern int  sysctl_netrom_transport_no_activity_timeout;
+extern int  sysctl_netrom_transport_packet_length;
+extern int  sysctl_netrom_routing_control;
 extern int  nr_rx_frame(struct sk_buff *, struct device *);
 extern void nr_destroy_socket(struct sock *);
 
@@ -122,6 +133,7 @@
 extern int  nr_route_frame(struct sk_buff *, ax25_cb *);
 extern int  nr_nodes_get_info(char *, char **, off_t, int, int);
 extern int  nr_neigh_get_info(char *, char **, off_t, int, int);
+extern void nr_rt_free(void);
 
 /* nr_subr.c */
 extern void nr_clear_queues(struct sock *);
@@ -134,7 +146,11 @@
 extern unsigned short nr_calculate_t1(struct sock *);
 extern void nr_calculate_rtt(struct sock *);
 
-/* ax25_timer */
+/* nr_timer.c */
 extern void nr_set_timer(struct sock *);
+
+/* sysctl_net_netrom.c */
+extern void nr_register_sysctl(void);
+extern void nr_unregister_sysctl(void);
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/include/net/rose.h linux/include/net/rose.h
--- v2.1.8/linux/include/net/rose.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/rose.h	Sun Nov 10 19:12:57 1996
@@ -0,0 +1,183 @@
+/*
+ *	Declarations of Rose type objects.
+ *
+ *	Jonathan Naylor G4KLX	25/8/96
+ */
+ 
+#ifndef _ROSE_H
+#define _ROSE_H 
+#include <linux/rose.h>
+
+#define	ROSE_ADDR_LEN			5
+
+#define	ROSE_MIN_LEN			3
+
+#define	GFI				0x10
+#define	Q_BIT				0x80
+#define	D_BIT				0x40
+#define	M_BIT				0x10
+ 
+#define	ROSE_CALL_REQUEST		0x0B
+#define	ROSE_CALL_ACCEPTED		0x0F
+#define	ROSE_CLEAR_REQUEST		0x13
+#define	ROSE_CLEAR_CONFIRMATION		0x17
+#define	ROSE_DATA			0x00
+#define	ROSE_INTERRUPT			0x23
+#define	ROSE_INTERRUPT_CONFIRMATION	0x27
+#define	ROSE_RR				0x01
+#define	ROSE_RNR			0x05
+#define	ROSE_REJ			0x09
+#define	ROSE_RESET_REQUEST		0x1B
+#define	ROSE_RESET_CONFIRMATION		0x1F
+#define	ROSE_REGISTRATION_REQUEST	0xF3
+#define	ROSE_REGISTRATION_CONFIRMATION	0xF7
+#define	ROSE_RESTART_REQUEST		0xFB
+#define	ROSE_RESTART_CONFIRMATION	0xFF
+#define	ROSE_DIAGNOSTIC			0xF1
+#define	ROSE_ILLEGAL			0xFD
+
+/* Define Link State constants. */
+
+#define ROSE_STATE_0		0		/* Ready */
+#define ROSE_STATE_1		1		/* Awaiting Call Accepted */
+#define ROSE_STATE_2		2		/* Awaiting Clear Confirmation */
+#define ROSE_STATE_3		3		/* Data Transfer */
+#define	ROSE_STATE_4		4		/* Awaiting Reset Confirmation */
+
+#define ROSE_DEFAULT_T0		(180 * PR_SLOWHZ)	/* Default T10 T20 value */
+#define ROSE_DEFAULT_T1		(200 * PR_SLOWHZ)	/* Default T11 T21 value */
+#define ROSE_DEFAULT_T2		(180 * PR_SLOWHZ)	/* Default T12 T22 value */
+#define	ROSE_DEFAULT_T3		(180 * PR_SLOWHZ)	/* Default T13 T23 value */
+#define	ROSE_DEFAULT_IDLE	(20 * 60 * PR_SLOWHZ)	/* Default No Activity value */
+#define	ROSE_DEFAULT_WINDOW	2			/* Default Window Size	*/
+#define ROSE_MODULUS 		8
+#define ROSE_MAX_WINDOW_SIZE	7			/* Maximum Window Allowable */
+#define	ROSE_PACLEN		128			/* Default Packet Length */
+
+#define	FAC_NATIONAL		0x00
+#define	FAC_CCITT		0x0F
+
+#define	FAC_NATIONAL_RAND	0x7F
+#define	FAC_NATIONAL_FLAGS	0x3F
+#define	FAC_NATIONAL_DEST_DIGI	0xE9
+#define	FAC_NATIONAL_SRC_DIGI	0xEB
+
+#define	FAC_CCITT_DEST_NSAP	0xC9
+#define	FAC_CCITT_SRC_NSAP	0xCB
+
+struct rose_neigh {
+	struct rose_neigh *next;
+	ax25_address      callsign;
+	ax25_digi         *digipeat;
+	struct device     *dev;
+	unsigned short    count;
+	unsigned int      number;
+	int               restarted;
+	struct sk_buff_head queue;
+	unsigned short    t0, t0timer;
+	struct timer_list timer;
+};
+
+struct rose_node {
+	struct rose_node  *next;
+	rose_address      address;
+	unsigned char     which;
+	unsigned char     count;
+	struct rose_neigh *neighbour[3];
+};
+
+struct rose_route {
+	struct rose_route *next;
+	unsigned int	  lci1,    lci2;
+	struct rose_neigh *neigh1, *neigh2;
+	unsigned int      rand;
+};
+
+typedef struct {
+	rose_address		source_addr,   dest_addr;
+	ax25_address		source_call,   dest_call;
+	unsigned char		source_ndigis, dest_ndigis;
+	ax25_address		source_digi,   dest_digi;
+	struct rose_neigh	*neighbour;
+	struct device		*device;
+	unsigned int		lci, rand;
+	unsigned char		state, condition, hdrincl;
+	unsigned short		vs, vr, va, vl;
+	unsigned short		timer;
+	unsigned short		t1, t2, t3, idle;
+	unsigned short		fraglen;
+	struct sk_buff_head	ack_queue;
+	struct sk_buff_head	frag_queue;
+	struct sock		*sk;		/* Backlink to socket */
+} rose_cb;
+
+/* af_rose.c */
+extern int  sysctl_rose_restart_request_timeout;
+extern int  sysctl_rose_call_request_timeout;
+extern int  sysctl_rose_reset_request_timeout;
+extern int  sysctl_rose_clear_request_timeout;
+extern int  sysctl_rose_no_activity_timeout;
+extern int  sysctl_rose_routing_control;
+extern int  rosecmp(rose_address *, rose_address *);
+extern char *rose2asc(rose_address *);
+extern struct sock *rose_find_socket(unsigned int, struct device *);
+extern unsigned int rose_new_lci(struct device *);
+extern int  rose_rx_call_request(struct sk_buff *, struct device *, struct rose_neigh *, unsigned int);
+extern void rose_destroy_socket(struct sock *);
+
+/* rose_dev.c */
+extern int  rose_rx_ip(struct sk_buff *, struct device *);
+extern int  rose_init(struct device *);
+
+#include <net/rosecall.h>
+
+/* rose_in.c */
+extern int  rose_process_rx_frame(struct sock *, struct sk_buff *);
+
+/* rose_link.c */
+extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short);
+extern void rose_transmit_restart_request(struct rose_neigh *);
+extern void rose_transmit_restart_confirmation(struct rose_neigh *);
+extern void rose_transmit_diagnostic(struct rose_neigh *, unsigned char);
+extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char);
+extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
+
+/* rose_out.c */
+extern void rose_output(struct sock *, struct sk_buff *);
+extern void rose_kick(struct sock *);
+extern void rose_enquiry_response(struct sock *);
+extern void rose_check_iframes_acked(struct sock *, unsigned short);
+
+/* rose_route.c */
+extern void rose_rt_device_down(struct device *);
+extern void rose_link_device_down(struct device *);
+extern struct device *rose_dev_first(void);
+extern struct device *rose_dev_get(rose_address *);
+extern struct device *rose_ax25_dev_get(char *);
+extern struct rose_neigh *rose_get_neigh(rose_address *);
+extern int  rose_rt_ioctl(unsigned int, void *);
+extern void rose_link_failed(ax25_address *, struct device *);
+extern int  rose_route_frame(struct sk_buff *, ax25_cb *);
+extern int  rose_nodes_get_info(char *, char **, off_t, int, int);
+extern int  rose_neigh_get_info(char *, char **, off_t, int, int);
+extern int  rose_routes_get_info(char *, char **, off_t, int, int);
+extern void rose_rt_free(void);
+
+/* rose_subr.c */
+extern void rose_clear_queues(struct sock *);
+extern void rose_frames_acked(struct sock *, unsigned short);
+extern void rose_requeue_frames(struct sock *);
+extern int  rose_validate_nr(struct sock *, unsigned short);
+extern void rose_write_internal(struct sock *, int);
+extern int  rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
+extern int  rose_parse_facilities(struct sk_buff *, rose_cb *);
+extern int  rose_create_facilities(unsigned char *, rose_cb *);
+
+/* rose_timer.c */
+extern void rose_set_timer(struct sock *);
+
+/* sysctl_net_rose.c */
+extern void rose_register_sysctl(void);
+extern void rose_unregister_sysctl(void);
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/include/net/rosecall.h linux/include/net/rosecall.h
--- v2.1.8/linux/include/net/rosecall.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/rosecall.h	Sun Nov 10 19:12:57 1996
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void rose_proto_init(struct net_proto *pro);
diff -u --recursive --new-file v2.1.8/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.8/linux/include/net/sock.h	Sun Nov 10 20:12:17 1996
+++ linux/include/net/sock.h	Tue Nov 12 15:17:03 1996
@@ -49,11 +49,14 @@
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>	/* struct sk_buff */
 #include <net/protocol.h>		/* struct inet_protocol */
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <net/netrom.h>
 #endif
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <net/rose.h>
+#endif
 #endif
 
 #if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
@@ -403,12 +406,6 @@
 	int			sndbuf;
 	unsigned short		type;
 	unsigned char		localroute;	/* Route locally only */
-#ifdef CONFIG_AX25
-	ax25_cb			*ax25;
-#ifdef CONFIG_NETROM
-	nr_cb			*nr;
-#endif
-#endif
   
 /*
  *	This is where all the private (optional) areas that don't
@@ -429,6 +426,15 @@
 #ifdef CONFIG_NUTCP		
 		struct tcp_opt		af_tcp;
 #endif		
+#endif
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+		ax25_cb			*ax25;
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+		nr_cb			*nr;
+#endif
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+		rose_cb			*rose;
+#endif
 #endif
 	} protinfo;  		
 
diff -u --recursive --new-file v2.1.8/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.8/linux/include/net/tcp.h	Sun Nov 10 20:12:17 1996
+++ linux/include/net/tcp.h	Tue Nov 12 15:20:20 1996
@@ -371,33 +371,18 @@
 
 extern struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX];
  
-/*
- *      This function returns the amount that we can raise the
- *      usable window based on the following constraints
- *  
- *	1. The window can never be shrunk once it is offered (RFC 793)
- *	2. We limit memory per socket
- */
-
 static __inline__ unsigned short tcp_raise_window(struct sock *sk)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	long free_space = sock_rspace(sk);
-	long window;
-
-	if (free_space > 1024)
-		free_space &= ~0x3FF; 
-
-	if(sk->window_clamp)
-		free_space = min(sk->window_clamp, free_space);
- 
+	long cur_win;
+	int res = 0;
+	
 	/* 
          * compute the actual window i.e. 
-         * old_window - received_bytes_on_that_win 
+         * old_window - received_bytes_on_that_win
 	 */
 
-
-	window = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup);
+	cur_win = tp->rcv_wup + tp->rcv_wnd - tp->rcv_nxt;
 
 
 	/*
@@ -406,32 +391,42 @@
 	 *	we have more free space to offer.
 	 */
 
-	if (window < (sk->mss << 1) && free_space > window)
-		return 1;
-
-	return 0;
+	if (cur_win < (sk->mss << 1))
+		res = 1;
+	return res;
 }
 
+/*
+ *      This function returns the amount that we can raise the
+ *      usable window based on the following constraints
+ *  
+ *	1. The window can never be shrunk once it is offered (RFC 793)
+ *	2. We limit memory per socket
+ */
+
+
 static __inline__ unsigned short tcp_select_window(struct sock *sk)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	long free_space = sock_rspace(sk);
 	long window;
+	long cur_win;
+	long usable;
 	
 	if (sk->window_clamp)
 		free_space = min(sk->window_clamp, free_space);
 	
-
 	/*
 	 * compute the actual window i.e.
 	 * old_window - received_bytes_on_that_win
 	 */
 
-	window = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup);
-
-	if ( window < 0 )
+	cur_win = tp->rcv_wup + tp->rcv_wnd - tp->rcv_nxt;
+	window  = tp->rcv_wnd;
+	
+	if ( cur_win < 0 )
 	{
-		window = 0;
+		cur_win = 0;
 		printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n",
 		       tp->rcv_wnd, tp->rcv_nxt, tp->rcv_wup);
 	}
@@ -450,43 +445,32 @@
 	 * It would be a good idea if it didn't break header prediction.
 	 * and BSD made the header predition standard...
 	 * It expects the same value in the header i.e. th->window to be
-	 * constant [in fact it's a good idea but they could document it
-	 * couldn't they ?] [PR].
-	 */
-	
-	/*
-	 *  If the actual window is blocking the sender then try
-	 *  to raise it.
+	 * constant
 	 */
-	
-	if (window < (sk->mss << 1))
+
+	if (tp->rcv_wnd >= free_space)
 	{
-		long usable;
+		if (cur_win > (sk->mss << 1))
+			goto out;
+	}
 
-		usable = free_space - window;
+	usable = free_space - cur_win;
 
-		if (usable < 0)
-		{
-			/* shouldn't happen */
-			usable = 0;
-		}
+#define WROUND(X, Y) ((X + (Y-1)) & (Y-1))
+		
+	window += WROUND(min(usable, sk->mss), 1024);
 
-		tp->rcv_wnd += (min(usable, sk->mss) + 0x3FF) & ~0x3FF;
-	}
+#undef WROUND
 
-#if 0
-	if (tp->rcv_wnd > free_space)
-	{
-		tp->rcv_wnd = free_space & ~0x3FF;
-	}
-#endif
-	if (tp->rcv_wnd < window)
+	if (window < cur_win)
 	{
-		tp->rcv_wnd = (window + 0x3FF) & ~0x3FF;
+		window = cur_win;
 	}
 
+  out:	
+	tp->rcv_wnd = window;
 	tp->rcv_wup = tp->rcv_nxt;
-	return tp->rcv_wnd;
+	return window;
 }
 
 /*
@@ -594,28 +578,14 @@
 
 }
 
+extern void __tcp_inc_slow_timer(struct tcp_sl_timer *slt);
 extern __inline__ void tcp_inc_slow_timer(int timer)
 {
 	struct tcp_sl_timer *slt = &tcp_slt_array[timer];
 	
 	if (slt->count == 0)
 	{
-		unsigned long now = jiffies;
-		unsigned long when;
-		unsigned long next;
-
-		slt->last = now;
-		
-		when = now + slt->period;
-		next = del_timer(&tcp_slow_timer);
-
-		if (next && ((long)(next - when) < 0))
-		{
-			when = next;
-		}
-		
-		tcp_slow_timer.expires = when;
-		add_timer(&tcp_slow_timer);
+		__tcp_inc_slow_timer(slt);
 	}
 
 	atomic_inc(&slt->count);
diff -u --recursive --new-file v2.1.8/linux/init/main.c linux/init/main.c
--- v2.1.8/linux/init/main.c	Sun Nov 10 20:12:18 1996
+++ linux/init/main.c	Tue Nov 12 13:08:43 1996
@@ -220,9 +220,6 @@
 static char * argv_rc[] = { "/bin/sh", NULL };
 static char * envp_rc[] = { "HOME=/", "TERM=linux", NULL };
 
-static char * argv[] = { "-/bin/sh",NULL };
-static char * envp[] = { "HOME=/usr/root", "TERM=linux", NULL };
-
 char *get_options(char *str, int *ints)
 {
 	char *cur = str;
@@ -882,12 +879,16 @@
 
 static int do_shell(void * shell)
 {
+	char *argv[2];
+
 	close(0);close(1);close(2);
 	setsid();
 	(void) open("/dev/tty1",O_RDWR,0);
 	(void) dup(0);
 	(void) dup(0);
-	return execve(shell, argv, envp);
+	argv[0] = shell;
+	argv[1] = NULL;
+	return execve(shell, argv, envp_rc);
 }
 
 #ifdef CONFIG_BLK_DEV_INITRD
diff -u --recursive --new-file v2.1.8/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.8/linux/kernel/ksyms.c	Sun Nov 10 20:12:19 1996
+++ linux/kernel/ksyms.c	Tue Nov 12 10:32:50 1996
@@ -15,6 +15,8 @@
 #include <linux/smp.h>
 #include <linux/fs.h>
 #include <linux/blkdev.h>
+#include <linux/cdrom.h>
+#include <linux/ucdrom.h>
 #include <linux/sched.h>
 #include <linux/kernel_stat.h>
 #include <linux/mm.h>
@@ -171,6 +173,14 @@
 	X(tty_unregister_driver),
 	X(tty_std_termios),
 
+#if defined(CONFIG_BLK_DEV_IDECD) || \
+    defined(CONFIG_BLK_DEV_SR) || \
+    defined(CONFIG_CM206)
+       X(register_cdrom),
+       X(unregister_cdrom),
+       X(cdrom_fops),
+#endif
+ 
 	/* block device driver support */
 	X(block_read),
 	X(block_write),
diff -u --recursive --new-file v2.1.8/linux/kernel/time.c linux/kernel/time.c
--- v2.1.8/linux/kernel/time.c	Tue Oct 29 19:58:48 1996
+++ linux/kernel/time.c	Mon Nov 11 15:38:07 1996
@@ -34,6 +34,24 @@
  */
 struct timezone sys_tz = { 0, 0};
 
+static void do_normal_gettime(struct timeval * tm)
+{
+        *tm=xtime;
+}
+
+void (*do_get_fast_time)(struct timeval *) = do_normal_gettime;
+
+/*
+ * Generic way to access 'xtime' (the current time of day).
+ * This can be changed if the platform provides a more accurate (and fast!) 
+ * version.
+ */
+
+void get_fast_time(struct timeval * t)
+{
+	do_get_fast_time(t);
+}
+
 #ifndef __alpha__
 
 /*
diff -u --recursive --new-file v2.1.8/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.8/linux/mm/filemap.c	Tue Oct 29 19:58:48 1996
+++ linux/mm/filemap.c	Tue Nov 12 14:22:09 1996
@@ -751,12 +751,12 @@
 	filp->f_reada = 1;
 	if (page_cache)
 		free_page(page_cache);
-	if (!read)
-		return error;
 	if (!IS_RDONLY(inode)) {
 		inode->i_atime = CURRENT_TIME;
 		inode->i_dirt = 1;
 	}
+	if (!read)
+		read = error;
 	return read;
 }
 
diff -u --recursive --new-file v2.1.8/linux/net/Config.in linux/net/Config.in
--- v2.1.8/linux/net/Config.in	Sun Nov 10 20:12:19 1996
+++ linux/net/Config.in	Sun Nov 10 19:12:57 1996
@@ -20,10 +20,10 @@
   bool 'Full internal IPX network' CONFIG_IPX_INTERN
 fi
 tristate 'Appletalk DDP' CONFIG_ATALK
-bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
-if [ "$CONFIG_AX25" = "y" ]; then
-  bool 'AX.25 over Ethernet' CONFIG_BPQETHER
-  bool 'Amateur Radio NET/ROM' CONFIG_NETROM
+tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
+if [ "$CONFIG_AX25" != "n" ]; then
+  dep_tristate 'Amateur Radio NET/ROM' CONFIG_NETROM $CONFIG_AX25
+  dep_tristate 'Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
diff -u --recursive --new-file v2.1.8/linux/net/Makefile linux/net/Makefile
--- v2.1.8/linux/net/Makefile	Sun Nov 10 20:12:23 1996
+++ linux/net/Makefile	Sun Nov 10 19:12:57 1996
@@ -9,13 +9,13 @@
 
 MOD_SUB_DIRS := ipv4
 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
-		netrom #decnet
+		netrom rose #decnet
 SUB_DIRS     := core ethernet unix
 MOD_LIST_NAME := NET_MISC_MODULES
 
 ifeq ($(CONFIG_NET),y)
 SUB_DIRS += 802
-endif 
+endif
 
 ifeq ($(CONFIG_INET),y)
 SUB_DIRS += ipv4
@@ -51,10 +51,26 @@
 
 ifeq ($(CONFIG_NETROM),y)
 SUB_DIRS += netrom
+else
+  ifeq ($(CONFIG_NETROM),m)
+  MOD_SUB_DIRS += netrom
+  endif
+endif
+
+ifeq ($(CONFIG_ROSE),y)
+SUB_DIRS += rose
+else
+  ifeq ($(CONFIG_ROSE),m)
+  MOD_SUB_DIRS += rose
+  endif
 endif
 
 ifeq ($(CONFIG_AX25),y)
 SUB_DIRS += ax25
+else
+  ifeq ($(CONFIG_AX25),m)
+  MOD_SUB_DIRS += ax25
+  endif
 endif
 
 L_TARGET     := network.a
diff -u --recursive --new-file v2.1.8/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.8/linux/net/appletalk/ddp.c	Tue Oct 29 19:58:48 1996
+++ linux/net/appletalk/ddp.c	Mon Nov 11 17:48:41 1996
@@ -1084,7 +1084,7 @@
 	err=verify_area(VERIFY_WRITE,optval,sizeof(int));
 	if (err) 
 		return err;
-	put_user(val,optval);
+	put_user(val, (int *)optval);
 	return(0);
 }
 
diff -u --recursive --new-file v2.1.8/linux/net/ax25/Makefile linux/net/ax25/Makefile
--- v2.1.8/linux/net/ax25/Makefile	Tue Apr  2 08:43:08 1996
+++ linux/net/ax25/Makefile	Sun Nov 10 19:12:57 1996
@@ -1,5 +1,5 @@
 #
-# Makefile for the Linux TCP/IP (INET) layer.
+# Makefile for the Linux AX.25 layer.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -9,11 +9,10 @@
 
 
 O_TARGET := ax25.o
-O_OBJS	 := af_ax25.o sysctl_net_ax25.o
+O_OBJS	 := sysctl_net_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
+M_OBJS   := $(O_TARGET)
 
-ifdef CONFIG_AX25
-O_OBJS	+= ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
-endif
+OX_OBJS  += af_ax25.o
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.8/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.8/linux/net/ax25/af_ax25.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ax25/af_ax25.c	Mon Nov 11 17:48:05 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 032
+ *	AX.25 release 033
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -81,7 +81,9 @@
  *      AX.25 032	Joerg(DL1BKE)		Fixed DAMA timeout error.
  *						ax25_send_frame() limits the number of enqueued
  *						datagrams per socket.
- *			Jonathan(G4KLX)		Remove auto-router.
+ *	AX.25 033	Jonathan(G4KLX)		Removed auto-router.
+ *			Hans(PE1AYX)		Converted to Module.
+ *			Joerg(DL1BKE)		Moved BPQ Ethernet to seperate driver.
  *
  *	To do:
  *		Restructure the ax25_rcv code to be cleaner/faster and
@@ -90,7 +92,8 @@
  */
  
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -117,7 +120,7 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/firewall.h>
-
+#include <linux/sysctl.h>
 #include <net/ip.h>
 #include <net/arp.h>
 
@@ -162,6 +165,42 @@
 }
 
 /*
+ *	ascii -> ax25 conversion
+ */
+ax25_address *asc2ax(char *callsign)
+{
+	static ax25_address addr;
+	char *s;
+	int n;
+
+	for (s = callsign, n = 0; n < 6; n++) {
+		if (*s != '\0' && *s != '-')
+			addr.ax25_call[n] = *s++;
+		else
+			addr.ax25_call[n] = ' ';
+		addr.ax25_call[n] <<= 1;
+		addr.ax25_call[n] &= 0xFE;
+	}
+
+	if (*s++ == '\0') {
+		addr.ax25_call[6] = 0x00;
+		return &addr;
+	}
+
+	addr.ax25_call[6] = *s++ - '0';
+	
+	if (*s != '\0') {
+		addr.ax25_call[6] *= 10;
+		addr.ax25_call[6] += *s++ - '0';
+	}
+
+	addr.ax25_call[6] <<= 1;
+	addr.ax25_call[6] &= 0x1E;
+
+	return &addr;
+}
+
+/*
  *	Compare two ax.25 addresses
  */
 int ax25cmp(ax25_address *a, ax25_address *b)
@@ -181,6 +220,22 @@
 }
 
 /*
+ *	Free an allocated ax25 control block. This is done to centralise
+ *	the MOD count code.
+ */
+static void ax25_free_cb(ax25_cb *ax25)
+{
+	if (ax25->digipeat != NULL) {
+		kfree_s(ax25->digipeat, sizeof(ax25_digi));
+		ax25->digipeat = NULL;
+	}
+	
+	kfree_s(ax25, sizeof(ax25_cb));
+
+	MOD_DEC_USE_COUNT;
+}
+
+/*
  *	Socket removal during an interrupt is now safe.
  */
 static void ax25_remove_socket(ax25_cb *ax25)
@@ -239,6 +294,10 @@
 {
 	struct device *dev = (struct device *)ptr;
 
+	/* Reject non AX.25 devices */
+	if (dev->type != ARPHRD_AX25)
+		return NOTIFY_DONE;
+
 	switch (event) {
 		case NETDEV_UP:
 			ax25_dev_device_up(dev);
@@ -425,8 +484,8 @@
 		while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) {
 			if (skb->sk != ax25->sk) {			/* A pending connection */
 				skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
-				ax25_set_timer(skb->sk->ax25);
-				skb->sk->ax25->state = AX25_STATE_0;
+				ax25_set_timer(skb->sk->protinfo.ax25);
+				skb->sk->protinfo.ax25->state = AX25_STATE_0;
 			}
 
 			kfree_skb(skb, FREE_READ);
@@ -441,20 +500,11 @@
 			ax25->timer.data     = (unsigned long)ax25;
 			add_timer(&ax25->timer);
 		} else {
-			if (ax25->digipeat != NULL) {
-				kfree_s(ax25->digipeat, sizeof(ax25_digi));
-				ax25->digipeat = NULL;
-			}
-		
 			sk_free(ax25->sk);
-			kfree_s(ax25, sizeof(*ax25));
+			ax25_free_cb(ax25);
 		}
 	} else {
-		if (ax25->digipeat != NULL) {
-			kfree_s(ax25->digipeat, sizeof(ax25_digi));
-			ax25->digipeat = NULL;
-		}	
-		kfree_s(ax25, sizeof(*ax25));
+		ax25_free_cb(ax25);
 	}
 
 	restore_flags(flags);
@@ -493,10 +543,12 @@
 			return -ENOENT;
 
 		case SIOCAX25ADDUID:
-			if(!suser())
+			if (!suser())
 				return -EPERM;
 			if (ax25_findbyuid(sax->sax25_uid))
 				return -EEXIST;
+			if (sax->sax25_uid == 0)
+				return -EINVAL;
 			a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL);
 			if (a == NULL)
 				return -ENOMEM;
@@ -509,7 +561,7 @@
 		case SIOCAX25DELUID: {
 			ax25_uid_assoc **l;
 			
-			if(!suser())
+			if (!suser())
 				return -EPERM;
 			l = &ax25_uid_list;
 			while ((*l) != NULL) {
@@ -558,9 +610,6 @@
 
 	switch (ax25_ctl.cmd) {
 		case AX25_KILL:
-#ifdef CONFIG_NETROM
-			nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
 			ax25_clear_queues(ax25);
 			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
 				
@@ -646,7 +695,7 @@
 	  		ax25->paclen = ax25_ctl.arg;
 	  		break;
 
-	  	case AX25_IPMAXQUEUE:
+	  	case AX25_MAXQUEUE:
 	  		if (ax25_ctl.arg < 1)
 	  			return -EINVAL;
 	  		ax25->maxqueue = ax25_ctl.arg;
@@ -669,6 +718,8 @@
 	if ((ax25 = (ax25_cb *)kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
 		return NULL;
 
+	MOD_INC_USE_COUNT;
+
 	skb_queue_head_init(&ax25->write_queue);
 	skb_queue_head_init(&ax25->frag_queue);
 	skb_queue_head_init(&ax25->ack_queue);
@@ -678,20 +729,27 @@
 
 	ax25->dama_slave = 0;
 
-	ax25->rtt     = (AX25_DEF_T1 * PR_SLOWHZ) / 2;
-	ax25->t1      = AX25_DEF_T1 * PR_SLOWHZ;
-	ax25->t2      = AX25_DEF_T2 * PR_SLOWHZ;
-	ax25->t3      = AX25_DEF_T3 * PR_SLOWHZ;
+	ax25->rtt     = AX25_DEF_T1 / 2;
+	ax25->t1      = AX25_DEF_T1;
+	ax25->t2      = AX25_DEF_T2;
+	ax25->t3      = AX25_DEF_T3;
 	ax25->n2      = AX25_DEF_N2;
 	ax25->paclen  = AX25_DEF_PACLEN;
-	ax25->maxqueue= AX25_DEF_IPMAXQUEUE;
+	ax25->maxqueue= AX25_DEF_MAXQUEUE;
 	ax25->idle    = AX25_DEF_IDLE;
 
-	ax25->modulus   = AX25_DEF_AXDEFMODE;
+	if (AX25_DEF_AXDEFMODE) {
+		ax25->modulus = EMODULUS;
+		ax25->window  = AX25_DEF_EWINDOW;
+	} else {
+		ax25->modulus = MODULUS;
+		ax25->window  = AX25_DEF_WINDOW;
+	}
+
 	ax25->fragno    = 0;
 	ax25->fraglen   = 0;
 	ax25->hdrincl   = 0;
-	ax25->backoff   = AX25_DEF_BACKOFF == 'E';
+	ax25->backoff   = AX25_DEF_BACKOFF;
 	ax25->condition = 0x00;
 	ax25->t1timer   = 0;
 	ax25->t2timer   = 0;
@@ -703,12 +761,6 @@
 	ax25->vr      = 0;
 	ax25->vs      = 0;
 
-	if (AX25_DEF_AXDEFMODE == EMODULUS) {
-		ax25->window = AX25_DEF_EWINDOW;
-	} else {
-		ax25->window = AX25_DEF_WINDOW;
-	}
-
 	ax25->device   = NULL;
 	ax25->digipeat = NULL;
 	ax25->sk       = NULL;
@@ -756,20 +808,20 @@
 	ax25->t3       = ax25_dev_get_value(dev, AX25_VALUES_T3);
 	ax25->n2       = ax25_dev_get_value(dev, AX25_VALUES_N2);
 	ax25->paclen   = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
-	ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_IPMAXQUEUE);
+	ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_MAXQUEUE);
 	ax25->idle     = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
 
 	ax25->dama_slave = 0;
 
-	ax25->modulus = ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE);
-
-	if (ax25->modulus == MODULUS) {
-		ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+	if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) {
+		ax25->modulus = EMODULUS;
+		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
 	} else {
-		ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+		ax25->modulus = MODULUS;
+		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
 	}
 
-	ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF) == 'E';
+	ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF);
 }
 
 int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest,
@@ -808,7 +860,7 @@
 
 	if (digi != NULL) {
 		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-			kfree_s(ax25, sizeof(ax25));
+			ax25_free_cb(ax25);
 			return 0;
 		}
 		*ax25->digipeat = *digi;
@@ -837,34 +889,35 @@
 }
 
 /*
+ *	Return the state of an AX.25 link given source, destination, and
+ *	device.
+ */
+int ax25_link_up(ax25_address *src, ax25_address *dest, struct device *dev)
+{
+	ax25_cb *ax25;
+
+	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
+		if (ax25->sk != NULL && ax25->sk->type != SOCK_SEQPACKET)
+			continue;
+
+		if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev)
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
  *	Find the AX.25 device that matches the hardware address supplied.
  */
 struct device *ax25rtr_get_dev(ax25_address *addr)
 {
 	struct device *dev;
 	
-	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (dev->flags & IFF_UP) {
-			switch (dev->type) {
-				case ARPHRD_AX25: /* Active kiss ax25 mode */ 
-					if (ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
-						return dev;
-					break;
-#ifdef CONFIG_BPQETHER
-				case ARPHRD_ETHER: {
-						ax25_address *dev_addr;
-
-						if ((dev_addr = ax25_bpq_get_addr(dev)) != NULL)
-							if (ax25cmp(addr, dev_addr) == 0)
-								return dev;
-					}
-					break;
-#endif
-				default:
-					break;
-			}
-		}
-	}
+	for (dev = dev_base; dev != NULL; dev = dev->next)
+		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25 &&
+		    ax25cmp(addr, (ax25_address*) dev->dev_addr) == 0)
+		     	return dev;
 
 	return NULL;
 }
@@ -873,7 +926,6 @@
  *	Handling for system calls applied via the various interfaces to an
  *	AX25 socket object
  */
- 
 static int ax25_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	return -EINVAL;
@@ -899,66 +951,66 @@
 	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
 		return err;
 
-	opt = get_fs_long((unsigned long *)optval);
+	get_user(opt, (int *)optval);
 
 	switch (optname) {
 		case AX25_WINDOW:
-			if (sk->ax25->modulus == MODULUS) {
+			if (sk->protinfo.ax25->modulus == MODULUS) {
 				if (opt < 1 || opt > 7)
 					return -EINVAL;
 			} else {
 				if (opt < 1 || opt > 63)
 					return -EINVAL;
 			}
-			sk->ax25->window = opt;
+			sk->protinfo.ax25->window = opt;
 			return 0;
 
 		case AX25_T1:
 			if (opt < 1)
 				return -EINVAL;
-			sk->ax25->rtt = (opt * PR_SLOWHZ) / 2;
+			sk->protinfo.ax25->rtt = (opt * PR_SLOWHZ) / 2;
 			return 0;
 
 		case AX25_T2:
 			if (opt < 1)
 				return -EINVAL;
-			sk->ax25->t2 = opt * PR_SLOWHZ;
+			sk->protinfo.ax25->t2 = opt * PR_SLOWHZ;
 			return 0;
 
 		case AX25_N2:
 			if (opt < 1 || opt > 31)
 				return -EINVAL;
-			sk->ax25->n2 = opt;
+			sk->protinfo.ax25->n2 = opt;
 			return 0;
 
 		case AX25_T3:
 			if (opt < 1)
 				return -EINVAL;
-			sk->ax25->t3 = opt * PR_SLOWHZ;
+			sk->protinfo.ax25->t3 = opt * PR_SLOWHZ;
 			return 0;
 			
 		case AX25_IDLE:
 			if (opt < 0)
 				return -EINVAL;
-			sk->ax25->idle = opt * PR_SLOWHZ * 60;
+			sk->protinfo.ax25->idle = opt * PR_SLOWHZ * 60;
 			return 0;
 
 		case AX25_BACKOFF:
-			sk->ax25->backoff = opt ? 1 : 0;
+			sk->protinfo.ax25->backoff = opt ? 1 : 0;
 			return 0;
 
 		case AX25_EXTSEQ:
-			sk->ax25->modulus = opt ? EMODULUS : MODULUS;
+			sk->protinfo.ax25->modulus = opt ? EMODULUS : MODULUS;
 			return 0;
 
 		case AX25_HDRINCL:
-			sk->ax25->hdrincl = opt ? 1 : 0;
+			sk->protinfo.ax25->hdrincl = opt ? 1 : 0;
 			return 0;
 			
 		case AX25_PACLEN:
 			if (opt < 16 || opt > 65535)
 				return -EINVAL;
-			sk->ax25->paclen = opt;
+			sk->protinfo.ax25->paclen = opt;
 			return 0;
 
 		default:
@@ -983,43 +1035,43 @@
 
 	switch (optname) {
 		case AX25_WINDOW:
-			val = sk->ax25->window;
+			val = sk->protinfo.ax25->window;
 			break;
 
 		case AX25_T1:
-			val = (sk->ax25->t1 * 2) / PR_SLOWHZ;
+			val = (sk->protinfo.ax25->t1 * 2) / PR_SLOWHZ;
 			break;
 
 		case AX25_T2:
-			val = sk->ax25->t2 / PR_SLOWHZ;
+			val = sk->protinfo.ax25->t2 / PR_SLOWHZ;
 			break;
 
 		case AX25_N2:
-			val = sk->ax25->n2;
+			val = sk->protinfo.ax25->n2;
 			break;
 
 		case AX25_T3:
-			val = sk->ax25->t3 / PR_SLOWHZ;
+			val = sk->protinfo.ax25->t3 / PR_SLOWHZ;
 			break;
 			
 		case AX25_IDLE:
-			val = sk->ax25->idle / (PR_SLOWHZ * 60);
+			val = sk->protinfo.ax25->idle / (PR_SLOWHZ * 60);
 			break;
 
 		case AX25_BACKOFF:
-			val = sk->ax25->backoff;
+			val = sk->protinfo.ax25->backoff;
 			break;
 
 		case AX25_EXTSEQ:
-			val = (sk->ax25->modulus == EMODULUS);
+			val = (sk->protinfo.ax25->modulus == EMODULUS);
 			break;
 
 		case AX25_HDRINCL:
-			val = sk->ax25->hdrincl;
+			val = sk->protinfo.ax25->hdrincl;
 			break;
 			
 		case AX25_PACLEN:
-			val = sk->ax25->paclen;
+			val = sk->protinfo.ax25->paclen;
 			break;
 
 		default:
@@ -1034,7 +1086,7 @@
 	if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
 		return err;
 
-	put_user(val, optval);
+	put_user(val, (int *) optval);
 
 	return 0;
 }
@@ -1085,9 +1137,12 @@
 				case AX25_P_ARP:
 				case AX25_P_IP:
 #endif
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 				case AX25_P_NETROM:
 #endif
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+				case AX25_P_ROSE:
+#endif
 					return -ESOCKTNOSUPPORT;
 				default:
 					break;
@@ -1133,8 +1188,8 @@
 		sk->sleep  = sock->wait;
 	}
 
-	ax25->sk = sk;
-	sk->ax25 = ax25;
+	ax25->sk          = sk;
+	sk->protinfo.ax25 = ax25;
 	
 	return 0;
 }
@@ -1164,7 +1219,7 @@
 			break;
 		default:
 			sk_free(sk);
-			kfree_s((void *)ax25, sizeof(*ax25));
+			ax25_free_cb(ax25);
 			return NULL;
 	}
 
@@ -1189,34 +1244,34 @@
 	sk->write_space  = def_callback1;
 	sk->error_report = def_callback1;
 
-	ax25->modulus = osk->ax25->modulus;
-	ax25->backoff = osk->ax25->backoff;
-	ax25->hdrincl = osk->ax25->hdrincl;
-	ax25->rtt     = osk->ax25->rtt;
-	ax25->t1      = osk->ax25->t1;
-	ax25->t2      = osk->ax25->t2;
-	ax25->t3      = osk->ax25->t3;
-	ax25->n2      = osk->ax25->n2;
-	ax25->idle    = osk->ax25->idle;
-	ax25->paclen  = osk->ax25->paclen;
+	ax25->modulus = osk->protinfo.ax25->modulus;
+	ax25->backoff = osk->protinfo.ax25->backoff;
+	ax25->hdrincl = osk->protinfo.ax25->hdrincl;
+	ax25->rtt     = osk->protinfo.ax25->rtt;
+	ax25->t1      = osk->protinfo.ax25->t1;
+	ax25->t2      = osk->protinfo.ax25->t2;
+	ax25->t3      = osk->protinfo.ax25->t3;
+	ax25->n2      = osk->protinfo.ax25->n2;
+	ax25->idle    = osk->protinfo.ax25->idle;
+	ax25->paclen  = osk->protinfo.ax25->paclen;
 
-	ax25->window   = osk->ax25->window;
-	ax25->maxqueue = osk->ax25->maxqueue;
+	ax25->window   = osk->protinfo.ax25->window;
+	ax25->maxqueue = osk->protinfo.ax25->maxqueue;
 
-	ax25->source_addr = osk->ax25->source_addr;
+	ax25->source_addr = osk->protinfo.ax25->source_addr;
 	
-	if (osk->ax25->digipeat != NULL) {
+	if (osk->protinfo.ax25->digipeat != NULL) {
 		if ((ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
 			sk_free(sk);
-			kfree_s(ax25, sizeof(*ax25));
+			ax25_free_cb(ax25);
 			return NULL;
 		}
 		
-		*ax25->digipeat = *osk->ax25->digipeat;
+		*ax25->digipeat = *osk->protinfo.ax25->digipeat;
 	}
 
-	sk->ax25 = ax25;
-	ax25->sk = sk;
+	sk->protinfo.ax25 = ax25;
+	ax25->sk          = sk;
 
 	return sk;
 }
@@ -1235,51 +1290,51 @@
 	if (sk == NULL) return 0;
 
 	if (sk->type == SOCK_SEQPACKET) {
-		switch (sk->ax25->state) {
+		switch (sk->protinfo.ax25->state) {
 			case AX25_STATE_0:
 				sk->state       = TCP_CLOSE;
 				sk->state_change(sk);
 				sk->dead        = 1;
-				ax25_destroy_socket(sk->ax25);
+				ax25_destroy_socket(sk->protinfo.ax25);
 				break;
 
 			case AX25_STATE_1:
-				ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
-				sk->ax25->state = AX25_STATE_0;
-				sk->state       = TCP_CLOSE;
+				ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND);
+				sk->protinfo.ax25->state = AX25_STATE_0;
+				sk->state                = TCP_CLOSE;
 				sk->state_change(sk);
-				sk->dead        = 1;
-				ax25_destroy_socket(sk->ax25);
+				sk->dead                 = 1;
+				ax25_destroy_socket(sk->protinfo.ax25);
 				break;
 
 			case AX25_STATE_2:
-				if (sk->ax25->dama_slave)
-					ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
+				if (sk->protinfo.ax25->dama_slave)
+					ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND);
 				else
-					ax25_send_control(sk->ax25, DM, POLLON, C_RESPONSE);
-				sk->ax25->state = AX25_STATE_0;
-				sk->state       = TCP_CLOSE;
+					ax25_send_control(sk->protinfo.ax25, DM, POLLON, C_RESPONSE);
+				sk->protinfo.ax25->state = AX25_STATE_0;
+				sk->state                = TCP_CLOSE;
 				sk->state_change(sk);
-				sk->dead        = 1;
-				ax25_destroy_socket(sk->ax25);
+				sk->dead                 = 1;
+				ax25_destroy_socket(sk->protinfo.ax25);
 				break;			
 
 			case AX25_STATE_3:
 			case AX25_STATE_4:
-				ax25_clear_queues(sk->ax25);
-				sk->ax25->n2count = 0;
-				if (!sk->ax25->dama_slave) {
-					ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
-					sk->ax25->t3timer = 0;
+				ax25_clear_queues(sk->protinfo.ax25);
+				sk->protinfo.ax25->n2count = 0;
+				if (!sk->protinfo.ax25->dama_slave) {
+					ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND);
+					sk->protinfo.ax25->t3timer = 0;
 				} else {
-					sk->ax25->t3timer = sk->ax25->t3;	/* DAMA slave timeout */
+					sk->protinfo.ax25->t3timer = sk->protinfo.ax25->t3;	/* DAMA slave timeout */
 				}
-				sk->ax25->t1timer = sk->ax25->t1 = ax25_calculate_t1(sk->ax25);
-				sk->ax25->state   = AX25_STATE_2;
-				sk->state         = TCP_CLOSE;
+				sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25);
+				sk->protinfo.ax25->state   = AX25_STATE_2;
+				sk->state                  = TCP_CLOSE;
 				sk->state_change(sk);
-				sk->dead          = 1;
-				sk->destroy       = 1;
+				sk->dead                   = 1;
+				sk->destroy                = 1;
 				break;
 
 			default:
@@ -1289,7 +1344,7 @@
 		sk->state       = TCP_CLOSE;
 		sk->state_change(sk);
 		sk->dead = 1;
-		ax25_destroy_socket(sk->ax25);
+		ax25_destroy_socket(sk->protinfo.ax25);
 	}
 
 	sock->data = NULL;	
@@ -1324,12 +1379,12 @@
 		return -EPERM;
 		
 	if (call == NULL)
-		sk->ax25->source_addr = addr->fsa_ax25.sax25_call;
+		sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call;
 	else
-		sk->ax25->source_addr = *call;
+		sk->protinfo.ax25->source_addr = *call;
 
 	if (sk->debug)
-		printk("AX25: source address set to %s\n", ax2asc(&sk->ax25->source_addr));
+		printk("AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr));
 
 	if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
 		if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) {
@@ -1355,8 +1410,8 @@
 			printk("AX25: bound to device %s\n", dev->name);
 	}
 
-	ax25_fillin_cb(sk->ax25, dev);
-	ax25_insert_socket(sk->ax25);
+	ax25_fillin_cb(sk->protinfo.ax25, dev);
+	ax25_insert_socket(sk->protinfo.ax25);
 
 	sk->zapped = 0;
 
@@ -1403,20 +1458,20 @@
 		if (addr->sax25_ndigis < 1 || addr->sax25_ndigis > AX25_MAX_DIGIS)
 			return -EINVAL;
 
-		if (sk->ax25->digipeat == NULL) {
-			if ((sk->ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
+		if (sk->protinfo.ax25->digipeat == NULL) {
+			if ((sk->protinfo.ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
 				return -ENOMEM;
 		}
 
-		sk->ax25->digipeat->ndigi = addr->sax25_ndigis;
+		sk->protinfo.ax25->digipeat->ndigi = addr->sax25_ndigis;
 
 		while (ct < addr->sax25_ndigis) {
-			sk->ax25->digipeat->repeated[ct] = 0;
-			sk->ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct];
+			sk->protinfo.ax25->digipeat->repeated[ct] = 0;
+			sk->protinfo.ax25->digipeat->calls[ct]    = fsa->fsa_digipeater[ct];
 			ct++;
 		}
 
-		sk->ax25->digipeat->lastrepeat = 0;
+		sk->protinfo.ax25->digipeat->lastrepeat = 0;
 	}
 
 	/*
@@ -1425,19 +1480,19 @@
 	 *	been filled in, error if it hasn't.
 	 */
 	if (sk->zapped) {
-		if ((err = ax25_rt_autobind(sk->ax25, &addr->sax25_call)) < 0)
+		if ((err = ax25_rt_autobind(sk->protinfo.ax25, &addr->sax25_call)) < 0)
 			return err;
-		ax25_fillin_cb(sk->ax25, sk->ax25->device);
-		ax25_insert_socket(sk->ax25);
+		ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->device);
+		ax25_insert_socket(sk->protinfo.ax25);
 	} else {
-		if (sk->ax25->device == NULL)
+		if (sk->protinfo.ax25->device == NULL)
 			return -EHOSTUNREACH;
 	}
 		
-	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->ax25->source_addr, &addr->sax25_call, sk->ax25->device) != NULL)
+	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, sk->protinfo.ax25->device) != NULL)
 		return -EBUSY;				/* Already such a connection */
 
-	sk->ax25->dest_addr = addr->sax25_call;
+	sk->protinfo.ax25->dest_addr = addr->sax25_call;
 	
 	/* First the easy one */
 	if (sk->type != SOCK_SEQPACKET) {
@@ -1450,13 +1505,13 @@
 	sock->state        = SS_CONNECTING;
 	sk->state          = TCP_SYN_SENT;
 	
-	if (ax25_dev_is_dama_slave(sk->ax25->device))
-		dama_establish_data_link(sk->ax25);
+	if (ax25_dev_is_dama_slave(sk->protinfo.ax25->device))
+		dama_establish_data_link(sk->protinfo.ax25);
 	else
-		ax25_establish_data_link(sk->ax25);
+		ax25_establish_data_link(sk->protinfo.ax25);
 		
-	sk->ax25->state     = AX25_STATE_1;
-	ax25_set_timer(sk->ax25);		/* Start going SABM SABM until a UA or a give up and DM */
+	sk->protinfo.ax25->state = AX25_STATE_1;
+	ax25_set_timer(sk->protinfo.ax25);		/* Start going SABM SABM until a UA or a give up and DM */
 	
 	/* Now the loop */
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
@@ -1473,8 +1528,7 @@
 		}
 	}
 
-	if (sk->state != TCP_ESTABLISHED) 
-	{
+	if (sk->state != TCP_ESTABLISHED) {
 		/* Not in ABM, not in WAIT_UA -> failed */
 		sti();
 		sock->state = SS_UNCONNECTED;
@@ -1558,28 +1612,28 @@
 			return -ENOTCONN;
 
 		sax->fsa_ax25.sax25_family = AF_AX25;
-		sax->fsa_ax25.sax25_call   = sk->ax25->dest_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.ax25->dest_addr;
 		sax->fsa_ax25.sax25_ndigis = 0;
 		*uaddr_len = sizeof(struct full_sockaddr_ax25);
 
-		if (sk->ax25->digipeat != NULL) {
-			ndigi = sk->ax25->digipeat->ndigi;
+		if (sk->protinfo.ax25->digipeat != NULL) {
+			ndigi = sk->protinfo.ax25->digipeat->ndigi;
 			sax->fsa_ax25.sax25_ndigis = ndigi;
 			for (i = 0; i < ndigi; i++)
-				sax->fsa_digipeater[i] = sk->ax25->digipeat->calls[i];
+				sax->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i];
 		}
 	} else {
 		sax->fsa_ax25.sax25_family = AF_AX25;
-		sax->fsa_ax25.sax25_call   = sk->ax25->source_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.ax25->source_addr;
 		sax->fsa_ax25.sax25_ndigis = 1;
 		*uaddr_len = sizeof(struct full_sockaddr_ax25);
 
-		if (sk->ax25->device != NULL)
-			memcpy(&sax->fsa_digipeater[0], sk->ax25->device->dev_addr, AX25_ADDR_LEN);
+		if (sk->protinfo.ax25->device != NULL)
+			memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN);
 		else
 			sax->fsa_digipeater[0] = null_ax25_address;
 	}
-		
+	
 	return 0;
 }
  
@@ -1675,11 +1729,9 @@
 	if (ax25cmp(&dest, dev_addr) == 0)
 		mine = 1;
 
-#ifdef CONFIG_NETROM
-	/* Also match on any NET/ROM callsign */
-	if (!mine && nr_dev_get(&dest) != NULL)
+	/* Also match on any registered callsign from L3/4 */
+	if (!mine && ax25_listen_mine(&dest, dev))
 		mine = 1;
-#endif	
 	
 	if ((*skb->data & ~0x10) == LAPB_UI) {	/* UI frame - bypass LAPB processing */
 		skb->h.raw = skb->data + 2;		/* skip control and pid */
@@ -1787,7 +1839,7 @@
 			return 0;
 		}
 		
-		ax25 = make->ax25;
+		ax25 = make->protinfo.ax25;
 
 		skb_queue_head(&sk->receive_queue, skb);
 
@@ -1797,7 +1849,6 @@
 
 		sk->ack_backlog++;
 	} else {
-#ifdef CONFIG_NETROM
 		if (!mine) {
 			kfree_skb(skb, FREE_READ);
 			return 0;
@@ -1811,13 +1862,6 @@
 
 		ax25_fillin_cb(ax25, dev);
 		ax25->idletimer = ax25->idle;
-#else
-		if (mine)
-			ax25_return_dm(dev, &src, &dest, &dp);
-
-		kfree_skb(skb, FREE_READ);
-		return 0;
-#endif
 	}
 
 	ax25->source_addr = dest;
@@ -1891,30 +1935,6 @@
 	return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
 }
 
-#ifdef CONFIG_BPQETHER
-/*
- *	Receive an AX.25 frame via an Ethernet interface.
- */
-static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
-{
-	ax25_address *port_call;
-	int len;
-
-	skb->sk = NULL;		/* Initially we don't know who it's for */
-
-	if ((port_call = ax25_bpq_get_addr(dev)) == NULL) {
-		kfree_skb(skb, FREE_READ);	/* We have no port callsign */
-		return 0;
-	}
-
-	len = skb->data[0] + skb->data[1] * 256 - 5;
-
-	skb_pull(skb, 2);	/* Remove the length bytes */
-	skb_trim(skb, len);	/* Set the length of the data */
-
-	return ax25_rcv(skb, dev, port_call, ptype);
-}
-#endif
 
 static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
 {
@@ -1939,7 +1959,7 @@
 	if (sk->zapped)
 		return -EADDRNOTAVAIL;
 		
-	if (sk->ax25->device == NULL)
+	if (sk->protinfo.ax25->device == NULL)
 		return -ENETUNREACH;
 		
 	if (usax) {
@@ -1967,7 +1987,7 @@
 		}
 
 		sax = *usax;
-		if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->ax25->dest_addr, &sax.sax25_call) != 0)
+		if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0)
 			return -EISCONN;
 		if (usax->sax25_ndigis == 0)
 			dp = NULL;
@@ -1977,8 +1997,8 @@
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		sax.sax25_family = AF_AX25;
-		sax.sax25_call   = sk->ax25->dest_addr;
-		dp = sk->ax25->digipeat;
+		sax.sax25_call   = sk->protinfo.ax25->dest_addr;
+		dp = sk->protinfo.ax25->digipeat;
 	}
 	
 	if (sk->debug)
@@ -2020,7 +2040,7 @@
 			return -ENOTCONN;
 		}
 
-		ax25_output(sk->ax25, skb);	/* Shove it onto the queue and kick */
+		ax25_output(sk->protinfo.ax25, skb);	/* Shove it onto the queue and kick */
 
 		return len;
 	} else {
@@ -2033,7 +2053,7 @@
 		}
 
 		/* Build an AX.25 header */
-		asmptr += (lv = build_ax25_addr(asmptr, &sk->ax25->source_addr, &sax.sax25_call, dp, C_COMMAND, MODULUS));
+		asmptr += (lv = build_ax25_addr(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, C_COMMAND, MODULUS));
 
 		if (sk->debug)
 			printk("Built header (%d bytes)\n",lv);
@@ -2046,7 +2066,7 @@
 		*asmptr = LAPB_UI;
 
 		/* Datagram frames go straight out of the door as UI */
-		ax25_queue_xmit(skb, sk->ax25->device, SOPRI_NORMAL);
+		ax25_queue_xmit(skb, sk->protinfo.ax25->device, SOPRI_NORMAL);
 
 		return len;
 	}
@@ -2079,7 +2099,7 @@
 	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
 		return er;
 
-	if (sk->ax25->hdrincl) {
+	if (sk->protinfo.ax25->hdrincl) {
 		length = skb->len + (skb->data - skb->h.raw);
 	} else {
 		if (sk->type == SOCK_SEQPACKET)
@@ -2193,25 +2213,14 @@
 		case SIOCAX25NOUID:	/* Set the default policy (default/bar) */
 			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long))) != 0)
 				return err;
-			if(!suser())
+			if (!suser())
 				return -EPERM;
-			amount = get_fs_long((void *)arg);
+			get_user(amount, (long *)arg);
 			if (amount > AX25_NOUID_BLOCK)
 				return -EINVAL;
 			ax25_uid_policy = amount;
 			return 0;
 
-#ifdef CONFIG_BPQETHER
-		case SIOCAX25BPQADDR:
-			if (!suser())
-				return -EPERM;
-			return ax25_bpq_ioctl(cmd, (void *)arg);
-#endif
-
-		case SIOCAX25GETPARMS:
-		case SIOCAX25SETPARMS:
-			return ax25_dev_ioctl(cmd, (void *)arg);
-
 		case SIOCADDRT:
 		case SIOCDELRT:
 		case SIOCAX25OPTRT:
@@ -2339,7 +2348,6 @@
 /*
  *	Called by socket.c on kernel start up
  */
-
 static struct packet_type ax25_packet_type = 
 {
 	0,	/* MUTTER ntohs(ETH_P_AX25),*/
@@ -2349,22 +2357,34 @@
 	NULL,
 };
 
-#ifdef CONFIG_BPQETHER
-static struct packet_type bpq_packet_type = 
-{
-	0,	/* MUTTER ntohs(ETH_P_BPQ),*/
-	0,		/* copy */
-	bpq_rcv,
-	NULL,
-	NULL,
-};
-#endif
-
 static struct notifier_block ax25_dev_notifier = {
 	ax25_device_event,
 	0
 };
 
+static struct symbol_table ax25_syms = {
+#include <linux/symtab_begin.h>
+	X(ax25_encapsulate),
+	X(ax25_rebuild_header),
+#if defined(CONFIG_NETROM_MODULE) || defined(CONFIG_ROSE_MODULE)
+	X(ax25_findbyuid),
+	X(ax25_link_up),
+	X(ax25_linkfail_register),
+	X(ax25_linkfail_release),
+	X(ax25_listen_register),
+	X(ax25_listen_release),
+	X(ax25_protocol_register),
+	X(ax25_protocol_release),
+	X(ax25_send_frame),
+	X(ax25_uid_policy),
+	X(ax25cmp),
+	X(ax2asc),
+	X(asc2ax),
+	X(null_ax25_address),
+#endif
+#include <linux/symtab_end.h>
+};
+
 #ifdef CONFIG_PROC_FS			  
 static struct proc_dir_entry proc_ax25_route = {
 	PROC_NET_AX25_ROUTE, 10, "ax25_route",
@@ -2385,43 +2405,27 @@
 	ax25_cs_get_info
 };
 #endif
-#ifdef CONFIG_BPQETHER
-static struct proc_dir_entry proc_ax25_bpqether = {
-	PROC_NET_AX25_BPQETHER, 13, "ax25_bpqether",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_net_inode_operations,
-	ax25_bpq_get_info
-};
-#endif
 
 void ax25_proto_init(struct net_proto *pro)
 {
 	sock_register(ax25_proto_ops.family, &ax25_proto_ops);
 	ax25_packet_type.type = htons(ETH_P_AX25);
 	dev_add_pack(&ax25_packet_type);	
-#ifdef CONFIG_BPQETHER
-	bpq_packet_type.type  = htons(ETH_P_BPQ);
-	dev_add_pack(&bpq_packet_type);
-#endif
 	register_netdevice_notifier(&ax25_dev_notifier);
+	register_symtab(&ax25_syms);
+	ax25_register_sysctl();
+
 #ifdef CONFIG_PROC_FS			  
 	proc_net_register(&proc_ax25_route);
 	proc_net_register(&proc_ax25);
 	proc_net_register(&proc_ax25_calls);
 #endif	
 
-	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.32 for Linux NET3.035 (Linux 2.0)\n");
-
-#ifdef CONFIG_BPQETHER
-	proc_net_register(&proc_ax25_bpqether);
-
-	printk(KERN_INFO "G8BPQ Encapsulation of AX.25 frames enabled\n");
-#endif
+	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.33 for Linux NET3.035 (Linux 2.0)\n");
 }
 
 /*
- *	A small shim to dev_queue_xmit to handle the difference between
- *	KISS AX.25 and BPQ AX.25.
+ *	A small shim to dev_queue_xmit to add the KISS control byte.
  */
 void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
@@ -2434,31 +2438,7 @@
 	}
 #endif	
 
-	skb->protocol = htons (ETH_P_AX25);
-
-#ifdef CONFIG_BPQETHER
-	if (dev->type == ARPHRD_ETHER) {
-		static char bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
-		int size;
-
-		if(skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {
-			printk(KERN_CRIT "ax25_queue_xmit: not enough space to add BPQ Ether header\n");
-			dev_kfree_skb(skb, FREE_WRITE);
-			return;
-		}
-
-		size = skb->len;
-	
-		ptr = skb_push(skb, 2);
-
-		*ptr++ = (size + 5) % 256;
-		*ptr++ = (size + 5) / 256;
-
-		dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
-		dev_queue_xmit(skb, dev, pri);
-		return;
-	} 
-#endif
+	skb->protocol = htons(ETH_P_AX25);
 
 	ptr = skb_push(skb, 1);
 	*ptr++ = 0;			/* KISS */
@@ -2513,7 +2493,7 @@
   			*buff++ = AX25_P_ARP;
   			break;
   		default:
-  			printk(KERN_ERR "wrong protocol type 0x%x2.2\n", type);
+  			printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type);
   			*buff++ = 0;
   			break;
  	}
@@ -2534,7 +2514,7 @@
 
 	if (bp[16] == AX25_P_IP) {
 		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
-		if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) {
+		if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE))) {
 			/*
 			 *	This is a workaround to try to keep the device locking
 			 *	straight until skb->free=0 is abolished post 1.4.
@@ -2586,6 +2566,35 @@
   	return 1;
 }	
 
+#endif
+
+#ifdef MODULE
+int init_module(void)
+{
+	ax25_proto_init(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_AX25_ROUTE);
+	proc_net_unregister(PROC_NET_AX25);
+	proc_net_unregister(PROC_NET_AX25_CALLS);
+	proc_net_unregister(PROC_NET_AX25_ROUTE);
+#endif
+	ax25_rt_free();
+
+	ax25_unregister_sysctl();
+
+	unregister_netdevice_notifier(&ax25_dev_notifier);
+
+	ax25_packet_type.type = htons(ETH_P_AX25);
+	dev_remove_pack(&ax25_packet_type);	
+
+	sock_unregister(ax25_proto_ops.family);
+}
 #endif
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v2.1.8/linux/net/ax25/ax25_in.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ax25/ax25_in.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 032
+ *	AX.25 release 033
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -32,12 +32,13 @@
  *			Joerg(DL1BKE)   Renamed it to "IDLE" with a slightly
  *					different behaviour. Fixed defrag
  *					routine (I hope)
- *	AX.25 032	Jonathan(G4KLX)	Remove auto-router.
- *			Darryl(G7LED)	AX.25 segmentation fixed.
+ *	AX.25 032	Darryl(G7LED)	AX.25 segmentation fixed.
+ *	AX.25 033	Jonathan(G4KLX)	Remove auto-router.
+ *					Modularisation changes.
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -59,9 +60,6 @@
 #include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
-#ifdef CONFIG_NETROM
-#include <net/netrom.h>
-#endif
 
 static int ax25_rx_iframe(ax25_cb *, struct sk_buff *);
 
@@ -153,6 +151,7 @@
  */
 static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
 {
+	int (*func)(struct sk_buff *, ax25_cb *);
 	volatile int queued = 0;
 	unsigned char pid;
 	
@@ -162,37 +161,29 @@
 	
 	pid = *skb->data;
 
-	switch (pid) {
-#ifdef CONFIG_NETROM
-		case AX25_P_NETROM:
-			if (ax25_dev_get_value(ax25->device, AX25_VALUES_NETROM)) {
-				skb_pull(skb, 1);	/* Remove PID */
-				queued = nr_route_frame(skb, ax25);
-			}
-			break;
-#endif
 #ifdef CONFIG_INET
-		case AX25_P_IP:
-			skb_pull(skb, 1);	/* Remove PID */
-			skb->h.raw = skb->data;
-			ip_rcv(skb, ax25->device, NULL);	/* Wrong ptype */
-			queued = 1;
-			break;
+	if (pid == AX25_P_IP) {
+		skb_pull(skb, 1);	/* Remove PID */
+		skb->h.raw = skb->data;
+		ip_rcv(skb, ax25->device, NULL);	/* Wrong ptype */
+		return 1;
+	}
 #endif
-		case AX25_P_SEGMENT:
-			skb_pull(skb, 1);	/* Remove PID */
-			queued = ax25_rx_fragment(ax25, skb);
-			break;
-
-		default:
-			if (ax25->sk != NULL && ax25_dev_get_value(ax25->device, AX25_VALUES_TEXT) && ax25->sk->protocol == pid) {
-				if (sock_queue_rcv_skb(ax25->sk, skb) == 0) {
-					queued = 1;
-				} else {
-					ax25->condition |= OWN_RX_BUSY_CONDITION;
-				}
-			}
-			break;
+	if (pid == AX25_P_SEGMENT) {
+		skb_pull(skb, 1);	/* Remove PID */
+		return ax25_rx_fragment(ax25, skb);
+	}
+
+	if ((func = ax25_protocol_function(pid)) != NULL) {
+		skb_pull(skb, 1);	/* Remove PID */
+		return (*func)(skb, ax25);
+	}
+	
+	if (ax25->sk != NULL && ax25_dev_get_value(ax25->device, AX25_VALUES_TEXT) && ax25->sk->protocol == pid) {
+		if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
+			queued = 1;
+		else
+			ax25->condition |= OWN_RX_BUSY_CONDITION;
 	}
 
 	return queued;
diff -u --recursive --new-file v2.1.8/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.1.8/linux/net/ax25/ax25_out.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ax25/ax25_out.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 032
+ *	AX.25 release 033
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -32,7 +32,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -u --recursive --new-file v2.1.8/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v2.1.8/linux/net/ax25/ax25_route.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ax25/ax25_route.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 032
+ *	AX.25 release 033
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -37,11 +37,12 @@
  *			Joerg(DL1BKE)	Fixed AX.25 routing of IP datagram and VC, new ioctl()
  *					"SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
  *					on routes.
- *	AX.25 032	Jonathan(G4KLX)	Remove auto-router.
+ *	AX.25 033	Jonathan(G4KLX)	Remove auto-router.
+ *			Joerg(DL1BKE)	Moved BPQ Ethernet driver to seperate device.
  */
  
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -72,11 +73,12 @@
 	char ip_mode;
 } *ax25_route = NULL;
 
-static struct ax25_dev {
-	struct ax25_dev *next;
-	struct device *dev;
-	unsigned short values[AX25_MAX_VALUES];
-} *ax25_device = NULL;
+struct ax25_dev ax25_device[AX25_MAX_DEVICES] = {
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}
+};
 
 static struct ax25_route *ax25_find_route(ax25_address *, struct device *);
 
@@ -504,30 +506,20 @@
 	return ' ';
 }
 
-static struct ax25_dev *ax25_dev_get_dev(struct device *dev)
-{
-	struct ax25_dev *s;
-
-	for (s = ax25_device; s != NULL; s = s->next)
-		if (s->dev == dev)
-			return s;
-	
-	return NULL;
-}
-
 /*
  *	Wow, a bit of data hiding. Is this C++ or what ?
  */
-unsigned short ax25_dev_get_value(struct device *dev, int valueno)
+int ax25_dev_get_value(struct device *dev, int valueno)
 {
-	struct ax25_dev *ax25_dev;
+	int i;
 
-	if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) {
-		printk(KERN_WARNING "ax25_dev_get_flag called with invalid device\n");
-		return 1;
-	}
+	for (i = 0; i < AX25_MAX_DEVICES; i++)
+		if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev)
+			return ax25_device[i].values[valueno];
+	
+	printk(KERN_WARNING "ax25_dev_get_value called with invalid device\n");
 
-	return ax25_dev->values[valueno];
+	return 0;
 }
 
 /*
@@ -536,17 +528,29 @@
  */
 void ax25_dev_device_up(struct device *dev)
 {
-	unsigned long flags;
-	struct ax25_dev *ax25_dev;
+	struct ax25_dev *ax25_dev = NULL;
+	int i;
 	
-	if ((ax25_dev = (struct ax25_dev *)kmalloc(sizeof(struct ax25_dev), GFP_ATOMIC)) == NULL)
-		return;		/* No space */
+	for (i = 0; i < AX25_MAX_DEVICES; i++) {
+		if (ax25_device[i].dev == NULL) {
+			ax25_dev = ax25_device + i;
+			break;
+		}
+	}
+
+	if (ax25_dev == NULL) {
+		printk(KERN_ERR "ax25_dev_device_up cannot find free AX.25 device\n");
+		return;
+	}
+
+	ax25_unregister_sysctl();
+
+	sprintf(ax25_dev->name, "%s.parms", dev->name);
 
-	ax25_dev->dev        = dev;
+	ax25_dev->dev = dev;
 
 	ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
 	ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
-	ax25_dev->values[AX25_VALUES_NETROM]    = AX25_DEF_NETROM;
 	ax25_dev->values[AX25_VALUES_TEXT]      = AX25_DEF_TEXT;
 	ax25_dev->values[AX25_VALUES_BACKOFF]   = AX25_DEF_BACKOFF;
 	ax25_dev->values[AX25_VALUES_CONMODE]   = AX25_DEF_CONMODE;
@@ -559,227 +563,45 @@
 	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
 	ax25_dev->values[AX25_VALUES_DIGI]      = AX25_DEF_DIGI;
 	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
-	ax25_dev->values[AX25_VALUES_IPMAXQUEUE]= AX25_DEF_IPMAXQUEUE;
+	ax25_dev->values[AX25_VALUES_MAXQUEUE]	= AX25_DEF_MAXQUEUE;
 
-	save_flags(flags);
-	cli();
-
-	ax25_dev->next = ax25_device;
-	ax25_device    = ax25_dev;
-
-	restore_flags(flags);
+	ax25_register_sysctl();
 }
 
 void ax25_dev_device_down(struct device *dev)
 {
-	struct ax25_dev *s, *t, *ax25_dev = ax25_device;
-	
-	while (ax25_dev != NULL) {
-		s        = ax25_dev;
-		ax25_dev = ax25_dev->next;
-
-		if (s->dev == dev) {
-			if (ax25_device == s) {
-				ax25_device = s->next;
-				kfree_s((void *)s, (sizeof *s));
-			} else {
-				for (t = ax25_device; t != NULL; t = t->next) {
-					if (t->next == s) {
-						t->next = s->next;
-						kfree_s((void *)s, sizeof(*s));
-						break;
-					}
-				}				
-			}
-		}
-	}
-}
-
-int ax25_dev_ioctl(unsigned int cmd, void *arg)
-{
-	struct ax25_parms_struct ax25_parms;
-	struct device *dev;
-	struct ax25_dev *ax25_dev;
-	int err;
+	int i;
 
-	switch (cmd) {
-		case SIOCAX25SETPARMS:
-			if (!suser())
-				return -EPERM;
-			if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_parms))) != 0)
-				return err;
-			copy_from_user(&ax25_parms, arg, sizeof(ax25_parms));
-			if ((dev = ax25rtr_get_dev(&ax25_parms.port_addr)) == NULL)
-				return -EINVAL;
-			if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_IPDEFMODE] != 'D' &&
-			    ax25_parms.values[AX25_VALUES_IPDEFMODE] != 'V')
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_AXDEFMODE] != MODULUS &&
-			    ax25_parms.values[AX25_VALUES_AXDEFMODE] != EMODULUS)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_NETROM] != 0 &&
-			    ax25_parms.values[AX25_VALUES_NETROM] != 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_TEXT] != 0 &&
-			    ax25_parms.values[AX25_VALUES_TEXT] != 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_BACKOFF] != 'E' &&
-			    ax25_parms.values[AX25_VALUES_BACKOFF] != 'L')
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_CONMODE] != 0 &&
-			    ax25_parms.values[AX25_VALUES_CONMODE] != 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_WINDOW] < 1 ||
-			    ax25_parms.values[AX25_VALUES_WINDOW] > 7)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_EWINDOW] < 1 ||
-			    ax25_parms.values[AX25_VALUES_EWINDOW] > 63)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_T1] < 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_T2] < 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_T3] < 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_IDLE] > 100)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_N2] < 1 ||
-			    ax25_parms.values[AX25_VALUES_N2] > 31)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_PACLEN] < 22)
-				return -EINVAL;
-			if ((ax25_parms.values[AX25_VALUES_DIGI] &
-			    ~(AX25_DIGI_INBAND | AX25_DIGI_XBAND)) != 0)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_IPMAXQUEUE] < 1)
-				return -EINVAL;
-			memcpy(ax25_dev->values, ax25_parms.values, AX25_MAX_VALUES * sizeof(short));
-			ax25_dev->values[AX25_VALUES_T1] *= PR_SLOWHZ;
-			ax25_dev->values[AX25_VALUES_T1] /= 2;
-			ax25_dev->values[AX25_VALUES_T2] *= PR_SLOWHZ;
-			ax25_dev->values[AX25_VALUES_T3] *= PR_SLOWHZ;
-			ax25_dev->values[AX25_VALUES_IDLE] *= PR_SLOWHZ * 60;
-			break;
+	ax25_unregister_sysctl();
 
-		case SIOCAX25GETPARMS:
-			if ((err = verify_area(VERIFY_WRITE, arg, sizeof(struct ax25_parms_struct))) != 0)
-				return err;
-			copy_from_user(&ax25_parms, arg, sizeof(ax25_parms));
-			if ((dev = ax25rtr_get_dev(&ax25_parms.port_addr)) == NULL)
-				return -EINVAL;
-			if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
-				return -EINVAL;
-			memcpy(ax25_parms.values, ax25_dev->values, AX25_MAX_VALUES * sizeof(short));
-			ax25_parms.values[AX25_VALUES_T1] *= 2;
-			ax25_parms.values[AX25_VALUES_T1] /= PR_SLOWHZ;
-			ax25_parms.values[AX25_VALUES_T2] /= PR_SLOWHZ;
-			ax25_parms.values[AX25_VALUES_T3] /= PR_SLOWHZ;
-			ax25_parms.values[AX25_VALUES_IDLE] /= PR_SLOWHZ * 60;
-			copy_to_user(arg, &ax25_parms, sizeof(ax25_parms));
-			break;
-	}
+	for (i = 0; i < AX25_MAX_DEVICES; i++)
+		if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev)
+			ax25_device[i].dev = NULL;
 
-	return 0;
+	ax25_register_sysctl();
 }
+      
+#ifdef MODULE
 
-#ifdef CONFIG_BPQETHER
-static struct ax25_bpqdev {
-	struct ax25_bpqdev *next;
-	struct device *dev;
-	ax25_address callsign;
-} *ax25_bpqdev = NULL;
-
-int ax25_bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+/*
+ *    Free all memory associated with routing and device structures.
+ */
+void ax25_rt_free(void)
 {
-	struct ax25_bpqdev *bpqdev;
-	int len     = 0;
-	off_t pos   = 0;
-	off_t begin = 0;
+	struct ax25_route *s, *ax25_rt = ax25_route;
   
-	cli();
-
-	len += sprintf(buffer, "dev  callsign\n");
-
-	for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
-		len += sprintf(buffer + len, "%-4s %-9s\n",
-			bpqdev->dev ? bpqdev->dev->name : "???",
-			ax2asc(&bpqdev->callsign));
-
-		pos = begin + len;
-
-		if (pos < offset) {
-			len   = 0;
-			begin = pos;
-		}
-		
-		if (pos > offset + length)
-			break;
-	}
-
-	sti();
-
-	*start = buffer + (offset - begin);
-	len   -= (offset - begin);
-
-	if (len > length) len = length;
-
-	return len;
-} 
-
-ax25_address *ax25_bpq_get_addr(struct device *dev)
-{
-	struct ax25_bpqdev *bpqdev;
-	
-	for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next)
-		if (bpqdev->dev == dev)
-			return &bpqdev->callsign;
-
-	return NULL;
-}
+	while (ax25_rt != NULL) {
+		s       = ax25_rt;
+		ax25_rt = ax25_rt->next;
 
-int ax25_bpq_ioctl(unsigned int cmd, void *arg)
-{
-	unsigned long flags;
-	struct ax25_bpqdev *bpqdev;
-	struct ax25_bpqaddr_struct bpqaddr;
-	struct device *dev;
-	int err;
+		if (s->digipeat != NULL)
+			kfree_s(s->digipeat, sizeof(ax25_digi));
 
-	switch (cmd) {
-		case SIOCAX25BPQADDR:
-			if ((err = verify_area(VERIFY_READ, arg, sizeof(bpqaddr))) != 0)
-				return err;
-			copy_from_user(&bpqaddr, arg, sizeof(bpqaddr));
-			if ((dev = dev_get(bpqaddr.dev)) == NULL)
-				return -EINVAL;
-			if (dev->type != ARPHRD_ETHER)
-				return -EINVAL;
-			for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
-				if (bpqdev->dev == dev) {
-					bpqdev->callsign = bpqaddr.addr;
-					return 0;
-				}
-			}
-			if ((bpqdev = (struct ax25_bpqdev *)kmalloc(sizeof(struct ax25_bpqdev), GFP_ATOMIC)) == NULL)
-				return -ENOMEM;
-			bpqdev->dev      = dev;
-			bpqdev->callsign = bpqaddr.addr;
-			save_flags(flags);
-			cli();
-			bpqdev->next = ax25_bpqdev;
-			ax25_bpqdev  = bpqdev;
-			restore_flags(flags);
-			break;
-			
-		default:
-			return -EINVAL;
+		kfree_s(s, sizeof(struct ax25_route));
 	}
-
-	return 0;
 }
 
 #endif
 
 #endif
+            
diff -u --recursive --new-file v2.1.8/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v2.1.8/linux/net/ax25/ax25_subr.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ax25/ax25_subr.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 032
+ *	AX.25 release 033
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -35,7 +35,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -u --recursive --new-file v2.1.8/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v2.1.8/linux/net/ax25/ax25_timer.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ax25/ax25_timer.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 032
+ *	AX.25 release 033
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -19,10 +19,11 @@
  *	AX.25 029	Alan(GW4PTS)	Switched to KA9Q constant names.
  *	AX.25 031	Joerg(DL1BKE)	Added DAMA support
  *	AX.25 032	Joerg(DL1BKE)	Fixed DAMA timeout bug
+ *	AX.25 033	Jonathan(G4KLX)	Modularisation functions.
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -43,9 +44,6 @@
 #include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
-#ifdef CONFIG_NETROM
-#include <net/netrom.h>
-#endif
 
 static void ax25_timer(unsigned long);
 
@@ -144,16 +142,14 @@
 		/* dl1bke 960114: T3 expires and we are in DAMA mode:  */
 		/*                send a DISC and abort the connection */
 		if (ax25->dama_slave) {
-#ifdef CONFIG_NETROM
-			nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
+			ax25_link_failed(&ax25->dest_addr, ax25->device);
 			ax25_clear_queues(ax25);
 			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
 				
 			ax25->state = AX25_STATE_0;
 			if (ax25->sk != NULL) {
 				if (ax25->sk->debug)
-					printk("T3 Timeout\n");
+					printk(KERN_DEBUG "AX.25 T3 Timeout\n");
 				ax25->sk->state = TCP_CLOSE;
 				ax25->sk->err   = ETIMEDOUT;
 				if (!ax25->sk->dead)
@@ -237,9 +233,7 @@
 		case AX25_STATE_1: 
 			if (ax25->n2count == ax25->n2) {
 				if (ax25->modulus == MODULUS) {
-#ifdef CONFIG_NETROM
-					nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
+					ax25_link_failed(&ax25->dest_addr, ax25->device);
 					ax25_clear_queues(ax25);
 					ax25->state = AX25_STATE_0;
 					if (ax25->sk != NULL) {
@@ -267,9 +261,7 @@
 
 		case AX25_STATE_2:
 			if (ax25->n2count == ax25->n2) {
-#ifdef CONFIG_NETROM
-				nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
+				ax25_link_failed(&ax25->dest_addr, ax25->device);
 				ax25_clear_queues(ax25);
 				ax25->state = AX25_STATE_0;
 				ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
@@ -297,15 +289,13 @@
 
 		case AX25_STATE_4:
 			if (ax25->n2count == ax25->n2) {
-#ifdef CONFIG_NETROM
-				nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
+				ax25_link_failed(&ax25->dest_addr, ax25->device);
 				ax25_clear_queues(ax25);
 				ax25_send_control(ax25, DM, POLLON, C_RESPONSE);
 				ax25->state = AX25_STATE_0;
 				if (ax25->sk != NULL) {
 					if (ax25->sk->debug)
-						printk("Link Failure\n");
+						printk(KERN_DEBUG "AX.25 link Failure\n");
 					ax25->sk->state = TCP_CLOSE;
 					ax25->sk->err   = ETIMEDOUT;
 					if (!ax25->sk->dead)
@@ -323,6 +313,230 @@
 	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 
 	ax25_set_timer(ax25);
+}
+
+/************************************************************************/
+/*	Module support functions follow.				*/
+/************************************************************************/
+
+static struct protocol_struct {
+	struct protocol_struct *next;
+	unsigned int pid;
+	int (*func)(struct sk_buff *, ax25_cb *);
+} *protocol_list = NULL;
+
+static struct linkfail_struct {
+	struct linkfail_struct *next;
+	void (*func)(ax25_address *, struct device *);
+} *linkfail_list = NULL;
+
+static struct listen_struct {
+	struct listen_struct *next;
+	ax25_address  callsign;
+	struct device *dev;
+} *listen_list = NULL;
+
+int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
+{
+	struct protocol_struct *protocol;
+	unsigned long flags;
+
+	if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
+		return 0;
+#ifdef CONFIG_INET
+	if (pid == AX25_P_IP || pid == AX25_P_ARP)
+		return 0;
+#endif
+	if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	protocol->pid  = pid;
+	protocol->func = func;
+	
+	save_flags(flags);
+	cli();
+	
+	protocol->next = protocol_list;
+	protocol_list  = protocol;
+	
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_protocol_release(unsigned int pid)
+{
+	struct protocol_struct *s, *protocol = protocol_list;
+	unsigned long flags;
+
+	if (protocol == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (protocol->pid == pid) {
+		protocol_list = protocol->next;
+		restore_flags(flags);
+		kfree_s(protocol, sizeof(struct protocol_struct));
+		return;
+	}
+
+	while (protocol != NULL && protocol->next != NULL) {
+		if (protocol->next->pid == pid) {
+			s = protocol->next;
+			protocol->next = protocol->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct protocol_struct));
+			return;
+		}
+		
+		protocol = protocol->next;
+	}
+	
+	restore_flags(flags);
+}
+
+int ax25_linkfail_register(void (*func)(ax25_address *, struct device *))
+{
+	struct linkfail_struct *linkfail;
+	unsigned long flags;
+
+	if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	linkfail->func = func;
+	
+	save_flags(flags);
+	cli();
+	
+	linkfail->next = linkfail_list;
+	linkfail_list  = linkfail;
+	
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_linkfail_release(void (*func)(ax25_address *, struct device *))
+{
+	struct linkfail_struct *s, *linkfail = linkfail_list;
+	unsigned long flags;
+
+	if (linkfail == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (linkfail->func == func) {
+		linkfail_list = linkfail->next;
+		restore_flags(flags);
+		kfree_s(linkfail, sizeof(struct linkfail_struct));
+		return;
+	}
+
+	while (linkfail != NULL && linkfail->next != NULL) {
+		if (linkfail->next->func == func) {
+			s = linkfail->next;
+			linkfail->next = linkfail->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct linkfail_struct));
+			return;
+		}
+		
+		linkfail = linkfail->next;
+	}
+	
+	restore_flags(flags);
+}
+
+int ax25_listen_register(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *listen;
+	unsigned long flags;
+
+	if (ax25_listen_mine(callsign, dev))
+		return 0;
+
+	if ((listen = (struct listen_struct *)kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	listen->callsign = *callsign;
+	listen->dev      = dev;
+	
+	save_flags(flags);
+	cli();
+	
+	listen->next = listen_list;
+	listen_list  = listen;
+	
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_listen_release(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *s, *listen = listen_list;
+	unsigned long flags;
+
+	if (listen == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
+		listen_list = listen->next;
+		restore_flags(flags);
+		kfree_s(listen, sizeof(struct listen_struct));
+		return;
+	}
+
+	while (listen != NULL && listen->next != NULL) {
+		if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
+			s = listen->next;
+			listen->next = listen->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct listen_struct));
+			return;
+		}
+		
+		listen = listen->next;
+	}
+	
+	restore_flags(flags);
+}
+
+int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
+{
+	struct protocol_struct *protocol;
+
+	for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+		if (protocol->pid == pid)
+			return protocol->func;
+
+	return NULL;
+}
+
+int ax25_listen_mine(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *listen;
+
+	for (listen = listen_list; listen != NULL; listen = listen->next)
+		if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
+			return 1;
+
+	return 0;
+}
+
+void ax25_link_failed(ax25_address *callsign, struct device *dev)
+{
+	struct linkfail_struct *linkfail;
+	
+	for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
+		(linkfail->func)(callsign, dev);
 }
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/net/ax25/sysctl_net_ax25.c linux/net/ax25/sysctl_net_ax25.c
--- v2.1.8/linux/net/ax25/sysctl_net_ax25.c	Tue Apr  2 09:03:35 1996
+++ linux/net/ax25/sysctl_net_ax25.c	Sun Nov 10 19:12:58 1996
@@ -7,7 +7,54 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <net/ax25.h>
 
-ctl_table ax25_table[] = {
+static int min_ax25[] = {0, 0, 0, 0, 0, 1,  1,  1 * PR_SLOWHZ,  1 * PR_SLOWHZ,
+	   0 * PR_SLOWHZ,     0 * PR_SLOWHZ,  1,   1, 0x00, 1};
+static int max_ax25[] = {1, 1, 1, 1, 1, 7, 63, 30 * PR_SLOWHZ, 20 * PR_SLOWHZ,
+	3600 * PR_SLOWHZ, 65535 * PR_SLOWHZ, 31, 512, 0x03, 20};
+
+static struct ctl_table_header *ax25_table_header;
+
+static ctl_table ax25_table[AX25_MAX_DEVICES + 1];
+
+static ctl_table ax25_dir_table[] = {
+	{NET_AX25, "ax25", NULL, 0, 0555, ax25_table},
+	{0}
+};
+
+static ctl_table ax25_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, ax25_dir_table},
 	{0}
 };
+
+void ax25_register_sysctl(void)
+{
+	int i, n;
+
+	memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table));
+
+	for (n = 0, i = 0; i < AX25_MAX_DEVICES; i++) {
+		if (ax25_device[i].dev != NULL) {
+			ax25_table[n].ctl_name     = n + 1;
+			ax25_table[n].procname     = ax25_device[i].name;
+			ax25_table[n].data         = &ax25_device[i].values;
+			ax25_table[n].maxlen       = AX25_MAX_VALUES * sizeof(int);
+			ax25_table[n].mode         = 0644;
+			ax25_table[n].child        = NULL;
+			ax25_table[n].proc_handler = &proc_dointvec_minmax;
+			ax25_table[n].strategy     = &sysctl_intvec;
+			ax25_table[n].de           = NULL;
+			ax25_table[n].extra1       = &min_ax25;
+			ax25_table[n].extra2       = &max_ax25;
+			n++;
+		}
+	}
+
+	ax25_table_header = register_sysctl_table(ax25_root_table, 1);
+}
+
+void ax25_unregister_sysctl(void)
+{
+	unregister_sysctl_table(ax25_table_header);
+}
diff -u --recursive --new-file v2.1.8/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.8/linux/net/core/dev.c	Sun Nov 10 20:12:24 1996
+++ linux/net/core/dev.c	Mon Nov 11 15:38:07 1996
@@ -45,7 +45,6 @@
  *		Alan Cox	:	Cleaned up the backlog initialise.
  *		Craig Metz	:	SIOCGIFCONF fix if space for under
  *					1 device.
- *		Molnar Ingo	:	skb->stamp hack for the Pentium
  *	    Thomas Bogendoerfer :	Return ENODEV for dev_open, if there
  *					is no device open function.
  *
@@ -411,11 +410,9 @@
 		/* copy outgoing packets to any sniffer packet handlers */
 		if (dev_nit) {
 			struct packet_type *ptype;
-#ifdef CONFIG_M586
-                        struct timeval dummy_tv;
-			do_gettimeofday( &dummy_tv );
-#endif
-			skb->stamp=xtime;
+
+			get_fast_time(&skb->stamp);
+
 			for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) 
 			{
 				/* Never send packets back to the socket
@@ -486,13 +483,7 @@
 	skb->sk = NULL;
 	skb->free = 1;
 	if(skb->stamp.tv_sec==0)
-	{
-#ifdef CONFIG_M586
-                        struct timeval dummy_tv;
-			do_gettimeofday( &dummy_tv );
-#endif
-		skb->stamp = xtime;
-	}
+		get_fast_time(&skb->stamp);
 
 	/*
 	 *	Check that we aren't overdoing things.
@@ -1369,6 +1360,7 @@
 extern int lance_init(void);
 extern int ni65_init(void);
 extern int pi_init(void);
+extern int bpq_init(void);
 extern int scc_init(void);
 extern void sdla_setup(void);
 extern void dlci_setup(void);
@@ -1419,6 +1411,9 @@
 #endif
 #if defined(CONFIG_PT)
 	pt_init();
+#endif
+#if defined(CONFIG_BPQETHER)
+	bpq_init();
 #endif
 #if defined(CONFIG_DLCI)
 	dlci_setup();
diff -u --recursive --new-file v2.1.8/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.8/linux/net/ipv4/arp.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ipv4/arp.c	Sun Nov 10 19:12:58 1996
@@ -99,9 +99,9 @@
 #include <net/tcp.h>
 #include <net/sock.h>
 #include <net/arp.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <net/netrom.h>
 #endif
 #endif
@@ -1707,8 +1707,8 @@
 
 	/* Fill out the arp protocol part. */
 	arp->ar_hrd = htons(dev->type);
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
 #else
 	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
@@ -1780,7 +1780,7 @@
 
   	switch (dev->type)
   	{
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 		case ARPHRD_AX25:
 			if(arp->ar_pro != htons(AX25_P_IP))
 			{
@@ -1789,7 +1789,7 @@
 			}
 			break;
 #endif
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 		case ARPHRD_NETROM:
 			if(arp->ar_pro != htons(AX25_P_IP))
 			{
@@ -2306,8 +2306,8 @@
 /*
  *	Convert hardware address to XX:XX:XX:XX ... form.
  */
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
 			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
 			else {
@@ -2326,7 +2326,7 @@
 			}
 			hbuffer[--k]=0;
 	
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 			}
 #endif
 			size = sprintf(buffer+len,
@@ -2416,3 +2416,39 @@
 	netlink_attach(NETLINK_ARPD, arpd_callback);
 #endif
 }
+
+#ifdef CONFIG_AX25_MODULE
+
+/*
+ *	ax25 -> ascii conversion
+ */
+char *ax2asc(ax25_address *a)
+{
+	static char buf[11];
+	char c, *s;
+	int n;
+
+	for (n = 0, s = buf; n < 6; n++) {
+		c = (a->ax25_call[n] >> 1) & 0x7F;
+
+		if (c != ' ') *s++ = c;
+	}
+	
+	*s++ = '-';
+
+	if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
+		*s++ = '1';
+		n -= 10;
+	}
+	
+	*s++ = n + '0';
+	*s++ = '\0';
+
+	if (*buf == '\0' || *buf == '-')
+	   return "*";
+
+	return buf;
+
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v2.1.8/linux/net/ipv4/rarp.c	Tue Oct 29 19:58:50 1996
+++ linux/net/ipv4/rarp.c	Sun Nov 10 19:12:58 1996
@@ -59,7 +59,7 @@
 #include <net/sock.h>
 #include <net/arp.h>
 #include <net/rarp.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>
 #endif
 #include <linux/proc_fs.h>
@@ -226,7 +226,7 @@
  */
 
 	if (
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 		(rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
 #endif
 		(rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
@@ -305,7 +305,7 @@
 			htype = ARPHRD_ETHER;
 			hlen = ETH_ALEN;
 			break;
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 		case ARPHRD_AX25:
 			htype = ARPHRD_AX25;
 			hlen = 7;
diff -u --recursive --new-file v2.1.8/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.8/linux/net/ipv4/tcp.c	Sun Nov 10 20:12:29 1996
+++ linux/net/ipv4/tcp.c	Sun Nov 10 19:53:13 1996
@@ -790,7 +790,7 @@
 	 * of the skb
 	 */
 
-	copy = min(sk->mss - tcp_size, skb->end - skb->tail);
+	copy = min(sk->mss - tcp_size, skb_tailroom(skb));
 	copy = min(copy, seglen);
 	
 	tcp_size += copy;
@@ -959,7 +959,7 @@
 			actual_win = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
 
 			if (copy > actual_win && 
-			    actual_win >= (sk->max_window >> 1))
+			    (((long) actual_win) >= (sk->max_window >> 1)))
 			{
 				copy = actual_win;
 			}
@@ -1063,7 +1063,7 @@
 			sk->write_seq += copy;
 		
 			tcp_send_skb(sk, skb);
-	
+
 			release_sock(sk);
 			lock_sock(sk);
 		}
diff -u --recursive --new-file v2.1.8/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.8/linux/net/ipv4/tcp_ipv4.c	Sun Nov 10 20:12:29 1996
+++ linux/net/ipv4/tcp_ipv4.c	Sun Nov 10 19:53:13 1996
@@ -934,6 +934,86 @@
 	return newsk;
 }
 
+struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct open_request *req;
+
+
+	/*
+	 *	assumption: the socket is not in use.
+	 *	as we checked the user count on tcp_rcv and we're
+	 *	running from a soft interrupt.
+	 */
+		
+	req = tp->syn_wait_queue;
+	
+
+	if (!req)
+	{
+		return sk;
+	}
+	
+	do {
+		struct tcp_v4_open_req *af_req;
+
+		af_req = (struct tcp_v4_open_req *) req;
+
+		if (af_req->rmt_addr == skb->saddr &&
+		    af_req->loc_addr == skb->daddr &&
+		    req->rmt_port == skb->h.th->source)
+		{
+			u32 flg;
+				
+			if (req->sk)
+			{
+				printk(KERN_DEBUG "BUG: syn_recv:"
+				       "socket exists\n");
+				break;
+			}
+
+			/* match */
+
+			/*
+			 *	Check for syn retransmission
+			 */
+			flg = *(((u32 *)skb->h.th) + 3);
+			flg &= __constant_htonl(0x002f0000);
+				
+			if ((flg == __constant_htonl(0x00020000)) &&
+			    (!after(skb->seq, req->rcv_isn)))
+			{
+				/*
+				 *	retransmited syn
+				 *	FIXME: must send an ack
+				 */
+				return NULL;
+			}
+
+			atomic_sub(skb->truesize, &sk->rmem_alloc);
+			sk = tp->af_specific->syn_recv_sock(sk, skb, req);
+
+			tcp_dec_slow_timer(TCP_SLT_SYNACK);
+
+			if (sk == NULL)
+			{
+				return NULL;
+			}
+
+			atomic_add(skb->truesize, &sk->rmem_alloc);
+			req->expires = 0UL;
+			req->sk = sk;
+			skb->sk = sk;
+			break;
+		}
+
+		req = req->dl_next;
+	} while (req != tp->syn_wait_queue);
+	
+
+	return sk;
+}
+
 /*
  *	From tcp_input.c
  */
@@ -1027,53 +1107,14 @@
 
 	if (sk->state == TCP_LISTEN)
 	{
-		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-		struct open_request *req;
-		struct tcp_v4_open_req *af_req;
-
 		/*
-		 *	assumption: the socket is not in use.
-		 *	as we checked the user count above and we're
-		 *	running from a soft interrupt.
+		 *	find possible connection requests
 		 */
-		
-		req = tp->syn_wait_queue;
-		af_req = (struct tcp_v4_open_req *) req;
-		
-		if (req)
-		{
-			do {
-				if (af_req->rmt_addr == saddr &&
-				    af_req->loc_addr == daddr &&
-				    req->rmt_port == th->source)
-				{
-					if (req->sk)
-					{
-						printk(KERN_DEBUG "bug: syn_recv socket "
-						       "exists\n");
-						break;
-					}
-
-					/* match */
-
-					atomic_sub(skb->truesize, &sk->rmem_alloc);
-					sk = tp->af_specific->syn_recv_sock(sk, skb, req);
-
-					tcp_dec_slow_timer(TCP_SLT_SYNACK);
-
-					if (sk == NULL)
-					{
-						goto no_tcp_socket;
-					}
-					
-					atomic_add(skb->truesize, &sk->rmem_alloc);
-					req->sk = sk;
-					skb->sk = sk;
-					break;
-				}
+		sk = tcp_v4_check_req(sk, skb);
 
-				req = req->dl_next;
-			} while (req != tp->syn_wait_queue);
+		if (sk == NULL)
+		{
+			goto discard_it;
 		}
 	}
 	
@@ -1257,7 +1298,7 @@
 	 */
 	sk->cong_window = 1;
 	sk->ssthresh = 0x7fffffff;
-
+	
 	sk->priority = 1;
 	sk->state = TCP_CLOSE;
 
diff -u --recursive --new-file v2.1.8/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.8/linux/net/ipv4/tcp_output.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv4/tcp_output.c	Sat Nov  9 19:50:27 1996
@@ -468,7 +468,7 @@
 	if (th1->urg)
 		return -1;
 
-	avail = skb->end - skb->tail;
+	avail = skb_tailroom(skb);
 
 	/*
 	 *  size of tcp payload
diff -u --recursive --new-file v2.1.8/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.8/linux/net/ipv4/tcp_timer.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv4/tcp_timer.c	Sun Nov 10 19:53:13 1996
@@ -472,20 +472,26 @@
 			    tp->syn_wait_queue)
 			{
 				struct open_request *req;
-				
+
 				req = tp->syn_wait_queue;
 
-				while (tp->syn_wait_queue &&
-				       (((long)(req->expires - now)) <= 0))
+				while (req && tp->syn_wait_queue)
 				{
 					struct open_request *conn;
 
 					conn = req;
 					req = req->dl_next;
 
-					if (conn->sk && conn->sk->state > TCP_SYN_RECV)
+					if (conn->sk)
+					{
+						if (req == tp->syn_wait_queue)
+							break;
 						continue;
+					}
 					
+					if ((long)(now - conn->expires) <= 0)
+						break;
+
 					tcp_synq_unlink(tp, conn);
 					
 					if (conn->retrans >= TCP_RETR1)
@@ -548,4 +554,26 @@
 		tcp_slow_timer.expires = now + next;
 		add_timer(&tcp_slow_timer);
 	}
+}
+
+void __tcp_inc_slow_timer(struct tcp_sl_timer *slt)
+{
+	unsigned long now = jiffies;
+	unsigned long next = 0;
+	unsigned long when;
+
+	slt->last = now;
+		
+	when = now + slt->period;
+	if (del_timer(&tcp_slow_timer))
+	{
+		next = tcp_slow_timer.expires;
+	}
+	if (next && ((long)(next - when) < 0))
+	{
+		when = next;
+	}
+		
+	tcp_slow_timer.expires = when;
+	add_timer(&tcp_slow_timer);
 }
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.8/linux/net/ipv6/addrconf.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv6/addrconf.c	Sat Nov  9 19:50:27 1996
@@ -492,6 +492,7 @@
 	else
 	{
 		rtmsg.rtmsg_prefixlen = 128;
+		rtmsg.rtmsg_dst.s6_addr32[0] = __constant_htonl(0xfe800000);
 		rtmsg.rtmsg_dst.s6_addr32[3] = dev->pa_dstaddr;
 		rtmsg.rtmsg_metric = 1;
 		rtmsg.rtmsg_flags = RTF_HOST|RTF_UP;
@@ -773,6 +774,62 @@
 
 }
 
+static void addrconf_ifdown(struct device *dev)
+{
+	struct inet6_dev *idev, **bidev;
+	struct inet6_ifaddr *ifa, **bifa;
+	int i;
+
+	start_bh_atomic();
+
+	bidev = &inet6_dev_lst;
+
+	for (idev = inet6_dev_lst; idev; idev = idev->next)
+	{
+		if (idev->dev == dev)
+		{
+			*bidev = idev->next;
+			break;
+		}
+		bidev = &idev;
+	}
+
+	if (idev == NULL)
+	{
+		printk(KERN_DEBUG "addrconf_ifdown: device not found\n"); 
+		return;
+	}
+	
+	/*
+	 *	FIXME: clear multicast group membership
+	 */
+
+	/*
+	 *	clean addr_list
+	 */
+
+	for (i=0; i<16; i++)
+	{
+		bifa = &inet6_addr_lst[i];
+		
+		for (ifa=inet6_addr_lst[i]; ifa; )
+		{
+			if (ifa->idev == idev)
+			{
+				*bifa = ifa->lst_next;
+				kfree(ifa);
+				ifa = *bifa;
+				continue;
+			}
+			bifa = &ifa;
+			ifa = ifa->lst_next;
+		}
+	}
+
+	kfree(idev);
+	end_bh_atomic();
+}
+
 /*
  *	Set destination address.
  *	Special case for SIT interfaces where we create a new "virtual"
@@ -848,6 +905,8 @@
 	if (ifp == NULL)
 		return -ENOMEM;
 
+	ifp->prefix_len = 128;
+
 	if (dev->flags & IFF_MULTICAST)
 	{
 		struct in6_addr maddr;
@@ -874,24 +933,26 @@
 	struct inet6_ifaddr * ifp;
 	struct in6_addr addr;
 	struct device *dev;
-	int flag;
+	int scope;
 
 	memset(&addr, 0, sizeof(struct in6_addr));
 
 	if (idev->dev->pa_dstaddr)
 	{
 		addr.s6_addr32[0] = __constant_htonl(0xfe800000);
-		flag = IFA_LINK;
+		scope = IFA_LINK;
 	}
 	else
 	{
-		flag = IFA_GLOBAL | IPV6_ADDR_COMPATv4;
+		scope = IPV6_ADDR_COMPATv4;
 	}
 
         for (dev = dev_base; dev != NULL; dev = dev->next) 
         {
 		if (dev->family == AF_INET && (dev->flags & IFF_UP))
 		{
+			int flag = scope;
+			
 			addr.s6_addr32[3] = dev->pa_addr;
 
 			if (dev->flags & IFF_LOOPBACK)
@@ -899,7 +960,7 @@
 				if (idev->dev->pa_dstaddr)
 					continue;
 				
-				flag = IFA_HOST | IPV6_ADDR_COMPATv4;
+				flag |= IFA_HOST;
 			}
 
 			ifp = ipv6_add_addr(idev, &addr, flag);
@@ -963,6 +1024,8 @@
 		 *	Remove all addresses from this interface
 		 *	and take the interface out of the list.
 		 */
+		addrconf_ifdown(dev);
+		rt6_ifdown(dev);
 		rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, 0, 0, dev->name, 0);
 
 		break;
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- v2.1.8/linux/net/ipv6/datagram.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv6/datagram.c	Sat Nov  9 19:50:27 1996
@@ -140,12 +140,6 @@
 					goto exit_f;
 				}
 
-				if (in6_dev && ifp->scope == IFA_LINK &&
-				    in6_dev != ifp->idev)
-				{
-					goto exit_f;
-				}
-
 				*src_addr = &src_info->ipi6_addr;
 				err = 0;
 			}
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/ipv6_output.c linux/net/ipv6/ipv6_output.c
--- v2.1.8/linux/net/ipv6/ipv6_output.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv6/ipv6_output.c	Sat Nov  9 19:50:27 1996
@@ -430,6 +430,7 @@
 		    int noblock)
 {
 	rt6_output_method_t output_method = default_output_method;
+	int	hlimit;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct dest_entry *dc = NULL;
 	struct in6_addr *daddr = dest;
@@ -448,11 +449,17 @@
 	}
 
 	addr_type = ipv6_addr_type(daddr);
-	if (addr_type & IPV6_ADDR_MULTICAST && dev == NULL)
+	if (addr_type & IPV6_ADDR_MULTICAST)
 	{
-		dev = np->mc_if;
+		hlimit = np->mcast_hops;
+		if (dev == NULL)
+		{
+			dev = np->mc_if;
+		}
 	}
-
+	else
+		hlimit = np->hop_limit;
+	
 	if (addr_type & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_SITELOCAL |
 			 IPV6_ADDR_MULTICAST))
 	{
@@ -601,7 +608,7 @@
 			hdr->payload_len = htons(pktlength - 
 						 sizeof(struct ipv6hdr));
 			
-			hdr->hop_limit = np->hop_limit;
+			hdr->hop_limit = hlimit;
 
 			memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
 			memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
@@ -732,7 +739,7 @@
 		hdr->payload_len = htons(unfrag_len + frag_len - 
 					 sizeof(struct ipv6hdr));
 		
-		hdr->hop_limit = np->hop_limit;
+		hdr->hop_limit = hlimit;
 
 		hdr->nexthdr = NEXTHDR_FRAGMENT;
 
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/ipv6_route.c linux/net/ipv6/ipv6_route.c
--- v2.1.8/linux/net/ipv6/ipv6_route.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv6/ipv6_route.c	Sat Nov  9 19:50:27 1996
@@ -838,7 +838,7 @@
 
 void fib6_flush(void)
 {
-	rt6_walk_tree(fib6_flush_1, NULL, 0);
+	rt6_walk_tree(fib6_flush_1, NULL, RT6_FILTER_NONE);
 }
 
 int ipv6_route_add(struct in6_rtmsg *rtmsg)
@@ -958,10 +958,12 @@
 	{
 		if (src_dev)
 		{
-			for (; rt; rt=rt->next)
+			struct rt6_info *sprt;
+
+			for (sprt=rt; sprt; sprt=sprt->next)
 			{
-				if (rt->rt_dev == src_dev)
-					return rt;
+				if (sprt->rt_dev == src_dev)
+					return sprt;
 			}
 			
 			if (flags & RTI_DEVRT)
@@ -980,7 +982,7 @@
 			return rt;
 		}
 
-		return last_resort_rt;		
+		return last_resort_rt;
 	}
 
 	return NULL;
@@ -1535,7 +1537,7 @@
 				args.timeout = DC_LONG_TIMEOUT;
 
 			args.more = 0;
-			rt6_walk_tree(dc_garbage_collect, &args, 0);
+			rt6_walk_tree(dc_garbage_collect, &args, RT6_FILTER_NONE);
 
 			last_gc_run = jiffies;
 			
@@ -1570,7 +1572,7 @@
 		 *	route expiry
 		 */
 		
-		rt6_walk_tree(rt6_rt_timeout, NULL, 1);
+		rt6_walk_tree(rt6_rt_timeout, NULL, RT6_FILTER_RTNODES);
 	}
 
 	restore_flags(flags);
@@ -1631,6 +1633,31 @@
 	return -EINVAL;
 }
 
+static void rt6_ifdown_scan(struct fib6_node *fn, void *arg)
+{
+	struct rt6_info *rt;
+	struct device *dev = (struct device *) arg;
+
+	for (rt = fn->leaf; rt; rt=rt->next)
+	{
+		if (((rt->rt_flags & RTI_DCACHE) == 0) && rt->rt_dev == dev)
+		{
+			struct rt6_req *req;
+
+			req = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
+			req->operation = RT_OPER_DEL;
+			req->ptr  = rt;
+			req->next = req->prev = NULL;
+			rt6_bh_mask |= RT_BH_REQUEST;
+		}
+	}
+}
+
+void rt6_ifdown(struct device *dev)
+{
+	rt6_walk_tree(rt6_ifdown_scan, (void *) dev, RT6_FILTER_RTNODES);
+}
+
 static void rt6_walk_tree(f_pnode func, void * arg, int filter)
 {
 	struct fib6_node *fn;
@@ -1751,7 +1778,7 @@
 	arg.skip = 0;
 	arg.len = 0;
 
-	rt6_walk_tree(rt6_info_node, &arg, 1);
+	rt6_walk_tree(rt6_info_node, &arg, RT6_FILTER_RTNODES);
 	
 	sfn.leaf = default_rt_list;
 	rt6_info_node(&sfn, &arg);
@@ -1795,6 +1822,11 @@
 
 #endif			/* CONFIG_PROC_FS */
 
+/*
+ *	init/cleanup code
+ *
+ */
+
 void ipv6_route_init(void)
 {
 #ifdef 	CONFIG_PROC_FS
@@ -1875,6 +1907,11 @@
 	struct in6_rtmsg *msg;
 	
 	skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+	if (skb == NULL)
+		return;
+
+	skb->free = 1;
+
 	msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
 	
 	msg->rtmsg_type = type;
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.8/linux/net/ipv6/ipv6_sockglue.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv6/ipv6_sockglue.c	Sun Nov 10 19:53:13 1996
@@ -197,7 +197,6 @@
 	case IPV6_DROP_MEMBERSHIP:
 	{
 		struct ipv6_mreq mreq;
-		struct inet6_ifaddr *ifp;
 		struct device *dev = NULL;
 		int err;
 
@@ -205,18 +204,28 @@
 		if(err)
 			return -EFAULT;
 		
-		if (ipv6_addr_any(&mreq.ipv6mr_interface))
+		if (mreq.ipv6mr_ifindex == 0)
 		{
-			/* 
-			 *	FIXME
-			 *	default multicast rule.
-			 */
+			struct in6_addr mcast;
+			struct dest_entry *dc;
+
+			ipv6_addr_set(&mcast, __constant_htonl(0xff000000),
+				      0, 0, 0);
+			dc = ipv6_dst_route(&mcast, NULL, 0);
+
+			if (dc)
+			{
+				dev = dc->rt.rt_dev;
+				ipv6_dst_unlock(dc);
+			}
 		}
 		else
 		{
-			if ((ifp = ipv6_chk_addr(&mreq.ipv6mr_interface)))
+			struct inet6_dev *idev;
+			
+			if ((idev = ipv6_dev_by_index(mreq.ipv6mr_ifindex)))
 			{
-				dev = ifp->idev->dev;
+				dev = idev->dev;
 			}
 		}
 
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.8/linux/net/ipv6/ndisc.c	Sun Nov 10 20:12:31 1996
+++ linux/net/ipv6/ndisc.c	Sat Nov  9 19:50:27 1996
@@ -29,6 +29,7 @@
 
 #define __NO_VERSION__
 #include <linux/module.h>
+#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -40,7 +41,6 @@
 #include <linux/if_arp.h>
 #include <linux/ipv6.h>
 #include <linux/icmpv6.h>
-#include <linux/config.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.8/linux/net/ipv6/sit.c	Sun Nov 10 20:12:31 1996
+++ linux/net/ipv6/sit.c	Sat Nov  9 19:50:27 1996
@@ -286,6 +286,9 @@
 {
 	struct sit_vif *vif;
 	struct device *dev;
+
+	if ((sit_device.flags & IFF_UP) == 0)
+		return NULL;
 	
 	vif = kmalloc(sizeof(struct sit_vif), GFP_KERNEL);
 	if (vif == NULL)
@@ -448,14 +451,30 @@
 	daddr = dev->pa_dstaddr;
 	if (daddr == 0)
 	{
-		addr6 = &skb->ipv6_hdr->daddr;
+		struct neighbour *neigh;
+
+		neigh = skb->nexthop;
+		if (neigh == NULL)
+		{
+			printk(KERN_DEBUG "sit: nexthop == NULL\n");
+			goto on_error;
+		}
+		
+		addr6 = &neigh->addr;
 		addr_type = ipv6_addr_type(addr6);
 
+		if (addr_type == IPV6_ADDR_ANY)
+		{
+			addr6 = &skb->ipv6_hdr->daddr;
+			addr_type = ipv6_addr_type(addr6);
+		}
+
 		if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
 		{
 			printk(KERN_DEBUG "sit_xmit: non v4 address\n");
 			goto on_error;
 		}
+		daddr = addr6->s6_addr32[3];
 	}
 
 	len = skb->tail - (skb->data + sizeof(struct ipv6hdr));
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.8/linux/net/ipv6/tcp_ipv6.c	Sun Nov 10 20:12:31 1996
+++ linux/net/ipv6/tcp_ipv6.c	Sun Nov 10 19:53:14 1996
@@ -779,6 +779,87 @@
 	tcp_statistics.TcpOutSegs++;
 }
 
+struct sock *tcp_v6_check_req(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct open_request *req;
+	
+
+	/*
+	 *	assumption: the socket is not in use.
+	 *	as we checked the user count on tcp_rcv and we're
+	 *	running from a soft interrupt.
+	 */
+		
+	req = tp->syn_wait_queue;
+	
+
+	if (!req)
+	{
+		return sk;
+	}
+	
+	do {
+		struct tcp_v6_open_req *af_req;
+
+		af_req = (struct tcp_v6_open_req *) req;
+
+		if (!ipv6_addr_cmp(&af_req->rmt_addr, &skb->ipv6_hdr->saddr) &&
+		    !ipv6_addr_cmp(&af_req->loc_addr, &skb->ipv6_hdr->daddr) &&
+		    req->rmt_port == skb->h.th->source)
+		{
+			u32 flg;
+				
+			if (req->sk)
+			{
+				printk(KERN_DEBUG "BUG: syn_recv:"
+				       "socket exists\n");
+				break;
+			}
+
+			/* match */
+
+			/*
+			 *	Check for syn retransmission
+			 */
+			flg = *(((u32 *)skb->h.th) + 3);
+			flg &= __constant_htonl(0x002f0000);
+				
+			if ((flg == __constant_htonl(0x00020000)) &&
+			    (!after(skb->seq, req->rcv_isn)))
+			{
+				/*
+				 *	retransmited syn
+				 *	FIXME: must send an ack
+				 */
+				return NULL;
+			}
+
+			atomic_sub(skb->truesize, &sk->rmem_alloc);
+			sk = tp->af_specific->syn_recv_sock(sk, skb, req);
+
+			tcp_dec_slow_timer(TCP_SLT_SYNACK);
+
+			if (sk == NULL)
+			{
+				return NULL;
+			}
+
+			atomic_add(skb->truesize, &sk->rmem_alloc);
+			req->expires = 0UL;
+			req->sk = sk;
+			skb->sk = sk;
+			break;
+		}
+
+		req = req->dl_next;
+	} while (req != tp->syn_wait_queue);
+	
+
+	return sk;
+
+}
+
 int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
 	       struct in6_addr *saddr, struct in6_addr *daddr,
 	       struct ipv6_options *opt, unsigned short len,
@@ -890,40 +971,14 @@
 	
 	if (sk->state == TCP_LISTEN)
 	{
-		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-		struct open_request *req;
-		struct tcp_v6_open_req *af_req;
+		/*
+		 *	find possible connection requests
+		 */
+		sk = tcp_v6_check_req(sk, skb);
 
-		req = tp->syn_wait_queue;
-		af_req = (struct tcp_v6_open_req *) req;
-		
-		if (req)
+		if (sk == NULL)
 		{
-			do {
-				if (!ipv6_addr_cmp(&af_req->rmt_addr, saddr) &&
-				    !ipv6_addr_cmp(&af_req->loc_addr, daddr) &&
-				    req->rmt_port == th->source)
-				{
-					/* match */
-					
-					atomic_sub(skb->truesize, &sk->rmem_alloc);
-					sk = tp->af_specific->syn_recv_sock(sk, skb,
-									    req);
-					tcp_dec_slow_timer(TCP_SLT_SYNACK);
-					
-					if (sk == NULL)
-					{
-						goto no_tcp_socket;
-					}
-					
-					atomic_add(skb->truesize, &sk->rmem_alloc);
-					req->sk = sk;
-					skb->sk = sk;
-					break;
-				}
-
-				req = req->dl_next;
-			} while (req != tp->syn_wait_queue);
+			goto discard_it;
 		}
 
 	}
diff -u --recursive --new-file v2.1.8/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.8/linux/net/ipv6/udp.c	Sun Nov 10 20:12:31 1996
+++ linux/net/ipv6/udp.c	Sun Nov 10 19:53:14 1996
@@ -564,7 +564,7 @@
 	}
 	
 	udh.uh.source = sk->dummy_th.source;
-	udh.uh.len = htons(ulen);
+	udh.uh.len = htons(len);
 	udh.uh.check = 0;
 	udh.iov = msg->msg_iov;
 	udh.wcheck = 0;
diff -u --recursive --new-file v2.1.8/linux/net/netrom/Makefile linux/net/netrom/Makefile
--- v2.1.8/linux/net/netrom/Makefile	Tue Apr  2 08:43:08 1996
+++ linux/net/netrom/Makefile	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 #
-# Makefile for the Linux TCP/IP (INET) layer.
+# Makefile for the Linux NET/ROM layer.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -8,11 +8,8 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := netrom.o
-O_OBJS	 := af_netrom.o sysctl_net_netrom.o
-
-ifdef CONFIG_NETROM
-O_OBJS	+= nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
-endif
+O_OBJS	 := af_netrom.o sysctl_net_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
+M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.8/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.8/linux/net/netrom/af_netrom.c	Tue Oct 29 19:58:50 1996
+++ linux/net/netrom/af_netrom.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	NET/ROM release 003
+ *	NET/ROM release 004
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -26,10 +26,11 @@
  *					a connection.
  *			Alan(GW4PTS)	sendmsg/recvmsg only. Fixed connect clear bug
  *					inherited from AX.25
+ *	NET/ROM 004	Jonathan(G4KLX)	Converted to module.
  */
   
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -60,7 +61,17 @@
 #include <net/arp.h>
 #include <linux/if_arp.h>
 
-struct nr_parms_struct nr_default;
+int sysctl_netrom_default_path_quality            = NR_DEFAULT_QUAL;
+int sysctl_netrom_obsolescence_count_initialiser  = NR_DEFAULT_OBS;
+int sysctl_netrom_network_ttl_initialiser         = NR_DEFAULT_TTL;
+int sysctl_netrom_transport_timeout               = NR_DEFAULT_T1;
+int sysctl_netrom_transport_maximum_tries         = NR_DEFAULT_N2;
+int sysctl_netrom_transport_acknowledge_delay     = NR_DEFAULT_T2;
+int sysctl_netrom_transport_busy_delay            = NR_DEFAULT_T4;
+int sysctl_netrom_transport_requested_window_size = NR_DEFAULT_WINDOW;
+int sysctl_netrom_transport_no_activity_timeout   = NR_DEFAULT_IDLE;
+int sysctl_netrom_transport_packet_length         = NR_DEFAULT_PACLEN;
+int sysctl_netrom_routing_control                 = 1;
 
 static unsigned short circuit = 0x101;
 
@@ -104,13 +115,13 @@
 	struct sock *s;
 	
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (s->nr->device == dev) {
-			s->nr->state  = NR_STATE_0;
-			s->nr->device = NULL;
-			s->state = TCP_CLOSE;
-			s->err   = ENETUNREACH;
+		if (s->protinfo.nr->device == dev) {
+			s->protinfo.nr->state  = NR_STATE_0;
+			s->protinfo.nr->device = NULL;
+			s->state               = TCP_CLOSE;
+			s->err                 = ENETUNREACH;
 			s->state_change(s);
-			s->dead  = 1;
+			s->dead                = 1;
 		}
 	}
 }
@@ -160,7 +171,7 @@
 	cli();
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (ax25cmp(&s->nr->source_addr, addr) == 0 && s->state == TCP_LISTEN) {
+		if (ax25cmp(&s->protinfo.nr->source_addr, addr) == 0 && s->state == TCP_LISTEN) {
 			restore_flags(flags);
 			return s;
 		}
@@ -182,7 +193,7 @@
 	cli();
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (s->nr->my_index == index && s->nr->my_id == id) {
+		if (s->protinfo.nr->my_index == index && s->protinfo.nr->my_id == id) {
 			restore_flags(flags);
 			return s;
 		}
@@ -205,7 +216,7 @@
 	cli();
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (s->nr->your_index == index && s->nr->your_id == id) {
+		if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id) {
 			restore_flags(flags);
 			return s;
 		}
@@ -252,7 +263,7 @@
 		if (skb->sk != sk) {			/* A pending connection */
 			skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
 			nr_set_timer(skb->sk);
-			skb->sk->nr->state = NR_STATE_0;
+			skb->sk->protinfo.nr->state = NR_STATE_0;
 		}
 
 		kfree_skb(skb, FREE_READ);
@@ -265,7 +276,7 @@
 		sk->timer.data     = (unsigned long)sk;
 		add_timer(&sk->timer);
 	} else {
-		kfree_s(sk->nr, sizeof(*sk->nr));
+		kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr));
 		sk_free(sk);
 	}
 
@@ -306,23 +317,23 @@
 		case NETROM_KILL:
 			nr_clear_queues(sk);
 			nr_write_internal(sk, NR_DISCREQ);
-			sk->nr->state = NR_STATE_0;
-			sk->state = TCP_CLOSE;
-			sk->err   = ENETRESET;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = ENETRESET;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead  = 1;
+			sk->dead               = 1;
 			nr_set_timer(sk);
 	  		break;
 
 	  	case NETROM_T1:
   			if (nr_ctl.arg < 1) 
   				return -EINVAL;
-  			sk->nr->rtt = (nr_ctl.arg * PR_SLOWHZ) / 2;
-  			sk->nr->t1 = nr_ctl.arg * PR_SLOWHZ;
+  			sk->protinfo.nr->rtt = (nr_ctl.arg * PR_SLOWHZ) / 2;
+  			sk->protinfo.nr->t1  = nr_ctl.arg * PR_SLOWHZ;
   			save_flags(flags); cli();
-  			if (sk->nr->t1timer > sk->nr->t1)
-  				sk->nr->t1timer = sk->nr->t1;
+  			if (sk->protinfo.nr->t1timer > sk->protinfo.nr->t1)
+  				sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
   			restore_flags(flags);
   			break;
 
@@ -330,17 +341,37 @@
 	  		if (nr_ctl.arg < 1) 
 	  			return -EINVAL;
 	  		save_flags(flags); cli();
-	  		sk->nr->t2 = nr_ctl.arg * PR_SLOWHZ;
-	  		if (sk->nr->t2timer > sk->nr->t2)
-	  			sk->nr->t2timer = sk->nr->t2;
+	  		sk->protinfo.nr->t2 = nr_ctl.arg * PR_SLOWHZ;
+	  		if (sk->protinfo.nr->t2timer > sk->protinfo.nr->t2)
+	  			sk->protinfo.nr->t2timer = sk->protinfo.nr->t2;
 	  		restore_flags(flags);
 	  		break;
 
 	  	case NETROM_N2:
 	  		if (nr_ctl.arg < 1 || nr_ctl.arg > 10) 
 	  			return -EINVAL;
-	  		sk->nr->n2count = 0;
-	  		sk->nr->n2 = nr_ctl.arg;
+	  		sk->protinfo.nr->n2count = 0;
+	  		sk->protinfo.nr->n2      = nr_ctl.arg;
+	  		break;
+
+	  	case NETROM_T4:
+	  		if (nr_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.nr->t4 = nr_ctl.arg * PR_SLOWHZ;
+	  		if (sk->protinfo.nr->t4timer > sk->protinfo.nr->t4)
+	  			sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
+	  		restore_flags(flags);
+	  		break;
+
+	  	case NETROM_IDLE:
+	  		if (nr_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.nr->idle = nr_ctl.arg * 60 * PR_SLOWHZ;
+	  		if (sk->protinfo.nr->idletimer > sk->protinfo.nr->idle)
+	  			sk->protinfo.nr->idletimer = sk->protinfo.nr->idle;
+	  		restore_flags(flags);
 	  		break;
 
 	  	case NETROM_PACLEN:
@@ -348,7 +379,7 @@
 	  			return -EINVAL;
 	  		if (nr_ctl.arg > 236) /* we probably want this */
 	  			printk(KERN_WARNING "nr_ctl_ioctl: Warning --- huge paclen %d\n", (int)nr_ctl.arg);
-	  		sk->nr->paclen = nr_ctl.arg;
+	  		sk->protinfo.nr->paclen = nr_ctl.arg;
 	  		break;
 
 	  	default:
@@ -378,35 +409,47 @@
 	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
 		return err;
 
-	opt = get_fs_long((unsigned long *)optval);
+	get_user(opt, (int *)optval);
 	
 	switch (optname) {
 		case NETROM_T1:
 			if (opt < 1)
 				return -EINVAL;
-			sk->nr->rtt = (opt * PR_SLOWHZ) / 2;
+			sk->protinfo.nr->rtt = (opt * PR_SLOWHZ) / 2;
 			return 0;
 
 		case NETROM_T2:
 			if (opt < 1)
 				return -EINVAL;
-			sk->nr->t2 = opt * PR_SLOWHZ;
+			sk->protinfo.nr->t2 = opt * PR_SLOWHZ;
 			return 0;
 			
 		case NETROM_N2:
 			if (opt < 1 || opt > 31)
 				return -EINVAL;
-			sk->nr->n2 = opt;
+			sk->protinfo.nr->n2 = opt;
+			return 0;
+			
+		case NETROM_T4:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.nr->t4 = opt * PR_SLOWHZ;
+			return 0;
+			
+		case NETROM_IDLE:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.nr->idle = opt * 60 * PR_SLOWHZ;
 			return 0;
 			
 		case NETROM_HDRINCL:
-			sk->nr->hdrincl = opt ? 1 : 0;
+			sk->protinfo.nr->hdrincl = opt ? 1 : 0;
 			return 0;
 
 		case NETROM_PACLEN:
 			if (opt < 1 || opt > 65536)
 				return -EINVAL;
-			sk->nr->paclen = opt;
+			sk->protinfo.nr->paclen = opt;
 			return 0;
 			
 		default:
@@ -431,23 +474,31 @@
 	
 	switch (optname) {
 		case NETROM_T1:
-			val = (sk->nr->t1 * 2) / PR_SLOWHZ;
+			val = (sk->protinfo.nr->t1 * 2) / PR_SLOWHZ;
 			break;
 			
 		case NETROM_T2:
-			val = sk->nr->t2 / PR_SLOWHZ;
+			val = sk->protinfo.nr->t2 / PR_SLOWHZ;
 			break;
 			
 		case NETROM_N2:
-			val = sk->nr->n2;
+			val = sk->protinfo.nr->n2;
 			break;
 						
+		case NETROM_T4:
+			val = sk->protinfo.nr->t4 / PR_SLOWHZ;
+			break;
+			
+		case NETROM_IDLE:
+			val = sk->protinfo.nr->idle / (PR_SLOWHZ * 60);
+			break;
+			
 		case NETROM_HDRINCL:
-			val = sk->nr->hdrincl;
+			val = sk->protinfo.nr->hdrincl;
 			break;
 
 		case NETROM_PACLEN:
-			val = sk->nr->paclen;
+			val = sk->protinfo.nr->paclen;
 			break;
 
 		default:
@@ -472,7 +523,7 @@
 	struct sock *sk = (struct sock *)sock->data;
 
 	if (sk->state != TCP_LISTEN) {
-		memset(&sk->nr->user_addr, '\0', AX25_ADDR_LEN);
+		memset(&sk->protinfo.nr->user_addr, '\0', AX25_ADDR_LEN);
 		sk->max_ack_backlog = backlog;
 		sk->state           = TCP_LISTEN;
 		return 0;
@@ -525,7 +576,7 @@
 	sk->priority      = SOPRI_NORMAL;
 	sk->mtu           = NETROM_MTU;	/* 236 */
 	sk->zapped        = 1;
-	sk->window	  = nr_default.window;
+	sk->window	  = sysctl_netrom_transport_requested_window_size;
 
 	sk->state_change = def_callback1;
 	sk->data_ready   = def_callback2;
@@ -543,16 +594,19 @@
 
 	nr->my_index = 0;
 	nr->my_id    = 0;
-	nr->rtt      = nr_default.timeout / 2;
-	nr->t1       = nr_default.timeout;
-	nr->t2       = nr_default.ack_delay;
-	nr->n2       = nr_default.tries;
-	nr->paclen   = nr_default.paclen;
-
-	nr->t1timer  = 0;
-	nr->t2timer  = 0;
-	nr->t4timer  = 0;
-	nr->n2count  = 0;
+	nr->rtt      = sysctl_netrom_transport_timeout / 2;
+	nr->t1       = sysctl_netrom_transport_timeout;
+	nr->t2       = sysctl_netrom_transport_acknowledge_delay;
+	nr->n2       = sysctl_netrom_transport_maximum_tries;
+	nr->t4       = sysctl_netrom_transport_busy_delay;
+	nr->idle     = sysctl_netrom_transport_no_activity_timeout;
+	nr->paclen   = sysctl_netrom_transport_packet_length;
+
+	nr->t1timer   = 0;
+	nr->t2timer   = 0;
+	nr->t4timer   = 0;
+	nr->idletimer = 0;
+	nr->n2count   = 0;
 
 	nr->va       = 0;
 	nr->vr       = 0;
@@ -575,8 +629,8 @@
 	memset(&nr->user_addr,   '\0', AX25_ADDR_LEN);
 	memset(&nr->dest_addr,   '\0', AX25_ADDR_LEN);
 
-	nr->sk = sk;
-	sk->nr = nr;
+	nr->sk          = sk;
+	sk->protinfo.nr = nr;
 
 	return 0;
 }
@@ -625,29 +679,32 @@
 	skb_queue_head_init(&nr->reseq_queue);
 	skb_queue_head_init(&nr->frag_queue);
 
-	nr->rtt      = osk->nr->rtt;
-	nr->t1       = osk->nr->t1;
-	nr->t2       = osk->nr->t2;
-	nr->n2       = osk->nr->n2;
-	nr->paclen   = osk->nr->paclen;
-
-	nr->device   = osk->nr->device;
-	nr->bpqext   = osk->nr->bpqext;
-	nr->hdrincl  = osk->nr->hdrincl;
+	nr->rtt      = osk->protinfo.nr->rtt;
+	nr->t1       = osk->protinfo.nr->t1;
+	nr->t2       = osk->protinfo.nr->t2;
+	nr->n2       = osk->protinfo.nr->n2;
+	nr->t4       = osk->protinfo.nr->t4;
+	nr->idle     = osk->protinfo.nr->idle;
+	nr->paclen   = osk->protinfo.nr->paclen;
+
+	nr->device   = osk->protinfo.nr->device;
+	nr->bpqext   = osk->protinfo.nr->bpqext;
+	nr->hdrincl  = osk->protinfo.nr->hdrincl;
 	nr->fraglen  = 0;
 
-	nr->t1timer  = 0;
-	nr->t2timer  = 0;
-	nr->t4timer  = 0;
-	nr->n2count  = 0;
+	nr->t1timer   = 0;
+	nr->t2timer   = 0;
+	nr->t4timer   = 0;
+	nr->idletimer = 0;
+	nr->n2count   = 0;
 
 	nr->va       = 0;
 	nr->vr       = 0;
 	nr->vs       = 0;
 	nr->vl       = 0;
 	
-	sk->nr = nr;
-	nr->sk = sk;
+	sk->protinfo.nr = nr;
+	nr->sk          = sk;
 
 	return sk;
 }
@@ -665,7 +722,7 @@
 
 	if (sk == NULL) return 0;
 
-	switch (sk->nr->state) {
+	switch (sk->protinfo.nr->state) {
 
 		case NR_STATE_0:
 			sk->state     = TCP_CLOSE;
@@ -675,34 +732,34 @@
 			break;
 
 		case NR_STATE_1:
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
 			sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			nr_destroy_socket(sk);
 			break;
 
 		case NR_STATE_2:
 			nr_write_internal(sk, NR_DISCACK);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
 			sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			nr_destroy_socket(sk);
 			break;			
 
 		case NR_STATE_3:
 			nr_clear_queues(sk);
-			sk->nr->n2count = 0;
+			sk->protinfo.nr->n2count = 0;
 			nr_write_internal(sk, NR_DISCREQ);
-			sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
-			sk->nr->t2timer = 0;
-			sk->nr->t4timer = 0;
-			sk->nr->state   = NR_STATE_2;
-			sk->state       = TCP_CLOSE;
+			sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
+			sk->protinfo.nr->t2timer = 0;
+			sk->protinfo.nr->t4timer = 0;
+			sk->protinfo.nr->state   = NR_STATE_2;
+			sk->state                = TCP_CLOSE;
 			sk->state_change(sk);
-			sk->dead        = 1;
-			sk->destroy     = 1;
+			sk->dead                 = 1;
+			sk->destroy              = 1;
 			break;
 
 		default:
@@ -742,8 +799,8 @@
 	if (addr->fsa_ax25.sax25_ndigis == 1) {
 		if (!suser())
 			return -EPERM;
-		sk->nr->user_addr   = addr->fsa_digipeater[0];
-		sk->nr->source_addr = addr->fsa_ax25.sax25_call;
+		sk->protinfo.nr->user_addr   = addr->fsa_digipeater[0];
+		sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call;
 	} else {
 		source = &addr->fsa_ax25.sax25_call;
 
@@ -753,11 +810,11 @@
 			user = source;
 		}
 
-		sk->nr->user_addr   = *user;
-		sk->nr->source_addr = *source;
+		sk->protinfo.nr->user_addr   = *user;
+		sk->protinfo.nr->source_addr = *source;
 	}
 
-	sk->nr->device = dev;
+	sk->protinfo.nr->device = dev;
 	nr_insert_socket(sk);
 
 	sk->zapped = 0;
@@ -792,7 +849,7 @@
 	sk->state   = TCP_CLOSE;	
 	sock->state = SS_UNCONNECTED;
 
-	if (addr_len != sizeof(struct sockaddr_ax25))
+	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 		return -EINVAL;
 
 	if (sk->zapped) {	/* Must bind first - autobinding in this may or may not work */
@@ -809,28 +866,28 @@
 			user = source;
 		}
 
-		sk->nr->user_addr   = *user;
-		sk->nr->source_addr = *source;
-		sk->nr->device      = dev;
+		sk->protinfo.nr->user_addr   = *user;
+		sk->protinfo.nr->source_addr = *source;
+		sk->protinfo.nr->device      = dev;
 
 		nr_insert_socket(sk);		/* Finish the bind */
 	}
 
-	sk->nr->dest_addr = addr->sax25_call;
+	sk->protinfo.nr->dest_addr = addr->sax25_call;
 
 	while (nr_find_socket((unsigned char)circuit / 256, (unsigned char)circuit % 256) != NULL)
 		circuit++;
 
-	sk->nr->my_index = circuit / 256;
-	sk->nr->my_id    = circuit % 256;
+	sk->protinfo.nr->my_index = circuit / 256;
+	sk->protinfo.nr->my_id    = circuit % 256;
 
 	circuit++;
 	
 	/* Move to connecting socket, start sending Connect Requests */
-	sock->state   = SS_CONNECTING;
-	sk->state     = TCP_SYN_SENT;
+	sock->state            = SS_CONNECTING;
+	sk->state              = TCP_SYN_SENT;
 	nr_establish_data_link(sk);
-	sk->nr->state = NR_STATE_1;
+	sk->protinfo.nr->state = NR_STATE_1;
 	nr_set_timer(sk);
 	
 	/* Now the loop */
@@ -932,13 +989,13 @@
 			return -ENOTCONN;
 		sax->fsa_ax25.sax25_family = AF_NETROM;
 		sax->fsa_ax25.sax25_ndigis = 1;
-		sax->fsa_ax25.sax25_call = sk->nr->user_addr;
-		sax->fsa_digipeater[0]   = sk->nr->dest_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.nr->user_addr;
+		sax->fsa_digipeater[0]     = sk->protinfo.nr->dest_addr;
 		*uaddr_len = sizeof(struct full_sockaddr_ax25);
 	} else {
 		sax->fsa_ax25.sax25_family = AF_NETROM;
 		sax->fsa_ax25.sax25_ndigis = 0;
-		sax->fsa_ax25.sax25_call   = sk->nr->source_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.nr->source_addr;
 		*uaddr_len = sizeof(struct sockaddr_ax25);
 	}
 
@@ -987,9 +1044,9 @@
 		skb->h.raw = skb->data;
 
 		if ((frametype & 0x0F) == NR_CONNACK && skb->len == 22)
-			sk->nr->bpqext = 1;
+			sk->protinfo.nr->bpqext = 1;
 		else
-			sk->nr->bpqext = 0;
+			sk->protinfo.nr->bpqext = 0;
 
 		return nr_process_rx_frame(sk, skb);
 	}
@@ -1012,15 +1069,15 @@
 	make->state         = TCP_ESTABLISHED;
 
 	/* Fill in his circuit details */
-	make->nr->source_addr = *dest;
-	make->nr->dest_addr   = *src;
-	make->nr->user_addr   = *user;
+	make->protinfo.nr->source_addr = *dest;
+	make->protinfo.nr->dest_addr   = *src;
+	make->protinfo.nr->user_addr   = *user;
 
-	make->nr->your_index = circuit_index;
-	make->nr->your_id    = circuit_id;
+	make->protinfo.nr->your_index  = circuit_index;
+	make->protinfo.nr->your_id     = circuit_id;
 
-	make->nr->my_index   = circuit / 256;
-	make->nr->my_id      = circuit % 256;
+	make->protinfo.nr->my_index    = circuit / 256;
+	make->protinfo.nr->my_id       = circuit % 256;
 	
 	circuit++;
 
@@ -1031,21 +1088,21 @@
 	/* L4 timeout negotiation */
 	if (skb->len == 37) {
 		timeout = skb->data[36] * 256 + skb->data[35];
-		if (timeout * PR_SLOWHZ < make->nr->rtt * 2)
-			make->nr->rtt = (timeout * PR_SLOWHZ) / 2;
-		make->nr->bpqext = 1;
+		if (timeout * PR_SLOWHZ < make->protinfo.nr->rtt * 2)
+			make->protinfo.nr->rtt = (timeout * PR_SLOWHZ) / 2;
+		make->protinfo.nr->bpqext = 1;
 	} else {
-		make->nr->bpqext = 0;
+		make->protinfo.nr->bpqext = 0;
 	}
 
 	nr_write_internal(make, NR_CONNACK);
 
-	make->nr->condition = 0x00;
-	make->nr->vs        = 0;
-	make->nr->va        = 0;
-	make->nr->vr        = 0;
-	make->nr->vl        = 0;
-	make->nr->state     = NR_STATE_3;
+	make->protinfo.nr->condition = 0x00;
+	make->protinfo.nr->vs        = 0;
+	make->protinfo.nr->va        = 0;
+	make->protinfo.nr->vr        = 0;
+	make->protinfo.nr->vl        = 0;
+	make->protinfo.nr->state     = NR_STATE_3;
 	sk->ack_backlog++;
 	make->pair = sk;
 
@@ -1080,14 +1137,14 @@
 	if (sk->zapped)
 		return -EADDRNOTAVAIL;
 
-	if (sk->nr->device == NULL)
+	if (sk->protinfo.nr->device == NULL)
 		return -ENETUNREACH;
 		
 	if (usax) {
 		if (msg->msg_namelen < sizeof(sax))
 			return -EINVAL;
 		sax = *usax;
-		if (ax25cmp(&sk->nr->dest_addr, &sax.sax25_call) != 0)
+		if (ax25cmp(&sk->protinfo.nr->dest_addr, &sax.sax25_call) != 0)
 			return -EISCONN;
 		if (sax.sax25_family != AF_NETROM)
 			return -EINVAL;
@@ -1095,7 +1152,7 @@
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		sax.sax25_family = AF_NETROM;
-		sax.sax25_call   = sk->nr->dest_addr;
+		sax.sax25_call   = sk->protinfo.nr->dest_addr;
 	}
 	
 	if (sk->debug)
@@ -1127,8 +1184,8 @@
 
 	/* Build a NET/ROM Transport header */
 
-	*asmptr++ = sk->nr->your_index;
-	*asmptr++ = sk->nr->your_id;
+	*asmptr++ = sk->protinfo.nr->your_index;
+	*asmptr++ = sk->protinfo.nr->your_id;
 	*asmptr++ = 0;		/* To be filled in later */
 	*asmptr++ = 0;		/*      Ditto            */
 	*asmptr++ = NR_INFO;
@@ -1190,7 +1247,7 @@
 	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
 		return er;
 
-	if (!sk->nr->hdrincl) {
+	if (!sk->protinfo.nr->hdrincl) {
 		skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 		skb->h.raw = skb->data;
 	}
@@ -1279,30 +1336,9 @@
 		case SIOCADDRT:
 		case SIOCDELRT:
 		case SIOCNRDECOBS:
-		case SIOCNRRTCTL:
 			if (!suser()) return -EPERM;
 			return nr_rt_ioctl(cmd, (void *)arg);
 
-		case SIOCNRGETPARMS: {
-			struct nr_parms_struct nr_parms;
-			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct nr_parms_struct))) != 0)
-				return err;
-			copy_from_user(&nr_parms, (void *)arg, sizeof(struct nr_parms_struct));
-			nr_parms = nr_default;
-			copy_to_user((void *)arg, &nr_parms, sizeof(struct nr_parms_struct));
-			return 0;
-		}
-
-		case SIOCNRSETPARMS: {
-			struct nr_parms_struct nr_parms;
-			if (!suser()) return -EPERM;
-			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(struct nr_parms_struct))) != 0)
-				return err;
-			copy_from_user(&nr_parms, (void *)arg, sizeof(struct nr_parms_struct));
-			nr_default = nr_parms;
-			return 0;
-		}
-		
  		case SIOCNRCTLCON:
  			if (!suser()) return -EPERM;
  			return nr_ctl_ioctl(cmd, (void *)arg);
@@ -1329,28 +1365,28 @@
 	len += sprintf(buffer, "user_addr dest_node src_node  dev    my  your  st  vs  vr  va    t1     t2    n2  rtt wnd paclen Snd-Q Rcv-Q\n");
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if ((dev = s->nr->device) == NULL)
+		if ((dev = s->protinfo.nr->device) == NULL)
 			devname = "???";
 		else
 			devname = dev->name;
 	
 		len += sprintf(buffer + len, "%-9s ",
-			ax2asc(&s->nr->user_addr));
+			ax2asc(&s->protinfo.nr->user_addr));
 		len += sprintf(buffer + len, "%-9s ",
-			ax2asc(&s->nr->dest_addr));
+			ax2asc(&s->protinfo.nr->dest_addr));
 		len += sprintf(buffer + len, "%-9s %-3s  %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %2d/%02d %3d %3d %6d %5d %5d\n",
-			ax2asc(&s->nr->source_addr),
-			devname, s->nr->my_index, s->nr->my_id,
-			s->nr->your_index, s->nr->your_id,
-			s->nr->state,
-			s->nr->vs, s->nr->vr, s->nr->va,
-			s->nr->t1timer / PR_SLOWHZ,
-			s->nr->t1      / PR_SLOWHZ,
-			s->nr->t2timer / PR_SLOWHZ,
-			s->nr->t2      / PR_SLOWHZ,
-			s->nr->n2count, s->nr->n2,
-			s->nr->rtt     / PR_SLOWHZ,
-			s->window, s->nr->paclen,
+			ax2asc(&s->protinfo.nr->source_addr),
+			devname, s->protinfo.nr->my_index, s->protinfo.nr->my_id,
+			s->protinfo.nr->your_index, s->protinfo.nr->your_id,
+			s->protinfo.nr->state,
+			s->protinfo.nr->vs, s->protinfo.nr->vr, s->protinfo.nr->va,
+			s->protinfo.nr->t1timer / PR_SLOWHZ,
+			s->protinfo.nr->t1      / PR_SLOWHZ,
+			s->protinfo.nr->t2timer / PR_SLOWHZ,
+			s->protinfo.nr->t2      / PR_SLOWHZ,
+			s->protinfo.nr->n2count, s->protinfo.nr->n2,
+			s->protinfo.nr->rtt     / PR_SLOWHZ,
+			s->window, s->protinfo.nr->paclen,
 			s->wmem_alloc, s->rmem_alloc);
 		
 		pos = begin + len;
@@ -1374,7 +1410,7 @@
 	return(len);
 } 
 
-static struct proto_ops nr_proto_ops = {
+struct proto_ops nr_proto_ops = {
 	AF_NETROM,
 	
 	nr_create,
@@ -1396,7 +1432,7 @@
 	nr_recvmsg
 };
 
-static struct notifier_block nr_dev_notifier = {
+struct notifier_block nr_dev_notifier = {
 	nr_device_event,
 	0
 };
@@ -1426,17 +1462,14 @@
 {
 	sock_register(nr_proto_ops.family, &nr_proto_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
-	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.5 for AX25.032 Linux 2.0\n");
+	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.5 for AX25.033 Linux 2.0\n");
+
+	if (!ax25_protocol_register(AX25_P_NETROM, nr_route_frame))
+		printk(KERN_ERR "NET/ROM unable to register protocol with AX.25\n");
+	if (!ax25_linkfail_register(nr_link_failed))
+		printk(KERN_ERR "NET/ROM unable to register linkfail handler with AX.25\n");
 
-	nr_default.quality    = NR_DEFAULT_QUAL;
-	nr_default.obs_count  = NR_DEFAULT_OBS;
-	nr_default.ttl        = NR_DEFAULT_TTL;
-	nr_default.timeout    = NR_DEFAULT_T1;
-	nr_default.ack_delay  = NR_DEFAULT_T2;
-	nr_default.busy_delay = NR_DEFAULT_T4;
-	nr_default.tries      = NR_DEFAULT_N2;
-	nr_default.window     = NR_DEFAULT_WINDOW;
-	nr_default.paclen     = NR_DEFAULT_PACLEN;
+	nr_register_sysctl();
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_nr);
diff -u --recursive --new-file v2.1.8/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v2.1.8/linux/net/netrom/nr_dev.c	Tue Oct 29 19:58:51 1996
+++ linux/net/netrom/nr_dev.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	NET/ROM release 003
+ *	NET/ROM release 004
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -17,15 +17,19 @@
  *	NET/ROM 002	Steve Whitehouse(GW7RRM) fixed the set_mac_address
  *	NET/ROM 003	Jonathan(G4KLX)	Put nr_rebuild_header into line with
  *					ax25_rebuild_header
+ *	NET/ROM 004	Jonathan(G4KLX)	Callsign registration with AX.25.
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <linux/module.h>
+#include <linux/proc_fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/fs.h>
 #include <linux/types.h>
+#include <linux/sysctl.h>
 #include <linux/string.h>
 #include <linux/socket.h>
 #include <linux/errno.h>
@@ -92,7 +96,7 @@
 	buff[6] |= SSSID_SPARE;
 	buff    += AX25_ADDR_LEN;
 
-	*buff++ = nr_default.ttl;
+	*buff++ = sysctl_netrom_network_ttl_initialiser;
 
 	*buff++ = NR_PROTO_IP;
 	*buff++ = NR_PROTO_IP;
@@ -151,8 +155,13 @@
 
 static int nr_set_mac_address(struct device *dev, void *addr)
 {
-	struct sockaddr *sa=addr;
+	struct sockaddr *sa = addr;
+
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
 	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
 
 	return 0;
 }
@@ -162,6 +171,10 @@
 	dev->tbusy = 0;
 	dev->start = 1;
 
+	MOD_INC_USE_COUNT;
+
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+
 	return 0;
 }
 
@@ -170,6 +183,10 @@
 	dev->tbusy = 1;
 	dev->start = 0;
 
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
+	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
@@ -239,8 +256,7 @@
 	dev->pa_mask		= 0;
 	dev->pa_alen		= sizeof(unsigned long);
 
-	dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
-	if (dev->priv == NULL)
+	if ((dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL)) == NULL)
 		return -ENOMEM;
 
 	memset(dev->priv, 0, sizeof(struct enet_statistics));
@@ -253,5 +269,61 @@
 
 	return 0;
 };
+
+#ifdef MODULE
+extern struct proto_ops nr_proto_ops;
+extern struct notifier_block nr_dev_notifier;
+
+static struct device dev_nr[] = {
+	{"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}
+};
+
+int init_module(void)
+{
+	int i;
+
+	for (i = 0; i < 4; i++)
+		register_netdev(&dev_nr[i]);
+
+	register_symtab(NULL);
+
+	nr_proto_init(NULL);
+	
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_NR);
+	proc_net_unregister(PROC_NET_NR_NEIGH);
+	proc_net_unregister(PROC_NET_NR_NODES);
+#endif
+	nr_rt_free();
+
+	ax25_protocol_release(AX25_P_NETROM);
+	ax25_linkfail_release(nr_link_failed);
+
+	unregister_netdevice_notifier(&nr_dev_notifier);
+
+	nr_unregister_sysctl();
+
+	sock_unregister(nr_proto_ops.family);
+	
+	for (i = 0; i < 4; i++) {
+		if (dev_nr[i].priv != NULL) {
+			kfree(dev_nr[i].priv);
+			dev_nr[i].priv = NULL;
+			unregister_netdev(&dev_nr[i]);
+		}
+	}
+}
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/net/netrom/nr_in.c linux/net/netrom/nr_in.c
--- v2.1.8/linux/net/netrom/nr_in.c	Tue Oct 29 19:58:51 1996
+++ linux/net/netrom/nr_in.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	NET/ROM release 003
+ *	NET/ROM release 004
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -26,7 +26,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -55,16 +55,16 @@
 	struct sk_buff *skbo, *skbn = skb;
 
 	if (more) {
-		sk->nr->fraglen += skb->len;
-		skb_queue_tail(&sk->nr->frag_queue, skb);
+		sk->protinfo.nr->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
 		return 0;
 	}
 	
-	if (!more && sk->nr->fraglen > 0) {	/* End of fragment */
-		sk->nr->fraglen += skb->len;
-		skb_queue_tail(&sk->nr->frag_queue, skb);
+	if (!more && sk->protinfo.nr->fraglen > 0) {	/* End of fragment */
+		sk->protinfo.nr->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
 
-		if ((skbn = alloc_skb(sk->nr->fraglen, GFP_ATOMIC)) == NULL)
+		if ((skbn = alloc_skb(sk->protinfo.nr->fraglen, GFP_ATOMIC)) == NULL)
 			return 1;
 
 		skbn->free = 1;
@@ -73,17 +73,17 @@
 		sk->rmem_alloc += skbn->truesize;
 		skbn->h.raw = skbn->data;
 
-		skbo = skb_dequeue(&sk->nr->frag_queue);
+		skbo = skb_dequeue(&sk->protinfo.nr->frag_queue);
 		memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
 		kfree_skb(skbo, FREE_READ);
 
-		while ((skbo = skb_dequeue(&sk->nr->frag_queue)) != NULL) {
+		while ((skbo = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL) {
 			skb_pull(skbo, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 			memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
 			kfree_skb(skbo, FREE_READ);
 		}
 
-		sk->nr->fraglen = 0;		
+		sk->protinfo.nr->fraglen = 0;		
 	}
 
 	return sock_queue_rcv_skb(sk, skbn);
@@ -100,19 +100,19 @@
 
 		case NR_CONNACK:
 			nr_calculate_rtt(sk);
-			sk->window         = skb->data[20];
-			sk->nr->your_index = skb->data[17];
-			sk->nr->your_id    = skb->data[18];
-			sk->nr->t1timer    = 0;
-			sk->nr->t2timer    = 0;
-			sk->nr->t4timer    = 0;
-			sk->nr->vs         = 0;
-			sk->nr->va         = 0;
-			sk->nr->vr         = 0;
-			sk->nr->vl	   = 0;
-			sk->nr->state      = NR_STATE_3;
-			sk->state          = TCP_ESTABLISHED;
-			sk->nr->n2count    = 0;
+			sk->protinfo.nr->your_index = skb->data[17];
+			sk->protinfo.nr->your_id    = skb->data[18];
+			sk->protinfo.nr->t1timer    = 0;
+			sk->protinfo.nr->t2timer    = 0;
+			sk->protinfo.nr->t4timer    = 0;
+			sk->protinfo.nr->vs         = 0;
+			sk->protinfo.nr->va         = 0;
+			sk->protinfo.nr->vr         = 0;
+			sk->protinfo.nr->vl	    = 0;
+			sk->protinfo.nr->state      = NR_STATE_3;
+			sk->protinfo.nr->n2count    = 0;
+			sk->window                  = skb->data[20];
+			sk->state                   = TCP_ESTABLISHED;
 			/* For WAIT_SABM connections we will produce an accept ready socket here */
 			if (!sk->dead)
 				sk->state_change(sk);
@@ -120,12 +120,12 @@
 
 		case NR_CONNACK | NR_CHOKE_FLAG:
 			nr_clear_queues(sk);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = ECONNREFUSED;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = ECONNREFUSED;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
 		default:
@@ -148,12 +148,12 @@
 			nr_write_internal(sk, NR_DISCACK);
 
 		case NR_DISCACK:
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = 0;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = 0;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
 		default:
@@ -188,22 +188,22 @@
 		case NR_DISCREQ:
 			nr_clear_queues(sk);
 			nr_write_internal(sk, NR_DISCACK);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = 0;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = 0;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
 		case NR_DISCACK:
 			nr_clear_queues(sk);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = ECONNRESET;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = ECONNRESET;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
 		case NR_INFOACK:
@@ -211,11 +211,11 @@
 		case NR_INFOACK | NR_NAK_FLAG:
 		case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG:
 			if (frametype & NR_CHOKE_FLAG) {
-				sk->nr->condition |= PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = nr_default.busy_delay;
+				sk->protinfo.nr->condition |= PEER_RX_BUSY_CONDITION;
+				sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
 			} else {
-				sk->nr->condition &= ~PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = 0;
+				sk->protinfo.nr->condition &= ~PEER_RX_BUSY_CONDITION;
+				sk->protinfo.nr->t4timer = 0;
 			}
 			if (!nr_validate_nr(sk, nr)) {
 				break;
@@ -224,7 +224,7 @@
 				nr_frames_acked(sk, nr);
 				nr_send_nak_frame(sk);
 			} else {
-				if (sk->nr->condition & PEER_RX_BUSY_CONDITION) {
+				if (sk->protinfo.nr->condition & PEER_RX_BUSY_CONDITION) {
 					nr_frames_acked(sk, nr);
 				} else {
 					nr_check_iframes_acked(sk, nr);
@@ -241,18 +241,18 @@
 		case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG:
 		case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG:
 			if (frametype & NR_CHOKE_FLAG) {
-				sk->nr->condition |= PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = nr_default.busy_delay;
+				sk->protinfo.nr->condition |= PEER_RX_BUSY_CONDITION;
+				sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
 			} else {
-				sk->nr->condition &= ~PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = 0;
+				sk->protinfo.nr->condition &= ~PEER_RX_BUSY_CONDITION;
+				sk->protinfo.nr->t4timer = 0;
 			}
 			if (nr_validate_nr(sk, nr)) {
 				if (frametype & NR_NAK_FLAG) {
 					nr_frames_acked(sk, nr);
 					nr_send_nak_frame(sk);
 				} else {
-					if (sk->nr->condition & PEER_RX_BUSY_CONDITION) {
+					if (sk->protinfo.nr->condition & PEER_RX_BUSY_CONDITION) {
 						nr_frames_acked(sk, nr);
 					} else {
 						nr_check_iframes_acked(sk, nr);
@@ -260,19 +260,19 @@
 				}
 			}
 			queued = 1;
-			skb_queue_head(&sk->nr->reseq_queue, skb);
-			if (sk->nr->condition & OWN_RX_BUSY_CONDITION)
+			skb_queue_head(&sk->protinfo.nr->reseq_queue, skb);
+			if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)
 				break;
 			skb_queue_head_init(&temp_queue);
 			do {
-				save_vr = sk->nr->vr;
-				while ((skbn = skb_dequeue(&sk->nr->reseq_queue)) != NULL) {
+				save_vr = sk->protinfo.nr->vr;
+				while ((skbn = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL) {
 					ns = skbn->data[17];
-					if (ns == sk->nr->vr) {
+					if (ns == sk->protinfo.nr->vr) {
 						if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) {
-							sk->nr->vr = (sk->nr->vr + 1) % NR_MODULUS;
+							sk->protinfo.nr->vr = (sk->protinfo.nr->vr + 1) % NR_MODULUS;
 						} else {
-							sk->nr->condition |= OWN_RX_BUSY_CONDITION;
+							sk->protinfo.nr->condition |= OWN_RX_BUSY_CONDITION;
 							skb_queue_tail(&temp_queue, skbn);
 						}
 					} else if (nr_in_rx_window(sk, ns)) {
@@ -283,18 +283,18 @@
 					}
 				}
 				while ((skbn = skb_dequeue(&temp_queue)) != NULL) {
-					skb_queue_tail(&sk->nr->reseq_queue, skbn);
+					skb_queue_tail(&sk->protinfo.nr->reseq_queue, skbn);
 				}
-			} while (save_vr != sk->nr->vr);
+			} while (save_vr != sk->protinfo.nr->vr);
 			/*
 			 * Window is full, ack it immediately.
 			 */
-			if (((sk->nr->vl + sk->window) % NR_MODULUS) == sk->nr->vr) {
+			if (((sk->protinfo.nr->vl + sk->window) % NR_MODULUS) == sk->protinfo.nr->vr) {
 				nr_enquiry_response(sk);
 			} else {
-				if (!(sk->nr->condition & ACK_PENDING_CONDITION)) {
-					sk->nr->t2timer = sk->nr->t2;
-					sk->nr->condition |= ACK_PENDING_CONDITION;
+				if (!(sk->protinfo.nr->condition & ACK_PENDING_CONDITION)) {
+					sk->protinfo.nr->t2timer = sk->protinfo.nr->t2;
+					sk->protinfo.nr->condition |= ACK_PENDING_CONDITION;
 				}
 			}
 			break;
@@ -311,12 +311,12 @@
 {
 	int queued = 0, frametype;
 	
-	if (sk->nr->state == NR_STATE_0 && sk->dead)
+	if (sk->protinfo.nr->state == NR_STATE_0 && sk->dead)
 		return queued;
 
-	if (sk->nr->state != NR_STATE_1 && sk->nr->state != NR_STATE_2 &&
-	    sk->nr->state != NR_STATE_3) {
-		printk(KERN_ERR "nr_process_rx_frame: frame received - state: %d\n", sk->nr->state);
+	if (sk->protinfo.nr->state != NR_STATE_1 && sk->protinfo.nr->state != NR_STATE_2 &&
+	    sk->protinfo.nr->state != NR_STATE_3) {
+		printk(KERN_ERR "nr_process_rx_frame: frame received - state: %d\n", sk->protinfo.nr->state);
 		return queued;
 	}
 
@@ -324,7 +324,7 @@
 
 	frametype = skb->data[19];
 
-	switch (sk->nr->state)
+	switch (sk->protinfo.nr->state)
 	{
 		case NR_STATE_1:
 			queued = nr_state1_machine(sk, skb, frametype);
diff -u --recursive --new-file v2.1.8/linux/net/netrom/nr_out.c linux/net/netrom/nr_out.c
--- v2.1.8/linux/net/netrom/nr_out.c	Tue Oct 29 19:58:51 1996
+++ linux/net/netrom/nr_out.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	NET/ROM release 003
+ *	NET/ROM release 004
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -19,7 +19,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -52,7 +52,7 @@
 	unsigned char transport[NR_TRANSPORT_LEN];
 	int err, frontlen, len, mtu;
 
-	mtu = sk->nr->paclen;
+	mtu = sk->protinfo.nr->paclen;
 	
 	if (skb->len - NR_TRANSPORT_LEN > mtu) {
 		/* Save a copy of the Transport Header */
@@ -93,7 +93,7 @@
 		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
 	}
 
-	if (sk->nr->state == NR_STATE_3)
+	if (sk->protinfo.nr->state == NR_STATE_3)
 		nr_kick(sk);
 }
 
@@ -106,10 +106,10 @@
 	if (skb == NULL)
 		return;
 
-	skb->data[2] = sk->nr->vs;
-	skb->data[3] = sk->nr->vr;
+	skb->data[2] = sk->protinfo.nr->vs;
+	skb->data[3] = sk->protinfo.nr->vr;
 
-	if (sk->nr->condition & OWN_RX_BUSY_CONDITION)
+	if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)
 		skb->data[4] |= NR_CHOKE_FLAG;
 
 	nr_transmit_buffer(sk, skb);	
@@ -119,23 +119,23 @@
 {
 	struct sk_buff *skb, *skbn;
 	
-	if ((skb = skb_peek(&sk->nr->ack_queue)) == NULL)
+	if ((skb = skb_peek(&sk->protinfo.nr->ack_queue)) == NULL)
 		return;
 		
 	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL)
 		return;
 
-	skbn->data[2] = sk->nr->va;
-	skbn->data[3] = sk->nr->vr;
+	skbn->data[2] = sk->protinfo.nr->va;
+	skbn->data[3] = sk->protinfo.nr->vr;
 
-	if (sk->nr->condition & OWN_RX_BUSY_CONDITION)
+	if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)
 		skbn->data[4] |= NR_CHOKE_FLAG;
 
 	nr_transmit_buffer(sk, skbn);
 
-	sk->nr->condition &= ~ACK_PENDING_CONDITION;
-	sk->nr->vl      = sk->nr->vr;
-	sk->nr->t1timer = 0;
+	sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
+	sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
+	sk->protinfo.nr->t1timer    = 0;
 }
 
 void nr_kick(struct sock *sk)
@@ -146,14 +146,14 @@
 
 	del_timer(&sk->timer);
 
-	start = (skb_peek(&sk->nr->ack_queue) == NULL) ? sk->nr->va : sk->nr->vs;
-	end   = (sk->nr->va + sk->window) % NR_MODULUS;
+	start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs;
+	end   = (sk->protinfo.nr->va + sk->window) % NR_MODULUS;
 
-	if (!(sk->nr->condition & PEER_RX_BUSY_CONDITION) &&
+	if (!(sk->protinfo.nr->condition & PEER_RX_BUSY_CONDITION) &&
 	    start != end                                  &&
 	    skb_peek(&sk->write_queue) != NULL) {
 
-		sk->nr->vs = start;
+		sk->protinfo.nr->vs = start;
 
 		/*
 		 * Transmit data until either we're out of data to send or
@@ -171,7 +171,7 @@
 				break;
 			}
 
-			next = (sk->nr->vs + 1) % NR_MODULUS;
+			next = (sk->protinfo.nr->vs + 1) % NR_MODULUS;
 			last = (next == end);
 
 			/*
@@ -179,20 +179,20 @@
 			 */
 			nr_send_iframe(sk, skbn);
 
-			sk->nr->vs = next;
+			sk->protinfo.nr->vs = next;
 
 			/*
 			 * Requeue the original data frame.
 			 */
-			skb_queue_tail(&sk->nr->ack_queue, skb);
+			skb_queue_tail(&sk->protinfo.nr->ack_queue, skb);
 
 		} while (!last && (skb = skb_dequeue(&sk->write_queue)) != NULL);
 
-		sk->nr->vl = sk->nr->vr;
-		sk->nr->condition &= ~ACK_PENDING_CONDITION;
+		sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
+		sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
 
-		if (sk->nr->t1timer == 0) {
-			sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
+		if (sk->protinfo.nr->t1timer == 0) {
+			sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
 		}
 	}
 
@@ -208,19 +208,19 @@
 	 */
 	dptr = skb_push(skb, NR_NETWORK_LEN);
 
-	memcpy(dptr, &sk->nr->source_addr, AX25_ADDR_LEN);
+	memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
 	dptr[6] &= ~LAPB_C;
 	dptr[6] &= ~LAPB_E;
 	dptr[6] |= SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 
-	memcpy(dptr, &sk->nr->dest_addr, AX25_ADDR_LEN);
+	memcpy(dptr, &sk->protinfo.nr->dest_addr, AX25_ADDR_LEN);
 	dptr[6] &= ~LAPB_C;
 	dptr[6] |= LAPB_E;
 	dptr[6] |= SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 
-	*dptr++ = nr_default.ttl;
+	*dptr++ = sysctl_netrom_network_ttl_initialiser;
 
 	skb->arp = 1;
 
@@ -242,13 +242,13 @@
 
 void nr_establish_data_link(struct sock *sk)
 {
-	sk->nr->condition = 0x00;
-	sk->nr->n2count   = 0;
+	sk->protinfo.nr->condition = 0x00;
+	sk->protinfo.nr->n2count   = 0;
 
 	nr_write_internal(sk, NR_CONNREQ);
 
-	sk->nr->t2timer = 0;
-	sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
+	sk->protinfo.nr->t2timer = 0;
+	sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
 }
 
 /*
@@ -258,31 +258,31 @@
 {
 	int frametype = NR_INFOACK;
 	
-	if (sk->nr->condition & OWN_RX_BUSY_CONDITION) {
+	if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION) {
 		frametype |= NR_CHOKE_FLAG;
 	} else {
-		if (skb_peek(&sk->nr->reseq_queue) != NULL) {
+		if (skb_peek(&sk->protinfo.nr->reseq_queue) != NULL) {
 			frametype |= NR_NAK_FLAG;
 		}
 	}
 	
 	nr_write_internal(sk, frametype);
 
-	sk->nr->vl = sk->nr->vr;
-	sk->nr->condition &= ~ACK_PENDING_CONDITION;
+	sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
+	sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
 }
 
 void nr_check_iframes_acked(struct sock *sk, unsigned short nr)
 {
-	if (sk->nr->vs == nr) {
+	if (sk->protinfo.nr->vs == nr) {
 		nr_frames_acked(sk, nr);
 		nr_calculate_rtt(sk);
-		sk->nr->t1timer = 0;
-		sk->nr->n2count = 0;
+		sk->protinfo.nr->t1timer = 0;
+		sk->protinfo.nr->n2count = 0;
 	} else {
-		if (sk->nr->va != nr) {
+		if (sk->protinfo.nr->va != nr) {
 			nr_frames_acked(sk, nr);
-			sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
+			sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.8/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v2.1.8/linux/net/netrom/nr_route.c	Tue Oct 29 19:58:51 1996
+++ linux/net/netrom/nr_route.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	NET/ROM release 003
+ *	NET/ROM release 004
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -23,7 +23,7 @@
  */
  
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -52,7 +52,6 @@
 #include <net/netrom.h>
 
 static unsigned int nr_neigh_no = 1;
-static int nr_route_on = 1;
 
 static struct nr_node  *nr_node_list  = NULL;
 static struct nr_neigh *nr_neigh_list = NULL;
@@ -96,7 +95,7 @@
 		if (ax25cmp(nr, ax25) == 0)
 			nr_neigh->quality = quality;
 		else
-			nr_neigh->quality = nr_default.quality;
+			nr_neigh->quality = sysctl_netrom_default_path_quality;
 		nr_neigh->locked   = 0;
 		nr_neigh->count    = 0;
 		nr_neigh->number   = nr_neigh_no++;
@@ -525,12 +524,6 @@
 	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
 		return dev;
 
-#ifdef CONFIG_BPQETHER
-	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER)
-		if (ax25_bpq_get_addr(dev) != NULL)
-			return dev;
-#endif
-	
 	return NULL;
 }
 
@@ -571,7 +564,6 @@
 	struct nr_route_struct nr_route;
 	struct device *dev;
 	int err;
-	long opt = 0;
 
 	switch (cmd) {
 
@@ -615,13 +607,6 @@
 		case SIOCNRDECOBS:
 			return nr_dec_obs();
 
-		case SIOCNRRTCTL:
-			if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0)
-				return err;
-			opt = get_fs_long((void *)arg);
-			nr_route_on = opt ? 1 : 0;
-			return 0;
-
 		default:
 			return -EINVAL;
 	}
@@ -672,12 +657,13 @@
 	nr_dest = (ax25_address *)(skb->data + 7);
 
 	if (ax25 != NULL)
-		nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, ax25->device, 0, nr_default.obs_count);
+		nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
+		            ax25->device, 0, sysctl_netrom_network_ttl_initialiser);
 
 	if ((dev = nr_dev_get(nr_dest)) != NULL)	/* Its for me */
 		return nr_rx_frame(skb, dev);
 
-	if (!nr_route_on && ax25 != NULL)
+	if (!sysctl_netrom_routing_control && ax25 != NULL)
 		return 0;
 
 	/* Its Time-To-Live has expired */
@@ -798,5 +784,32 @@
 
 	return len;
 } 
+
+#ifdef MODULE
+
+/*
+ *	Free all memory associated with the nodes and routes lists.
+ */
+void nr_rt_free(void)
+{
+	struct nr_neigh *s, *nr_neigh = nr_neigh_list;
+	struct nr_node  *t, *nr_node  = nr_node_list;
+
+	while (nr_node != NULL) {
+		t       = nr_node;
+		nr_node = nr_node->next;
+
+		nr_remove_node(t);
+	}
+
+	while (nr_neigh != NULL) {
+		s        = nr_neigh;
+		nr_neigh = nr_neigh->next;
+
+		nr_remove_neigh(s);
+	}
+}
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.1.8/linux/net/netrom/nr_subr.c linux/net/netrom/nr_subr.c
--- v2.1.8/linux/net/netrom/nr_subr.c	Tue Oct 29 19:58:51 1996
+++ linux/net/netrom/nr_subr.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	NET/ROM release 003
+ *	NET/ROM release 004
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -18,7 +18,7 @@
  */
  
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -54,17 +54,17 @@
 		kfree_skb(skb, FREE_WRITE);
 	}
 
-	while ((skb = skb_dequeue(&sk->nr->ack_queue)) != NULL) {
+	while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL) {
 		skb->sk   = sk;
 		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	}
 
-	while ((skb = skb_dequeue(&sk->nr->reseq_queue)) != NULL) {
+	while ((skb = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL) {
 		kfree_skb(skb, FREE_READ);
 	}
 
-	while ((skb = skb_dequeue(&sk->nr->frag_queue)) != NULL) {
+	while ((skb = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL) {
 		kfree_skb(skb, FREE_READ);
 	}
 }
@@ -81,13 +81,13 @@
 	/*
 	 * Remove all the ack-ed frames from the ack queue.
 	 */
-	if (sk->nr->va != nr) {
-		while (skb_peek(&sk->nr->ack_queue) != NULL && sk->nr->va != nr) {
-		        skb = skb_dequeue(&sk->nr->ack_queue);
+	if (sk->protinfo.nr->va != nr) {
+		while (skb_peek(&sk->protinfo.nr->ack_queue) != NULL && sk->protinfo.nr->va != nr) {
+		        skb = skb_dequeue(&sk->protinfo.nr->ack_queue);
 		        skb->sk   = sk;
 			skb->free = 1;
 			kfree_skb(skb, FREE_WRITE);
-			sk->nr->va = (sk->nr->va + 1) % NR_MODULUS;
+			sk->protinfo.nr->va = (sk->protinfo.nr->va + 1) % NR_MODULUS;
 		}
 	}
 }
@@ -101,7 +101,7 @@
 {
 	struct sk_buff *skb, *skb_prev = NULL;
 
-	while ((skb = skb_dequeue(&sk->nr->ack_queue)) != NULL) {
+	while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL) {
 		if (skb_prev == NULL)
 			skb_queue_head(&sk->write_queue, skb);
 		else
@@ -116,14 +116,14 @@
  */
 int nr_validate_nr(struct sock *sk, unsigned short nr)
 {
-	unsigned short vc = sk->nr->va;
+	unsigned short vc = sk->protinfo.nr->va;
 
-	while (vc != sk->nr->vs) {
+	while (vc != sk->protinfo.nr->vs) {
 		if (nr == vc) return 1;
 		vc = (vc + 1) % NR_MODULUS;
 	}
 	
-	if (nr == sk->nr->vs) return 1;
+	if (nr == sk->protinfo.nr->vs) return 1;
 
 	return 0;
 }
@@ -133,8 +133,8 @@
  */
 int nr_in_rx_window(struct sock *sk, unsigned short ns)
 {
-	unsigned short vc = sk->nr->vr;
-	unsigned short vt = (sk->nr->vl + sk->window) % NR_MODULUS;
+	unsigned short vc = sk->protinfo.nr->vr;
+	unsigned short vt = (sk->protinfo.nr->vl + sk->window) % NR_MODULUS;
 
 	while (vc != vt) {
 		if (ns == vc) return 1;
@@ -161,7 +161,7 @@
 			len += 17;
 			break;
 		case NR_CONNACK:
-			len += (sk->nr->bpqext) ? 2 : 1;
+			len += (sk->protinfo.nr->bpqext) ? 2 : 1;
 			break;
 		case NR_DISCREQ:
 		case NR_DISCACK:
@@ -185,19 +185,19 @@
 	switch (frametype & 0x0F) {
 
 		case NR_CONNREQ:
-			timeout  = (sk->nr->rtt / PR_SLOWHZ) * 2;
-			*dptr++  = sk->nr->my_index;
-			*dptr++  = sk->nr->my_id;
+			timeout  = (sk->protinfo.nr->rtt / PR_SLOWHZ) * 2;
+			*dptr++  = sk->protinfo.nr->my_index;
+			*dptr++  = sk->protinfo.nr->my_id;
 			*dptr++  = 0;
 			*dptr++  = 0;
 			*dptr++  = frametype;
 			*dptr++  = sk->window;
-			memcpy(dptr, &sk->nr->user_addr, AX25_ADDR_LEN);
+			memcpy(dptr, &sk->protinfo.nr->user_addr, AX25_ADDR_LEN);
 			dptr[6] &= ~LAPB_C;
 			dptr[6] &= ~LAPB_E;
 			dptr[6] |= SSSID_SPARE;
 			dptr    += AX25_ADDR_LEN;
-			memcpy(dptr, &sk->nr->source_addr, AX25_ADDR_LEN);
+			memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
 			dptr[6] &= ~LAPB_C;
 			dptr[6] &= ~LAPB_E;
 			dptr[6] |= SSSID_SPARE;
@@ -207,29 +207,29 @@
 			break;
 
 		case NR_CONNACK:
-			*dptr++ = sk->nr->your_index;
-			*dptr++ = sk->nr->your_id;
-			*dptr++ = sk->nr->my_index;
-			*dptr++ = sk->nr->my_id;
+			*dptr++ = sk->protinfo.nr->your_index;
+			*dptr++ = sk->protinfo.nr->your_id;
+			*dptr++ = sk->protinfo.nr->my_index;
+			*dptr++ = sk->protinfo.nr->my_id;
 			*dptr++ = frametype;
 			*dptr++ = sk->window;
-			if (sk->nr->bpqext) *dptr++ = nr_default.ttl;
+			if (sk->protinfo.nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser;
 			break;
 
 		case NR_DISCREQ:
 		case NR_DISCACK:
-			*dptr++ = sk->nr->your_index;
-			*dptr++ = sk->nr->your_id;
+			*dptr++ = sk->protinfo.nr->your_index;
+			*dptr++ = sk->protinfo.nr->your_id;
 			*dptr++ = 0;
 			*dptr++ = 0;
 			*dptr++ = frametype;
 			break;
 
 		case NR_INFOACK:
-			*dptr++ = sk->nr->your_index;
-			*dptr++ = sk->nr->your_id;
+			*dptr++ = sk->protinfo.nr->your_index;
+			*dptr++ = sk->protinfo.nr->your_id;
 			*dptr++ = 0;
-			*dptr++ = sk->nr->vr;
+			*dptr++ = sk->protinfo.nr->vr;
 			*dptr++ = frametype;
 			break;
 	}
@@ -270,7 +270,7 @@
 	dptr[6] |= SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 
-	*dptr++ = nr_default.ttl;
+	*dptr++ = sysctl_netrom_network_ttl_initialiser;
 
 	*dptr++ = skb->data[15];
 	*dptr++ = skb->data[16];
@@ -293,12 +293,12 @@
 {
 	int n, t;
 	
-	for (t = 2, n = 0; n < sk->nr->n2count; n++)
+	for (t = 2, n = 0; n < sk->protinfo.nr->n2count; n++)
 		t *= 2;
 
 	if (t > 8) t = 8;
 
-	return t * sk->nr->rtt;
+	return t * sk->protinfo.nr->rtt;
 }
 
 /*
@@ -306,21 +306,21 @@
  */
 void nr_calculate_rtt(struct sock *sk)
 {
-	if (sk->nr->t1timer > 0 && sk->nr->n2count == 0)
-		sk->nr->rtt = (9 * sk->nr->rtt + sk->nr->t1 - sk->nr->t1timer) / 10;
+	if (sk->protinfo.nr->t1timer > 0 && sk->protinfo.nr->n2count == 0)
+		sk->protinfo.nr->rtt = (9 * sk->protinfo.nr->rtt + sk->protinfo.nr->t1 - sk->protinfo.nr->t1timer) / 10;
 
 #ifdef	NR_T1CLAMPLO
 	/* Don't go below one tenth of a second */
-	if (sk->nr->rtt < (NR_T1CLAMPLO))
-		sk->nr->rtt = (NR_T1CLAMPLO);
+	if (sk->protinfo.nr->rtt < (NR_T1CLAMPLO))
+		sk->protinfo.nr->rtt = (NR_T1CLAMPLO);
 #else   /* Failsafe - some people might have sub 1/10th RTTs :-) **/
-        if (sk->nr->rtt == 0)
-                sk->nr->rtt = PR_SLOWHZ;
+        if (sk->protinfo.nr->rtt == 0)
+                sk->protinfo.nr->rtt = PR_SLOWHZ;
 #endif
 #ifdef  NR_T1CLAMPHI
         /* OR above clamped seconds **/
-        if (sk->nr->rtt > (NR_T1CLAMPHI))
-                sk->nr->rtt = (NR_T1CLAMPHI);
+        if (sk->protinfo.nr->rtt > (NR_T1CLAMPHI))
+                sk->protinfo.nr->rtt = (NR_T1CLAMPHI);
 #endif
 }
 
diff -u --recursive --new-file v2.1.8/linux/net/netrom/nr_timer.c linux/net/netrom/nr_timer.c
--- v2.1.8/linux/net/netrom/nr_timer.c	Tue Oct 29 19:58:51 1996
+++ linux/net/netrom/nr_timer.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /*
- *	NET/ROM release 003
+ *	NET/ROM release 004
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -17,7 +17,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -87,7 +87,7 @@
 {
 	struct sock *sk = (struct sock *)param;
 
-	switch (sk->nr->state) {
+	switch (sk->protinfo.nr->state) {
 		case NR_STATE_0:
 			/* Magic here: If we listen() and a new link dies before it
 			   is accepted() it isn't 'dead' so doesn't get removed. */
@@ -102,11 +102,11 @@
 			/*
 			 * Check for the state of the receive buffer.
 			 */
-			if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->nr->condition & OWN_RX_BUSY_CONDITION)) {
-				sk->nr->condition &= ~OWN_RX_BUSY_CONDITION;
+			if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)) {
+				sk->protinfo.nr->condition &= ~OWN_RX_BUSY_CONDITION;
 				nr_write_internal(sk, NR_INFOACK);
-				sk->nr->condition &= ~ACK_PENDING_CONDITION;
-				sk->nr->vl = sk->nr->vr;
+				sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
+				sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
 				break;
 			}
 			/*
@@ -119,72 +119,72 @@
 			break;
 	}
 
-	if (sk->nr->t2timer > 0 && --sk->nr->t2timer == 0) {
-		if (sk->nr->state == NR_STATE_3) {
-			if (sk->nr->condition & ACK_PENDING_CONDITION) {
-				sk->nr->condition &= ~ACK_PENDING_CONDITION;
+	if (sk->protinfo.nr->t2timer > 0 && --sk->protinfo.nr->t2timer == 0) {
+		if (sk->protinfo.nr->state == NR_STATE_3) {
+			if (sk->protinfo.nr->condition & ACK_PENDING_CONDITION) {
+				sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
 				nr_enquiry_response(sk);
 			}
 		}
 	}
 
-	if (sk->nr->t4timer > 0 && --sk->nr->t4timer == 0) {
-		sk->nr->condition &= ~PEER_RX_BUSY_CONDITION;
+	if (sk->protinfo.nr->t4timer > 0 && --sk->protinfo.nr->t4timer == 0) {
+		sk->protinfo.nr->condition &= ~PEER_RX_BUSY_CONDITION;
 	}
 
-	if (sk->nr->t1timer == 0 || --sk->nr->t1timer > 0) {
+	if (sk->protinfo.nr->t1timer == 0 || --sk->protinfo.nr->t1timer > 0) {
 		nr_reset_timer(sk);
 		return;
 	}
 
-	switch (sk->nr->state) {
+	switch (sk->protinfo.nr->state) {
 		case NR_STATE_1: 
-			if (sk->nr->n2count == sk->nr->n2) {
+			if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
 				nr_clear_queues(sk);
-				sk->nr->state = NR_STATE_0;
-				sk->state     = TCP_CLOSE;
-				sk->err       = ETIMEDOUT;
+				sk->protinfo.nr->state = NR_STATE_0;
+				sk->state              = TCP_CLOSE;
+				sk->err                = ETIMEDOUT;
 				if (!sk->dead)
 					sk->state_change(sk);
-				sk->dead      = 1;
+				sk->dead               = 1;
 			} else {
-				sk->nr->n2count++;
+				sk->protinfo.nr->n2count++;
 				nr_write_internal(sk, NR_CONNREQ);
 			}
 			break;
 
 		case NR_STATE_2:
-			if (sk->nr->n2count == sk->nr->n2) {
+			if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
 				nr_clear_queues(sk);
-				sk->nr->state = NR_STATE_0;
-				sk->state     = TCP_CLOSE;
-				sk->err       = ETIMEDOUT;
+				sk->protinfo.nr->state = NR_STATE_0;
+				sk->state              = TCP_CLOSE;
+				sk->err                = ETIMEDOUT;
 				if (!sk->dead)
 					sk->state_change(sk);
-				sk->dead      = 1;
+				sk->dead               = 1;
 			} else {
-				sk->nr->n2count++;
+				sk->protinfo.nr->n2count++;
 				nr_write_internal(sk, NR_DISCREQ);
 			}
 			break;
 
 		case NR_STATE_3:
-			if (sk->nr->n2count == sk->nr->n2) {
+			if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
 				nr_clear_queues(sk);
-				sk->nr->state = NR_STATE_0;
-				sk->state     = TCP_CLOSE;
-				sk->err       = ETIMEDOUT;
+				sk->protinfo.nr->state = NR_STATE_0;
+				sk->state              = TCP_CLOSE;
+				sk->err                = ETIMEDOUT;
 				if (!sk->dead)
 					sk->state_change(sk);
-				sk->dead      = 1;
+				sk->dead               = 1;
 			} else {
-				sk->nr->n2count++;
+				sk->protinfo.nr->n2count++;
 				nr_requeue_frames(sk);
 			}
 			break;
 	}
 
-	sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
+	sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
 
 	nr_set_timer(sk);
 }
diff -u --recursive --new-file v2.1.8/linux/net/netrom/sysctl_net_netrom.c linux/net/netrom/sysctl_net_netrom.c
--- v2.1.8/linux/net/netrom/sysctl_net_netrom.c	Tue Apr  2 09:03:35 1996
+++ linux/net/netrom/sysctl_net_netrom.c	Sun Nov 10 19:12:58 1996
@@ -1,5 +1,5 @@
 /* -*- linux-c -*-
- * sysctl_net_netrom.c: sysctl interface to net NETROM subsystem.
+ * sysctl_net_netrom.c: sysctl interface to net NET/ROM subsystem.
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/netrom directory entry (empty =) ). [MS]
@@ -7,7 +7,83 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <net/ax25.h>
+#include <net/netrom.h>
 
-ctl_table netrom_table[] = {
+/*
+ *	Values taken from NET/ROM documentation.
+ */
+static int min_quality[] = {0}, max_quality[] = {255};
+static int min_obs[]     = {0}, max_obs[]     = {255};
+static int min_ttl[]     = {0}, max_ttl[]     = {255};
+static int min_t1[]      = {5 * PR_SLOWHZ};
+static int max_t1[]      = {600 * PR_SLOWHZ};
+static int min_n2[]      = {2}, max_n2[]      = {127};
+static int min_t2[]      = {1 * PR_SLOWHZ};
+static int max_t2[]      = {60 * PR_SLOWHZ};
+static int min_t4[]      = {1 * PR_SLOWHZ};
+static int max_t4[]      = {1000 * PR_SLOWHZ};
+static int min_window[]  = {1}, max_window[]  = {127};
+static int min_idle[]    = {0 * PR_SLOWHZ};
+static int max_idle[]    = {65535 * PR_SLOWHZ};
+static int min_n1[]      = {1}, max_n1[]      = {236};
+static int min_route[]   = {0}, max_route[]   = {1};
+
+static struct ctl_table_header *nr_table_header;
+
+static ctl_table nr_table[] = {
+        {NET_NETROM_DEFAULT_PATH_QUALITY, "default_path_quality",
+         &sysctl_netrom_default_path_quality, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_quality, &max_quality},
+        {NET_NETROM_OBSOLESCENCE_COUNT_INITIALISER, "obsolescence_count_initialiser",
+         &sysctl_netrom_obsolescence_count_initialiser, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_obs, &max_obs},
+        {NET_NETROM_NETWORK_TTL_INITIALISER, "network_ttl_initialiser",
+         &sysctl_netrom_network_ttl_initialiser, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_ttl, &max_ttl},
+        {NET_NETROM_TRANSPORT_TIMEOUT, "transport_timeout",
+         &sysctl_netrom_transport_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t1, &max_t1},
+        {NET_NETROM_TRANSPORT_MAXIMUM_TRIES, "transport_maximum_tries",
+         &sysctl_netrom_transport_maximum_tries, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_n2, &max_n2},
+        {NET_NETROM_TRANSPORT_ACKNOWLEDGE_DELAY, "transport_acknowledge_delay",
+         &sysctl_netrom_transport_acknowledge_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t2, &max_t2},
+        {NET_NETROM_TRANSPORT_BUSY_DELAY, "transport_busy_delay",
+         &sysctl_netrom_transport_busy_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t4, &max_t4},
+        {NET_NETROM_TRANSPORT_REQUESTED_WINDOW_SIZE, "transport_requested_window_size",
+         &sysctl_netrom_transport_requested_window_size, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_window, &max_window},
+        {NET_NETROM_TRANSPORT_NO_ACTIVITY_TIMEOUT, "transport_no_activity_timeout",
+         &sysctl_netrom_transport_no_activity_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_idle, &max_idle},
+        {NET_NETROM_TRANSPORT_PACKET_LENGTH, "transport_packet_length",
+         &sysctl_netrom_transport_packet_length, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_n1, &max_n1},
+        {NET_NETROM_ROUTING_CONTROL, "routing_control",
+         &sysctl_netrom_routing_control, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route},
+	{0}
+};
+
+static ctl_table nr_dir_table[] = {
+	{NET_NETROM, "netrom", NULL, 0, 0555, nr_table},
 	{0}
 };
+
+static ctl_table nr_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, nr_dir_table},
+	{0}
+};
+
+void nr_register_sysctl(void)
+{
+	nr_table_header = register_sysctl_table(nr_root_table, 1);
+}
+
+void nr_unregister_sysctl(void)
+{
+	unregister_sysctl_table(nr_table_header);
+}
diff -u --recursive --new-file v2.1.8/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.8/linux/net/netsyms.c	Sun Nov 10 20:12:31 1996
+++ linux/net/netsyms.c	Sun Nov 10 19:12:58 1996
@@ -14,10 +14,6 @@
 #include <linux/trdevice.h>
 #include <linux/ioport.h>
 
-#ifdef CONFIG_AX25
-#include <net/ax25.h>
-#endif
-
 #ifdef CONFIG_INET
 #include <linux/ip.h>
 #include <linux/etherdevice.h>
@@ -164,7 +160,7 @@
 	X(tcp_init_xmit_timers),
 	X(tcp_clear_xmit_timers),
 	X(tcp_slt_array),
-	X(tcp_slow_timer),
+	X(__tcp_inc_slow_timer),
 	X(tcp_statistics),
 	X(tcp_rcv_state_process),
 	X(tcp_do_sendmsg),
@@ -217,10 +213,6 @@
 #endif
 
         /* support for loadable net drivers */
-#ifdef CONFIG_AX25
-	X(ax25_encapsulate),
-	X(ax25_rebuild_header),
-#endif
 #ifdef CONFIG_INET
 	X(register_netdev),
 	X(unregister_netdev),
@@ -234,6 +226,7 @@
 	X(dev_alloc_skb),
 	X(dev_kfree_skb),
 	X(skb_device_unlock),
+	X(skb_device_locked),
 	X(netif_rx),
 	X(dev_tint),
 	X(irq2dev_map),
@@ -250,6 +243,8 @@
 	X(tty_register_ldisc),
 	X(kill_fasync),
 	X(arp_query),
+	X(ip_rcv),
+	X(arp_rcv),
 #endif  /* CONFIG_INET */
 
 #ifdef CONFIG_NETLINK
diff -u --recursive --new-file v2.1.8/linux/net/protocols.c linux/net/protocols.c
--- v2.1.8/linux/net/protocols.c	Sun Nov 10 20:12:31 1996
+++ linux/net/protocols.c	Sun Nov 10 19:12:58 1996
@@ -33,6 +33,9 @@
 #ifdef CONFIG_NETROM
 #include <net/nrcall.h>
 #endif
+#ifdef CONFIG_ROSE
+#include <net/rosecall.h>
+#endif
 #endif
 #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
 #if ! ( defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) )
@@ -65,9 +68,12 @@
   { "RIF",	rif_init },				/* RIF for Token ring		*/
 #endif  
 #ifdef CONFIG_AX25  
-  { "AX.25",	ax25_proto_init },
+  { "AX.25",	ax25_proto_init },			/* Amateur Radio AX.25 */
 #ifdef CONFIG_NETROM
-  { "NET/ROM",	nr_proto_init },
+  { "NET/ROM",	nr_proto_init },			/* Amateur Radio NET/ROM */
+#endif
+#ifdef CONFIG_ROSE
+  { "Rose",	rose_proto_init },			/* Amateur Radio X.25 PLP */
 #endif
 #endif  
 #ifdef	CONFIG_INET
@@ -81,6 +87,9 @@
 #endif
 #ifdef CONFIG_ATALK
   { "DDP",	atalk_proto_init },			/* Netatalk Appletalk driver	*/
+#endif
+#ifdef CONFIG_X25
+  { "X.25",	x25_proto_init },			/* CCITT X.25 Packet Layer */
 #endif
   { NULL,	NULL		}			/* End marker			*/
 };
diff -u --recursive --new-file v2.1.8/linux/net/rose/Makefile linux/net/rose/Makefile
--- v2.1.8/linux/net/rose/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/Makefile	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux Rose (X.25 PLP) layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := rose.o
+O_OBJS	 := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+tar:
+		tar -cvf /dev/f1 .
diff -u --recursive --new-file v2.1.8/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.8/linux/net/rose/af_rose.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/af_rose.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,1445 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from af_netrom.c.
+ */
+  
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/stat.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <net/rose.h>
+#include <linux/proc_fs.h>
+#include <net/ip.h>
+#include <net/arp.h>
+#include <linux/if_arp.h>
+
+int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0;
+int sysctl_rose_call_request_timeout    = ROSE_DEFAULT_T1;
+int sysctl_rose_reset_request_timeout   = ROSE_DEFAULT_T2;
+int sysctl_rose_clear_request_timeout   = ROSE_DEFAULT_T3;
+int sysctl_rose_no_activity_timeout     = ROSE_DEFAULT_IDLE;
+int sysctl_rose_routing_control         = 1;
+
+static unsigned int lci = 1;
+
+static struct sock *volatile rose_list = NULL;
+
+/*
+ *	Convert a Rose address into text.
+ */
+char *rose2asc(rose_address *addr)
+{
+	static char buffer[11];
+
+	if (addr->rose_addr[0] == 0x00 && addr->rose_addr[1] == 0x00 &&
+	    addr->rose_addr[2] == 0x00 && addr->rose_addr[3] == 0x00 &&
+	    addr->rose_addr[4] == 0x00) {
+		strcpy(buffer, "*");
+	} else {
+		sprintf(buffer, "%02X%02X%02X%02X%02X", addr->rose_addr[0] & 0xFF,
+						addr->rose_addr[1] & 0xFF,
+						addr->rose_addr[2] & 0xFF,
+						addr->rose_addr[3] & 0xFF,
+						addr->rose_addr[4] & 0xFF);
+	}
+
+	return buffer;
+}
+
+/*
+ *	Compare two Rose addresses, 0 == equal.
+ */
+int rosecmp(rose_address *addr1, rose_address *addr2)
+{
+	int i;
+
+	for (i = 0; i < 5; i++)
+		if (addr1->rose_addr[i] != addr2->rose_addr[i])
+			return 1;
+			
+	return 0;
+}
+
+/*
+ *	Socket removal during an interrupt is now safe.
+ */
+static void rose_remove_socket(struct sock *sk)
+{
+	struct sock *s;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_list) == sk) {
+		rose_list = s->next;
+		restore_flags(flags);
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == sk) {
+			s->next = sk->next;
+			restore_flags(flags);
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	Kill all bound sockets on a dropped device.
+ */
+static void rose_kill_by_device(struct device *dev)
+{
+	struct sock *s;
+	
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->device == dev) {
+			s->protinfo.rose->state  = ROSE_STATE_0;
+			s->protinfo.rose->device = NULL;
+			s->state                 = TCP_CLOSE;
+			s->err                   = ENETUNREACH;
+			s->state_change(s);
+			s->dead                  = 1;
+		}
+	}
+}
+
+/*
+ *	Handle device status changes.
+ */
+static int rose_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+
+	if (event != NETDEV_DOWN)
+		return NOTIFY_DONE;
+		
+	rose_kill_by_device(dev);
+	rose_rt_device_down(dev);
+	rose_link_device_down(dev);
+	
+	return NOTIFY_DONE;
+}
+
+/*
+ *	Add a socket to the bound sockets list.
+ */
+static void rose_insert_socket(struct sock *sk)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	sk->next = rose_list;
+	rose_list  = sk;
+
+	restore_flags(flags);
+}
+
+/*
+ *	Find a socket that wants to accept the Call Request we just
+ *	received.
+ */
+static struct sock *rose_find_listener(ax25_address *call)
+{
+	unsigned long flags;
+	struct sock *s;
+
+	save_flags(flags);
+	cli();
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	restore_flags(flags);
+	return NULL;
+}
+
+/*
+ *	Find a connected Rose socket given my LCI and device.
+ */
+struct sock *rose_find_socket(unsigned int lci, struct device *dev)
+{
+	struct sock *s;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour->dev == dev) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	restore_flags(flags);
+
+	return NULL;
+}
+
+/*
+ *	Find a unique LCI for a given device.
+ */
+unsigned int rose_new_lci(struct device *dev)
+{
+	lci++;
+	if (lci > 4095) lci = 1;
+
+	while (rose_find_socket(lci, dev) != NULL) {
+		lci++;
+		if (lci > 4095) lci = 1;
+	}
+
+	return lci;
+}
+
+/*
+ *	Deferred destroy.
+ */
+void rose_destroy_socket(struct sock *);
+
+/*
+ *	Handler for deferred kills.
+ */
+static void rose_destroy_timer(unsigned long data)
+{
+	rose_destroy_socket((struct sock *)data);
+}
+
+/*
+ *	This is called from user mode and the timers. Thus it protects itself against
+ *	interrupt users but doesn't worry about being called during work.
+ *	Once it is removed from the queue no interrupt or bottom half will
+ *	touch it and we are (fairly 8-) ) safe.
+ */
+void rose_destroy_socket(struct sock *sk)	/* Not static as it's used by the timer */
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	
+	del_timer(&sk->timer);
+	
+	rose_remove_socket(sk);
+	rose_clear_queues(sk);		/* Flush the queues */
+	
+	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+		if (skb->sk != sk) {			/* A pending connection */
+			skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
+			rose_set_timer(skb->sk);
+			skb->sk->protinfo.rose->state = ROSE_STATE_0;
+		}
+
+		kfree_skb(skb, FREE_READ);
+	}
+	
+	if (sk->wmem_alloc || sk->rmem_alloc) { /* Defer: outstanding buffers */
+		init_timer(&sk->timer);
+		sk->timer.expires  = jiffies + 10 * HZ;
+		sk->timer.function = rose_destroy_timer;
+		sk->timer.data     = (unsigned long)sk;
+		add_timer(&sk->timer);
+	} else {
+		kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose));
+		sk_free(sk);
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	Handling for system calls applied via the various interfaces to a
+ *	Rose socket object.
+ */
+ 
+static int rose_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+/*
+ * dl1bke 960311: set parameters for existing Rose connections,
+ *		  includes a KILL command to abort any connection.
+ *		  VERY useful for debugging ;-)
+ */
+static int rose_ctl_ioctl(const unsigned int cmd, void *arg)
+{
+	struct rose_ctl_struct rose_ctl;
+	struct sock *sk;
+	unsigned long flags;
+	struct device *dev;
+	int err;
+	
+	if ((err = verify_area(VERIFY_READ, arg, sizeof(rose_ctl))) != 0)
+		return err;
+
+	copy_from_user(&rose_ctl, arg, sizeof(rose_ctl));
+
+	if ((dev = rose_ax25_dev_get(rose_ctl.dev)) == NULL)
+		return -EINVAL;
+	
+	if ((sk = rose_find_socket(rose_ctl.lci, dev)) == NULL)
+		return -ENOTCONN;
+
+	switch (rose_ctl.cmd) {
+		case ROSE_KILL:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = ENETRESET;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			rose_set_timer(sk);
+	  		break;
+
+	  	case ROSE_T0:
+  			if (rose_ctl.arg < 1) 
+  				return -EINVAL;
+  			if (sk->protinfo.rose->neighbour != NULL) {
+	  			save_flags(flags); cli();
+	  			sk->protinfo.rose->neighbour->t0 = rose_ctl.arg * PR_SLOWHZ;
+	  			restore_flags(flags);
+	  		}
+  			break;
+
+	  	case ROSE_T1:
+  			if (rose_ctl.arg < 1) 
+  				return -EINVAL;
+  			save_flags(flags); cli();
+  			sk->protinfo.rose->t1 = rose_ctl.arg * PR_SLOWHZ;
+  			restore_flags(flags);
+  			break;
+
+	  	case ROSE_T2:
+	  		if (rose_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.rose->t2 = rose_ctl.arg * PR_SLOWHZ;
+	  		restore_flags(flags);
+	  		break;
+
+	  	case ROSE_T3:
+	  		if (rose_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.rose->t3 = rose_ctl.arg * PR_SLOWHZ;
+	  		restore_flags(flags);
+	  		break;
+
+	  	case ROSE_IDLE:
+	  		if (rose_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		sk->protinfo.rose->idle = rose_ctl.arg * 60 * PR_SLOWHZ;
+	  		restore_flags(flags);
+	  		break;
+
+	  	default:
+	  		return -EINVAL;
+	  }
+	  
+	  return 0;
+}
+
+static int rose_setsockopt(struct socket *sock, int level, int optname,
+	char *optval, int optlen)
+{
+	struct sock *sk;
+	int err, opt;
+
+	sk = (struct sock *)sock->data;
+	
+	if (level == SOL_SOCKET)
+		return sock_setsockopt(sk, level, optname, optval, optlen);
+
+	if (level != SOL_ROSE)
+		return -EOPNOTSUPP;
+
+	if (optval == NULL)
+		return -EINVAL;
+
+	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
+		return err;
+
+	get_user(opt, (int *)optval);
+	
+	switch (optname) {
+		case ROSE_T0:
+			if (opt < 1)
+				return -EINVAL;
+			if (sk->protinfo.rose->neighbour != NULL)
+				sk->protinfo.rose->neighbour->t0 = opt * PR_SLOWHZ;
+			return 0;
+
+		case ROSE_T1:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t1 = opt * PR_SLOWHZ;
+			return 0;
+
+		case ROSE_T2:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t2 = opt * PR_SLOWHZ;
+			return 0;
+			
+		case ROSE_T3:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t3 = opt * PR_SLOWHZ;
+			return 0;
+			
+		case ROSE_IDLE:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->idle = opt * 60 * PR_SLOWHZ;
+			return 0;
+			
+		case ROSE_HDRINCL:
+			sk->protinfo.rose->hdrincl = opt ? 1 : 0;
+			return 0;
+
+		default:
+			return -ENOPROTOOPT;
+	}
+}
+
+static int rose_getsockopt(struct socket *sock, int level, int optname,
+	char *optval, int *optlen)
+{
+	struct sock *sk;
+	int val = 0;
+	int err; 
+
+	sk = (struct sock *)sock->data;
+	
+	if (level == SOL_SOCKET)
+		return sock_getsockopt(sk, level, optname, optval, optlen);
+	
+	if (level != SOL_ROSE)
+		return -EOPNOTSUPP;
+	
+	switch (optname) {
+		case ROSE_T0:
+			if (sk->protinfo.rose->neighbour != NULL)
+				val = sk->protinfo.rose->neighbour->t0 / PR_SLOWHZ;
+			else
+				val = sysctl_rose_restart_request_timeout / PR_SLOWHZ;
+			break;
+			
+		case ROSE_T1:
+			val = sk->protinfo.rose->t1 / PR_SLOWHZ;
+			break;
+			
+		case ROSE_T2:
+			val = sk->protinfo.rose->t2 / PR_SLOWHZ;
+			break;
+			
+		case ROSE_T3:
+			val = sk->protinfo.rose->t3 / PR_SLOWHZ;
+			break;
+						
+		case ROSE_IDLE:
+			val = sk->protinfo.rose->idle / (PR_SLOWHZ * 60);
+			break;
+						
+		case ROSE_HDRINCL:
+			val = sk->protinfo.rose->hdrincl;
+			break;
+
+		default:
+			return -ENOPROTOOPT;
+	}
+
+	if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
+		return err;
+
+	put_user(sizeof(int), (unsigned long *)optlen);
+
+	if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
+		return err;
+
+	put_user(val, (unsigned long *)optval);
+
+	return 0;
+}
+
+static int rose_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = (struct sock *)sock->data;
+
+	if (sk->state != TCP_LISTEN) {
+		sk->protinfo.rose->dest_ndigis = 0;
+		memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN);
+		memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN);
+		memset(&sk->protinfo.rose->dest_digi, '\0', AX25_ADDR_LEN);
+		sk->max_ack_backlog = backlog;
+		sk->state           = TCP_LISTEN;
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static void def_callback1(struct sock *sk)
+{
+	if (!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static void def_callback2(struct sock *sk, int len)
+{
+	if (!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static int rose_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if (sock->type != SOCK_SEQPACKET || protocol != 0)
+		return -ESOCKTNOSUPPORT;
+
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+		return -ENOMEM;
+
+	if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return -ENOMEM;
+	}
+
+	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->write_queue);
+	skb_queue_head_init(&sk->back_log);
+
+	init_timer(&sk->timer);
+
+	sk->socket        = sock;
+	sk->type          = sock->type;
+	sk->protocol      = protocol;
+	sk->allocation	  = GFP_KERNEL;
+	sk->rcvbuf        = SK_RMEM_MAX;
+	sk->sndbuf        = SK_WMEM_MAX;
+	sk->state         = TCP_CLOSE;
+	sk->priority      = SOPRI_NORMAL;
+	sk->mtu           = ROSE_MTU;	/* 128 */
+	sk->zapped        = 1;
+	sk->window	  = ROSE_DEFAULT_WINDOW;
+
+	sk->state_change = def_callback1;
+	sk->data_ready   = def_callback2;
+	sk->write_space  = def_callback1;
+	sk->error_report = def_callback1;
+
+	if (sock != NULL) {
+		sock->data = (void *)sk;
+		sk->sleep  = sock->wait;
+	}
+
+	skb_queue_head_init(&rose->ack_queue);
+	skb_queue_head_init(&rose->frag_queue);
+
+	rose->lci      = 0;
+
+	rose->t1       = sysctl_rose_call_request_timeout;
+	rose->t2       = sysctl_rose_reset_request_timeout;
+	rose->t3       = sysctl_rose_clear_request_timeout;
+	rose->idle     = sysctl_rose_no_activity_timeout;
+
+	rose->timer    = 0;
+
+	rose->va       = 0;
+	rose->vr       = 0;
+	rose->vs       = 0;
+	rose->vl       = 0;
+
+	rose->fraglen    = 0;
+	rose->hdrincl    = 0;
+	rose->state      = ROSE_STATE_0;
+	rose->neighbour  = NULL;
+	rose->device     = NULL;
+
+	rose->source_ndigis = 0;
+	rose->dest_ndigis   = 0;
+
+	memset(&rose->source_addr, '\0', ROSE_ADDR_LEN);
+	memset(&rose->dest_addr,   '\0', ROSE_ADDR_LEN);
+	memset(&rose->source_call, '\0', AX25_ADDR_LEN);
+	memset(&rose->dest_call,   '\0', AX25_ADDR_LEN);
+	memset(&rose->source_digi, '\0', AX25_ADDR_LEN);
+	memset(&rose->dest_digi,   '\0', AX25_ADDR_LEN);
+
+	rose->sk          = sk;
+	sk->protinfo.rose = rose;
+
+	return 0;
+}
+
+static struct sock *rose_make_new(struct sock *osk)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if (osk->type != SOCK_SEQPACKET)
+		return NULL;
+
+	if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return NULL;
+	}
+
+	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->write_queue);
+	skb_queue_head_init(&sk->back_log);
+
+	init_timer(&sk->timer);
+
+	sk->type        = osk->type;
+	sk->socket      = osk->socket;
+	sk->priority    = osk->priority;
+	sk->protocol    = osk->protocol;
+	sk->rcvbuf      = osk->rcvbuf;
+	sk->sndbuf      = osk->sndbuf;
+	sk->debug       = osk->debug;
+	sk->state       = TCP_ESTABLISHED;
+	sk->window      = osk->window;
+	sk->mtu         = osk->mtu;
+	sk->sleep       = osk->sleep;
+	sk->zapped      = osk->zapped;
+
+	sk->state_change = def_callback1;
+	sk->data_ready   = def_callback2;
+	sk->write_space  = def_callback1;
+	sk->error_report = def_callback1;
+
+	skb_queue_head_init(&rose->ack_queue);
+	skb_queue_head_init(&rose->frag_queue);
+
+	rose->t1       = osk->protinfo.rose->t1;
+	rose->t2       = osk->protinfo.rose->t2;
+	rose->t3       = osk->protinfo.rose->t3;
+	rose->idle     = osk->protinfo.rose->idle;
+
+	rose->device   = osk->protinfo.rose->device;
+	rose->hdrincl  = osk->protinfo.rose->hdrincl;
+	rose->fraglen  = 0;
+
+	rose->timer    = 0;
+
+	rose->va       = 0;
+	rose->vr       = 0;
+	rose->vs       = 0;
+	rose->vl       = 0;
+	
+	sk->protinfo.rose = rose;
+	rose->sk          = sk;
+
+	return sk;
+}
+
+static int rose_dup(struct socket *newsock, struct socket *oldsock)
+{
+	struct sock *sk = (struct sock *)oldsock->data;
+
+	return rose_create(newsock, sk->protocol);
+}
+
+static int rose_release(struct socket *sock, struct socket *peer)
+{
+	struct sock *sk = (struct sock *)sock->data;
+
+	if (sk == NULL) return 0;
+
+	switch (sk->protinfo.rose->state) {
+
+		case ROSE_STATE_0:
+			sk->state     = TCP_CLOSE;
+			sk->state_change(sk);
+			sk->dead      = 1;
+			rose_destroy_socket(sk);
+			break;
+
+		case ROSE_STATE_1:
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->state_change(sk);
+			sk->dead                 = 1;
+			rose_destroy_socket(sk);
+			break;
+
+		case ROSE_STATE_2:
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->state_change(sk);
+			sk->dead                 = 1;
+			rose_destroy_socket(sk);
+			break;			
+
+		case ROSE_STATE_3:
+		case ROSE_STATE_4:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			sk->protinfo.rose->timer = sk->protinfo.rose->t3;
+			sk->protinfo.rose->state = ROSE_STATE_2;
+			sk->state                = TCP_CLOSE;
+			sk->state_change(sk);
+			sk->dead                 = 1;
+			sk->destroy              = 1;
+			break;
+
+		default:
+			break;
+	}
+
+	sock->data = NULL;	
+	sk->socket = NULL;	/* Not used, but we should do this. **/
+
+	return 0;
+}
+
+static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sock *sk;
+	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
+	struct device *dev;
+	ax25_address *user, *source;
+	
+	sk = (struct sock *)sock->data;
+
+	if (sk->zapped == 0)
+		return -EIO;
+		
+	if (addr_len != sizeof(struct sockaddr_rose))
+		return -EINVAL;
+
+	if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
+		if (sk->debug)
+			printk("Rose: bind failed: invalid address\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	source = &addr->srose_call;
+
+	if ((user = ax25_findbyuid(current->euid)) == NULL) {
+		if (ax25_uid_policy && !suser())
+			return -EPERM;
+		user = source;
+	}
+
+	sk->protinfo.rose->source_addr = addr->srose_addr;
+	sk->protinfo.rose->source_call = *user;
+	sk->protinfo.rose->device      = dev;
+
+	if (addr->srose_ndigis == 1) {
+		sk->protinfo.rose->source_ndigis = 1;
+		sk->protinfo.rose->source_digi   = addr->srose_digi;
+	}
+
+	rose_insert_socket(sk);
+
+	sk->zapped = 0;
+
+	if (sk->debug)
+		printk("Rose: socket is bound\n");
+
+	return 0;
+}
+
+static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
+	ax25_address *user;
+	struct device *dev;
+	
+	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
+		sock->state = SS_CONNECTED;
+		return 0;	/* Connect completed during a ERESTARTSYS event */
+	}
+	
+	if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
+		sock->state = SS_UNCONNECTED;
+		return -ECONNREFUSED;
+	}
+	
+	if (sk->state == TCP_ESTABLISHED)
+		return -EISCONN;	/* No reconnect on a seqpacket socket */
+		
+	sk->state   = TCP_CLOSE;	
+	sock->state = SS_UNCONNECTED;
+
+	if (addr_len != sizeof(struct sockaddr_rose))
+		return -EINVAL;
+
+	if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL)	
+		return -ENETUNREACH;
+
+	if (sk->zapped) {	/* Must bind first - autobinding in this may or may not work */
+		sk->zapped = 0;
+
+		if ((dev = rose_dev_first()) == NULL)
+			return -ENETUNREACH;
+
+		if ((user = ax25_findbyuid(current->euid)) == NULL)
+			return -EINVAL;
+
+		memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN);
+		sk->protinfo.rose->source_call = *user;
+		sk->protinfo.rose->device      = dev;
+
+		rose_insert_socket(sk);		/* Finish the bind */
+	}
+
+	sk->protinfo.rose->dest_addr = addr->srose_addr;
+	sk->protinfo.rose->dest_call = addr->srose_call;
+	if (addr->srose_ndigis == 1) {
+		sk->protinfo.rose->dest_ndigis = 1;
+		sk->protinfo.rose->dest_digi   = addr->srose_digi;
+	}
+	sk->protinfo.rose->lci       = rose_new_lci(sk->protinfo.rose->neighbour->dev);
+
+	/* Move to connecting socket, start sending Connect Requests */
+	sock->state   = SS_CONNECTING;
+	sk->state     = TCP_SYN_SENT;
+
+	sk->protinfo.rose->state = ROSE_STATE_1;
+	sk->protinfo.rose->timer = sk->protinfo.rose->t1;
+	rose_write_internal(sk, ROSE_CALL_REQUEST);
+
+	rose_set_timer(sk);
+	
+	/* Now the loop */
+	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+		return -EINPROGRESS;
+		
+	cli();	/* To avoid races on the sleep */
+
+	/*
+	 * A Connect Ack with Choke or timeout or failed routing will go to closed.
+	 */
+	while (sk->state == TCP_SYN_SENT) {
+		interruptible_sleep_on(sk->sleep);
+		if (current->signal & ~current->blocked) {
+			sti();
+			return -ERESTARTSYS;
+		}
+	}
+
+	if (sk->state != TCP_ESTABLISHED) {
+		sti();
+		sock->state = SS_UNCONNECTED;
+		return sock_error(sk);	/* Always set at this point */
+	}
+	
+	sock->state = SS_CONNECTED;
+
+	sti();
+	
+	return 0;
+}
+	
+static int rose_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct sock *sk;
+	struct sock *newsk;
+	struct sk_buff *skb;
+
+	if (newsock->data)
+		sk_free(newsock->data);
+
+	newsock->data = NULL;
+	
+	sk = (struct sock *)sock->data;
+
+	if (sk->type != SOCK_SEQPACKET)
+		return -EOPNOTSUPP;
+	
+	if (sk->state != TCP_LISTEN)
+		return -EINVAL;
+		
+	/*
+	 *	The write queue this time is holding sockets ready to use
+	 *	hooked into the SABM we saved
+	 */
+	do {
+		cli();
+		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
+			if (flags & O_NONBLOCK) {
+				sti();
+				return 0;
+			}
+			interruptible_sleep_on(sk->sleep);
+			if (current->signal & ~current->blocked) {
+				sti();
+				return -ERESTARTSYS;
+			}
+		}
+	} while (skb == NULL);
+
+	newsk = skb->sk;
+	newsk->pair = NULL;
+	sti();
+
+	/* Now attach up the new socket */
+	skb->sk = NULL;
+	kfree_skb(skb, FREE_READ);
+	sk->ack_backlog--;
+	newsock->data = newsk;
+
+	return 0;
+}
+
+static int rose_getname(struct socket *sock, struct sockaddr *uaddr,
+	int *uaddr_len, int peer)
+{
+	struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr;
+	struct sock *sk;
+	
+	sk = (struct sock *)sock->data;
+	
+	if (peer != 0) {
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+		srose->srose_family = AF_ROSE;
+		srose->srose_ndigis = 0;
+		srose->srose_addr   = sk->protinfo.rose->dest_addr;
+		srose->srose_call   = sk->protinfo.rose->dest_call;
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+		*uaddr_len = sizeof(struct sockaddr_rose);
+	} else {
+		srose->srose_family = AF_ROSE;
+		srose->srose_ndigis = 0;
+		srose->srose_addr   = sk->protinfo.rose->source_addr;
+		srose->srose_call   = sk->protinfo.rose->source_call;
+		if (sk->protinfo.rose->source_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->source_digi;
+		}
+		*uaddr_len = sizeof(struct sockaddr_rose);
+	}
+
+	return 0;
+}
+ 
+int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_neigh *neigh, unsigned int lci)
+{
+	struct sock *sk;
+	struct sock *make;	
+	rose_cb rose;
+
+	skb->sk = NULL;		/* Initially we don't know who it's for */
+
+	/*
+	 *	skb->data points to the rose frame start
+	 */
+
+	/*
+	 * XXX This is an error.
+	 */
+	if (!rose_parse_facilities(skb, &rose)) {
+		return 0;
+	}
+		
+	sk = rose_find_listener(&rose.source_call);
+
+	/*
+	 * We can't accept the Call Request.
+	 */
+	if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = rose_make_new(sk)) == NULL) {
+		rose_transmit_clear_request(neigh, lci, 0x01);
+		return 0;
+	}
+
+	skb->sk     = make;
+	make->state = TCP_ESTABLISHED;
+
+	make->protinfo.rose->lci           = lci;
+	make->protinfo.rose->dest_addr     = rose.dest_addr;
+	make->protinfo.rose->dest_call     = rose.dest_call;
+	make->protinfo.rose->dest_ndigis   = rose.dest_ndigis;
+	make->protinfo.rose->dest_digi     = rose.dest_digi;
+	make->protinfo.rose->source_addr   = rose.source_addr;
+	make->protinfo.rose->source_call   = rose.source_call;
+	make->protinfo.rose->source_ndigis = rose.source_ndigis;
+	make->protinfo.rose->source_digi   = rose.source_digi;
+	make->protinfo.rose->neighbour     = neigh;
+	make->protinfo.rose->device        = dev;
+	
+	rose_write_internal(make, ROSE_CALL_ACCEPTED);
+
+	make->protinfo.rose->condition = 0x00;
+	make->protinfo.rose->vs        = 0;
+	make->protinfo.rose->va        = 0;
+	make->protinfo.rose->vr        = 0;
+	make->protinfo.rose->vl        = 0;
+	make->protinfo.rose->state     = ROSE_STATE_3;
+	sk->ack_backlog++;
+	make->pair = sk;
+
+	rose_insert_socket(make);
+
+	skb_queue_head(&sk->receive_queue, skb);
+
+	rose_set_timer(make);
+
+	if (!sk->dead)
+		sk->data_ready(sk, skb->len);
+
+	return 1;
+}
+
+static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name;
+	int err;
+	struct sockaddr_rose srose;
+	struct sk_buff *skb;
+	unsigned char *asmptr;
+	int size;
+	
+	if (sk->err)
+		return sock_error(sk);
+
+	if (flags)
+		return -EINVAL;
+
+	if (sk->zapped)
+		return -EADDRNOTAVAIL;
+
+	if (sk->protinfo.rose->device == NULL)
+		return -ENETUNREACH;
+		
+	if (usrose) {
+		if (msg->msg_namelen < sizeof(srose))
+			return -EINVAL;
+		srose = *usrose;
+		if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 ||
+		    ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0)
+			return -EISCONN;
+		if (srose.srose_ndigis == 1 && sk->protinfo.rose->dest_ndigis == 1) {
+			if (ax25cmp(&sk->protinfo.rose->dest_digi, &srose.srose_digi) != 0)
+				return -EISCONN;
+		}
+		if (srose.srose_family != AF_ROSE)
+			return -EINVAL;
+	} else {
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+
+		srose.srose_family = AF_ROSE;
+		srose.srose_addr   = sk->protinfo.rose->dest_addr;
+		srose.srose_call   = sk->protinfo.rose->dest_call;
+		srose.srose_ndigis = 0;
+
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose.srose_ndigis = 1;
+			srose.srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+	}
+	
+	if (sk->debug)
+		printk("Rose: sendto: Addresses built.\n");
+
+	/* Build a packet */
+	if (sk->debug)
+		printk("Rose: sendto: building packet.\n");
+
+	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
+
+	if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+		return err;
+
+	skb->sk   = sk;
+	skb->free = 1;
+	skb->arp  = 1;
+
+	skb_reserve(skb, size - len);
+	
+	/*
+	 *	Push down the Rose header
+	 */
+
+	asmptr = skb_push(skb, ROSE_MIN_LEN);
+
+	if (sk->debug)
+		printk("Building Rose Header.\n");
+
+	/* Build a Rose Transport header */
+
+	*asmptr++ = ((sk->protinfo.rose->lci >> 8) & 0x0F) | GFI;
+	*asmptr++ = (sk->protinfo.rose->lci >> 0) & 0xFF;
+	*asmptr++ = ROSE_DATA;
+	
+	if (sk->debug)
+		printk("Built header.\n");
+
+	/*
+	 *	Put the data on the end
+	 */
+
+	skb->h.raw = skb_put(skb, len);
+
+	asmptr = skb->h.raw;
+	
+	if (sk->debug)
+		printk("Rose: Appending user data\n");
+
+	/* User data follows immediately after the Rose transport header */
+	memcpy_fromiovec(asmptr, msg->msg_iov, len);
+
+	if (sk->debug)
+		printk("Rose: Transmitting buffer\n");
+
+	if (sk->state != TCP_ESTABLISHED) {
+		kfree_skb(skb, FREE_WRITE);
+		return -ENOTCONN;
+	}
+
+	rose_output(sk, skb);	/* Shove it onto the queue */
+
+	return len;
+}
+
+
+static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
+		   int flags, int *addr_len)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name;
+	int copied;
+	struct sk_buff *skb;
+	int er;
+
+	if (sk->err)
+		return sock_error(sk);
+	
+	if (addr_len != NULL)
+		*addr_len = sizeof(*srose);
+
+	/*
+	 * This works for seqpacket too. The receiver has ordered the queue for
+	 * us! We do one quick check first though
+	 */
+	if (sk->state != TCP_ESTABLISHED)
+		return -ENOTCONN;
+
+	/* Now we can treat all alike */
+	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+		return er;
+
+	if (!sk->protinfo.rose->hdrincl) {
+		skb_pull(skb, ROSE_MIN_LEN);
+		skb->h.raw = skb->data;
+	}
+
+	copied = (size < skb->len) ? size : skb->len;
+	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	
+	if (srose != NULL) {
+		struct sockaddr_rose addr;
+		
+		addr.srose_family = AF_ROSE;
+		addr.srose_addr   = sk->protinfo.rose->dest_addr;
+		addr.srose_call   = sk->protinfo.rose->dest_call;
+		addr.srose_ndigis = 0;
+
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			addr.srose_ndigis = 1;
+			addr.srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+
+		*srose = addr;
+
+		*addr_len = sizeof(*srose);
+	}
+
+	skb_free_datagram(sk, skb);
+
+	return copied;
+}
+
+static int rose_shutdown(struct socket *sk, int how)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rose_select(struct socket *sock , int sel_type, select_table *wait)
+{
+	struct sock *sk = (struct sock *)sock->data;
+
+	return datagram_select(sk, sel_type, wait);
+}
+
+static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	int err;
+	long amount = 0;
+
+	switch (cmd) {
+		case TIOCOUTQ:
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+				return err;
+			amount = sk->sndbuf - sk->wmem_alloc;
+			if (amount < 0)
+				amount = 0;
+			put_user(amount, (unsigned long *)arg);
+			return 0;
+
+		case TIOCINQ: {
+			struct sk_buff *skb;
+			/* These two are safe on a single CPU system as only user tasks fiddle here */
+			if ((skb = skb_peek(&sk->receive_queue)) != NULL)
+				amount = skb->len - 20;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+				return err;
+			put_user(amount, (unsigned long *)arg);
+			return 0;
+		}
+
+		case SIOCGSTAMP:
+			if (sk != NULL) {
+				if (sk->stamp.tv_sec==0)
+					return -ENOENT;
+				if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
+					return err;
+				copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval));
+				return 0;
+			}
+			return -EINVAL;
+
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+		case SIOCGIFBRDADDR:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFMETRIC:
+		case SIOCSIFMETRIC:
+			return -EINVAL;
+
+		case SIOCADDRT:
+		case SIOCDELRT:
+			if (!suser()) return -EPERM;
+			return rose_rt_ioctl(cmd, (void *)arg);
+
+ 		case SIOCRSCTLCON:
+ 			if (!suser()) return -EPERM;
+ 			return rose_ctl_ioctl(cmd, (void *)arg);
+ 
+ 		default:
+			return dev_ioctl(cmd, (void *)arg);
+	}
+
+	/*NOTREACHED*/
+	return 0;
+}
+
+static int rose_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct sock *s;
+	struct device *dev;
+	const char *devname, *callsign;
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+  
+	cli();
+
+	len += sprintf(buffer, "dest_addr  dest_call dest_digi src_addr   src_call  src_digi  dev   lci st vs vr va   t  t1  t2  t3 Snd-Q Rcv-Q\n");
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if ((dev = s->protinfo.rose->device) == NULL)
+			devname = "???";
+		else
+			devname = dev->name;
+
+		len += sprintf(buffer + len, "%-10s %-9s ",
+			rose2asc(&s->protinfo.rose->dest_addr),
+			ax2asc(&s->protinfo.rose->dest_call));
+		len += sprintf(buffer + len, "%-9s ",
+			ax2asc(&s->protinfo.rose->dest_digi));
+
+		if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0)
+			callsign = "??????-?";
+		else
+			callsign = ax2asc(&s->protinfo.rose->source_call);
+
+		len += sprintf(buffer + len, "%-10s %-9s ",
+			rose2asc(&s->protinfo.rose->source_addr),
+			callsign);
+		len += sprintf(buffer + len, "%-9s %-5s %3.3X  %d  %d  %d  %d %3d %3d %3d %3d %5d %5d\n",
+			ax2asc(&s->protinfo.rose->source_digi),
+			devname,  s->protinfo.rose->lci & 0x0FFF,
+			s->protinfo.rose->state,
+			s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va,
+			s->protinfo.rose->timer / PR_SLOWHZ,
+			s->protinfo.rose->t1    / PR_SLOWHZ,
+			s->protinfo.rose->t2    / PR_SLOWHZ,
+			s->protinfo.rose->t3    / PR_SLOWHZ,
+			s->wmem_alloc, s->rmem_alloc);
+		
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+		
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return(len);
+} 
+
+struct proto_ops rose_proto_ops = {
+	AF_ROSE,
+	
+	rose_create,
+	rose_dup,
+	rose_release,
+	rose_bind,
+	rose_connect,
+	rose_socketpair,
+	rose_accept,
+	rose_getname,
+	rose_select,
+	rose_ioctl,
+	rose_listen,
+	rose_shutdown,
+	rose_setsockopt,
+	rose_getsockopt,
+	rose_fcntl,
+	rose_sendmsg,
+	rose_recvmsg
+};
+
+struct notifier_block rose_dev_notifier = {
+	rose_device_event,
+	0
+};
+
+void rose_proto_init(struct net_proto *pro)
+{
+	sock_register(rose_proto_ops.family, &rose_proto_ops);
+	register_netdevice_notifier(&rose_dev_notifier);
+	printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.033 Linux 2.1\n");
+
+	if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame))
+		printk(KERN_ERR "Rose unable to register protocol with AX.25\n");
+	if (!ax25_linkfail_register(rose_link_failed))
+		printk(KERN_ERR "Rose unable to register linkfail handler with AX.25\n");
+
+	rose_register_sysctl();
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RS, 4, "rose",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations, 
+		rose_get_info
+	});
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RS_NEIGH, 10, "rose_neigh",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations, 
+		rose_neigh_get_info
+	});
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RS_NODES, 10, "rose_nodes",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations, 
+		rose_nodes_get_info
+	});
+
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RS_ROUTES, 11, "rose_routes",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations, 
+		rose_routes_get_info
+	});
+#endif	
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- v2.1.8/linux/net/rose/rose_dev.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_dev.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,298 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from nr_dev.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/sysctl.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>	/* For the statistics structure. */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <net/ax25.h>
+#include <net/rose.h>
+
+/*
+ *	Only allow IP over Rose frames through if the netrom device is up.
+ */
+
+int rose_rx_ip(struct sk_buff *skb, struct device *dev)
+{
+	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+
+	if (!dev->start) {
+		stats->rx_errors++;
+		return 0;
+	}
+
+	stats->rx_packets++;
+	skb->protocol = htons(ETH_P_IP);
+
+	/* Spoof incoming device */
+	skb->dev = dev;
+
+	skb->h.raw = skb->data;
+	ip_rcv(skb, skb->dev, NULL);
+
+	return 1;
+}
+
+static int rose_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+	void *daddr, void *saddr, unsigned len)
+{
+	unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2);
+
+	*buff++ = GFI | Q_BIT;
+	*buff++ = 0x00;
+	*buff++ = ROSE_DATA;
+	*buff++ = 0x7F;
+	*buff++ = AX25_P_IP;
+
+	if (daddr != NULL)
+		return 37;
+	
+	return -37;	
+}
+
+static int rose_rebuild_header(void *buff, struct device *dev,
+	unsigned long raddr, struct sk_buff *skb)
+{
+	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+	unsigned char *bp = (unsigned char *)buff;
+	struct sk_buff *skbn;
+
+	if (!arp_query(bp + 7, raddr, dev)) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 1;
+	}
+
+	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 1;
+	}
+
+	skbn->sk = skb->sk;
+	
+	if (skbn->sk != NULL)
+		atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	if (!rose_route_frame(skbn, NULL)) {
+		dev_kfree_skb(skbn, FREE_WRITE);
+		stats->tx_errors++;
+	}
+
+	stats->tx_packets++;
+
+	return 1;
+}
+
+static int rose_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+
+	return 0;
+}
+
+static int rose_open(struct device *dev)
+{
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+
+	return 0;
+}
+
+static int rose_close(struct device *dev)
+{
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static int rose_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+
+	if (skb == NULL || dev == NULL)
+		return 0;
+
+	if (!dev->start) {
+		printk(KERN_ERR "rose: xmit call when iface is down\n");
+		return 1;
+	}
+
+	cli();
+
+	if (dev->tbusy != 0) {
+		sti();
+		stats->tx_errors++;
+		return 1;
+	}
+
+	dev->tbusy = 1;
+
+	sti();
+
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	stats->tx_errors++;
+
+	dev->tbusy = 0;
+
+	mark_bh(NET_BH);
+
+	return 0;
+}
+
+static struct enet_statistics *rose_get_stats(struct device *dev)
+{
+	return (struct enet_statistics *)dev->priv;
+}
+
+int rose_init(struct device *dev)
+{
+	int i;
+
+	dev->mtu		= ROSE_PACLEN - 2;
+	dev->tbusy		= 0;
+	dev->hard_start_xmit	= rose_xmit;
+	dev->open		= rose_open;
+	dev->stop		= rose_close;
+
+	dev->hard_header	= rose_header;
+	dev->hard_header_len	= AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
+	dev->addr_len		= ROSE_ADDR_LEN;
+	dev->type		= ARPHRD_ROSE;
+	dev->rebuild_header	= rose_rebuild_header;
+	dev->set_mac_address    = rose_set_mac_address;
+
+	/* New-style flags. */
+	dev->flags		= 0;
+	dev->family		= AF_INET;
+
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr		= 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= sizeof(unsigned long);
+
+	if ((dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	memset(dev->priv, 0, sizeof(struct enet_statistics));
+
+	dev->get_stats = rose_get_stats;
+
+	/* Fill in the generic fields of the device structure. */
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	return 0;
+};
+
+#ifdef MODULE
+extern struct proto_ops rose_proto_ops;
+extern struct notifier_block rose_dev_notifier;
+
+static struct device dev_rose[] = {
+	{"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
+};
+
+int init_module(void)
+{
+	register_netdev(&dev_rose[0]);
+	register_netdev(&dev_rose[1]);
+
+	register_symtab(NULL);
+
+	rose_proto_init(NULL);
+	
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_RS);
+	proc_net_unregister(PROC_NET_RS_NEIGH);
+	proc_net_unregister(PROC_NET_RS_NODES);
+	proc_net_unregister(PROC_NET_RS_ROUTES);
+#endif
+	rose_rt_free();
+
+	ax25_protocol_release(AX25_P_ROSE);
+	ax25_linkfail_release(rose_link_failed);
+
+	rose_unregister_sysctl();
+
+	unregister_netdevice_notifier(&rose_dev_notifier);
+
+	sock_unregister(rose_proto_ops.family);
+	
+	for (i = 0; i < 2; i++) {
+		if (dev_rose[i].priv != NULL) {
+			kfree(dev_rose[i].priv);
+			dev_rose[i].priv = NULL;
+			unregister_netdev(&dev_rose[i]);
+		}
+	}
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_in.c linux/net/rose/rose_in.c
--- v2.1.8/linux/net/rose/rose_in.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_in.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,335 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	Most of this code is based on the SDL diagrams published in the 7th
+ *	ARRL Computer Networking Conference papers. The diagrams have mistakes
+ *	in them, but are mostly correct. Before you modify the code could you
+ *	read the SDL diagrams as the code is not obvious and probably very
+ *	easy to break;
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from nr_in.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h>			/* For ip_rcv */
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+static int rose_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
+{
+	struct sk_buff *skbo, *skbn = skb;
+
+	if (more) {
+		sk->protinfo.rose->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.rose->frag_queue, skb);
+		return 0;
+	}
+	
+	if (!more && sk->protinfo.rose->fraglen > 0) {	/* End of fragment */
+		sk->protinfo.rose->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.rose->frag_queue, skb);
+
+		if ((skbn = alloc_skb(sk->protinfo.rose->fraglen, GFP_ATOMIC)) == NULL)
+			return 1;
+
+		skbn->free = 1;
+		skbn->arp  = 1;
+		skbn->sk   = sk;
+		sk->rmem_alloc += skbn->truesize;
+		skbn->h.raw = skbn->data;
+
+		skbo = skb_dequeue(&sk->protinfo.rose->frag_queue);
+		memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+		kfree_skb(skbo, FREE_READ);
+
+		while ((skbo = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL) {
+			skb_pull(skbo, ROSE_MIN_LEN);
+			memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+			kfree_skb(skbo, FREE_READ);
+		}
+
+		sk->protinfo.rose->fraglen = 0;		
+	}
+
+	return sock_queue_rcv_skb(sk, skbn);
+}
+
+/*
+ * State machine for state 1, Awaiting Call Accepted State.
+ * The handling of the timer(s) is in file rose_timer.c.
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+	switch (frametype) {
+
+		case ROSE_CALL_ACCEPTED:
+			sk->protinfo.rose->timer = 0;
+			sk->protinfo.rose->vs    = 0;
+			sk->protinfo.rose->va    = 0;
+			sk->protinfo.rose->vr    = 0;
+			sk->protinfo.rose->vl    = 0;
+			sk->protinfo.rose->state = ROSE_STATE_3;
+			sk->state                = TCP_ESTABLISHED;
+			if (!sk->dead)
+				sk->state_change(sk);
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+			rose_clear_queues(sk);
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = ECONNREFUSED;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		default:		/* XXX */
+			printk(KERN_WARNING "rose: unknown %02X in state 1\n", frametype);
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Clear Confirmation State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+	switch (frametype) {
+
+		case ROSE_CLEAR_REQUEST:
+		case ROSE_CLEAR_CONFIRMATION:
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		default:		/* XXX */
+			printk(KERN_WARNING "rose: unknown %02X in state 2\n", frametype);
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m)
+{
+	int queued = 0;
+
+	switch (frametype) {
+
+		case ROSE_RESET_REQUEST:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
+			sk->protinfo.rose->condition = 0x00;
+			sk->protinfo.rose->vs        = 0;
+			sk->protinfo.rose->vr        = 0;
+			sk->protinfo.rose->va        = 0;
+			sk->protinfo.rose->vl        = 0;
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		case ROSE_RR:
+		case ROSE_RNR:
+			if (frametype == ROSE_RNR) {
+				sk->protinfo.rose->condition |= PEER_RX_BUSY_CONDITION;
+			} else {
+				sk->protinfo.rose->condition &= ~PEER_RX_BUSY_CONDITION;
+			}
+			if (!rose_validate_nr(sk, nr)) {
+				rose_clear_queues(sk);
+				rose_write_internal(sk, ROSE_RESET_REQUEST);
+				sk->protinfo.rose->condition = 0x00;
+				sk->protinfo.rose->vs        = 0;
+				sk->protinfo.rose->vr        = 0;
+				sk->protinfo.rose->va        = 0;
+				sk->protinfo.rose->vl        = 0;
+				sk->protinfo.rose->state     = ROSE_STATE_4;
+				sk->protinfo.rose->timer     = sk->protinfo.rose->t2;
+			} else {
+				if (sk->protinfo.rose->condition & PEER_RX_BUSY_CONDITION) {
+					rose_frames_acked(sk, nr);
+				} else {
+					rose_check_iframes_acked(sk, nr);
+				}
+			}
+			break;
+			
+		case ROSE_DATA:	/* XXX */
+			sk->protinfo.rose->condition &= ~PEER_RX_BUSY_CONDITION;
+			if (!rose_validate_nr(sk, nr)) {
+				rose_clear_queues(sk);
+				rose_write_internal(sk, ROSE_RESET_REQUEST);
+				sk->protinfo.rose->condition = 0x00;
+				sk->protinfo.rose->vs        = 0;
+				sk->protinfo.rose->vr        = 0;
+				sk->protinfo.rose->va        = 0;
+				sk->protinfo.rose->vl        = 0;
+				sk->protinfo.rose->state     = ROSE_STATE_4;
+				sk->protinfo.rose->timer     = sk->protinfo.rose->t2;
+				break;
+			}
+			if (sk->protinfo.rose->condition & PEER_RX_BUSY_CONDITION) {
+				rose_frames_acked(sk, nr);
+			} else {
+				rose_check_iframes_acked(sk, nr);
+			}
+			if (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION)
+				break;
+			if (ns == sk->protinfo.rose->vr) {
+				if (rose_queue_rx_frame(sk, skb, m) == 0) {
+					sk->protinfo.rose->vr = (sk->protinfo.rose->vr + 1) % ROSE_MODULUS;
+					queued = 1;
+				} else {
+					sk->protinfo.rose->condition |= OWN_RX_BUSY_CONDITION;
+				}
+			}
+			/*
+			 * If the window is full, ack the frame.
+			 */
+			if (((sk->protinfo.rose->vl + sk->window) % ROSE_MODULUS) == sk->protinfo.rose->vr)
+				rose_enquiry_response(sk);
+			break;
+
+		default:
+			printk(KERN_WARNING "rose: unknown %02X in state 3\n", frametype);
+			break;
+	}
+
+	return queued;
+}
+
+/*
+ * State machine for state 4, Awaiting Reset Confirmation State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+	switch (frametype) {
+
+		case ROSE_RESET_CONFIRMATION:
+		case ROSE_RESET_REQUEST:
+			sk->protinfo.rose->timer     = 0;
+			sk->protinfo.rose->condition = 0x00;
+			sk->protinfo.rose->va        = 0;
+			sk->protinfo.rose->vr        = 0;
+			sk->protinfo.rose->vs        = 0;
+			sk->protinfo.rose->vl        = 0;
+			sk->protinfo.rose->state     = ROSE_STATE_3;
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+			sk->protinfo.rose->timer = 0;
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		default:		/* XXX */
+			printk(KERN_WARNING "rose: unknown %02X in state 4\n", frametype);
+			break;
+	}
+
+	return 0;
+}
+
+/* Higher level upcall for a LAPB frame */
+int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
+{
+	int queued = 0, frametype, ns, nr, q, d, m;
+	
+	if (sk->protinfo.rose->state == ROSE_STATE_0 && sk->dead)
+		return queued;
+
+	if (sk->protinfo.rose->state != ROSE_STATE_1 && sk->protinfo.rose->state != ROSE_STATE_2 &&
+	    sk->protinfo.rose->state != ROSE_STATE_3 && sk->protinfo.rose->state != ROSE_STATE_4) {
+		printk(KERN_ERR "rose_process_rx_frame: frame received - state: %d\n", sk->protinfo.rose->state);
+		return queued;
+	}
+
+	del_timer(&sk->timer);
+
+	frametype = rose_decode(skb, &ns, &nr, &q, &d, &m);
+
+	switch (sk->protinfo.rose->state) {
+		case ROSE_STATE_1:
+			queued = rose_state1_machine(sk, skb, frametype);
+			break;
+		case ROSE_STATE_2:
+			queued = rose_state2_machine(sk, skb, frametype);
+			break;
+		case ROSE_STATE_3:
+			queued = rose_state3_machine(sk, skb, frametype, ns, nr, q, d, m);
+			break;
+		case ROSE_STATE_4:
+			queued = rose_state4_machine(sk, skb, frametype);
+			break;
+	}
+
+	rose_set_timer(sk);
+
+	return queued;
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_link.c linux/net/rose/rose_link.c
--- v2.1.8/linux/net/rose/rose_link.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_link.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,301 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from rose_timer.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/firewall.h>
+#include <net/rose.h>
+
+static void rose_link_timer(unsigned long);
+
+/*
+ *	Linux set/reset timer routines
+ */
+static void rose_link_set_timer(struct rose_neigh *neigh)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	del_timer(&neigh->timer);
+	restore_flags(flags);
+
+	neigh->timer.next     = neigh->timer.prev = NULL;	
+	neigh->timer.data     = (unsigned long)neigh;
+	neigh->timer.function = &rose_link_timer;
+
+	neigh->timer.expires  = jiffies + 10;
+	add_timer(&neigh->timer);
+}
+
+static void rose_link_reset_timer(struct rose_neigh *neigh)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	del_timer(&neigh->timer);
+	restore_flags(flags);
+
+	neigh->timer.data     = (unsigned long)neigh;
+	neigh->timer.function = &rose_link_timer;
+	neigh->timer.expires  = jiffies + 10;
+	add_timer(&neigh->timer);
+}
+
+/*
+ *	Rose link TIMER 
+ *
+ *	This routine is called every 500ms. Decrement timer by this
+ *	amount - if expired then process the event.
+ */
+static void rose_link_timer(unsigned long param)
+{
+	struct rose_neigh *neigh = (struct rose_neigh *)param;
+
+	if (neigh->t0timer == 0 || --neigh->t0timer > 0) {
+		rose_link_reset_timer(neigh);
+		return;
+	}
+
+	/*
+	 * T0 for a link has expired.
+	 */
+	rose_transmit_restart_request(neigh);
+
+	neigh->t0timer = neigh->t0;
+
+	rose_link_set_timer(neigh);
+}
+
+/*
+ *	This handles all restart and diagnostic frames.
+ */
+void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigned short frametype)
+{
+	struct sk_buff *skbn;
+
+	switch (frametype) {
+		case ROSE_RESTART_REQUEST:
+			neigh->t0timer   = 0;
+			neigh->restarted = 1;
+			del_timer(&neigh->timer);
+			rose_transmit_restart_confirmation(neigh);
+			break;
+
+		case ROSE_RESTART_CONFIRMATION:
+			neigh->t0timer   = 0;
+			neigh->restarted = 1;
+			del_timer(&neigh->timer);
+			break;
+
+		case ROSE_DIAGNOSTIC:
+			printk(KERN_WARNING "rose: diagnostic #%d\n", skb->data[3]);
+			break;
+			
+		default:
+			printk(KERN_WARNING "rose: received unknown %02X with LCI 000\n", frametype);
+			break;
+	}
+
+	if (neigh->restarted) {
+		while ((skbn = skb_dequeue(&neigh->queue)) != NULL)
+			if (!ax25_send_frame(skbn, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+				kfree_skb(skbn, FREE_WRITE);
+	}
+}
+
+/*
+ *	This routine is called when a Restart Request is needed
+ */
+void rose_transmit_restart_request(struct rose_neigh *neigh)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 3);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = GFI;
+	*dptr++ = 0x00;
+	*dptr++ = ROSE_RESTART_REQUEST;
+	*dptr++ = 0x00;
+	*dptr++ = 0;
+
+	skb->free = 1;
+	skb->sk   = NULL;
+
+	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine is called when a Restart Confirmation is needed
+ */
+void rose_transmit_restart_confirmation(struct rose_neigh *neigh)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 1);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = GFI;
+	*dptr++ = 0x00;
+	*dptr++ = ROSE_RESTART_CONFIRMATION;
+
+	skb->free = 1;
+	skb->sk   = NULL;
+
+	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine is called when a Diagnostic is required.
+ */
+void rose_transmit_diagnostic(struct rose_neigh *neigh, unsigned char diag)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 2;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 2);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = GFI;
+	*dptr++ = 0x00;
+	*dptr++ = ROSE_DIAGNOSTIC;
+	*dptr++ = diag;
+
+	skb->free = 1;
+	skb->sk   = NULL;
+
+	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine is called when a Clear Request is needed outside of the context
+ * of a connected socket.
+ */
+void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 3);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = ((lci >> 8) & 0x0F) | GFI;
+	*dptr++ = ((lci >> 0) & 0xFF);
+	*dptr++ = ROSE_CLEAR_REQUEST;
+	*dptr++ = cause;
+	*dptr++ = 0x00;
+
+	skb->free = 1;
+	skb->sk   = NULL;
+
+	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
+{
+	unsigned char *dptr;
+
+#ifdef CONFIG_FIREWALL
+	if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+		return 0;
+#endif
+
+	if (!ax25_link_up((ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->dev))
+		neigh->restarted = 0;
+
+	dptr = skb_push(skb, 1);
+	*dptr++ = AX25_P_ROSE;
+
+	skb->arp  = 1;
+	skb->free = 1;
+
+	if (neigh->restarted) {
+		if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+			kfree_skb(skb, FREE_WRITE);
+	} else {
+		skb_queue_tail(&neigh->queue, skb);
+		
+		if (neigh->t0timer == 0) {
+			rose_transmit_restart_request(neigh);
+			neigh->t0timer = neigh->t0;
+			rose_link_set_timer(neigh);
+		}
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_out linux/net/rose/rose_out
--- v2.1.8/linux/net/rose/rose_out	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_out	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,254 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from nr_out.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+/*
+ *	This is where all Rose frames pass;
+ */
+void rose_output(struct sock *sk, struct sk_buff *skb)
+{
+	struct sk_buff *skbn;
+	unsigned char header[ROSE_MIN_LEN];
+	int err, frontlen, len;
+
+	if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) {
+		/* Save a copy of the Header */
+		memcpy(header, skb->data, ROSE_MIN_LEN);
+		skb_pull(skb, ROSE_MIN_LEN);
+
+		frontlen = skb_headroom(skb);
+
+		while (skb->len > 0) {
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, 0, &err)) == NULL)
+				return;
+
+			skbn->sk   = sk;
+			skbn->free = 1;
+			skbn->arp  = 1;
+
+			skb_reserve(skbn, frontlen);
+
+			len = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN;
+
+			/* Copy the user data */
+			memcpy(skb_put(skbn, len), skb->data, len);
+			skb_pull(skb, len);
+
+			/* Duplicate the Header */
+			skb_push(skbn, ROSE_MIN_LEN);
+			memcpy(skbn->data, header, ROSE_MIN_LEN);
+
+			if (skb->len > 0)
+				skbn->data[2] |= M_BIT;
+		
+			skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
+		}
+		
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+	} else {
+		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
+	}
+
+	if (sk->protinfo.rose->state == ROSE_STATE_3)
+		rose_kick(sk);
+}
+
+/* 
+ *	This procedure is passed a buffer descriptor for an iframe. It builds
+ *	the rest of the control part of the frame and then writes it out.
+ */
+static void rose_send_iframe(struct sock *sk, struct sk_buff *skb, int last)
+{
+	if (skb == NULL)
+		return;
+
+	if (last)
+		skb->data[0] |= D_BIT;
+
+	skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0;
+	skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E;
+
+	rose_transmit_buffer(sk, skb);	
+}
+
+void rose_send_nak_frame(struct sock *sk)
+{
+	struct sk_buff *skb, *skbn;
+	
+	if ((skb = skb_peek(&sk->protinfo.rose->ack_queue)) == NULL)
+		return;
+		
+	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL)
+		return;
+
+	skbn->data[2] = sk->protinfo.rose->va;
+	skbn->data[3] = sk->protinfo.rose->vr;
+
+	if (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION)
+		skbn->data[4] |= NR_CHOKE_FLAG;
+
+	rose_transmit_buffer(sk, skbn);
+
+	sk->protinfo.rose->condition &= ~ACK_PENDING_CONDITION;
+	sk->protinfo.rose->vl      = sk->protinfo.rose->vr;
+	sk->protinfo.rose->t1timer = 0;
+}
+
+void rose_kick(struct sock *sk)
+{
+	struct sk_buff *skb, *skbn;
+	int last = 1;
+	unsigned short start, end, next;
+
+	del_timer(&sk->timer);
+
+	start = (skb_peek(&sk->protinfo.rose->ack_queue) == NULL) ? sk->protinfo.rose->va : sk->protinfo.rose->vs;
+	end   = (sk->protinfo.rose->va + sk->window) % ROSE_MODULUS;
+
+	if (!(sk->protinfo.rose->condition & PEER_RX_BUSY_CONDITION) &&
+	    start != end                                  &&
+	    skb_peek(&sk->write_queue) != NULL) {
+
+		sk->protinfo.rose->vs = start;
+
+		/*
+		 * Transmit data until either we're out of data to send or
+		 * the window is full.
+		 */
+
+		/*
+		 * Dequeue the frame and copy it.
+		 */
+		skb  = skb_dequeue(&sk->write_queue);
+
+		do {
+			if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+				skb_queue_head(&sk->write_queue, skb);
+				break;
+			}
+
+			next = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
+			last = (next == end);
+
+			/*
+			 * Transmit the frame copy.
+			 */
+			rose_send_iframe(sk, skbn, last);
+
+			sk->protinfo.rose->vs = next;
+
+			/*
+			 * Requeue the original data frame.
+			 */
+			skb_queue_tail(&sk->protinfo.rose->ack_queue, skb);
+
+		} while (!last && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+
+		sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+		sk->protinfo.rose->condition &= ~ACK_PENDING_CONDITION;
+	}
+
+	rose_set_timer(sk);
+}
+
+void rose_transmit_buffer(struct sock *sk, struct sk_buff *skb)
+{
+	unsigned char *dptr;
+
+	dptr  = skb_push(skb, 1);
+	*dptr = AX25_P_ROSE;
+
+	skb->arp = 1;
+
+	if (!ax25_send_frame(skb, (ax25_address *)sk->protinfo.rose->neighbour->dev->dev_addr, &sk->protinfo.rose->neighbour->callsign, sk->protinfo.rose->neighbour->digipeat, sk->protinfo.rose->neighbour->dev)) {
+		kfree_skb(skb, FREE_WRITE);
+
+		sk->state = TCP_CLOSE;
+		sk->err   = ENETUNREACH;
+		if (!sk->dead)
+			sk->state_change(sk);
+		sk->dead  = 1;
+	}
+}
+
+/*
+ * The following routines are taken from page 170 of the 7th ARRL Computer
+ * Networking Conference paper, as is the whole state machine.
+ */
+
+void rose_establish_data_link(struct sock *sk)
+{
+	sk->protinfo.rose->condition = 0x00;
+
+	rose_write_internal(sk, ROSE_CALL_REQUEST);
+
+	sk->protinfo.rose->t1timer = sk->protinfo.rose->t1;
+}
+
+/*
+ * Never send a NAK when we are CHOKEd.
+ */
+void rose_enquiry_response(struct sock *sk)
+{
+	int frametype = NR_INFOACK;
+	
+	if (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION)
+		frametype |= NR_CHOKE_FLAG;
+	
+	rose_write_internal(sk, frametype);
+
+	sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+	sk->protinfo.rose->condition &= ~ACK_PENDING_CONDITION;
+}
+
+void rose_check_iframes_acked(struct sock *sk, unsigned short nr)
+{
+	if (sk->protinfo.rose->vs == nr) {
+		rose_frames_acked(sk, nr);
+	} else {
+		if (sk->protinfo.rose->va != nr) {
+			rose_frames_acked(sk, nr);
+		}
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_out.c linux/net/rose/rose_out.c
--- v2.1.8/linux/net/rose/rose_out.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_out.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,193 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from nr_out.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+/*
+ *	This is where all Rose frames pass;
+ */
+void rose_output(struct sock *sk, struct sk_buff *skb)
+{
+	struct sk_buff *skbn;
+	unsigned char header[ROSE_MIN_LEN];
+	int err, frontlen, len;
+
+	if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) {
+		/* Save a copy of the Header */
+		memcpy(header, skb->data, ROSE_MIN_LEN);
+		skb_pull(skb, ROSE_MIN_LEN);
+
+		frontlen = skb_headroom(skb);
+
+		while (skb->len > 0) {
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, 0, &err)) == NULL)
+				return;
+
+			skbn->sk   = sk;
+			skbn->free = 1;
+			skbn->arp  = 1;
+
+			skb_reserve(skbn, frontlen);
+
+			len = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN;
+
+			/* Copy the user data */
+			memcpy(skb_put(skbn, len), skb->data, len);
+			skb_pull(skb, len);
+
+			/* Duplicate the Header */
+			skb_push(skbn, ROSE_MIN_LEN);
+			memcpy(skbn->data, header, ROSE_MIN_LEN);
+
+			if (skb->len > 0)
+				skbn->data[2] |= M_BIT;
+		
+			skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
+		}
+		
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+	} else {
+		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
+	}
+
+	if (sk->protinfo.rose->state == ROSE_STATE_3)
+		rose_kick(sk);
+}
+
+/* 
+ *	This procedure is passed a buffer descriptor for an iframe. It builds
+ *	the rest of the control part of the frame and then writes it out.
+ */
+static void rose_send_iframe(struct sock *sk, struct sk_buff *skb)
+{
+	if (skb == NULL)
+		return;
+
+	skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0;
+	skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E;
+
+	rose_transmit_link(skb, sk->protinfo.rose->neighbour);	
+}
+
+void rose_kick(struct sock *sk)
+{
+	struct sk_buff *skb, *skbn;
+	int last = 1;
+	unsigned short start, end, next;
+
+	del_timer(&sk->timer);
+
+	start = (skb_peek(&sk->protinfo.rose->ack_queue) == NULL) ? sk->protinfo.rose->va : sk->protinfo.rose->vs;
+	end   = (sk->protinfo.rose->va + sk->window) % ROSE_MODULUS;
+
+	if (!(sk->protinfo.rose->condition & PEER_RX_BUSY_CONDITION) &&
+	    start != end                                  &&
+	    skb_peek(&sk->write_queue) != NULL) {
+
+		sk->protinfo.rose->vs = start;
+
+		/*
+		 * Transmit data until either we're out of data to send or
+		 * the window is full.
+		 */
+
+		/*
+		 * Dequeue the frame and copy it.
+		 */
+		skb  = skb_dequeue(&sk->write_queue);
+
+		do {
+			if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+				skb_queue_head(&sk->write_queue, skb);
+				break;
+			}
+
+			next = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
+			last = (next == end);
+
+			/*
+			 * Transmit the frame copy.
+			 */
+			rose_send_iframe(sk, skbn);
+
+			sk->protinfo.rose->vs = next;
+
+			/*
+			 * Requeue the original data frame.
+			 */
+			skb_queue_tail(&sk->protinfo.rose->ack_queue, skb);
+
+		} while (!last && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+
+		sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+	}
+
+	rose_set_timer(sk);
+}
+
+/*
+ * The following routines are taken from page 170 of the 7th ARRL Computer
+ * Networking Conference paper, as is the whole state machine.
+ */
+
+void rose_enquiry_response(struct sock *sk)
+{
+	if (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION) {
+		rose_write_internal(sk, ROSE_RNR);
+	} else {
+		rose_write_internal(sk, ROSE_RR);
+	}
+
+	sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+}
+
+void rose_check_iframes_acked(struct sock *sk, unsigned short nr)
+{
+	if (sk->protinfo.rose->vs == nr) {
+		rose_frames_acked(sk, nr);
+	} else {
+		if (sk->protinfo.rose->va != nr) {
+			rose_frames_acked(sk, nr);
+		}
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.1.8/linux/net/rose/rose_route.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_route.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,810 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from nr_route.c.
+ */
+ 
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/arp.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/firewall.h>
+#include <net/rose.h>
+
+static unsigned int rose_neigh_no = 1;
+
+static struct rose_node  *rose_node_list  = NULL;
+static struct rose_neigh *rose_neigh_list = NULL;
+static struct rose_route *rose_route_list = NULL;
+
+static void rose_remove_neigh(struct rose_neigh *);
+
+/*
+ *	Add a new route to a node, and in the process add the node and the
+ *	neighbour if it is new.
+ */
+static int rose_add_node(struct rose_route_struct *rose_route, struct device *dev)
+{
+	struct rose_node  *rose_node;
+	struct rose_neigh *rose_neigh;
+	unsigned long flags;
+	int i;
+
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
+		if (rosecmp(&rose_route->address, &rose_node->address) == 0)
+			break;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev)
+			break;
+
+	if (rose_neigh == NULL) {
+		if ((rose_neigh = (struct rose_neigh *)kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL)
+			return -ENOMEM;
+
+		rose_neigh->callsign  = rose_route->neighbour;
+		rose_neigh->digipeat  = NULL;
+		rose_neigh->dev       = dev;
+		rose_neigh->count     = 0;
+		rose_neigh->number    = rose_neigh_no++;
+		rose_neigh->restarted = 0;
+		skb_queue_head_init(&rose_neigh->queue);
+		rose_neigh->t0        = sysctl_rose_restart_request_timeout;
+		rose_neigh->t0timer   = 0;
+		init_timer(&rose_neigh->timer);
+
+		if (rose_route->ndigis != 0) {
+			if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
+				kfree_s(rose_neigh, sizeof(*rose_neigh));
+				return -ENOMEM;
+			}
+			rose_neigh->digipeat->ndigi = rose_route->ndigis;
+			for (i = 0; i < rose_route->ndigis; i++)
+				rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i];
+		}
+			
+		save_flags(flags); cli();
+		rose_neigh->next = rose_neigh_list;
+		rose_neigh_list  = rose_neigh;
+		restore_flags(flags);
+	}
+
+	if (rose_node == NULL) {
+		if ((rose_node = (struct rose_node *)kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL)
+			return -ENOMEM;
+
+		rose_node->address = rose_route->address;
+		rose_node->which   = 0;
+		rose_node->count   = 1;
+
+		rose_node->neighbour[0] = rose_neigh;
+			
+		save_flags(flags); cli();
+		rose_node->next = rose_node_list;
+		rose_node_list  = rose_node;
+		restore_flags(flags);
+		
+		rose_neigh->count++;
+
+		return 0;
+	}
+
+	/* We have space at the bottom, slot it in */
+	if (rose_node->count < 3) {
+		rose_node->neighbour[2] = rose_node->neighbour[1];
+		rose_node->neighbour[1] = rose_node->neighbour[0];
+
+		rose_node->neighbour[0] = rose_neigh;
+	
+		rose_node->count++;
+		rose_neigh->count++;
+	}
+
+	return 0;
+}
+
+static void rose_remove_node(struct rose_node *rose_node)
+{
+	struct rose_node *s;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_node_list) == rose_node) {
+		rose_node_list = rose_node->next;
+		restore_flags(flags);
+		kfree_s(rose_node, sizeof(struct rose_node));
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == rose_node) {
+			s->next = rose_node->next;
+			restore_flags(flags);
+			kfree_s(rose_node, sizeof(struct rose_node));
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+static void rose_remove_neigh(struct rose_neigh *rose_neigh)
+{
+	struct rose_neigh *s;
+	unsigned long flags;
+	struct sk_buff *skb;
+
+	del_timer(&rose_neigh->timer);
+	
+	while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+	
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_neigh_list) == rose_neigh) {
+		rose_neigh_list = rose_neigh->next;
+		restore_flags(flags);
+		if (rose_neigh->digipeat != NULL)
+			kfree_s(rose_neigh->digipeat, sizeof(ax25_digi));
+		kfree_s(rose_neigh, sizeof(struct rose_neigh));
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == rose_neigh) {
+			s->next = rose_neigh->next;
+			restore_flags(flags);
+			if (rose_neigh->digipeat != NULL)
+				kfree_s(rose_neigh->digipeat, sizeof(ax25_digi));
+			kfree_s(rose_neigh, sizeof(struct rose_neigh));
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+static void rose_remove_route(struct rose_route *rose_route)
+{
+	struct rose_route *s;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_route_list) == rose_route) {
+		rose_route_list = rose_route->next;
+		restore_flags(flags);
+		kfree_s(rose_route, sizeof(struct rose_route));
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == rose_route) {
+			s->next = rose_route->next;
+			restore_flags(flags);
+			kfree_s(rose_route, sizeof(struct rose_route));
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	"Delete" a node. Strictly speaking remove a route to a node. The node
+ *	is only deleted if no routes are left to it.
+ */
+static int rose_del_node(struct rose_route_struct *rose_route, struct device *dev)
+{
+	struct rose_node  *rose_node;
+	struct rose_neigh *rose_neigh;
+	int i;
+	
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
+		if (rosecmp(&rose_route->address, &rose_node->address) == 0)
+			break;
+
+	if (rose_node == NULL) return -EINVAL;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev)
+			break;
+
+	if (rose_neigh == NULL) return -EINVAL;
+	
+	for (i = 0; i < rose_node->count; i++) {
+		if (rose_node->neighbour[i] == rose_neigh) {
+			rose_neigh->count--;
+
+			if (rose_neigh->count == 0)
+				rose_remove_neigh(rose_neigh);
+				
+			rose_node->count--;
+			
+			if (rose_node->count == 0) {
+				rose_remove_node(rose_node);
+			} else {
+				switch (i) {
+					case 0:
+						rose_node->neighbour[0] = rose_node->neighbour[1];
+					case 1:
+						rose_node->neighbour[1] = rose_node->neighbour[2];
+					case 2:
+						break;
+				}
+			}
+
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/*
+ *	A device has been removed. Remove its routes and neighbours.
+ */
+void rose_rt_device_down(struct device *dev)
+{
+	struct rose_neigh *s, *rose_neigh = rose_neigh_list;
+	struct rose_node  *t, *rose_node;
+	int i;
+
+	while (rose_neigh != NULL) {
+		s          = rose_neigh;
+		rose_neigh = rose_neigh->next;
+		
+		if (s->dev == dev) {
+			rose_node = rose_node_list;
+
+			while (rose_node != NULL) {
+				t         = rose_node;
+				rose_node = rose_node->next;
+				
+				for (i = 0; i < t->count; i++) {
+					if (t->neighbour[i] == s) {
+						t->count--;
+
+						switch (i) {
+							case 0:
+								t->neighbour[0] = t->neighbour[1];
+							case 1:
+								t->neighbour[1] = t->neighbour[2];
+							case 2:
+								break;
+						}
+					}
+				}
+				
+				if (t->count <= 0)
+					rose_remove_node(t);
+			}
+			
+			rose_remove_neigh(s);
+		}
+	}
+}
+
+/*
+ *	A device has been removed. Remove its links.
+ */
+void rose_route_device_down(struct device *dev)
+{
+	struct rose_route *s, *rose_route = rose_route_list;
+
+	while (rose_route != NULL) {
+		s         = rose_route;
+		rose_route = rose_route->next;
+		
+		if (s->neigh1->dev == dev || s->neigh2->dev == dev)
+			rose_remove_route(s);
+	}
+}
+
+/*
+ *	Check that the device given is a valid AX.25 interface that is "up".
+ */
+struct device *rose_ax25_dev_get(char *devname)
+{
+	struct device *dev;
+
+	if ((dev = dev_get(devname)) == NULL)
+		return NULL;
+
+	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
+		return dev;
+
+	return NULL;
+}
+
+/*
+ *	Find the first active Rose device, usually "rose0".
+ */
+struct device *rose_dev_first(void)
+{
+	struct device *dev, *first = NULL;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next)
+		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
+			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
+				first = dev;
+
+	return first;
+}
+
+/*
+ *	Find the Rose device for the given address.
+ */
+struct device *rose_dev_get(rose_address *addr)
+{
+	struct device *dev;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next)
+		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
+			return dev;
+
+	return NULL;
+}
+
+/*
+ *	Find a neighbour given a Rose address.
+ */
+struct rose_neigh *rose_get_neigh(rose_address *addr)
+{
+	struct rose_node *node;
+	
+	for (node = rose_node_list; node != NULL; node = node->next)
+		if (rosecmp(&node->address, addr) == 0)
+			break;
+			
+	if (node == NULL) return NULL;
+	
+	if (node->which >= node->count) return NULL;
+	
+	return node->neighbour[node->which];
+}
+
+/*
+ *	Handle the ioctls that control the routing functions.
+ */
+int rose_rt_ioctl(unsigned int cmd, void *arg)
+{
+	struct rose_route_struct rose_route;
+	struct device *dev;
+	int err;
+
+	switch (cmd) {
+
+		case SIOCADDRT:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0)
+				return err;
+			copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct));
+			if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
+				return -EINVAL;
+			if (rose_dev_get(&rose_route.address) != NULL)	/* Can't add routes to ourself */
+				return -EINVAL;
+			return rose_add_node(&rose_route, dev);
+
+		case SIOCDELRT:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0)
+				return err;
+			copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct));
+			if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
+				return -EINVAL;
+			return rose_del_node(&rose_route, dev);
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * 	A level 2 link has timed out, therefore it appears to be a poor link,
+ *	then don't use that neighbour until it is reset. XXX others.
+ */
+void rose_link_failed(ax25_address *callsign, struct device *dev)
+{
+	struct rose_neigh *rose_neigh;
+	struct rose_node  *rose_node;
+	struct sk_buff    *skb;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (ax25cmp(&rose_neigh->callsign, callsign) == 0 && rose_neigh->dev == dev)
+			break;
+			
+	if (rose_neigh == NULL) return;
+
+	rose_neigh->restarted = 0;
+	rose_neigh->t0timer   = 0;
+	del_timer(&rose_neigh->timer);
+
+	while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+	
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
+		if (rose_node->which < rose_node->count && rose_node->neighbour[rose_node->which] == rose_neigh)
+			rose_node->which++;
+}
+
+/*
+ * 	A device has been "downed" remove its link status. XXX others.
+ */
+void rose_link_device_down(struct device *dev)
+{
+	struct rose_neigh *rose_neigh;
+	struct rose_node  *rose_node;
+	struct sk_buff    *skb;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
+		if (rose_neigh->dev == dev) {
+			rose_neigh->restarted = 0;
+			rose_neigh->t0timer   = 0;
+			del_timer(&rose_neigh->timer);
+
+			while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
+				kfree_skb(skb, FREE_WRITE);
+	
+			for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
+				if (rose_node->which < rose_node->count && rose_node->neighbour[rose_node->which] == rose_neigh)
+					rose_node->which++;
+		}
+	}
+}
+
+/*
+ *	Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
+ *	indicates an internally generated frame.
+ */
+int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
+{
+	struct rose_neigh *rose_neigh, *new_neigh;
+	struct rose_node  *rose_node;
+	struct rose_route *rose_route;
+	rose_address *dest_addr;
+	struct sock *sk;
+	unsigned short frametype;
+	unsigned int lci;
+	struct device *dev;
+	unsigned long flags;
+
+#ifdef CONFIG_FIREWALL
+	if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+		return 0;
+#endif
+
+	frametype = skb->data[2];
+	lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->device == rose_neigh->dev)
+			break;
+
+	if (rose_neigh == NULL)
+		return 0;
+
+	/*
+	 *	LCI of zero is always for us, and its always a restart
+	 * 	frame.
+	 */
+	if (lci == 0) {
+		rose_link_rx_restart(skb, rose_neigh, frametype);
+		return 0;
+	}
+
+	/*
+	 *	Find an existing socket.
+	 */
+	if ((sk = rose_find_socket(lci, rose_neigh->dev)) != NULL) {
+		skb->h.raw = skb->data;
+		return rose_process_rx_frame(sk, skb);
+	}
+
+	/*
+	 *	Is is a Call Request and is it for us ?
+	 */
+	if (frametype == ROSE_CALL_REQUEST) {
+		dest_addr = (rose_address *)(skb->data + 4);
+
+		if ((dev = rose_dev_get(dest_addr)) != NULL)
+			return rose_rx_call_request(skb, dev, rose_neigh, lci);
+	}
+
+	if (!sysctl_rose_routing_control) {
+		rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+		return 0;
+	}
+
+	/*
+	 *	Route it to the next in line if we have an entry for it.
+	 */
+
+	/*
+	 *	We should check for the random number in the facilities
+	 *	here. XXX.
+	 */
+	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
+		if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) {
+			skb->data[0] &= 0xF0;
+			skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
+			skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
+			rose_transmit_link(skb, rose_route->neigh2);
+			if (frametype == ROSE_CLEAR_CONFIRMATION)
+				rose_remove_route(rose_route);
+			return 1;
+		}
+		if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) {
+			skb->data[0] &= 0xF0;
+			skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F;
+			skb->data[1]  = (rose_route->lci1 >> 0) & 0xFF;
+			rose_transmit_link(skb, rose_route->neigh1);
+			if (frametype == ROSE_CLEAR_CONFIRMATION)
+				rose_remove_route(rose_route);
+			return 1;
+		}
+	}
+
+	/*
+	 *	We know that:
+	 *	1. The frame isn't for us,
+	 *	2. It isn't "owned" by any existing route.
+	 */
+	if (frametype != ROSE_CALL_REQUEST)	/* XXX */
+		return 0;
+
+	dest_addr = (rose_address *)(skb->data + 4);
+
+	/*
+	 *	Create a new route entry, if we can.
+	 */
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
+		if (rosecmp(&rose_node->address, dest_addr) == 0)
+			break;
+	/*
+	 *	Its an unknown node, or is unreachable.
+	 */
+	if (rose_node == NULL || rose_node->which >= rose_node->count) {
+		rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+		return 0;
+	}
+
+	if ((rose_route = (struct rose_route *)kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
+		rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+		return 0;
+	}
+
+	new_neigh = rose_node->neighbour[rose_node->which];
+
+	rose_route->lci1   = lci;
+	rose_route->neigh1 = rose_neigh;
+	rose_route->lci2   = rose_new_lci(new_neigh->dev);
+	rose_route->neigh2 = new_neigh;
+
+	save_flags(flags); cli();
+	rose_route->next = rose_route_list;
+	rose_route_list  = rose_route;
+	restore_flags(flags);
+
+	skb->data[0] &= 0xF0;
+	skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
+	skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
+
+	rose_transmit_link(skb, rose_route->neigh2);
+
+	return 1;
+}
+
+int rose_nodes_get_info(char *buffer, char **start, off_t offset,
+		      int length, int dummy)
+{
+	struct rose_node *rose_node;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+	int i;
+
+	cli();
+
+	len += sprintf(buffer, "address    w n neigh neigh neigh\n");
+
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) {
+		len += sprintf(buffer + len, "%-10s %d %d",
+			rose2asc(&rose_node->address),
+			rose_node->which + 1,
+			rose_node->count);			
+
+		for (i = 0; i < rose_node->count; i++)
+			len += sprintf(buffer + len, " %05d",
+				rose_node->neighbour[i]->number);
+
+		len += sprintf(buffer + len, "\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+int rose_neigh_get_info(char *buffer, char **start, off_t offset,
+		      int length, int dummy)
+{
+	struct rose_neigh *rose_neigh;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "addr  callsign  dev  count  restart     t0\n");
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
+		len += sprintf(buffer + len, "%05d %-9s %-4s   %3d      %3s  %3d/%03d\n",
+			rose_neigh->number,
+			ax2asc(&rose_neigh->callsign),
+			rose_neigh->dev ? rose_neigh->dev->name : "???",
+			rose_neigh->count,
+			(rose_neigh->restarted) ? "yes" : "no",
+			rose_neigh->t0timer / PR_SLOWHZ,
+			rose_neigh->t0      / PR_SLOWHZ);
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+		
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+int rose_routes_get_info(char *buffer, char **start, off_t offset,
+		      int length, int dummy)
+{
+	struct rose_route *rose_route;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "lci  callsign  dev  <-> lci  callsign  dev\n");
+
+	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
+		len += sprintf(buffer + len, "%3.3X %-9s %-4s     ",
+			rose_route->lci1,
+			ax2asc(&rose_route->neigh1->callsign),
+			rose_route->neigh1->dev ? rose_route->neigh1->dev->name : "???");
+		len += sprintf(buffer + len, "%3.3X %-9s %-4s\n",
+			rose_route->lci2,
+			ax2asc(&rose_route->neigh2->callsign),
+			rose_route->neigh2->dev ? rose_route->neigh2->dev->name : "???");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+		
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+#ifdef MODULE
+
+/*
+ *	Release all memory associated with Rose routing structures.
+ */
+void rose_rt_free(void)
+{
+	struct rose_neigh *s, *rose_neigh = rose_neigh_list;
+	struct rose_node  *t, *rose_node  = rose_node_list;
+	struct rose_route *u, *rose_route = rose_route_list;
+
+	while (rose_neigh != NULL) {
+		s          = rose_neigh;
+		rose_neigh = rose_neigh->next;
+		
+		rose_remove_neigh(s);
+	}
+
+	while (rose_node != NULL) {
+		t          = rose_node;
+		rose_node = rose_node->next;
+		
+		rose_remove_node(t);
+	}
+
+	while (rose_route != NULL) {
+		u          = rose_route;
+		rose_route = rose_route->next;
+		
+		rose_remove_route(u);
+	}
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- v2.1.8/linux/net/rose/rose_subr.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_subr.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,494 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from nr_subr.c
+ */
+ 
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+/*
+ *	This routine purges all of the queues of frames.
+ */
+void rose_clear_queues(struct sock *sk)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
+		skb->sk   = sk;
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+	}
+
+	while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL) {
+		skb->sk   = sk;
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+	}
+
+	while ((skb = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL) {
+		kfree_skb(skb, FREE_READ);
+	}
+}
+
+/*
+ * This routine purges the input queue of those frames that have been
+ * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
+ * SDL diagram.
+ */
+void rose_frames_acked(struct sock *sk, unsigned short nr)
+{
+	struct sk_buff *skb;
+
+	/*
+	 * Remove all the ack-ed frames from the ack queue.
+	 */
+	if (sk->protinfo.rose->va != nr) {
+		while (skb_peek(&sk->protinfo.rose->ack_queue) != NULL && sk->protinfo.rose->va != nr) {
+		        skb = skb_dequeue(&sk->protinfo.rose->ack_queue);
+		        skb->sk   = sk;
+			skb->free = 1;
+			kfree_skb(skb, FREE_WRITE);
+			sk->protinfo.rose->va = (sk->protinfo.rose->va + 1) % ROSE_MODULUS;
+		}
+	}
+}
+
+/*
+ * Requeue all the un-ack-ed frames on the output queue to be picked
+ * up by rose_kick called from the timer. This arrangement handles the
+ * possibility of an empty output queue.
+ */
+void rose_requeue_frames(struct sock *sk)
+{
+	struct sk_buff *skb, *skb_prev = NULL;
+
+	while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL) {
+		if (skb_prev == NULL)
+			skb_queue_head(&sk->write_queue, skb);
+		else
+			skb_append(skb_prev, skb);
+		skb_prev = skb;
+	}
+}
+
+/*
+ *	Validate that the value of nr is between va and vs. Return true or
+ *	false for testing.
+ */
+int rose_validate_nr(struct sock *sk, unsigned short nr)
+{
+	unsigned short vc = sk->protinfo.rose->va;
+
+	while (vc != sk->protinfo.rose->vs) {
+		if (nr == vc) return 1;
+		vc = (vc + 1) % ROSE_MODULUS;
+	}
+	
+	if (nr == sk->protinfo.rose->vs) return 1;
+
+	return 0;
+}
+
+/* 
+ *  This routine is called when the packet layer internally generates a
+ *  control frame.
+ */
+void rose_write_internal(struct sock *sk, int frametype)
+{
+	struct sk_buff *skb;
+	unsigned char  *dptr;
+	unsigned char  lci1, lci2;
+	char buffer[100];
+	int len, faclen = 0;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
+	
+	switch (frametype) {
+		case ROSE_CALL_REQUEST:
+			len   += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN;
+			faclen = rose_create_facilities(buffer, sk->protinfo.rose);
+			len   += faclen;
+			break;
+		case ROSE_CALL_ACCEPTED:
+		case ROSE_CLEAR_REQUEST:
+		case ROSE_RESET_REQUEST:
+		case ROSE_DIAGNOSTIC:
+			len   += 2;
+			break;
+		case ROSE_INTERRUPT:
+			len   += 1;
+			break;
+	}
+	
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	/*
+	 *	Space for AX.25 header and PID.
+	 */
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1);
+	
+	dptr = skb_put(skb, skb_tailroom(skb));
+
+	lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F;
+	lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF;
+
+	switch (frametype) {
+	
+		case ROSE_CALL_REQUEST:
+			*dptr++ = GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = 0xAA;
+			memcpy(dptr, &sk->protinfo.rose->dest_addr,  ROSE_ADDR_LEN);
+			dptr   += ROSE_ADDR_LEN;
+			memcpy(dptr, &sk->protinfo.rose->source_addr, ROSE_ADDR_LEN);
+			dptr   += ROSE_ADDR_LEN;
+			memcpy(dptr, buffer, faclen);
+			dptr   += faclen;
+			break;
+
+		case ROSE_CALL_ACCEPTED:
+			*dptr++ = GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = 0x00;		/* Address length */
+			*dptr++ = 0;		/* Facilities length */
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+		case ROSE_RESET_REQUEST:
+			*dptr++ = GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = 0x00;		/* XXX */
+			*dptr++ = 0x00;		/* XXX */
+			break;
+
+		case ROSE_INTERRUPT:
+			*dptr++ = GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = 0x00;		/* XXX */
+			break;
+
+		case ROSE_RR:
+		case ROSE_RNR:
+		case ROSE_REJ:
+			*dptr++ = GFI | lci1;
+			*dptr++ = lci2;
+			*dptr   = frametype;
+			*dptr++ |= (sk->protinfo.rose->vr << 5) & 0xE0;
+			break;
+		
+		case ROSE_CLEAR_CONFIRMATION:
+		case ROSE_INTERRUPT_CONFIRMATION:
+		case ROSE_RESET_CONFIRMATION:
+			*dptr++ = GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++  = frametype;
+			break;
+
+		default:
+			printk(KERN_ERR "rose_write_internal: invalid frametype %02X\n", frametype);
+			kfree_skb(skb, FREE_WRITE);
+			return;
+	}
+
+	rose_transmit_link(skb, sk->protinfo.rose->neighbour);
+}
+
+int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m)
+{
+	unsigned char *frame;
+	
+	frame = skb->data;
+	
+	*ns = *nr = *q = *d = *m = 0;
+	
+	switch (frame[2]) {
+		case ROSE_CALL_REQUEST:
+		case ROSE_CALL_ACCEPTED:
+		case ROSE_CLEAR_REQUEST:
+		case ROSE_CLEAR_CONFIRMATION:
+		case ROSE_INTERRUPT:
+		case ROSE_INTERRUPT_CONFIRMATION:
+		case ROSE_RESET_REQUEST:
+		case ROSE_RESET_CONFIRMATION:
+		case ROSE_RESTART_REQUEST:
+		case ROSE_RESTART_CONFIRMATION:
+		case ROSE_REGISTRATION_REQUEST:
+		case ROSE_REGISTRATION_CONFIRMATION:
+		case ROSE_DIAGNOSTIC:
+			return frame[2];
+		default:
+			break;
+	}
+
+	if ((frame[2] & 0x1F) == ROSE_RR  ||
+	    (frame[2] & 0x1F) == ROSE_RNR ||
+	    (frame[2] & 0x1F) == ROSE_REJ) {
+		*nr = (frame[2] >> 5) & 0x07;
+		return frame[2] & 0x1F;
+	}
+
+	if ((frame[2] & 0x01) == ROSE_DATA) {
+		*q  = (frame[0] & Q_BIT) == Q_BIT;
+		*d  = (frame[0] & D_BIT) == D_BIT;
+		*m  = (frame[2] & M_BIT) == M_BIT;
+		*nr = (frame[2] >> 5) & 0x07;
+		*ns = (frame[2] >> 1) & 0x07;
+		return ROSE_DATA;
+	}
+	
+	return ROSE_ILLEGAL;
+}
+
+static int rose_parse_national(unsigned char *p, rose_cb *rose, int len)
+{
+	unsigned char l, n = 0;
+
+	do {
+		switch (*p & 0xC0) {
+			case 0x00:
+				p   += 2;
+				n   += 2;
+				len -= 2;
+				break;
+				
+			case 0x40:
+				if (*p == FAC_NATIONAL_RAND)
+					rose->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);
+				p   += 3;
+				n   += 3;
+				len -= 3;
+				break;
+
+			case 0x80:
+				p   += 4;
+				n   += 4;
+				len -= 4;
+				break;
+				
+			case 0xC0:
+				l = p[1];
+				if (*p == FAC_NATIONAL_DEST_DIGI) {
+					memcpy(&rose->source_digi, p + 2, AX25_ADDR_LEN);
+					rose->source_ndigis = 1;
+				}
+				if (*p == FAC_NATIONAL_SRC_DIGI) {
+					memcpy(&rose->dest_digi,   p + 2, AX25_ADDR_LEN);
+					rose->dest_ndigis = 1;
+				}
+				p   += l + 2;
+				n   += l + 2;
+				len -= l + 2;
+				break;
+		}
+	} while (*p != 0x00 && len > 0);
+
+	return n;
+}
+
+static int rose_parse_ccitt(unsigned char *p, rose_cb *rose, int len)
+{
+	unsigned char l, n = 0;
+	char callsign[11];
+
+	do {
+		switch (*p & 0xC0) {
+			case 0x00:
+				p   += 2;
+				n   += 2;
+				len -= 2;
+				break;
+				
+			case 0x40:
+				p   += 3;
+				n   += 3;
+				len -= 3;
+				break;
+
+			case 0x80:
+				p   += 4;
+				n   += 4;
+				len -= 4;
+				break;
+				
+			case 0xC0:
+				l = p[1];
+				if (*p == FAC_CCITT_DEST_NSAP) {
+					memcpy(&rose->source_addr, p + 7, ROSE_ADDR_LEN);
+					memcpy(callsign, p + 12,   l - 10);
+					callsign[l - 10] = '\0';
+					rose->source_call = *asc2ax(callsign);
+				}
+				if (*p == FAC_CCITT_SRC_NSAP) {
+					memcpy(&rose->dest_addr, p + 7, ROSE_ADDR_LEN);
+					memcpy(callsign, p + 12, l - 10);
+					callsign[l - 10] = '\0';
+					rose->dest_call = *asc2ax(callsign);
+				}
+				p   += l + 2;
+				n   += l + 2;
+				len -= l + 2;
+				break;
+		}
+	} while (*p != 0x00 && len > 0);
+
+	return n;
+}
+
+int rose_parse_facilities(struct sk_buff *skb, rose_cb *rose)
+{
+	int facilities_len, len;
+	unsigned char *p;
+
+	memset(rose, 0x00, sizeof(rose_cb));
+
+	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
+	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
+
+	p = skb->data + len + 4;
+
+	facilities_len = *p++;
+	
+	if (facilities_len == 0)
+		return 0;
+
+	while (facilities_len > 0) {
+		if (*p == 0x00) {
+			facilities_len--;
+			p++;
+
+			switch (*p) {
+				case FAC_NATIONAL:		/* National */
+					len = rose_parse_national(p + 1, rose, facilities_len - 1);
+					facilities_len -= len + 1;
+					p += len + 1;
+					break;
+					
+				case FAC_CCITT:		/* CCITT */
+					len = rose_parse_ccitt(p + 1, rose, facilities_len - 1);
+					facilities_len -= len + 1;
+					p += len + 1;
+					break;
+					
+				default:
+					printk(KERN_DEBUG "rose_parse_facilities: unknown facilities family %02X\n", *p);
+					facilities_len--;
+					p++;
+					break;
+			}
+		}
+	}
+
+	return 1;
+}
+
+int rose_create_facilities(unsigned char *buffer, rose_cb *rose)
+{
+	unsigned char *p = buffer + 1;
+	char *callsign;
+	int len;
+	
+	/* National Facilities */
+	if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) {
+		*p++ = 0x00;
+		*p++ = FAC_NATIONAL;
+
+		if (rose->rand != 0) {
+			*p++ = FAC_NATIONAL_RAND;
+			*p++ = (rose->rand >> 8) & 0xFF;
+			*p++ = (rose->rand >> 0) & 0xFF;
+		}
+		
+		if (rose->source_ndigis == 1) {
+			*p++ = FAC_NATIONAL_SRC_DIGI;
+			*p++ = AX25_ADDR_LEN;
+			memcpy(p, &rose->source_digi, AX25_ADDR_LEN);
+			p   += AX25_ADDR_LEN;
+		}
+
+		if (rose->dest_ndigis == 1) {
+			*p++ = FAC_NATIONAL_DEST_DIGI;
+			*p++ = AX25_ADDR_LEN;
+			memcpy(p, &rose->dest_digi, AX25_ADDR_LEN);
+			p   += AX25_ADDR_LEN;
+		}
+	}
+
+	*p++ = 0x00;
+	*p++ = FAC_CCITT;
+	
+	*p++ = FAC_CCITT_DEST_NSAP;
+	
+	callsign = ax2asc(&rose->dest_call);
+
+	*p++ = strlen(callsign) + 10;
+	*p++ = (strlen(callsign) + 9) * 2;		/* ??? */
+
+	*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
+	*p++ = ROSE_ADDR_LEN * 2;
+	memcpy(p, &rose->dest_addr, ROSE_ADDR_LEN);
+	p   += ROSE_ADDR_LEN;
+
+	memcpy(p, callsign, strlen(callsign));
+	p   += strlen(callsign);
+	
+	*p++ = FAC_CCITT_SRC_NSAP;
+
+	callsign = ax2asc(&rose->source_call);
+
+	*p++ = strlen(callsign) + 10;
+	*p++ = (strlen(callsign) + 9) * 2;		/* ??? */
+
+	*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
+	*p++ = ROSE_ADDR_LEN * 2;
+	memcpy(p, &rose->source_addr, ROSE_ADDR_LEN);
+	p   += ROSE_ADDR_LEN;
+
+	memcpy(p, callsign, strlen(callsign));
+	p   += strlen(callsign);
+
+	len       = p - buffer;
+	buffer[0] = len - 1;
+	
+	return len;
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/rose_timer.c linux/net/rose/rose_timer.c
--- v2.1.8/linux/net/rose/rose_timer.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/rose_timer.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,152 @@
+/*
+ *	Rose release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	History
+ *	Rose 001	Jonathan(G4KLX)	Cloned from nr_timer.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+static void rose_timer(unsigned long);
+
+/*
+ *	Linux set/reset timer routines
+ */
+void rose_set_timer(struct sock *sk)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	del_timer(&sk->timer);
+	restore_flags(flags);
+
+	sk->timer.next     = sk->timer.prev = NULL;	
+	sk->timer.data     = (unsigned long)sk;
+	sk->timer.function = &rose_timer;
+
+	sk->timer.expires = jiffies + 10;
+	add_timer(&sk->timer);
+}
+
+static void rose_reset_timer(struct sock *sk)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	del_timer(&sk->timer);
+	restore_flags(flags);
+
+	sk->timer.data     = (unsigned long)sk;
+	sk->timer.function = &rose_timer;
+	sk->timer.expires  = jiffies + 10;
+	add_timer(&sk->timer);
+}
+
+/*
+ *	Rose TIMER 
+ *
+ *	This routine is called every 500ms. Decrement timer by this
+ *	amount - if expired then process the event.
+ */
+static void rose_timer(unsigned long param)
+{
+	struct sock *sk = (struct sock *)param;
+
+	switch (sk->protinfo.rose->state) {
+		case ROSE_STATE_0:
+			/* Magic here: If we listen() and a new link dies before it
+			   is accepted() it isn't 'dead' so doesn't get removed. */
+			if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) {
+				del_timer(&sk->timer);
+				rose_destroy_socket(sk);
+				return;
+			}
+			break;
+
+		case ROSE_STATE_3:
+			/*
+			 * Check for the state of the receive buffer.
+			 */
+			if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION)) {
+				sk->protinfo.rose->condition &= ~OWN_RX_BUSY_CONDITION;
+				sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
+				rose_write_internal(sk, ROSE_RR);
+				break;
+			}
+			/*
+			 * Check for frames to transmit.
+			 */
+			rose_kick(sk);
+			break;
+
+		default:
+			break;
+	}
+
+	if (sk->protinfo.rose->timer == 0 || --sk->protinfo.rose->timer > 0) {
+		rose_reset_timer(sk);
+		return;
+	}
+
+	/*
+	 * Timer has expired, it may have been T1, T2, or T3. We can tell
+	 * by the socket state.
+	 */
+	switch (sk->protinfo.rose->state) {
+		case ROSE_STATE_1:	/* T1 */
+		case ROSE_STATE_4:	/* T2 */
+			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			sk->protinfo.rose->state = ROSE_STATE_2;
+			sk->protinfo.rose->timer = sk->protinfo.rose->t3;
+			break;
+
+		case ROSE_STATE_2:	/* T3 */
+			rose_clear_queues(sk);
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = ETIMEDOUT;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+	}
+
+	rose_set_timer(sk);
+}
+
+#endif
diff -u --recursive --new-file v2.1.8/linux/net/rose/sysctl_net_rose.c linux/net/rose/sysctl_net_rose.c
--- v2.1.8/linux/net/rose/sysctl_net_rose.c	Thu Jan  1 02:00:00 1970
+++ linux/net/rose/sysctl_net_rose.c	Sun Nov 10 19:12:58 1996
@@ -0,0 +1,62 @@
+/* -*- linux-c -*-
+ * sysctl_net_rose.c: sysctl interface to net Rose subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/rose directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <net/ax25.h>
+#include <net/rose.h>
+
+static int min_timer[] = {1 * PR_SLOWHZ};
+static int max_timer[] = {300 * PR_SLOWHZ};
+static int min_idle[]  = {0 * PR_SLOWHZ};
+static int max_idle[]  = {65535 * PR_SLOWHZ};
+static int min_route[] = {0};
+static int max_route[] = {0};
+
+static struct ctl_table_header *rose_table_header;
+
+static ctl_table rose_table[] = {
+        {NET_ROSE_RESTART_REQUEST_TIMEOUT, "restart_request_timeout",
+         &sysctl_rose_restart_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_CALL_REQUEST_TIMEOUT, "call_request_timeout",
+         &sysctl_rose_call_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_RESET_REQUEST_TIMEOUT, "reset_request_timeout",
+         &sysctl_rose_reset_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_CLEAR_REQUEST_TIMEOUT, "clear_request_timeout",
+         &sysctl_rose_clear_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_NO_ACTIVITY_TIMEOUT, "no_activity_timeout",
+         &sysctl_rose_no_activity_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_idle, &max_idle},
+        {NET_ROSE_ROUTING_CONTROL, "routing_control",
+         &sysctl_rose_routing_control, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route},
+	{0}
+};
+
+static ctl_table rose_dir_table[] = {
+	{NET_ROSE, "rose", NULL, 0, 0555, rose_table},
+	{0}
+};
+
+static ctl_table rose_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, rose_dir_table},
+	{0}
+};
+
+void rose_register_sysctl(void)
+{
+	rose_table_header = register_sysctl_table(rose_root_table, 1);
+}
+
+void rose_unregister_sysctl(void)
+{
+	unregister_sysctl_table(rose_table_header);
+}
diff -u --recursive --new-file v2.1.8/linux/net/socket.c linux/net/socket.c
--- v2.1.8/linux/net/socket.c	Sun Nov 10 20:12:31 1996
+++ linux/net/socket.c	Sun Nov 10 19:53:14 1996
@@ -67,7 +67,7 @@
 #include <linux/proc_fs.h>
 #include <linux/firewall.h>
 
-#ifdef CONFIG_KERNELD
+#if defined(CONFIG_KERNELD) && defined(CONFIG_NET)
 #include <linux/kerneld.h>
 #endif
 
@@ -536,8 +536,13 @@
 	/* Locate the correct protocol family. */
 	i = find_protocol_family(family);
 
-#ifdef CONFIG_KERNELD
-	/* Attempt to load a protocol module if the find failed. */
+#if defined(CONFIG_KERNELD) && defined(CONFIG_NET)
+	/* Attempt to load a protocol module if the find failed. 
+	 * 
+	 * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user 
+	 * requested real, full-featured networking support upon configuration.
+	 * Otherwise module support will break!
+	 */
 	if (i < 0)
 	{
 		char module_name[30];
diff -u --recursive --new-file v2.1.8/linux/net/sysctl_net.c linux/net/sysctl_net.c
--- v2.1.8/linux/net/sysctl_net.c	Sun Nov 10 20:12:31 1996
+++ linux/net/sysctl_net.c	Sun Nov 10 19:12:58 1996
@@ -28,14 +28,6 @@
 extern ctl_table atalk_table[];
 #endif
 
-#ifdef CONFIG_NETROM
-extern ctl_table netrom_table[];
-#endif
-
-#ifdef CONFIG_AX25
-extern ctl_table ax25_table[];
-#endif
-
 extern ctl_table core_table[], unix_table[];
 
 #ifdef CONFIG_NET
@@ -65,12 +57,6 @@
 #endif
 #ifdef CONFIG_ATALK
         {NET_ATALK,  "appletalk", NULL, 0, 0555, atalk_table},
-#endif
-#ifdef CONFIG_NETROM
-	{NET_NETROM, "netrom",    NULL, 0, 0555, netrom_table},
-#endif
-#ifdef CONFIG_AX25
-	{NET_AX25,   "ax25",      NULL, 0, 0555, ax25_table},
 #endif
 #ifdef CONFIG_BRIDGE
         {NET_BRIDGE, "bridge",    NULL, 0, 0555, bridge_table},
diff -u --recursive --new-file v2.1.8/linux/scripts/ksymoops.cc linux/scripts/ksymoops.cc
--- v2.1.8/linux/scripts/ksymoops.cc	Tue Apr  9 07:07:40 1996
+++ linux/scripts/ksymoops.cc	Sun Nov 10 14:04:07 1996
@@ -50,7 +50,7 @@
     friend class NameList;
 
   private:
-    long address_;
+    unsigned long address_;
     char* name_;
     long offset_;
     long extent_;
@@ -107,7 +107,7 @@
   public:
     int valid() { return (cardinality_ > 0); }
 	
-    KSym* find(long address);
+    KSym* find(unsigned long address);
     void decode(unsigned char* code, long eip_addr);
     
   public:
@@ -115,7 +115,7 @@
 };
 
 KSym*
-NameList::find(long address)
+NameList::find(unsigned long address)
 {
     if (!valid())
 	return 0;
@@ -292,7 +292,7 @@
 	    if (ksym)
 		cout << ">>EIP: " << *ksym << endl;
 	} else if (strequ(buffer, "Trace:") && names.valid()) {
-	    long address;
+	    unsigned long address;
 	    while ((cin >> buffer) && 
 		   (sscanf(buffer, " [<%x>]", &address) == 1) &&
 		   address > 0xc) {
