diff -u --recursive --new-file v2.1.46/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.46/linux/Documentation/Changes	Mon Jun 16 16:35:52 1997
+++ linux/Documentation/Changes	Wed Jul 23 11:02:04 1997
@@ -29,7 +29,7 @@
    Also, don't forget http://www.linuxhq.com/ for all your Linux kernel
 needs.
 
-Last updated: May 31, 1997.
+Last updated: July 22. 1997
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Minimal Requirements
@@ -39,11 +39,11 @@
 encountered a bug!  If you're unsure what version you're currently
 running, the suggested command should tell you.
 
-- Kernel modules	 modutils-2.1.34         ; insmod -v
+- Kernel modules	 modutils-2.1.42         ; insmod -V
 - Gnu C 		 2.7.2.1                 ; gcc --version
 - Binutils		 2.8.1.0.1               ; ld -v
-- Linux C Library	 5.4.23                  ; ls -l /lib/libc.so.*
-- Dynamic Linker (ld.so) 1.8.5                   ; ldd -v
+- Linux C Library	 5.4.33                  ; ls -l /lib/libc.so.*
+- Dynamic Linker (ld.so) 1.9.2                   ; ldd -v
 - Linux C++ Library	 2.7.2.1                 ; ls -l /usr/lib/libg++.so.*
 - Procps		 1.01                    ; ps --version
 - Procinfo               0.11                    ; procinfo -v
@@ -51,8 +51,9 @@
 - Net-tools              1.41                    ; hostname -V
 - Loadlin                1.6a
 - Sh-utils               1.16                    ; expr --v
-- Autofs                 0.3.3                   ; automount --version
+- Autofs                 0.3.7                   ; automount --version
 - NFS                    0.4.21                  ; showmount --version
+- Bash                   1.14.7                  ; bash -version
 
 Upgrade notes
 *************
@@ -78,12 +79,12 @@
    For modules to work, you need to be running libc-5.4.x or greater.
 Since updates to libc fix other problems as well (security flaws, for
 example) and since 5.4.7 is missing a few needed symbols, try to get
-the latest 5.4.x you can.  Currently, libc-5.4.23 is the latest public
+the latest 5.4.x you can.  Currently, libc-5.4.33 is the latest public
 release.
 
    If you upgrade to libc-5.4.x, you also have to upgrade your dynamic
-linker (ld.so) to at least 1.8.5, or all sorts of weirdness will
-happen.  Actually, ld.so-1.8.2 and later will work, but 1.8.5 is widely
+linker (ld.so) to at least 1.9.2, or all sorts of weirdness will
+happen.  Actually, ld.so-1.8.2 and later will work, but 1.9.2 is widely
 available, so if you need to upgrade, use it.  If you get a release
 later than 1.8.5, avoid 1.8.10 as it introduces a few bugs that are
 fixed in later releases.
@@ -94,7 +95,8 @@
 Modules
 =======
 
-   You need to upgrade to modutils-2.1.34 for kernels 2.1.34 and later.
+   You need to upgrade to modutils-2.1.42 for kernels 2.1.42 and later.
+This version will also work with 2.0.x kernels.
 
 Binutils
 ========
@@ -146,7 +148,8 @@
 many memory utils, which have to be upgraded.  Get the new procinfo and
 procps (which, AFAIK, is not yet available) to fix this.  Until you
 upgrade, programs which read /proc/meminfo will seg-fault or give an
-error.
+error.  There is an unofficial update to 1.12.2 available that fixes
+most problems.
 
 Mount and network file systems
 ==============================
@@ -167,7 +170,7 @@
 ======
 
    A new "stable" version of DOSEMU is available for 2.1.x kernels.
-Upgrade to 0.66.1 or later.
+Upgrade to 0.66.7 or later.
 
 Loadlin
 =======
@@ -192,6 +195,13 @@
 /dev/lp0 with the new Plug-and-Play driver.  If printing breaks with
 the new driver, try checking your lpd configuration.
 
+Bash
+====
+
+   Old versions of Bash fail to properly handle symlinks, which can
+cause problems when compiling modules.  Upgrade to at least 1.14 to fix
+this problem.
+
 Where to get the files
 **********************
 
@@ -218,12 +228,12 @@
 Linux C Library
 ===============
 
-The 5.4.23 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.23.bin.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.23.bin.tar.gz
-Installation notes for 5.4.23:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.23
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.23
+The 5.4.33 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.33.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.33.bin.tar.gz
+Installation notes for 5.4.33:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.33
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.33
 
 Linux C++ Library
 =================
@@ -238,16 +248,16 @@
 Dynamic Linker
 ==============
 
-The 1.8.5 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/ld.so-1.8.5.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/ld.so-1.8.5.tar.gz
+The 1.9.2 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/ld.so-1.9.2.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/ld.so-1.9.2.tar.gz
 
 Modules utilities
 =================
 
-The 2.1.34 release:
-ftp://ftp.redhat.com/pub/alphabits/modutils-2.1.34.tar.gz
-ftp://ftp.kernel.org/pub/linux/kernel/v2.1/modutils-2.1.34.tar.gz
+The 2.1.42 release:
+ftp://ftp.redhat.com/pub/alphabits/modutils-2.1.42.tar.gz
+ftp://ftp.kernel.org/pub/linux/kernel/v2.1/modutils-2.1.42.tar.gz
 
 Procps utilities
 ================
@@ -255,6 +265,8 @@
 The 1.01 release:
 ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.01.tar.gz
 ftp://sunsite.unc.edu/pub/Linux/system/status/ps/procps-1.01.tgz
+The unofficial 1.12.2 release:
+ftp://ftp.debian.org/pub/debian/hamm/hamm/source/base/procps_1.12.2.tar.gz
 
 Procinfo utilities
 ==================
@@ -278,9 +290,9 @@
 DOSEMU
 ======
 
-The 0.66.1 release:
-ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu0.66.1.tgz
-ftp://sunsite.unc.edu/pub/Linux/system/emulators/dosemu0.66.1.tgz
+The 0.66.7 release:
+ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu0.66.7.tgz
+ftp://sunsite.unc.edu/pub/Linux/system/emulators/dosemu0.66.7.tgz
 
 Loadlin
 =======
@@ -305,8 +317,8 @@
 Autofs
 ======
 
-The 0.3.3 release:
-ftp://ftp.kernel.org/pub/linux/daemons/autofs/autofs-0.3.3.tar.gz
+The 0.3.7 release:
+ftp://ftp.kernel.org/pub/linux/daemons/autofs/autofs-0.3.7.tar.gz
 
 NFS
 ===
@@ -328,6 +340,12 @@
 The 3.2 release:
 ftp://ftp.uni-paderborn.de/pub/linux/local/yp/ypbind-3.2.tar.gz
 
+Bash
+====
+
+The 1.14.7 release:
+ftp://prep.ai.mit.edu/pub/gnu/bash-1.14.7.tar.gz
+
 Other Info
 ==========
 
@@ -335,12 +353,19 @@
 favorite local linux mirror.  If you can, please get them from a closer
 site before checking sunsite.
 
-   Also, for those of you running Red Hat (or RPM on a different
+   You may also want to check for updated versions of this software in a
+package format for the distribution you use.
+
+   For those of you running Red Hat (or RPM on a different
 distribution), most of these are available in RPM format.  Check around
 your favorite Red Hat mirror site before installing the non-RPM
 version.  Remember, you might need to use the -force option to get the
 upgrade to install.  ftp://ftp.redhat.com/pub/contrib/ will have almost
 everything you need.
+
+   Those of you running Debian (or a different distribution that
+supports .deb packages) can look in the "unstable" and
+"project/experimental" directories of your favorite Debian mirror.
 
    For others, David Bourgin has put together a package of everything
 necessary to quickly and easily upgrade to 2.1.x.  See
diff -u --recursive --new-file v2.1.46/linux/Documentation/binfmt_misc.txt linux/Documentation/binfmt_misc.txt
--- v2.1.46/linux/Documentation/binfmt_misc.txt	Thu Jul 17 10:06:03 1997
+++ linux/Documentation/binfmt_misc.txt	Mon Jul 21 12:49:08 1997
@@ -69,20 +69,63 @@
 Emulating binfmt_java:
 ======================
 
-To emulate binfmt_java the following register-strings are necessary
-(the first two for byte-compiled Java binaries, the third for applets
-contained in a html-file). Register exactly in this order!
-  ":Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/java:"
-  ":JavaC:e::class::/usr/local/java/bin/java:"
+To emulate binfmt_java the following register-strings could be used: 
+for compiled Java programs use
+  ":Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/javawrapper:"
+for simple applet support use
   ":Applet:E::html::/usr/local/java/bin/appletviewer:"
+for more selective applet support (like binfmt_java) use
+  ":Applet:M::\<\!--applet::/usr/local/java/bin/appletviewer:"
 
-To add a Java-executable to your path you can either make a symbolic
-link to the .class file elsewhere in your path (cut the .class-extension
-in the destination name for convenience) or add the directory of your
-.class files to your PATH environment. In both cases, ensure that the
-.class files are in your CLASSPATH environment!
+Note, that for the more selective applet support you have to modify
+existing html-files to contain <!--applet--> in the first line to
+let this work!
+
+For the compiled Java programs you need a wrapper script like the
+following (this is because Java is broken in case of the filename
+handling):
+
+====================== Cut here ===================
+#!/bin/bash
+# /usr/local/java/bin/javawrapper - the wrapper for binfmt_misc/java
+CLASS=$1
+
+# if classname is a link, we follow it (this could be done easier - how?)
+if [ -L "$1" ] ; then
+	CLASS=`ls --color=no -l $1 | tr -s '\t ' '  ' | cut -d ' ' -f 11` 
+fi
+CLASSN=`basename $CLASS | sed s/\.class$//`
+CLASSP=`dirname $CLASS`
+
+FOO=$PATH
+PATH=$CLASSPATH
+if [ -z "`type -p -a $CLASSN.class`" ] ; then
+	# class is not in CLASSPATH
+	if [ -e "$CLASSP/$CLASSN.class" ] ; then
+		# append dir of class to CLASSPATH
+		if [ -z "${CLASSPATH}" ] ; then
+			export CLASSPATH=$CLASSP
+		else
+			export CLASSPATH=$CLASSP:$CLASSPATH
+		fi
+	else
+		# uh! now we would have to create a symbolic link - really
+		# ugly, i.e. print a message that one has to change the setup
+		echo "Hey! This is not a good setup to run $1 !"
+		exit 1
+	fi
+fi
+PATH=$FOO
+
+shift
+/usr/local/java/bin/java $CLASSN $@
+====================== Cut here ===================
+
+To add a Java program to your path best put a symbolic link to the main
+.class file into /usr/bin (or another place you like) omitting the .class
+extension. The directory containing the original .class file will be
+added to your CLASSPATH during execution.
 
-This is sort of ugly - Javas filename handling is just broken.
 
 
 HINTS:
diff -u --recursive --new-file v2.1.46/linux/Documentation/filesystems/romfs.txt linux/Documentation/filesystems/romfs.txt
--- v2.1.46/linux/Documentation/filesystems/romfs.txt	Fri Jan  3 03:03:41 1997
+++ linux/Documentation/filesystems/romfs.txt	Wed Jul 23 10:38:24 1997
@@ -8,19 +8,18 @@
 file system which doesn't take up useful memory from the router
 functions in the basement of your office.
 
-For comparison, both the older minix and xiafs filesystems (compiled
-as module) need more than 20000 bytes, while romfs is less than a
-page, about 4000 bytes (assuming ix86 code).  Under the same
-conditions, the msdos filesystem would need about 30K (and does not
-support device nodes or symlinks), while the nfs module with nfsroot
-is about 57K.  Furthermore, as a bit unfair comparison, an actual
-rescue disk used up 3202 blocks with ext2, while with romfs, it needed
-3079 blocks.
+For comparison, both the older minix and xiafs (the latter is now
+defunct) filesystems, compiled as module need more than 20000 bytes,
+while romfs is less than a page, about 4000 bytes (assuming i586
+code).  Under the same conditions, the msdos filesystem would need
+about 30K (and does not support device nodes or symlinks), while the
+nfs module with nfsroot is about 57K.  Furthermore, as a bit unfair
+comparison, an actual rescue disk used up 3202 blocks with ext2, while
+with romfs, it needed 3079 blocks.
 
 To create such a file system, you'll need a user program named
-genromfs.  It is (or will be shortly) available via ftp on
-sunsite.unc.edu and its mirrors, in the /pub/Linux/system/Filesystems/
-directory.
+genromfs.  It is available via anonymous ftp on sunsite.unc.edu and
+its mirrors, in the /pub/Linux/system/recovery/ directory.
 
 As the name suggests, romfs could be also used (space-efficiently) on
 various read-only medias, like (E)EPROM disks if someone will have the
@@ -61,7 +60,7 @@
 	+---+---+---+---+
   0	| - | r | o | m |  \
 	+---+---+---+---+	The ASCII representation of those bytes
-  4	| 1 | f | s | - |  /	(i.e. "-rom1fs-"
+  4	| 1 | f | s | - |  /	(i.e. "-rom1fs-")
 	+---+---+---+---+
   8	|   full size	|	The number of accessible bytes in this fs.
 	+---+---+---+---+
@@ -77,7 +76,7 @@
 now on) must be in big endian order.
 
 The first eight bytes identify the filesystem, even for the casual
-reader.  After that in the 3rd longword, it contains the number of
+inspector.  After that, in the 3rd longword, it contains the number of
 bytes accessible from the start of this filesystem.  The 4th longword
 is the checksum of the first 512 bytes (or the number of bytes
 accessible, whichever is smallest).  The applied algorithm is the same
@@ -101,7 +100,7 @@
  12	|   checksum	|	Covering the meta data, including the file
 	+---+---+---+---+	  name, and padding
  16	| file name     |	The zero terminated name of the file,
-	:               :	padded to 16 byte boundary.
+	:               :	padded to 16 byte boundary
 	+---+---+---+---+
  xx	| file data	|
 	:		:
@@ -112,9 +111,10 @@
 the file; while bit 4 shows if the file is executable or not.  The
 permissions are assumed to be world readable, if this bit is not set,
 and world executable if it is; except the character and block devices,
-they are readable only for the owner.  The owner of every file is user
-and group 0, this should never be a problem for the intended use.  The
-mapping of the 8 possible values to file types is the following:
+they are never accessible for other than owner.  The owner of every
+file is user and group 0, this should never be a problem for the
+intended use.  The mapping of the 8 possible values to file types is
+the following:
 
 	  mapping		spec.info means
  0	hard link	link destination [file header]
@@ -128,14 +128,14 @@
 
 Note that hard links are specifically marked in this filesystem, but
 they will behave as you can expect (i.e. share the inode number).
-Note also that your responsibility to not create hard link loops, and
-creating all the . and .. links for directories.  This is normally
-done correctly by the genromfs program.  Please refrain from using the
-executable bits on the socket and fifo special files, they may have
-other uses in the future.  Additionally, please remember that only
-regular files, and symlinks are supposed to have a nonzero size field;
-they contain the number of bytes available directly after the (padded)
-file name.
+Note also that it is your responsibility to not create hard link
+loops, and creating all the . and .. links for directories.  This is
+normally done correctly by the genromfs program.  Please refrain from
+using the executable bits for special purposes on the socket and fifo
+special files, they may have other uses in the future.  Additionally,
+please remember that only regular files, and symlinks are supposed to
+have a nonzero size field; they contain the number of bytes available
+directly after the (padded) file name.
 
 Another thing to note is that romfs works on file headers and data
 aligned to 16 byte boundaries, but most hardware devices and the block
@@ -145,8 +145,44 @@
 
 If you have any problems or suggestions concerning this file system,
 please contact me.  However, think twice before wanting me to add
-features and code, because the primary advantage of this file system
-is the small code.
+features and code, because the primary and most important advantage of
+this file system is the small code.  On the other hand, don't be
+alarmed, I'm not getting that much romfs related mail.  Now I can
+understand why Avery wrote poems in the arcnet docs to get some more
+feedback. :)
+
+romfs has also a mailing list, and to date, it hasn't received any
+traffic, so you are welcome to join it to discuss your ideas. :)
+
+It's run by ezmlm, so you can subscribe to it by sending a message
+to romfs-subscribe@shadow.banki.hu, the content is irrelevant.
+
+Pending issues:
+
+- Permissions and owner information are pretty essential features of a
+Un*x like system, but romfs does not provide the full possibilities.
+I have never found this limiting, but others might.
+
+- The file system is read only, so it can be very small, but in case
+one would want to write _anything_ to a file system, he still needs
+a writable file system, thus negating the size advantages.  Possible
+solutions: implement write access as a compile-time option, or a new,
+similarly small writable filesystem for ram disks.
+
+- Since the files are only required to have alignment on a 16 byte
+boundary, it is currently possibly suboptimal to read or execute files
+from the filesystem.  It might be resolved by reordering file data to
+have most of it (i.e. except the start and the end) laying at "natural"
+boundaries, thus it would be possible to directly map a big portion of
+the file contents to the mm subsystem.
+
+- Compression might be an useful feature, but memory is quite a
+limiting factor in my eyes.
+
+- Where it is used?
+
+- Does it work on other architectures than intel and motorola?
+
 
 Have fun,
 Janos Farkas <chexum@shadow.banki.hu>
diff -u --recursive --new-file v2.1.46/linux/Documentation/filesystems/smbfs.txt linux/Documentation/filesystems/smbfs.txt
--- v2.1.46/linux/Documentation/filesystems/smbfs.txt	Thu Apr 11 23:49:29 1996
+++ linux/Documentation/filesystems/smbfs.txt	Thu Jul 24 11:07:27 1997
@@ -9,5 +9,5 @@
 
 To use smbfs, you need a special mount program, which can be found in
 the ksmbfs package, found on
-sunsite.unc.edu:/pub/Linux/system/Filesystems/smbfs.
 
+	sunsite.unc.edu:/pub/Linux/system/filesystems/smbfs
diff -u --recursive --new-file v2.1.46/linux/Makefile linux/Makefile
--- v2.1.46/linux/Makefile	Sun Jul 20 20:41:58 1997
+++ linux/Makefile	Wed Jul 23 11:06:03 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 46
+SUBLEVEL = 47
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -119,7 +119,7 @@
 NETWORKS	=net/network.a
 DRIVERS		=drivers/block/block.a \
 		 drivers/char/char.a \
-	         drivers/pnp/pnp.a
+	         drivers/misc/misc.a
 LIBS		=$(TOPDIR)/lib/lib.a
 SUBDIRS		=kernel drivers mm fs net ipc lib
 
@@ -147,6 +147,10 @@
 
 ifdef CONFIG_SBUS
 DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
+endif
+
+ifdef CONFIG_PNP
+DRIVERS := $(DRIVERS) drivers/pnp/pnp.a
 endif
 
 include arch/$(ARCH)/Makefile
diff -u --recursive --new-file v2.1.46/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.46/linux/arch/alpha/config.in	Mon Jun 16 16:35:53 1997
+++ linux/arch/alpha/config.in	Wed Jul 23 11:06:03 1997
@@ -99,7 +99,10 @@
   tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
 fi
 tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
-tristate 'Parallel port support' CONFIG_PNP_PARPORT
+tristate 'Parallel port support' CONFIG_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+fi
 endmenu
 
 source drivers/pnp/Config.in
diff -u --recursive --new-file v2.1.46/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v2.1.46/linux/arch/i386/boot/setup.S	Wed Apr 16 14:14:59 1997
+++ linux/arch/i386/boot/setup.S	Wed Jul 23 15:32:24 1997
@@ -26,6 +26,10 @@
 !
 ! Video handling moved to video.S by Martin Mares, March 1996
 ! <mj@k332.feld.cvut.cz>
+!
+! Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
+! parsons) to avoid loadlin confusion, July 1997
+!
 
 #define __ASSEMBLY__
 #include <linux/config.h>
@@ -241,53 +245,39 @@
 loader_ok:
 ! Get memory size (extended mem, kB)
 
-#ifdef STANDARD_MEMORY_BIOS_CALL
-	mov	ah,#0x88
-	int	0x15
-	mov	[2],ax
-#else
-	push	ax
-	push	cx
-	push	dx
-				! which bootloader ?
-	seg cs
-	mov	al,byte ptr type_of_loader
-	and	al,#0xf0
-	cmp	al,#0x10
-	jne	try_xe801	! not Loadlin
-	seg cs
-	cmp	byte ptr type_of_loader,#0x16
-	jbe	oldstylemem	! Loadlin <= 1.6 don't like that
-try_xe801:
-	mov	ax,#0xe801
-	int	0x15
-	jc	oldstylemem
+#ifndef STANDARD_MEMORY_BIOS_CALL
+	push	ebx
 
-! memory size is (ax+(64*bx)) * 1024; we store bx+(ax/64)
+        xor     ebx,ebx		! preload new memory slot with 0k
+        mov	[0x1e0], ebx
 
-	mov	[2],bx		! store extended memory size
-	xor	dx,dx
-	mov	cx,#64		! convert lower memory size from K into
-	div	cx		! 64k chunks.
-
-	add	[2],ax		! add lower memory into total size.
-	jmp	gotmem
+        mov     ax,#0xe801
+	int     0x15
+	jc      oldstylemem
+
+! memory size is in 1k chunksizes, to avoid confusing loadlin.
+! we store the 0xe801 memory size in a completely different place,
+! because it will most likely be longer than 16 bits.
+! (use 1e0 because that's what Larry Augustine uses in his
+! alternative new memory detection scheme, and it's sensible
+! to write everything into the same place.)
+
+	and     ebx, #0xffff    ! clear sign extend
+	shl     ebx, 6          ! and go from 64k to 1k chunks
+	mov     [0x1e0],ebx     ! store extended memory size
+
+	and     eax, #0xffff    ! clear sign extend
+ 	add     [0x1e0],eax     ! and add lower memory into total size.
+  
+	! and fall into the old memory detection code to populate the
+	! compatability slot.
 
+	pop	ebx
 oldstylemem:
+#endif
 	mov	ah,#0x88
 	int	0x15
-	or	ax,ax		! some BIOSes report ZERO for 64meg
-	mov	word ptr [2],#0x400
-	jz	gotmem
-	mov	cx,#64		! got memory size in kbytes, so we need to
-	xor	dx,dx		! adjust to 64k chunks for the system.
-	div	cx
 	mov	[2],ax
-gotmem:
-	pop	dx
-	pop	cx
-	pop	ax
-#endif
 
 ! Set the keyboard repeat rate to the max
 
diff -u --recursive --new-file v2.1.46/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.46/linux/arch/i386/config.in	Mon Jun 16 16:35:53 1997
+++ linux/arch/i386/config.in	Wed Jul 23 11:06:03 1997
@@ -46,7 +46,10 @@
 	 PPro		CONFIG_M686" Pentium
 bool 'Video mode selection support' CONFIG_VIDEO_SELECT
 
-tristate 'Parallel port support' CONFIG_PNP_PARPORT
+tristate 'Parallel port support' CONFIG_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+fi
 
 endmenu
 
diff -u --recursive --new-file v2.1.46/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.46/linux/arch/i386/defconfig	Thu Jul 17 10:06:03 1997
+++ linux/arch/i386/defconfig	Thu Jul 24 12:30:39 1997
@@ -31,7 +31,7 @@
 # CONFIG_M586 is not set
 CONFIG_M686=y
 # CONFIG_VIDEO_SELECT is not set
-# CONFIG_PNP_PARPORT is not set
+# CONFIG_PARPORT is not set
 
 #
 # Plug and Play support
diff -u --recursive --new-file v2.1.46/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.46/linux/arch/i386/kernel/setup.c	Thu Jul 17 10:06:03 1997
+++ linux/arch/i386/kernel/setup.c	Wed Jul 23 15:32:24 1997
@@ -92,6 +92,9 @@
  */
 #define PARAM	empty_zero_page
 #define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#ifndef STANDARD_MEMORY_BIOS_CALL
+#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
+#endif
 #ifdef CONFIG_APM
 #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
 #endif
@@ -120,6 +123,7 @@
 	unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
 	unsigned long memory_start, memory_end;
+	unsigned long memory_alt_end;
 	char c = ' ', *to = command_line, *from = COMMAND_LINE;
 	int len = 0;
 	static unsigned char smptrap=0;
@@ -143,10 +147,15 @@
 		BIOS_revision = SYS_DESC_TABLE.table[2];
 	}
 	aux_device_present = AUX_DEVICE_INFO;
-#ifdef STANDARD_MEMORY_BIOS_CALL
 	memory_end = (1<<20) + (EXT_MEM_K<<10);
-#else
-	memory_end = (1<<20) + (EXT_MEM_K*64L*1024L);	/* 64kb chunks */
+#ifndef STANDARD_MEMORY_BIOS_CALL
+	memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
+	if (memory_alt_end > memory_end) {
+	    printk("Memory: sized by int13 0e801h\n");
+	    memory_end = memory_alt_end;
+	}
+	else
+	    printk("Memory: sized by int13 088h\n");
 #endif
 	memory_end &= PAGE_MASK;
 #ifdef CONFIG_BLK_DEV_RAM
diff -u --recursive --new-file v2.1.46/linux/arch/mips/config.in linux/arch/mips/config.in
--- v2.1.46/linux/arch/mips/config.in	Thu Jun 26 12:33:36 1997
+++ linux/arch/mips/config.in	Wed Jul 23 11:06:03 1997
@@ -80,7 +80,7 @@
 bool 'Sysctl support' CONFIG_SYSCTL
 
 if [ "$CONFIG_SGI" != "y" ]; then
-  tristate 'Parallel port support' CONFIG_PNP_PARPORT
+  tristate 'Parallel port support' CONFIG_PARPORT
 fi
 
 endmenu
diff -u --recursive --new-file v2.1.46/linux/arch/sparc64/kernel/sys32.S linux/arch/sparc64/kernel/sys32.S
--- v2.1.46/linux/arch/sparc64/kernel/sys32.S	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc64/kernel/sys32.S	Mon Jul 21 09:53:20 1997
@@ -1,4 +1,4 @@
-/* $Id: sys32.S,v 1.1 1997/06/29 03:38:56 davem Exp $
+/* $Id: sys32.S,v 1.2 1997/07/20 09:18:47 davem Exp $
  * sys32.S: I-cache tricks for 32-bit compatability layer simple
  *          conversions.
  *
@@ -306,7 +306,7 @@
 	.globl		sys32_sched_setparam, sys32_sched_getparam, sys32_signal
 	.globl		sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname
 	.globl		sys32_sethostname, sys32_gethostname, sys32_setdomainname
-	.globl		sys32_time, sys32_swapoff, sys32_swapon, sys32_nfsservctl
+	.globl		sys32_time, sys32_swapoff, sys32_swapon
 	.globl		sys32_create_module, sys32_init_module, sys32_delete_module
 sys32_bdflush:
 	sra		%o1, 0, %o1
@@ -418,10 +418,4 @@
 	srl		%o0, 0, %o0
 	mov		%o7, %g1
 	call		sys_swapon
-	 mov		%g1, %o7
-sys32_nfsservctl:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_nfsservctl
 	 mov		%g1, %o7
diff -u --recursive --new-file v2.1.46/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.1.46/linux/arch/sparc64/kernel/sys_sparc32.c	Thu Jul 17 10:06:04 1997
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Mon Jul 21 09:53:20 1997
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.43 1997/07/17 02:20:45 davem Exp $
+/* $Id: sys_sparc32.c,v 1.44 1997/07/20 09:18:47 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -32,6 +32,12 @@
 #include <linux/quota.h>
 #include <linux/file.h>
 #include <linux/module.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/nfsd/syscall.h>
+#include <linux/module.h>
 
 #include <asm/types.h>
 #include <asm/poll.h>
@@ -2519,3 +2525,271 @@
 }
 
 #endif  /* CONFIG_MODULES */
+
+/* Stuff for NFS server syscalls... */
+struct nfsctl_svc32 {
+	u16			svc32_port;
+	s32			svc32_nthreads;
+};
+
+struct nfsctl_client32 {
+	s8			cl32_ident[NFSCLNT_IDMAX+1];
+	s32			cl32_naddr;
+	struct in_addr		cl32_addrlist[NFSCLNT_ADDRMAX];
+	s32			cl32_fhkeytype;
+	s32			cl32_fhkeylen;
+	u8			cl32_fhkey[NFSCLNT_KEYMAX];
+};
+
+struct nfsctl_export32 {
+	s8			ex32_client[NFSCLNT_IDMAX+1];
+	s8			ex32_path[NFS_MAXPATHLEN+1];
+	__kernel_dev_t32	ex32_dev;
+	__kernel_ino_t32	ex32_ino;
+	s32			ex32_flags;
+	__kernel_uid_t32	ex32_anon_uid;
+	__kernel_gid_t32	ex32_anon_gid;
+};
+
+struct nfsctl_uidmap32 {
+	u32			ug32_ident;   /* char * */
+	__kernel_uid_t32	ug32_uidbase;
+	s32			ug32_uidlen;
+	u32			ug32_udimap;  /* uid_t * */
+	__kernel_uid_t32	ug32_gidbase;
+	s32			ug32_gidlen;
+	u32			ug32_gdimap;  /* gid_t * */
+};
+
+struct nfsctl_fhparm32 {
+	struct sockaddr		gf32_addr;
+	__kernel_dev_t32	gf32_dev;
+	__kernel_ino_t32	gf32_ino;
+	s32			gf32_version;
+};
+
+struct nfsctl_arg32 {
+	s32			ca32_version;	/* safeguard */
+	union {
+		struct nfsctl_svc32	u32_svc;
+		struct nfsctl_client32	u32_client;
+		struct nfsctl_export32	u32_export;
+		struct nfsctl_uidmap32	u32_umap;
+		struct nfsctl_fhparm32	u32_getfh;
+		u32			u32_debug;
+	} u;
+#define ca32_svc	u.u32_svc
+#define ca32_client	u.u32_client
+#define ca32_export	u.u32_export
+#define ca32_umap	u.u32_umap
+#define ca32_getfh	u.u32_getfh
+#define ca32_authd	u.u32_authd
+#define ca32_debug	u.u32_debug
+};
+
+union nfsctl_res32 {
+	struct knfs_fh		cr32_getfh;
+	u32			cr32_debug;
+};
+
+static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	if(__get_user(karg->ca_version, &arg32->ca32_version)			||
+	   __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port)	||
+	   __get_user(karg->ca_svc.svc_nthreads, &arg32->ca32_svc.svc32_nthreads))
+		return -EFAULT;
+	return 0;
+}
+
+static int nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	if(__get_user(karg->ca_version, &arg32->ca32_version)			||
+	   copy_from_user(&karg->ca_client.cl_ident[0],
+			  &arg32->ca32_client.cl32_ident[0],
+			  NFSCLNT_IDMAX)					||
+	   __get_user(karg->ca_client.cl_naddr, &arg32->ca32_client.cl32_naddr)	||
+	   copy_from_user(&karg->ca_client.cl_addrlist[0],
+			  &arg32->ca32_client.cl32_addrlist[0],
+			  (sizeof(struct in_addr) * NFSCLNT_ADDRMAX))		||
+	   __get_user(karg->ca_client.cl_fhkeytype,
+		      &arg32->ca32_client.cl32_fhkeytype)			||
+	   __get_user(karg->ca_client.cl_fhkeylen,
+		      &arg32->ca32_client.cl32_fhkeylen)			||
+	   copy_from_user(&karg->ca_client.cl_fhkey[0],
+			  &arg32->ca32_client.cl32_fhkey[0],
+			  NFSCLNT_KEYMAX))
+		return -EFAULT;
+	return 0;
+}
+
+static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	if(__get_user(karg->ca_version, &arg32->ca32_version)			||
+	   copy_from_user(&karg->ca_export.ex_client[0],
+			  &arg32->ca32_export.ex32_client[0],
+			  NFSCLNT_IDMAX)					||
+	   copy_from_user(&karg->ca_export.ex_path[0],
+			  &arg32->ca32_export.ex32_path[0],
+			  NFS_MAXPATHLEN)					||
+	   __get_user(karg->ca_export.ex_dev,
+		      &arg32->ca32_export.ex32_dev)				||
+	   __get_user(karg->ca_export.ex_ino,
+		      &arg32->ca32_export.ex32_ino)				||
+	   __get_user(karg->ca_export.ex_flags,
+		      &arg32->ca32_export.ex32_flags)				||
+	   __get_user(karg->ca_export.ex_anon_uid,
+		      &arg32->ca32_export.ex32_anon_uid)			||
+	   __get_user(karg->ca_export.ex_anon_gid,
+		      &arg32->ca32_export.ex32_anon_gid))
+		return -EFAULT;
+	return 0;
+}
+
+static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	u32 uaddr;
+	int i;
+
+	memset(karg, 0, sizeof(*karg));
+	if(__get_user(karg->ca_version, &arg32->ca32_version))
+		return -EFAULT;
+	karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER);
+	if(!karg->ca_umap.ug_ident)
+		return -ENOMEM;
+	if(__get_user(uaddr, &arg32->ca32_umap.ug32_ident))
+		return -EFAULT;
+	if(strncpy_from_user(karg->ca_umap.ug_ident,
+			     (char *)A(uaddr), PAGE_SIZE) <= 0)
+		return -EFAULT;
+	if(__get_user(karg->ca_umap.ug_uidbase,
+		      &arg32->ca32_umap.ug32_uidbase)		||
+	   __get_user(karg->ca_umap.ug_uidlen,
+		      &arg32->ca32_umap.ug32_uidlen)		||
+	   __get_user(uaddr, &arg32->ca32_umap.ug32_udimap))
+		return -EFAULT;
+	karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen),
+					  GFP_USER);
+	if(!karg->ca_umap.ug_udimap)
+		return -EFAULT;
+	for(i = 0; i < karg->ca_umap.ug_uidlen; i++)
+		if(__get_user(karg->ca_umap.ug_udimap[i],
+			      &(((__kernel_uid_t32 *)A(uaddr))[i])))
+			return -EFAULT;
+	if(__get_user(karg->ca_umap.ug_gidbase,
+		      &arg32->ca32_umap.ug32_gidbase)		||
+	   __get_user(karg->ca_umap.ug_uidlen,
+		      &arg32->ca32_umap.ug32_gidlen)		||
+	   __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap))
+		return -EFAULT;
+	karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen),
+					  GFP_USER);
+	if(!karg->ca_umap.ug_gdimap)
+		return -EFAULT;
+	for(i = 0; i < karg->ca_umap.ug_gidlen; i++)
+		if(__get_user(karg->ca_umap.ug_gdimap[i],
+			      &(((__kernel_gid_t32 *)A(uaddr))[i])))
+			return -EFAULT;
+
+	/* Success! */
+	return 0;
+}
+
+static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	if(__get_user(karg->ca_version, &arg32->ca32_version)	||
+	   copy_from_user(&karg->ca_getfh.gf_addr,
+			  &arg32->ca32_getfh.gf32_addr,
+			  (sizeof(struct sockaddr)))		||
+	   __get_user(karg->ca_getfh.gf_dev,
+		      &arg32->ca32_getfh.gf32_dev)		||
+	   __get_user(karg->ca_getfh.gf_ino,
+		      &arg32->ca32_getfh.gf32_ino)		||
+	   __get_user(karg->ca_getfh.gf_version,
+		      &arg32->ca32_getfh.gf32_version))
+		return -EFAULT;
+	return 0;
+}
+
+static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32)
+{
+	if(copy_to_user(&res32->cr32_getfh,
+			&kres->cr_getfh,
+			sizeof(res32->cr32_getfh))		||
+	   __put_user(kres->cr_debug, &res32->cr32_debug))
+		return -EFAULT;
+	return 0;
+}
+
+extern asmlinkage int sys_nfsservctl(int cmd,
+				     struct nfsctl_arg *arg,
+				     union nfsctl_res *resp);
+
+int asmlinkage sys32_nfsservctl(int cmd, u32 u_argp, u32 u_resp)
+{
+	struct nfsctl_arg32 *arg32 = (struct nfsctl_arg32 *)A(u_argp);
+	union nfsctl_res32 *res32 = (union nfsctl_res32 *)A(u_resp);
+	struct nfsctl_arg *karg = NULL;
+	union nfsctl_res *kres = NULL;
+	unsigned long oldfs;
+	int err;
+
+	karg = kmalloc(sizeof(*karg), GFP_USER);
+	if(!karg)
+		return -ENOMEM;
+	if(res32) {
+		kres = kmalloc(sizeof(*kres), GFP_USER);
+		if(!kres) {
+			kfree(karg);
+			return -ENOMEM;
+		}
+	}
+	switch(cmd) {
+	case NFSCTL_SVC:
+		err = nfs_svc32_trans(karg, arg32);
+		break;
+	case NFSCTL_ADDCLIENT:
+		err = nfs_clnt32_trans(karg, arg32);
+		break;
+	case NFSCTL_DELCLIENT:
+		err = nfs_clnt32_trans(karg, arg32);
+		break;
+	case NFSCTL_EXPORT:
+		err = nfs_exp32_trans(karg, arg32);
+		break;
+	/* This one is unimplemented, be we're ready for it. */
+	case NFSCTL_UGIDUPDATE:
+		err = nfs_uud32_trans(karg, arg32);
+		break;
+	case NFSCTL_GETFH:
+		err = nfs_getfh32_trans(karg, arg32);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	if(err)
+		goto done;
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	err = sys_nfsservctl(cmd, karg, kres);
+	set_fs(oldfs);
+
+	if(!err && cmd == NFSCTL_GETFH)
+		err = nfs_getfh32_res_trans(kres, res32);
+
+done:
+	if(karg) {
+		if(cmd == NFSCTL_UGIDUPDATE) {
+			if(karg->ca_umap.ug_ident)
+				kfree(karg->ca_umap.ug_ident);
+			if(karg->ca_umap.ug_udimap)
+				kfree(karg->ca_umap.ug_udimap);
+			if(karg->ca_umap.ug_gdimap)
+				kfree(karg->ca_umap.ug_gdimap);
+		}
+		kfree(karg);
+	}
+	if(kres)
+		kfree(kres);
+	return err;
+}
diff -u --recursive --new-file v2.1.46/linux/drivers/Makefile linux/drivers/Makefile
--- v2.1.46/linux/drivers/Makefile	Wed Apr 23 19:01:17 1997
+++ linux/drivers/Makefile	Wed Jul 23 11:06:03 1997
@@ -7,9 +7,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-SUB_DIRS     := block char net pnp #streams
+SUB_DIRS     := block char net misc #streams
 MOD_SUB_DIRS := $(SUB_DIRS) sbus
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn pnp
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn misc pnp
 
 ifdef CONFIG_PCI
 SUB_DIRS += pci
@@ -35,6 +35,15 @@
 else
   ifeq ($(CONFIG_SOUND),m)
   MOD_SUB_DIRS += sound
+  endif
+endif
+
+ifeq ($(CONFIG_PNP),y)
+SUB_DIRS += pnp
+MOD_SUB_DIRS += pnp
+else
+  ifeq ($(CONFIG_PNP),m)
+  MOD_SUB_DIRS += pnp
   endif
 endif
 
diff -u --recursive --new-file v2.1.46/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.46/linux/drivers/char/Config.in	Thu Jul 17 10:06:04 1997
+++ linux/drivers/char/Config.in	Wed Jul 23 11:06:03 1997
@@ -19,6 +19,7 @@
 fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
+   tristate 'Comtrol Rocketport support' CONFIG_ROCKETPORT
    tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA
    if [ "$CONFIG_DIGIEPCA" = "n" ]; then
       tristate 'Digiboard PC/Xx Support' CONFIG_DIGI
@@ -36,8 +37,8 @@
      int '  FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
    fi
 fi
-if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
-  dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PNP_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
   if [ "$CONFIG_PRINTER" != "n" ]; then
     bool '  Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
   fi
diff -u --recursive --new-file v2.1.46/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.46/linux/drivers/char/Makefile	Thu Jul 17 10:06:04 1997
+++ linux/drivers/char/Makefile	Mon Jul 21 12:41:20 1997
@@ -59,6 +59,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_ROCKETPORT),y)
+L_OBJS += rocket.o
+else
+  ifeq ($(CONFIG_ROCKETPORT),m)
+  M_OBJS += rocket.o
+  endif
+endif
+
 ifeq ($(CONFIG_DIGI),y)
 L_OBJS += pcxx.o
 else
diff -u --recursive --new-file v2.1.46/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.46/linux/drivers/char/lp.c	Thu Jul 17 10:06:04 1997
+++ linux/drivers/char/lp.c	Wed Jul 23 11:07:12 1997
@@ -10,8 +10,11 @@
  * "lp=" command line parameters added by Grant Guenther, grant@torque.net
  * lp_read (Status readback) support added by Carsten Gross,
  *                                             carsten@sol.wohnheim.uni-ulm.de
+ * Support for parport by Philip Blundell <Philip.Blundell@pobox.com>
  */
 
+/* This driver is about due for a rewrite. */
+
 #include <linux/module.h>
 
 #include <linux/config.h>
@@ -23,7 +26,6 @@
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
-#include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -31,12 +33,7 @@
 #include <linux/parport.h>
 #include <linux/lp.h>
 
-/* the BIOS manuals say there can be up to 4 lpt devices
- * but I have not seen a board where the 4th address is listed
- * if you have different hardware change the table below
- * please let me know if you have different equipment
- * if you have more than 3 printers, remember to increase LP_NO
- */
+/* if you have more than 3 printers, remember to increase LP_NO */
 struct lp_struct lp_table[] =
 {
  {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
@@ -164,7 +161,7 @@
 static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct parport *pb = (struct parport *) dev_id;
-	struct ppd *pd = pb->cad;
+	struct pardevice *pd = pb->cad;
 	struct lp_struct *lp_dev = (struct lp_struct *) pd->private;
 
 	if (lp_dev->lp_wait_q)
@@ -501,8 +498,9 @@
 static int lp_release(struct inode * inode, struct file * file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
+	unsigned int irq;
 
-	if (LP_IRQ(minor) > 0) {
+	if ((irq = LP_IRQ(minor))) {
 		kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 		lp_table[minor].lp_buffer = NULL;
 	}
@@ -630,7 +628,7 @@
 
 static int parport_ptr = 0;
 
-__initfunc(void lp_setup(char *str, int *ints))
+void lp_setup(char *str, int *ints)
 {
 	/* Ugh. */
 	if (!strncmp(str, "parport", 7)) {
@@ -678,23 +676,18 @@
 	return 0;
 }
 
-__initfunc(int lp_init(void))
+int lp_init(void)
 {
 	int count = 0;
 	struct parport *pb;
   
-	if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
-  		printk("lp: unable to get major %d\n", LP_MAJOR);
-  		return -EIO;
-	}
-
 	if (parport[0] == -2) return 0;
 
 	pb = parport_enumerate();
 
 	while (pb) {
 		/* We only understand PC-style ports. */
-		if (pb->modes & PARPORT_MODE_SPP) {
+		if (pb->modes & PARPORT_MODE_PCSPP) {
 			if (parport[0] == -1 || lp_searchfor(parport, count) ||
 			    (parport[0] == -3 &&
 			     pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
@@ -704,12 +697,8 @@
 						lp_interrupt, PARPORT_DEV_TRAN,
 						(void *) &lp_table[count]);
 				lp_table[count].flags |= LP_EXIST;
-				printk(KERN_INFO "lp%d: using %s at 0x%x, ", 
-				       count, pb->name, pb->base);
-				if (pb->irq == PARPORT_IRQ_NONE)
-					printk("polling.\n");
-				else
-					printk("irq %d.\n", pb->irq);
+				printk(KERN_INFO "lp%d: using %s (%s).\n", 
+				       count, pb->name, (pb->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
 			}
 			if (++count == LP_NO)
 				break;
@@ -720,9 +709,14 @@
   	/* Successful specified devices increase count
   	 * Unsuccessful specified devices increase failed
   	 */
-  	if (count)
-  		return 0;
-  
+  	if (count) {
+		if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
+			printk("lp: unable to get major %d\n", LP_MAJOR);
+			return -EIO;
+		}
+		return 0;
+	}
+
 	printk(KERN_INFO "lp: driver loaded but no devices found\n");
 #ifdef MODULE
 	return 0;
diff -u --recursive --new-file v2.1.46/linux/drivers/char/rocket.c linux/drivers/char/rocket.c
--- v2.1.46/linux/drivers/char/rocket.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/rocket.c	Mon Jul 21 12:41:21 1997
@@ -0,0 +1,2978 @@
+/*
+ * Rocketport device driver for Linux
+ *
+ * Written by Theodore Ts'o, 1995, 1996, 1997.
+ * 
+ * Copyright (C) 1995, 1996, 1997 by Comtrol, Inc.
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Minor number schema:
+ *
+ * +-------------------------------+
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * +---+-------+-------+-----------+
+ * | C | Board |  AIOP | Port #    |
+ * +---+-------+-------+-----------+
+ *
+ * C=0 implements normal POSIX tty.
+ * C=1 is reserved for the callout device.
+ * 
+ * Normally, the user won't have to worry about the AIOP; as far as
+ * the user is concerned, the lower 5 bits of the minor number address
+ * the ports on a particular board (from 0 up to 32).
+ */
+
+/* Kernel includes */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#if (defined(CONFIG_PCI) && (LINUX_VERSION_CODE >= 131072))
+#define ENABLE_PCI
+#endif
+
+#if (LINUX_VERSION_CODE > 66304)
+#define NEW_MODULES
+#endif
+
+#ifdef NEW_MODULES
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/config.h>
+#else /* !NEW_MODULES */
+#ifdef MODVERSIONS
+#define MODULE
+#endif
+#include <linux/config.h>
+#include <linux/module.h>
+#endif /* NEW_MODULES */
+
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/major.h>
+#include <linux/ioport.h>
+#ifdef ENABLE_PCI
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#endif
+#if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */
+#include <linux/init.h>
+#else
+#define __initfunc(x)	x
+#endif
+	
+#include "rocket_int.h"
+#include <linux/rocket.h>
+
+#define ROCKET_VERSION "1.14a"
+#define ROCKET_DATE "19-Jul-97"
+
+#define ROCKET_PARANOIA_CHECK
+
+#undef ROCKET_DEBUG_OPEN
+#undef ROCKET_DEBUG_INTR
+#undef ROCKET_DEBUG_WRITE
+#undef ROCKET_DEBUG_FLOW
+#undef ROCKET_DEBUG_THROTTLE
+#undef ROCKET_DEBUG_WAIT_UNTIL_SENT
+#undef ROCKET_DEBUG_RECEIVE
+#undef ROCKET_DEBUG_HANGUP
+	
+
+/*   CAUTION!!!!!  The TIME_STAT Function relies on the Pentium 64 bit
+ *    register.  For various reasons related to 1.2.13, the test for this
+ *    register is omitted from this driver.  If you are going to enable
+ *    this option, make sure you are running a Pentium CPU and that a
+ *    cat of /proc/cpuinfo shows ability TS Counters as Yes.  Warning part
+ *    done, don't cry to me if you enable this options and things won't
+ *    work.  If it gives you any problems, then disable the option.  The code
+ *    in this function is pretty straight forward, if it breaks on your
+ *    CPU, there is probably something funny about your CPU.
+ */
+
+#undef TIME_STAT	/* For performing timing statistics on driver. */
+			/* Produces printks, one every TIME_COUNTER loops, eats */
+			/* some of your CPU time.  Good for testing or */
+			/* other checking, otherwise, leave it undefed */
+			/* Doug Ledford */
+#define TIME_STAT_CPU 100      /* This needs to be set to your processor speed */
+                               /* For example, 100Mhz CPU, set this to 100 */
+#define TIME_COUNTER 180000    /* This is how many iterations to run before */
+			      /* performing the printk statements.   */
+			      /* 6000 = 1 minute, 360000 = 1 hour, etc. */
+			      /* Since time_stat is long long, this */
+			      /* Can be really high if you want :)  */
+#undef TIME_STAT_VERBOSE   /* Undef this if you want a terse log message. */
+
+#define _INLINE_ inline
+
+/*
+ * Until we get a formal timer assignment
+ */
+#ifndef COMTROL_TIMER
+#define COMTROL_TIMER 26
+#endif
+
+#ifndef NEW_MODULES
+/*
+ * NB. we must include the kernel idenfication string in to install the module.
+ */
+#include <linux/version.h>
+/*static*/ char kernel_version[] = UTS_RELEASE;
+#endif
+
+static struct r_port *rp_table[MAX_RP_PORTS];
+static struct tty_struct *rocket_table[MAX_RP_PORTS];
+static unsigned int xmit_flags[NUM_BOARDS];
+static struct termios *rocket_termios[MAX_RP_PORTS];
+static struct termios *rocket_termios_locked[MAX_RP_PORTS];
+static void rp_wait_until_sent(struct tty_struct *tty, int timeout);
+static void rp_flush_buffer(struct tty_struct *tty);
+
+static struct tty_driver rocket_driver, callout_driver;
+static int rocket_refcount = 0;
+
+static int rp_num_ports_open = 0;
+
+unsigned long board1 = 0;
+unsigned long board2 = 0;
+unsigned long board3 = 0;
+unsigned long board4 = 0;
+unsigned long controller = 0;
+unsigned long support_low_speed = 0;
+int rp_baud_base = 460800;
+static unsigned long rcktpt_io_addr[NUM_BOARDS];
+static int max_board;
+#ifdef TIME_STAT
+static unsigned long long time_stat = 0;
+static unsigned long time_stat_short = 0;
+static unsigned long time_stat_long = 0;
+static unsigned long time_counter = 0;
+#endif
+
+/*
+ * Provide backwards compatibility for kernels prior to 2.1.8.
+ */
+#if (LINUX_VERSION_CODE < 131336)
+int copy_from_user(void *to, const void *from_user, unsigned long len)
+{
+	int	error;
+
+	error = verify_area(VERIFY_READ, from_user, len);
+	if (error)
+		return len;
+	memcpy_fromfs(to, from_user, len);
+	return 0;
+}
+
+int copy_to_user(void *to_user, const void *from, unsigned long len)
+{
+	int	error;
+	
+	error = verify_area(VERIFY_WRITE, to_user, len);
+	if (error)
+		return len;
+	memcpy_tofs(to_user, from, len);
+	return 0;
+}
+#else
+#include <asm/uaccess.h>
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by rp_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static void rp_start(struct tty_struct *tty);
+
+static inline int rocket_paranoia_check(struct r_port *info,
+					dev_t device, const char *routine)
+{
+#ifdef ROCKET_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for rocketport struct (%d, %d) in %s\n";
+	if (!info)
+		return 1;
+	if (info->magic != RPORT_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * Here begins the interrupt/polling routine for the Rocketport!
+ */
+static _INLINE_ void rp_do_receive(struct r_port *info, struct tty_struct *tty,
+				   CHANNEL_t *cp, unsigned int ChanStatus)
+{
+	unsigned int CharNStat;
+	int ToRecv, wRecv, space, count;
+	unsigned char	*cbuf;
+	char		*fbuf;
+	
+	ToRecv= sGetRxCnt(cp);
+	space = 2*TTY_FLIPBUF_SIZE;
+	cbuf = tty->flip.char_buf;
+	fbuf = tty->flip.flag_buf;
+	count = 0;
+#ifdef ROCKET_DEBUG_INTR
+	printk("rp_do_receive(%d, %d)...", ToRecv, space);
+#endif
+	if (ToRecv == 0 || (space <= 0))
+		return;
+	
+	/*
+	 * determine how many we can actually read in.  If we can't
+	 * read any in then we have a software overrun condition.
+	 */
+	if (ToRecv > space)
+		ToRecv = space;
+	
+	/*
+	 * if status indicates there are errored characters in the
+	 * FIFO, then enter status mode (a word in FIFO holds
+	 * character and status).
+	 */
+	if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) {
+		if (!(ChanStatus & STATMODE)) {
+#ifdef ROCKET_DEBUG_RECEIVE
+			printk("Entering STATMODE...");
+#endif
+			ChanStatus |= STATMODE;
+			sEnRxStatusMode(cp);
+		}
+	}
+
+	/* 
+	 * if we previously entered status mode, then read down the
+	 * FIFO one word at a time, pulling apart the character and
+	 * the status.  Update error counters depending on status
+	 */
+	if (ChanStatus & STATMODE) {
+#ifdef ROCKET_DEBUG_RECEIVE
+		printk("Ignore %x, read %x...", info->ignore_status_mask,
+		       info->read_status_mask);
+#endif
+		while (ToRecv) {
+			CharNStat= sInW(sGetTxRxDataIO(cp));
+
+#ifdef ROCKET_DEBUG_RECEIVE
+			printk("%x...", CharNStat);
+#endif
+
+			if (CharNStat & STMBREAKH)
+				CharNStat &= ~(STMFRAMEH | STMPARITYH);
+			if (CharNStat & info->ignore_status_mask) {
+				ToRecv--;
+				continue;
+			}
+			CharNStat &= info->read_status_mask;
+			if (CharNStat & STMBREAKH) {
+				*fbuf++ = TTY_BREAK;
+#if 0
+				if (info->flags & ROCKET_SAK)
+					do_SAK(tty);
+#endif
+			} else if (CharNStat & STMPARITYH)
+				*fbuf++ = TTY_PARITY;
+			else if (CharNStat & STMFRAMEH)
+				*fbuf++ = TTY_FRAME;
+			else if (CharNStat & STMRCVROVRH)
+				*fbuf++ =TTY_OVERRUN;
+			else
+				*fbuf++ = 0;
+			*cbuf++ = CharNStat & 0xff;
+			count++;
+			ToRecv--;
+		}
+
+		/*
+		 * after we've emptied the FIFO in status mode, turn
+		 * status mode back off
+		 */
+		if (sGetRxCnt(cp) == 0) {
+#ifdef ROCKET_DEBUG_RECEIVE
+			printk("Status mode off.\n");
+#endif
+			sDisRxStatusMode(cp);
+		}
+	} else {
+		/*
+		 * we aren't in status mode, so read down the FIFO two
+		 * characters at time by doing repeated word IO
+		 * transfer.
+		 */
+		wRecv= ToRecv >> 1;
+		if (wRecv)
+			sInStrW(sGetTxRxDataIO(cp), cbuf,
+				wRecv);
+		if (ToRecv & 1)
+			cbuf[ToRecv-1] = sInB(sGetTxRxDataIO(cp));
+		memset(fbuf, 0, ToRecv);
+		cbuf += ToRecv;
+		fbuf += ToRecv;
+		count += ToRecv;
+	}
+	tty->ldisc.receive_buf(tty, tty->flip.char_buf,
+			       tty->flip.flag_buf, count);
+}
+
+/*
+ * This routine is called when a transmit interrupt is found.  It's
+ * responsible for pushing data found in the transmit buffer out to
+ * the serial card.
+ */
+static _INLINE_ void rp_do_transmit(struct r_port *info)
+{
+	int	c;
+	CHANNEL_t *cp = &info->channel;
+	struct tty_struct *tty;
+	
+#ifdef ROCKET_DEBUG_INTR
+	printk("rp_do_transmit ");
+#endif
+	if (!info)
+		return;
+	if (!info->tty) {
+		printk("rp: WARNING rp_do_transmit called with info->tty==NULL\n");
+		xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+		return;
+	}
+	tty = info->tty;
+	info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+	while (1) {
+		if (tty->stopped || tty->hw_stopped)
+			break;
+		c = MIN(info->xmit_fifo_room,
+			MIN(info->xmit_cnt,
+			    XMIT_BUF_SIZE - info->xmit_tail));
+		if (c <= 0 || info->xmit_fifo_room <= 0)
+			break;
+		sOutStrW(sGetTxRxDataIO(cp),
+			 info->xmit_buf + info->xmit_tail, c/2);
+		if (c & 1)
+			sOutB(sGetTxRxDataIO(cp),
+			      info->xmit_buf[info->xmit_tail + c -
+					     1]);
+		info->xmit_tail += c;
+		info->xmit_tail &= XMIT_BUF_SIZE-1;
+		info->xmit_cnt -= c;
+		info->xmit_fifo_room -= c;
+#ifdef ROCKET_DEBUG_INTR
+		printk("tx %d chars...", c);
+#endif
+	}
+	if (info->xmit_cnt == 0)
+		xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+	if (info->xmit_cnt < WAKEUP_CHARS) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+#ifdef ROCKET_DEBUG_INTR
+	printk("(%d,%d,%d,%d)...", info->xmit_cnt, info->xmit_head,
+	       info->xmit_tail, info->xmit_fifo_room);
+#endif
+}
+
+/*
+ * This function is called for each port which has signalled an
+ * interrupt.  It checks what interrupts are pending and services
+ * them. 
+ */
+static _INLINE_ void rp_handle_port(struct r_port *info)
+{
+	CHANNEL_t *cp;
+	struct tty_struct *tty;
+	unsigned int IntMask, ChanStatus;
+
+	if (!info)
+		return;
+	if ( (info->flags & ROCKET_INITIALIZED) == 0 ) {
+		printk("rp: WARNING: rp_handle_port called with info->flags & NOT_INIT\n");
+		return;
+	}
+	if (!info->tty) {
+		printk("rp: WARNING: rp_handle_port called with info->tty==NULL\n");
+		return;
+	}
+	cp = &info->channel;
+	tty = info->tty;
+
+	IntMask = sGetChanIntID(cp) & info->intmask;
+#ifdef ROCKET_DEBUG_INTR
+	printk("rp_interrupt %02x...", IntMask);
+#endif
+	ChanStatus= sGetChanStatus(cp);
+	if (IntMask & RXF_TRIG) {	/* Rx FIFO trigger level */
+		rp_do_receive(info, tty, cp, ChanStatus);
+	}
+#if 0
+	if (IntMask & SRC_INT) {	/* Special receive condition */
+	}
+#endif
+	if (IntMask & DELTA_CD) {	/* CD change  */
+#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || \
+     defined(ROCKET_DEBUG_HANGUP))
+		printk("ttyR%d CD now %s...", info->line,
+		       (ChanStatus & CD_ACT) ? "on" : "off");
+#endif
+		if (!(ChanStatus & CD_ACT) &&
+		    info->cd_status &&
+		    !((info->flags & ROCKET_CALLOUT_ACTIVE) &&
+		      (info->flags & ROCKET_CALLOUT_NOHUP))) {
+#ifdef ROCKET_DEBUG_HANGUP
+			printk("CD drop, calling hangup.\n");
+#endif
+			tty_hangup(tty);
+		}
+		info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0;
+		wake_up_interruptible(&info->open_wait);
+	}
+#ifdef ROCKET_DEBUG_INTR
+	if (IntMask & DELTA_CTS) {	/* CTS change */
+		printk("CTS change...\n");
+	}
+	if (IntMask & DELTA_DSR) {	/* DSR change */
+		printk("DSR change...\n");
+	}
+#endif
+}
+
+/*
+ * The top level polling routine.
+ */
+static void rp_do_poll(void)
+{
+	CONTROLLER_t *ctlp;
+	int ctrl, aiop, ch, line;
+	unsigned int xmitmask;
+	unsigned char CtlMask, AiopMask;
+
+#ifdef TIME_STAT
+	unsigned long low=0, high=0, loop_time;
+	unsigned long long time_stat_tmp=0, time_stat_tmp2=0;
+
+	__asm__(".byte 0x0f,0x31"
+		:"=a" (low), "=d" (high));
+	time_stat_tmp = high;
+	time_stat_tmp <<= 32;
+	time_stat_tmp += low;
+#endif /* TIME_STAT */
+
+	for (ctrl=0; ctrl < max_board; ctrl++) {
+		if (rcktpt_io_addr[ctrl] <= 0)
+			continue;
+		ctlp= sCtlNumToCtlPtr(ctrl);
+
+#ifdef ENABLE_PCI
+		if(ctlp->BusType == isPCI)
+			CtlMask= sPCIGetControllerIntStatus(ctlp);
+		else
+#endif
+			CtlMask= sGetControllerIntStatus(ctlp);
+		for (aiop=0; CtlMask; CtlMask >>= 1, aiop++) {
+			if (CtlMask & 1) {
+				AiopMask= sGetAiopIntStatus(ctlp, aiop);
+				for (ch=0; AiopMask; AiopMask >>= 1, ch++) {
+					if (AiopMask & 1) {
+						line = (ctrl << 5) | 
+							(aiop << 3) | ch;
+						rp_handle_port(rp_table[line]);
+					}
+				}
+			}
+		}
+		xmitmask = xmit_flags[ctrl];
+		for (line = ctrl << 5; xmitmask; xmitmask >>= 1, line++) {
+			if (xmitmask & 1)
+				rp_do_transmit(rp_table[line]);
+		}
+	}
+
+	/*
+	 * Reset the timer so we get called at the next clock tick.
+	 */
+	if (rp_num_ports_open) {
+		timer_active |= 1 << COMTROL_TIMER;
+	}
+#ifdef TIME_STAT
+	__asm__(".byte 0x0f,0x31"
+		:"=a" (low), "=d" (high));
+	time_stat_tmp2 = high;
+	time_stat_tmp2 <<= 32;
+	time_stat_tmp2 += low;
+	time_stat_tmp2 -= time_stat_tmp;
+	time_stat += time_stat_tmp2;
+	if (time_counter == 0) 
+		time_stat_short = time_stat_long = time_stat_tmp2;
+	else {
+		if ( time_stat_tmp2 < time_stat_short )
+			time_stat_short = time_stat_tmp2;
+		else if ( time_stat_tmp2 > time_stat_long )
+			time_stat_long = time_stat_tmp2;
+	}
+	if ( ++time_counter == TIME_COUNTER ) {
+		loop_time = (unsigned long) ( ((unsigned long)(time_stat >> 32) * ( (unsigned long)(0xffffffff)/(TIME_STAT_CPU * TIME_COUNTER) ) ) + ((unsigned long)time_stat/(TIME_STAT_CPU*TIME_COUNTER)));
+#ifdef TIME_STAT_VERBOSE
+		printk("rp_do_poll: Interrupt Timings\n");
+		printk("     %5ld iterations; %ld us min,\n",
+		       (long)TIME_COUNTER, (time_stat_short/TIME_STAT_CPU));
+		printk("     %5ld us max, %ld us average per iteration.\n",
+		       (time_stat_long/TIME_STAT_CPU), loop_time);
+		printk("We want to use < 5,000 us for an iteration.\n");
+#else /* TIME_STAT_VERBOSE */
+		printk("rp: %ld loops: %ld min, %ld max, %ld us/loop.\n",
+		       (long)TIME_COUNTER, (time_stat_short/TIME_STAT_CPU),
+		       (time_stat_long/TIME_STAT_CPU), loop_time);
+#endif /* TIME_STAT_VERBOSE */
+		time_counter = time_stat = 0;
+		time_stat_short = time_stat_long = 0;
+	}
+#endif /* TIME_STAT */
+}
+/*
+ * Here ends the interrupt/polling routine.
+ */
+
+
+/*
+ * This function initializes the r_port structure, as well as enabling
+ * the port on the RocketPort board.
+ */
+static void init_r_port(int board, int aiop, int chan)
+{
+	struct r_port *info;
+	int line;
+	CONTROLLER_T *ctlp;
+	CHANNEL_t	*cp;
+	
+	line = (board << 5) | (aiop << 3) | chan;
+
+	ctlp= sCtlNumToCtlPtr(board);
+
+	info = kmalloc(sizeof(struct r_port), GFP_KERNEL);
+	if (!info) {
+		printk("Couldn't allocate info struct for line #%d\n", line);
+		return;
+	}
+	memset(info, 0, sizeof(struct r_port));
+	
+	info->magic = RPORT_MAGIC;
+	info->line = line;
+	info->ctlp = ctlp;
+	info->board = board;
+	info->aiop = aiop;
+	info->chan = chan;
+	info->closing_wait = 3000;
+	info->close_delay = 50;
+	info->callout_termios =callout_driver.init_termios;
+	info->normal_termios = rocket_driver.init_termios;
+
+	info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD |
+		DELTA_CTS | DELTA_DSR;
+	if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) {
+		printk("Rocketport sInitChan(%d, %d, %d) failed!\n",
+		       board, aiop, chan);
+		kfree(info);
+		return;
+	}
+	cp = &info->channel;
+	rp_table[line] = info;
+}
+
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300,
+	600, 1200, 1800, 2400, 4800, 9600, 19200,
+	38400, 57600, 115200, 230400, 460800, 0 };
+
+/*
+ * This routine configures a rocketport port so according to its
+ * termio settings.
+ */
+static void configure_r_port(struct r_port *info)
+{
+	unsigned cflag;
+	unsigned long 	flags;
+	int	i, bits, baud;
+	CHANNEL_t	*cp;
+	
+	if (!info->tty || !info->tty->termios)
+		return;
+	cp = &info->channel;
+	cflag = info->tty->termios->c_cflag;
+
+	/* Byte size and parity */
+	if ((cflag & CSIZE) == CS8) {
+		sSetData8(cp);
+		bits = 10;
+	} else {
+		sSetData7(cp);
+		bits = 9;
+	}
+        if (cflag & CSTOPB) {
+		sSetStop2(cp);
+		bits++;
+	} else {
+		sSetStop1(cp);
+	}
+	
+	if (cflag & PARENB) {
+		sEnParity(cp);
+		bits++;
+		if (cflag & PARODD) {
+			sSetOddParity(cp);
+		} else {
+			sSetEvenParity(cp);
+		}
+	} else {
+		sDisParity(cp);
+	}
+	
+	/* baud rate */
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 4) 
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (i == 15) {
+		if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+			i += 1;
+		if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+			i += 2;
+		if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+			i += 3;
+		if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+			i += 4;
+	}
+	baud = baud_table[i] ? baud_table[i] : 9600;
+	info->cps = baud / bits;
+	sSetBaud(cp, (rp_baud_base/baud) - 1);
+	
+	if (cflag & CRTSCTS) {
+		info->intmask |= DELTA_CTS;
+		sEnCTSFlowCtl(cp);
+	} else {
+		info->intmask &= ~DELTA_CTS;
+		sDisCTSFlowCtl(cp);
+	}
+	sSetRTS(&info->channel);
+	if (cflag & CLOCAL)
+		info->intmask &= ~DELTA_CD;
+	else {
+		save_flags(flags); cli();
+		if (sGetChanStatus(cp) & CD_ACT)
+			info->cd_status = 1;
+		else
+			info->cd_status = 0;
+		info->intmask |= DELTA_CD;
+		restore_flags(flags);
+	}
+	/*
+	 * Set up ignore/read mask words
+	 */
+	info->read_status_mask = STMRCVROVRH | 0xFF;
+	if (I_INPCK(info->tty))
+		info->read_status_mask |= STMFRAMEH | STMPARITYH;
+	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+		info->read_status_mask |= STMBREAKH;
+	
+	/*
+	 * Characters to ignore
+	 */
+	info->ignore_status_mask = 0;
+	if (I_IGNPAR(info->tty))
+		info->ignore_status_mask |= STMFRAMEH | STMPARITYH;
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_status_mask |= STMBREAKH;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too.  (For real raw support).
+		 */
+		if (I_IGNPAR(info->tty))
+			info->ignore_status_mask |= STMRCVROVRH;
+	}
+}
+
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct r_port *info)
+{
+	struct wait_queue wait = { current, NULL };
+	int		retval;
+	int		do_clocal = 0;
+	unsigned long	flags;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp))
+		return ((info->flags & ROCKET_HUP_NOTIFY) ? 
+			-EAGAIN : -ERESTARTSYS);
+	if (info->flags & ROCKET_CLOSING) {
+		interruptible_sleep_on(&info->close_wait);
+		return ((info->flags & ROCKET_HUP_NOTIFY) ? 
+			-EAGAIN : -ERESTARTSYS);
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ROCKET_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ROCKET_CALLOUT_ACTIVE) &&
+		    (info->flags & ROCKET_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ROCKET_CALLOUT_ACTIVE) &&
+		    (info->flags & ROCKET_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ROCKET_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (info->flags & ROCKET_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ROCKET_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ROCKET_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rp_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef ROCKET_DEBUG_OPEN
+	printk("block_til_ready before block: ttyR%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	save_flags(flags); cli();
+	if (!tty_hung_up_p(filp))
+		info->count--;
+	restore_flags(flags);
+	info->blocked_open++;
+	while (1) {
+		if (!(info->flags & ROCKET_CALLOUT_ACTIVE) &&
+		    (tty->termios->c_cflag & CBAUD)) {
+			sSetDTR(&info->channel);
+			sSetRTS(&info->channel);
+		}
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ROCKET_INITIALIZED)) {
+			if (info->flags & ROCKET_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+			break;
+		}
+		if (!(info->flags & ROCKET_CALLOUT_ACTIVE) &&
+		    !(info->flags & ROCKET_CLOSING) &&
+		    (do_clocal || (sGetChanStatusLo(&info->channel) &
+				   CD_ACT)))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef ROCKET_DEBUG_OPEN
+		printk("block_til_ready blocking: ttyR%d, count = %d, flags=0x%0x\n",
+		       info->line, info->count, info->flags);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	cli();
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	restore_flags(flags);
+	info->blocked_open--;
+#ifdef ROCKET_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttyR%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ROCKET_NORMAL_ACTIVE;
+	return 0;
+}	
+
+/*
+ * This routine is called whenever a rocketport board is opened.
+ */
+static int rp_open(struct tty_struct *tty, struct file * filp)
+{
+	struct r_port *info;
+	int	line, retval;
+	CHANNEL_t	*cp;
+	unsigned long page;
+	
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= MAX_RP_PORTS))
+		return -ENODEV;
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+	page = get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	tty->driver_data = info = rp_table[line];
+	
+	if (info->flags & ROCKET_CLOSING) {
+		interruptible_sleep_on(&info->close_wait);
+		free_page(page);
+		return ((info->flags & ROCKET_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+	}
+	
+	/*
+	 * We must not sleep from here until the port is marked fully
+	 * in use.
+	 */
+	if (rp_table[line] == NULL) {
+		tty->flags = (1 << TTY_IO_ERROR);
+		free_page(page);
+		return 0;
+	}
+	if (!info) {
+		printk("rp_open: rp_table[%d] is NULL!\n", line);
+		free_page(page);
+		return -EIO;
+	}
+	if (info->xmit_buf)
+		free_page(page);
+	else
+		info->xmit_buf = (unsigned char *) page;
+	info->tty = tty;
+
+	if (info->flags & ROCKET_CLOSING) {
+		interruptible_sleep_on(&info->close_wait);
+		return ((info->flags & ROCKET_HUP_NOTIFY) ? 
+			-EAGAIN : -ERESTARTSYS);
+	}
+
+	if (info->count++ == 0) {
+#ifdef MODULE
+		MOD_INC_USE_COUNT;
+#endif
+		rp_num_ports_open++;
+#ifdef ROCKET_DEBUG_OPEN
+		printk("rocket mod++ = %d...", rp_num_ports_open);
+#endif
+	}
+#ifdef ROCKET_DEBUG_OPEN
+	printk("rp_open ttyR%d, count=%d\n", info->line, info->count);
+#endif
+	/*
+	 * Info->count is now 1; so it's safe to sleep now.
+	 */
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+	
+	cp = &info->channel;
+	sSetRxTrigger(cp, TRIG_1);
+	if (sGetChanStatus(cp) & CD_ACT)
+		info->cd_status = 1;
+	else
+		info->cd_status = 0;
+	sDisRxStatusMode(cp);
+	sFlushRxFIFO(cp);	
+	sFlushTxFIFO(cp);	
+
+	sEnInterrupts(cp, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN));
+	sSetRxTrigger(cp, TRIG_1);
+
+	sGetChanStatus(cp);
+	sDisRxStatusMode(cp);
+	sClrTxXOFF(cp);
+
+	sDisCTSFlowCtl(cp);
+	sDisTxSoftFlowCtl(cp);
+
+	sEnRxFIFO(cp);
+	sEnTransmit(cp);
+
+	info->flags |= ROCKET_INITIALIZED;
+	
+	configure_r_port(info);
+	if (tty->termios->c_cflag & CBAUD) {
+		sSetDTR(cp);
+		sSetRTS(cp);
+	}
+	
+	timer_active |= 1 << COMTROL_TIMER;
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef ROCKET_DEBUG_OPEN
+		printk("rp_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+	if ((info->count == 1) && (info->flags & ROCKET_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else 
+			*tty->termios = info->callout_termios;
+		configure_r_port(info);
+	}
+
+	return 0;
+}
+
+static void rp_close(struct tty_struct *tty, struct file * filp)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	unsigned long flags;
+	int timeout;
+	CHANNEL_t	*cp;
+
+	if (rocket_paranoia_check(info, tty->device, "rp_close"))
+		return;
+
+#ifdef ROCKET_DEBUG_OPEN
+	printk("rp_close ttyR%d, count = %d\n", info->line, info->count);
+#endif
+	
+	save_flags(flags); cli();
+	
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rp_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rp_close: bad serial port count for ttyR%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ROCKET_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ROCKET_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ROCKET_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+	
+	cp = &info->channel;
+
+	/*
+	 * Notify the line discpline to only process XON/XOFF characters
+	 */
+	tty->closing = 1;
+
+	/*
+	 * If transmission was throttled by the application request,
+	 * just flush the xmit buffer.
+	 */
+#if (LINUX_VERSION_CODE >= 131343)
+	if (tty->flow_stopped)
+		rp_flush_buffer(tty);
+#endif
+
+	/*
+	 * Wait for the transmit buffer to clear
+	 */
+	if (info->closing_wait != ROCKET_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * Before we drop DTR, make sure the UART transmitter
+	 * has completely drained; this is especially
+	 * important if there is a transmit FIFO!
+	 */
+	timeout = (sGetTxCnt(cp)+1) * HZ / info->cps;
+	if (timeout == 0)
+		timeout = 1;
+	rp_wait_until_sent(tty, timeout);
+	
+	xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+	sDisTransmit(cp);
+	sDisInterrupts(cp, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN));
+	sDisCTSFlowCtl(cp);
+	sDisTxSoftFlowCtl(cp);
+	sClrTxXOFF(cp);
+	sFlushRxFIFO(cp);	
+	sFlushTxFIFO(cp);
+	sClrRTS(cp);
+	if (C_HUPCL(tty)) {
+		sClrDTR(cp);
+	}
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&info->open_wait);
+	} else {
+		if (info->xmit_buf) {
+			free_page((unsigned long) info->xmit_buf);
+			info->xmit_buf = 0;
+		}
+	}
+	info->flags &= ~(ROCKET_INITIALIZED | ROCKET_CLOSING |
+			 ROCKET_CALLOUT_ACTIVE | ROCKET_NORMAL_ACTIVE);
+	tty->closing = 0;
+	wake_up_interruptible(&info->close_wait);
+	
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+	rp_num_ports_open--;
+#ifdef ROCKET_DEBUG_OPEN
+	printk("rocket mod-- = %d...", rp_num_ports_open);
+#endif
+	restore_flags(flags);
+	
+#ifdef ROCKET_DEBUG_OPEN
+	printk("rp_close ttyR%d complete shutdown\n", info->line);
+#endif
+	
+}
+
+static void rp_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	CHANNEL_t *cp;
+	unsigned cflag;
+	
+
+	if (rocket_paranoia_check(info, tty->device, "rp_set_termios"))
+		return;
+
+	cflag = tty->termios->c_cflag;
+
+	if (cflag == old_termios->c_cflag)
+		return;
+
+	/*
+	 * This driver doesn't support CS5 or CS6
+	 */
+	if (((cflag & CSIZE) == CS5) ||
+	    ((cflag & CSIZE) == CS6))
+		tty->termios->c_cflag = ((cflag & ~CSIZE) |
+					 (old_termios->c_cflag & CSIZE));
+
+	configure_r_port(info);
+
+	cp = &info->channel;
+
+	/* Handle transition to B0 status */
+	if ((old_termios->c_cflag & CBAUD) &&
+	    !(tty->termios->c_cflag & CBAUD)) {
+		sClrDTR(cp);
+		sClrRTS(cp);
+	}
+	
+	/* Handle transition away from B0 status */
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    (tty->termios->c_cflag & CBAUD)) {
+		if (!tty->hw_stopped ||
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			sSetRTS(cp);
+		}
+		sSetDTR(cp);
+	}
+	
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rp_start(tty);
+	}
+}
+
+/*
+ * Here are the routines used by rp_ioctl
+ */
+
+static void send_break(	struct r_port * info, int duration)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+	cli();
+	sSendBreak(&info->channel);
+	schedule();
+	sClrBreak(&info->channel);
+	sti();
+}
+
+static int get_modem_info(struct r_port * info, unsigned int *value)
+{
+	unsigned int control, result, ChanStatus;
+
+	ChanStatus = sGetChanStatusLo(&info->channel);
+	
+	control = info->channel.TxControl[3];
+	result =  ((control & SET_RTS) ? TIOCM_RTS : 0)
+		| ((control & SET_DTR) ? TIOCM_DTR : 0)
+		| ((ChanStatus  & CD_ACT) ? TIOCM_CAR : 0)
+			/* TIOCM_RNG not supported */
+		| ((ChanStatus  & DSR_ACT) ? TIOCM_DSR : 0)
+		| ((ChanStatus  & CTS_ACT) ? TIOCM_CTS : 0);
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_modem_info(struct r_port * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	unsigned int arg;
+
+	if (copy_from_user(&arg, value, sizeof(int)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS)
+			info->channel.TxControl[3] |= SET_RTS;
+		if (arg & TIOCM_DTR)
+			info->channel.TxControl[3] |= SET_DTR;
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			info->channel.TxControl[3] &= ~SET_RTS;
+		if (arg & TIOCM_DTR)
+			info->channel.TxControl[3] &= ~SET_DTR;
+		break;
+	case TIOCMSET:
+		info->channel.TxControl[3] =
+			((info->channel.TxControl[3] & ~(SET_RTS | SET_DTR))
+			 | ((arg & TIOCM_RTS) ? SET_RTS : 0)
+			 | ((arg & TIOCM_DTR) ? SET_DTR : 0));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sOutDW(info->channel.IndexAddr,
+	       *(DWord_t *) &(info->channel.TxControl[0]));
+	
+	return 0;
+}
+
+static int get_config(struct r_port * info, struct rocket_config * retinfo)
+{
+	struct rocket_config tmp;
+  
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.line = info->line;
+	tmp.flags = info->flags;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
+	
+	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_config(struct r_port * info, struct rocket_config * new_info)
+{
+	struct rocket_config new_serial;
+
+	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+		return -EFAULT;
+
+	if (!suser()) {
+		if ((new_serial.flags & ~ROCKET_USR_MASK) !=
+		    (info->flags & ~ROCKET_USR_MASK))
+			return -EPERM;
+		info->flags = ((info->flags & ~ROCKET_USR_MASK) |
+			       (new_serial.flags & ROCKET_USR_MASK));
+		configure_r_port(info);
+		return 0;
+	}
+	
+	info->flags = ((info->flags & ~ROCKET_FLAGS) |
+			(new_serial.flags & ROCKET_FLAGS));
+	info->close_delay = new_serial.close_delay;
+	info->closing_wait = new_serial.closing_wait;
+	configure_r_port(info);
+	
+	return 0;
+}
+
+static int get_ports(struct r_port * info, struct rocket_ports * retports)
+{
+	struct rocket_ports tmp;
+	int	board, port, index;
+  
+	if (!retports)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.tty_major = rocket_driver.major;
+	tmp.callout_major = callout_driver.major;
+	for (board = 0; board < 4; board++) {
+		index = board << 5;
+		for (port = 0; port < 32; port++, index++) {
+			if (rp_table[index])
+				tmp.port_bitmap[board] |= 1 << port;
+		}
+	}
+	if (copy_to_user(retports,&tmp,sizeof(*retports)))
+		return -EFAULT;
+	return 0;
+}
+
+static int rp_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int tmp;
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	int retval;
+
+	if (cmd != RCKP_GET_PORTS &&
+	    rocket_paranoia_check(info, tty->device, "rp_ioctl"))
+		return -ENODEV;
+
+	switch (cmd) {
+		case TCSBRK:	/* SVID version: non-zero arg --> no break */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			if (!arg) {
+				send_break(info, HZ/4);	/* 1/4 second */
+				if (current->signal & ~current->blocked)
+					return -EINTR;
+			}
+			return 0;
+		case TCSBRKP:	/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			send_break(info, arg ? arg*(HZ/10) : HZ/4);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			return 0;
+		case TIOCGSOFTCAR:
+			tmp = C_CLOCAL(tty) ? 1 : 0;
+			if (copy_to_user((void *)arg, &tmp, sizeof(int)))
+				return -EFAULT;
+			return 0;
+		case TIOCSSOFTCAR:
+			if (copy_from_user(&tmp, (void *)arg, sizeof(int)))
+				return -EFAULT;
+
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (tmp ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case RCKP_GET_STRUCT:
+			if (copy_to_user((void *) arg, info,
+					 sizeof(struct r_port)))
+				return -EFAULT;
+			return 0;
+
+		case RCKP_GET_CONFIG:
+			return get_config(info, (struct rocket_config *) arg);
+		case RCKP_SET_CONFIG:
+			return set_config(info, (struct rocket_config *) arg);
+			
+		case RCKP_GET_PORTS:
+			return get_ports(info, (struct rocket_ports *) arg);
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+#if (defined(ROCKET_DEBUG_FLOW) || defined(ROCKET_DEBUG_THROTTLE))
+static char *rp_tty_name(struct tty_struct *tty, char *buf)
+{
+	if (tty)
+		sprintf(buf, "%s%d", tty->driver.name,
+			MINOR(tty->device) - tty->driver.minor_start +
+			tty->driver.name_base);
+	else
+		strcpy(buf, "NULL tty");
+	return buf;
+}
+#endif
+
+static void rp_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct r_port *info = (struct r_port *)tty->driver_data;
+	CHANNEL_t *cp;
+
+	if (rocket_paranoia_check(info, tty->device, "rp_send_xchar"))
+		return;
+
+	cp = &info->channel;
+	if (sGetTxCnt(cp)) 
+		sWriteTxPrioByte(cp, ch);
+	else
+		sWriteTxByte(sGetTxRxDataIO(cp), ch);
+}
+
+static void rp_throttle(struct tty_struct * tty)
+{
+	struct r_port *info = (struct r_port *)tty->driver_data;
+	CHANNEL_t *cp;
+#ifdef ROCKET_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("throttle %s: %d....\n", rp_tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (rocket_paranoia_check(info, tty->device, "rp_throttle"))
+		return;
+
+	cp = &info->channel;
+	if (I_IXOFF(tty))
+		rp_send_xchar(tty, STOP_CHAR(tty));
+	
+	sClrRTS(&info->channel);
+}
+
+static void rp_unthrottle(struct tty_struct * tty)
+{
+	struct r_port *info = (struct r_port *)tty->driver_data;
+	CHANNEL_t *cp;
+#ifdef ROCKET_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("unthrottle %s: %d....\n", rp_tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (rocket_paranoia_check(info, tty->device, "rp_throttle"))
+		return;
+
+	cp = &info->channel;
+	if (I_IXOFF(tty))
+		rp_send_xchar(tty, START_CHAR(tty));
+
+	sSetRTS(&info->channel);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rp_stop() and rp_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rp_stop(struct tty_struct *tty)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+#ifdef ROCKET_DEBUG_FLOW
+	char	buf[64];
+	
+	printk("stop %s: %d %d....\n", rp_tty_name(tty, buf),
+	       info->xmit_cnt, info->xmit_fifo_room);
+#endif
+
+	if (rocket_paranoia_check(info, tty->device, "rp_stop"))
+		return;
+
+	if (sGetTxCnt(&info->channel))
+		sDisTransmit(&info->channel);
+}
+
+static void rp_start(struct tty_struct *tty)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+#ifdef ROCKET_DEBUG_FLOW
+	char	buf[64];
+	
+	printk("start %s: %d %d....\n", rp_tty_name(tty, buf),
+	       info->xmit_cnt, info->xmit_fifo_room);
+#endif
+
+	if (rocket_paranoia_check(info, tty->device, "rp_stop"))
+		return;
+
+	sEnTransmit(&info->channel);
+	xmit_flags[info->line >> 5] |= (1 << (info->line & 0x1f));
+}
+
+/*
+ * rp_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct r_port *info = (struct r_port *)tty->driver_data;
+	CHANNEL_t *cp;
+	unsigned long orig_jiffies;
+	int check_time, exit_time;
+	int txcnt;
+	
+	if (rocket_paranoia_check(info, tty->device, "rp_wait_until_sent"))
+		return;
+
+	cp = &info->channel;
+
+	orig_jiffies = jiffies;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+	printk("In RP_wait_until_sent(%d) (jiff=%lu)...", timeout, jiffies);
+	printk("cps=%d...", info->cps);
+#endif
+	while (1) {
+		txcnt = sGetTxCnt(cp);
+		if (!txcnt) {
+			if (sGetChanStatusLo(cp) & TXSHRMT)
+				break;
+			check_time = (HZ / info->cps) / 5;
+		} else
+			check_time = HZ * txcnt / info->cps;
+		if (timeout) {
+			exit_time = orig_jiffies + timeout - jiffies;
+			if (exit_time <= 0)
+				break;
+			if (exit_time < check_time)
+				check_time = exit_time;
+		}
+		if (check_time == 0)
+			check_time = 1;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+		printk("txcnt = %d (jiff=%lu,check=%d)...", txcnt,
+		       jiffies, check_time);
+#endif
+		current->state = TASK_INTERRUPTIBLE;
+		current->counter = 0;	/* make us low-priority */
+		current->timeout = jiffies + check_time;
+		schedule();
+		if (current->signal & ~current->blocked)
+			break;
+	}
+	current->state = TASK_RUNNING;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+	printk("txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
+#endif
+}
+
+/*
+ * rp_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rp_hangup(struct tty_struct *tty)
+{
+	CHANNEL_t	*cp;
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	
+	if (rocket_paranoia_check(info, tty->device, "rp_hangup"))
+		return;
+
+#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP))
+	printk("rp_hangup of ttyR%d...", info->line);
+#endif
+	/*
+	 * If the port is in the process of being closed, just force
+	 * the transmit buffer to be empty, and let rp_close handle
+	 * the clean up.
+	 */
+	if (info->flags & ROCKET_CLOSING) {
+		cli();
+		info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+		sti();
+		wake_up_interruptible(&tty->write_wait);
+		return;
+	}
+	if (info->count) {
+#ifdef MODULE
+		MOD_DEC_USE_COUNT;
+#endif
+		rp_num_ports_open--;
+	}
+	
+	xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+	info->count = 0;
+	info->flags &= ~(ROCKET_NORMAL_ACTIVE|ROCKET_CALLOUT_ACTIVE);
+	info->tty = 0;
+
+	cp = &info->channel;
+	sDisRxFIFO(cp);
+	sDisTransmit(cp);
+	sDisInterrupts(cp, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN));
+	sDisCTSFlowCtl(cp);
+	sDisTxSoftFlowCtl(cp);
+	sClrTxXOFF(cp);
+	info->flags &= ~ROCKET_INITIALIZED;
+	
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * The Rocketport write routines.  The Rocketport driver uses a
+ * double-buffering strategy, with the twist that if the in-memory CPU
+ * buffer is empty, and there's space in the transmit FIFO, the
+ * writing routines will write directly to transmit FIFO.
+ *
+ * This gets a little tricky, but I'm pretty sure I got it all right.
+ */
+static void rp_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	CHANNEL_t	*cp;
+
+	if (rocket_paranoia_check(info, tty->device, "rp_put_char"))
+		return;
+
+#ifdef ROCKET_DEBUG_WRITE
+	printk("rp_put_char %c...", ch);
+#endif
+	
+	cp = &info->channel;
+
+	if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0)
+		info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+	if (tty->stopped || tty->hw_stopped ||
+	    info->xmit_fifo_room == 0 || info->xmit_cnt != 0) {
+		info->xmit_buf[info->xmit_head++] = ch;
+		info->xmit_head &= XMIT_BUF_SIZE-1;
+		info->xmit_cnt++;
+		xmit_flags[info->line >> 5] |= (1 << (info->line & 0x1f));
+	} else {
+		sOutB(sGetTxRxDataIO(cp), ch);
+		info->xmit_fifo_room--;
+	}
+}
+
+#if (LINUX_VERSION_CODE > 66304)
+static int rp_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+#else
+static int rp_write(struct tty_struct * tty, int from_user,
+		    unsigned char *buf, int count)
+#endif	
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	CHANNEL_t	*cp;
+	const unsigned char	*b;
+	int		c, retval = 0;
+	unsigned long	flags;
+
+	if (count <= 0 || rocket_paranoia_check(info, tty->device, "rp_write"))
+		return 0;
+
+#ifdef ROCKET_DEBUG_WRITE
+	printk("rp_write %d chars...", count);
+#endif
+	cp = &info->channel;
+
+	if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0)
+		info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+	if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0
+	    && info->xmit_fifo_room >= 0) {
+		c = MIN(count, info->xmit_fifo_room);
+		b = buf;
+		if (from_user) {
+			down(&tmp_buf_sem);
+			c -= copy_from_user(tmp_buf, buf, c);
+			b = tmp_buf;
+			up(&tmp_buf_sem);
+			/* In case we got pre-empted */
+			if (!c) {
+				retval = -EFAULT;
+				goto end;
+			}
+			if (info->tty == 0)
+				goto end;
+			c = MIN(c, info->xmit_fifo_room);
+		}
+		sOutStrW(sGetTxRxDataIO(cp), b, c/2);
+		if (c & 1)
+			sOutB(sGetTxRxDataIO(cp), b[c-1]);
+		retval += c;
+		buf += c;
+		count -= c;
+		info->xmit_fifo_room -= c;
+	}
+	if (!count)
+		goto end;
+	
+	save_flags(flags);
+	while (1) {
+		cli();
+		if (info->tty == 0) {
+			restore_flags(flags);
+			goto end;
+		}
+		c = MIN(count, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1,
+				   XMIT_BUF_SIZE - info->xmit_head));
+		if (c <= 0)
+			break;
+
+		b = buf;
+		if (from_user) {
+			down(&tmp_buf_sem);
+			c -= copy_from_user(tmp_buf, buf, c);
+			b = tmp_buf;
+			up(&tmp_buf_sem);
+			if (!c) {
+				if (retval == 0)
+					retval = -EFAULT;
+				goto end_intr;
+			}
+			/* In case we got pre-empted */
+			if (info->tty == 0)
+				goto end_intr;
+			c = MIN(c, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1,
+				       XMIT_BUF_SIZE - info->xmit_head));
+				
+		}
+		memcpy(info->xmit_buf + info->xmit_head, b, c);
+		info->xmit_head = (info->xmit_head + c) & (XMIT_BUF_SIZE-1);
+		info->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		retval += c;
+	}
+end_intr:
+	if ((retval > 0) && !tty->stopped && !tty->hw_stopped)
+		xmit_flags[info->line >> 5] |= (1 << (info->line & 0x1f));
+	restore_flags(flags);
+end:
+	if (info->xmit_cnt < WAKEUP_CHARS) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+	return retval;
+}
+
+/*
+ * Return the number of characters that can be sent.  We estimate
+ * only using the in-memory transmit buffer only, and ignore the
+ * potential space in the transmit FIFO.
+ */
+static int rp_write_room(struct tty_struct *tty)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	int	ret;
+
+	if (rocket_paranoia_check(info, tty->device, "rp_write_room"))
+		return 0;
+
+	ret = XMIT_BUF_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+#ifdef ROCKET_DEBUG_WRITE
+	printk("rp_write_room returns %d...", ret);
+#endif
+	return ret;
+}
+
+/*
+ * Return the number of characters in the buffer.  Again, this only
+ * counts those characters in the in-memory transmit buffer.
+ */
+static int rp_chars_in_buffer(struct tty_struct *tty)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	CHANNEL_t	*cp;
+
+	if (rocket_paranoia_check(info, tty->device, "rp_chars_in_buffer"))
+		return 0;
+
+	cp = &info->channel;
+
+#ifdef ROCKET_DEBUG_WRITE
+	printk("rp_chars_in_buffer returns %d...", info->xmit_cnt);
+#endif
+	return info->xmit_cnt;
+}
+
+static void rp_flush_buffer(struct tty_struct *tty)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	CHANNEL_t	*cp;
+
+	if (rocket_paranoia_check(info, tty->device, "rp_flush_buffer"))
+		return;
+
+	cli();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	sti();
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	
+	cp = &info->channel;
+	
+	sFlushTxFIFO(cp);
+}
+
+#ifdef ENABLE_PCI
+__initfunc(int register_PCI(int i, char bus, char device_fn))
+{
+	int	num_aiops, aiop, max_num_aiops, num_chan, chan;
+	unsigned int	aiopio[MAX_AIOPS_PER_BOARD];
+	char *str;
+	CONTROLLER_t	*ctlp;
+	unsigned short	vendor_id, device_id;
+	int	ret, error;
+	unsigned int port;
+
+	error = pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID,
+		&vendor_id);
+	ret = pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID,
+		&device_id);
+	if (error == 0)
+		error = ret;
+	ret = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,
+		&port);
+	rcktpt_io_addr[i] = (unsigned long) port;
+	if (error == 0)
+		error = ret;
+
+	if (error) {
+		printk("PCI RocketPort error: %s not initializing due to error"
+		       "reading configuration space\n",
+		       pcibios_strerror(error));
+		return(0);
+	}
+
+	--rcktpt_io_addr[i];
+	switch(device_id) {
+	case PCI_DEVICE_ID_RP4QUAD:
+		str = "Quadcable";
+		max_num_aiops = 1;
+		break;
+	case PCI_DEVICE_ID_RP8OCTA:
+		str = "Octacable";
+		max_num_aiops = 1;
+		break;
+	case PCI_DEVICE_ID_RP8INTF:
+		str = "8";
+		max_num_aiops = 1;
+		break;
+	case PCI_DEVICE_ID_RP8J:
+		str = "8J";
+		max_num_aiops = 1;
+		break;
+	case PCI_DEVICE_ID_RP16INTF:
+		str = "16";
+		max_num_aiops = 2;
+		break;
+	case PCI_DEVICE_ID_RP32INTF:
+		str = "32";
+		max_num_aiops = 4;
+		break;
+	default:
+		str = "(unknown/unsupported)";
+		max_num_aiops = 0;
+		break;
+	}
+	for(aiop=0;aiop < max_num_aiops;aiop++)
+		aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40);
+	ctlp = sCtlNumToCtlPtr(i);
+	num_aiops = sPCIInitController(ctlp, i,
+					aiopio, max_num_aiops, 0,
+					FREQ_DIS, 0);
+	printk("Rocketport controller #%d found at %d:%d, "
+	       "%d AIOP(s) (PCI Rocketport %s)\n", i, bus, device_fn,
+	       num_aiops, str);
+	if(num_aiops <= 0) {
+		rcktpt_io_addr[i] = 0;
+		return(0);
+	}
+	for(aiop = 0;aiop < num_aiops; aiop++) {
+		sResetAiopByNum(ctlp, aiop);
+		sEnAiop(ctlp, aiop);
+		num_chan = sGetAiopNumChan(ctlp, aiop);
+		for(chan=0;chan < num_chan; chan++)
+			init_r_port(i, aiop, chan);
+	}
+	return(1);
+}
+
+__initfunc(static int init_PCI(int boards_found))
+{
+	unsigned char	bus, device_fn;
+	int	i, count = 0;
+
+	for(i=0; i < (NUM_BOARDS - boards_found); i++) {
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA,
+			i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8INTF,
+			i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16INTF,
+			i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP32INTF,
+			i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+	}
+	return(count);
+}
+#endif
+
+__initfunc(static int init_ISA(int i, int *reserved_controller))
+{
+	int	num_aiops, num_chan;
+	int	aiop, chan;
+	unsigned int	aiopio[MAX_AIOPS_PER_BOARD];	
+	CONTROLLER_t	*ctlp;
+
+	if (rcktpt_io_addr[i] == 0)
+		return(0);
+
+	if (check_region(rcktpt_io_addr[i],64)) {
+		printk("RocketPort board address 0x%lx in use...\n",
+			rcktpt_io_addr[i]);
+		rcktpt_io_addr[i] = 0;
+		return(0);
+	}
+	
+	for (aiop=0; aiop<MAX_AIOPS_PER_BOARD; aiop++)
+		aiopio[aiop]= rcktpt_io_addr[i] + (aiop * 0x400);
+	ctlp= sCtlNumToCtlPtr(i);
+	num_aiops = sInitController(ctlp, i, controller + (i*0x400),
+				    aiopio, MAX_AIOPS_PER_BOARD, 0,
+				    FREQ_DIS, 0);
+	if (num_aiops <= 0) {
+		rcktpt_io_addr[i] = 0;
+		return(0);
+	}
+	for (aiop = 0; aiop < num_aiops; aiop++) {
+		sResetAiopByNum(ctlp, aiop);
+		sEnAiop(ctlp, aiop);
+		num_chan = sGetAiopNumChan(ctlp,aiop);
+		for (chan=0; chan < num_chan; chan++)
+			init_r_port(i, aiop, chan);
+	}
+	printk("Rocketport controller #%d found at 0x%lx, "
+	       "%d AIOPs\n", i, rcktpt_io_addr[i],
+	       num_aiops);
+	if (rcktpt_io_addr[i] + 0x40 == controller) {
+		*reserved_controller = 1;
+		request_region(rcktpt_io_addr[i], 68,
+				       "Comtrol Rocketport");
+	} else {
+		request_region(rcktpt_io_addr[i], 64,
+			       "Comtrol Rocketport");
+	}
+	return(1);
+}
+
+
+/*
+ * The module "startup" routine; it's run when the module is loaded.
+ */
+__initfunc(int rp_init(void))
+{
+	int i, retval, pci_boards_found, isa_boards_found;
+	int	reserved_controller = 0;
+
+	printk("Rocketport device driver module, version %s, %s\n",
+	       ROCKET_VERSION, ROCKET_DATE);
+
+	/*
+	 * Set up the timer channel.  If it is already in use by
+	 * some other driver, give up.
+	 */
+	if (timer_table[COMTROL_TIMER].fn) {
+		printk("rocket.o: Timer channel %d already in use!\n",
+		       COMTROL_TIMER);
+		return -EBUSY;
+	}
+	timer_table[COMTROL_TIMER].fn = rp_do_poll;
+	timer_table[COMTROL_TIMER].expires = 0;
+	
+	/*
+	 * Initialize the array of pointers to our own internal state
+	 * structures.
+	 */
+	memset(rp_table, 0, sizeof(rp_table));
+	memset(xmit_flags, 0, sizeof(xmit_flags));
+
+	if (board1 == 0)
+		board1 = 0x180;
+	if (controller == 0)
+		controller = board1 + 0x40;
+
+	if (check_region(controller, 4)) {
+		printk("Controller IO addresses in use, unloading driver.\n");
+		return -EBUSY;
+	}
+	
+	rcktpt_io_addr[0] = board1;
+	rcktpt_io_addr[1] = board2;
+	rcktpt_io_addr[2] = board3;
+	rcktpt_io_addr[3] = board4;
+
+	/*
+	 * If support_low_speed is set, use the slow clock prescale,
+	 * which supports 50 bps
+	 */
+	if (support_low_speed) {
+		sClockPrescale = 0x19;	/* mod 9 (divide by 10) prescale */
+		rp_baud_base = 230400;
+	} else {
+		sClockPrescale = 0x14; /* mod 4 (devide by 5) prescale */
+		rp_baud_base = 460800;
+	}
+	
+	/*
+	 * OK, let's probe each of the controllers looking for boards.
+	 */
+	isa_boards_found = 0;
+	pci_boards_found = 0;
+	for (i=0; i < NUM_BOARDS; i++) {
+		if(init_ISA(i, &reserved_controller))
+			isa_boards_found++;
+	}
+#ifdef ENABLE_PCI
+	if (pcibios_present()) {
+		if(isa_boards_found < NUM_BOARDS)
+			pci_boards_found = init_PCI(isa_boards_found);
+	} else {
+		printk("No PCI BIOS found\n");
+	}
+#endif
+	max_board = pci_boards_found + isa_boards_found;
+	
+	if (max_board == 0) {
+		printk("No rocketport ports found; unloading driver.\n");
+		timer_table[COMTROL_TIMER].fn = 0;
+		return -ENODEV;
+	}
+
+	if (reserved_controller == 0)
+		request_region(controller, 4, "Comtrol Rocketport");
+
+	/*
+	 * Set up the tty driver structure and then register this
+	 * driver with the tty layer.
+	 */
+	memset(&rocket_driver, 0, sizeof(struct tty_driver));
+	rocket_driver.magic = TTY_DRIVER_MAGIC;
+	rocket_driver.name = "ttyR";
+	rocket_driver.major = TTY_ROCKET_MAJOR;
+	rocket_driver.minor_start = 0;
+	rocket_driver.num = MAX_RP_PORTS;
+	rocket_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	rocket_driver.subtype = SERIAL_TYPE_NORMAL;
+	rocket_driver.init_termios = tty_std_termios;
+	rocket_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	rocket_driver.flags = TTY_DRIVER_REAL_RAW;
+	rocket_driver.refcount = &rocket_refcount;
+	rocket_driver.table = rocket_table;
+	rocket_driver.termios = rocket_termios;
+	rocket_driver.termios_locked = rocket_termios_locked;
+
+	rocket_driver.open = rp_open;
+	rocket_driver.close = rp_close;
+	rocket_driver.write = rp_write;
+	rocket_driver.put_char = rp_put_char;
+	rocket_driver.write_room = rp_write_room;
+	rocket_driver.chars_in_buffer = rp_chars_in_buffer;
+	rocket_driver.flush_buffer = rp_flush_buffer;
+	rocket_driver.ioctl = rp_ioctl;
+	rocket_driver.throttle = rp_throttle;
+	rocket_driver.unthrottle = rp_unthrottle;
+	rocket_driver.set_termios = rp_set_termios;
+	rocket_driver.stop = rp_stop;
+	rocket_driver.start = rp_start;
+	rocket_driver.hangup = rp_hangup;
+#if (LINUX_VERSION_CODE >= 131343)
+	rocket_driver.send_xchar = rp_send_xchar;
+	rocket_driver.wait_until_sent = rp_wait_until_sent;
+#endif
+
+	/*
+	 * The callout device is just like normal device except for
+	 * the minor number and the subtype code.
+	 */
+	callout_driver = rocket_driver;
+	callout_driver.name = "cur";
+	callout_driver.major = CUA_ROCKET_MAJOR;
+	callout_driver.minor_start = 0;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	
+	retval = tty_register_driver(&callout_driver);
+	if (retval < 0) {
+		printk("Couldn't install Rocketport callout driver "
+		       "(error %d)\n", -retval);
+		return -1;
+	}
+
+	retval = tty_register_driver(&rocket_driver);
+	if (retval < 0) {
+		printk("Couldn't install tty Rocketport driver "
+		       "(error %d)\n", -retval);
+		return -1;
+	}
+#ifdef ROCKET_DEBUG_OPEN
+	printk("Rocketport driver is major %d, callout is %d\n",
+	       rocket_driver.major, callout_driver.major);
+#endif
+
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return rp_init();
+}
+
+void
+cleanup_module( void) {
+	int	retval;
+	int	i;
+	int	released_controller = 0;
+	
+	retval = tty_unregister_driver(&callout_driver);
+	if (retval) {
+		printk("Error %d while trying to unregister "
+		       "rocketport callout driver\n", -retval);
+	}
+	retval = tty_unregister_driver(&rocket_driver);
+	if (retval) {
+		printk("Error %d while trying to unregister "
+		       "rocketport driver\n", -retval);
+	}
+	for (i = 0; i < MAX_RP_PORTS; i++) {
+		if (rp_table[i])
+			kfree(rp_table[i]);
+	}
+	for (i=0; i < NUM_BOARDS; i++) {
+		if (rcktpt_io_addr[i] <= 0)
+			continue;
+		if (rcktpt_io_addr[i] + 0x40 == controller) {
+			released_controller++;
+			release_region(rcktpt_io_addr[i], 68);
+		} else
+			release_region(rcktpt_io_addr[i], 64);
+		if (released_controller == 0)
+			release_region(controller, 4);
+	}
+	if (tmp_buf)
+		free_page((unsigned long) tmp_buf);
+	timer_table[COMTROL_TIMER].fn = 0;
+}
+#endif
+
+/***********************************************************************
+		Copyright 1994 Comtrol Corporation.
+			All Rights Reserved.
+
+The following source code is subject to Comtrol Corporation's
+Developer's License Agreement.
+
+This source code is protected by United States copyright law and 
+international copyright treaties.
+
+This source code may only be used to develop software products that
+will operate with Comtrol brand hardware.
+
+You may not reproduce nor distribute this source code in its original
+form but must produce a derivative work which includes portions of
+this source code only.
+
+The portions of this source code which you use in your derivative
+work must bear Comtrol's copyright notice:
+
+		Copyright 1994 Comtrol Corporation.
+
+***********************************************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+static Byte_t RData[RDATASIZE] =
+{
+   0x00, 0x09, 0xf6, 0x82,
+   0x02, 0x09, 0x86, 0xfb,
+   0x04, 0x09, 0x00, 0x0a,
+   0x06, 0x09, 0x01, 0x0a,
+   0x08, 0x09, 0x8a, 0x13,
+   0x0a, 0x09, 0xc5, 0x11,
+   0x0c, 0x09, 0x86, 0x85,
+   0x0e, 0x09, 0x20, 0x0a,
+   0x10, 0x09, 0x21, 0x0a,
+   0x12, 0x09, 0x41, 0xff,
+   0x14, 0x09, 0x82, 0x00,
+   0x16, 0x09, 0x82, 0x7b,
+   0x18, 0x09, 0x8a, 0x7d,
+   0x1a, 0x09, 0x88, 0x81,
+   0x1c, 0x09, 0x86, 0x7a,
+   0x1e, 0x09, 0x84, 0x81,
+   0x20, 0x09, 0x82, 0x7c,
+   0x22, 0x09, 0x0a, 0x0a 
+};
+
+static Byte_t RRegData[RREGDATASIZE]=
+{
+   0x00, 0x09, 0xf6, 0x82,             /* 00: Stop Rx processor */
+   0x08, 0x09, 0x8a, 0x13,             /* 04: Tx software flow control */
+   0x0a, 0x09, 0xc5, 0x11,             /* 08: XON char */
+   0x0c, 0x09, 0x86, 0x85,             /* 0c: XANY */
+   0x12, 0x09, 0x41, 0xff,             /* 10: Rx mask char */
+   0x14, 0x09, 0x82, 0x00,             /* 14: Compare/Ignore #0 */
+   0x16, 0x09, 0x82, 0x7b,             /* 18: Compare #1 */
+   0x18, 0x09, 0x8a, 0x7d,             /* 1c: Compare #2 */
+   0x1a, 0x09, 0x88, 0x81,             /* 20: Interrupt #1 */
+   0x1c, 0x09, 0x86, 0x7a,             /* 24: Ignore/Replace #1 */
+   0x1e, 0x09, 0x84, 0x81,             /* 28: Interrupt #2 */
+   0x20, 0x09, 0x82, 0x7c,             /* 2c: Ignore/Replace #2 */
+   0x22, 0x09, 0x0a, 0x0a              /* 30: Rx FIFO Enable */
+};
+
+CONTROLLER_T sController[CTL_SIZE] =
+{
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}},
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}},
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}},
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}
+};
+
+#if 0
+/* IRQ number to MUDBAC register 2 mapping */
+Byte_t sIRQMap[16] =
+{
+   0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80
+};
+#endif
+
+Byte_t sBitMapClrTbl[8] =
+{
+   0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f
+};
+
+Byte_t sBitMapSetTbl[8] =
+{
+   0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
+};
+
+int sClockPrescale = 0x14;
+
+/***************************************************************************
+Function: sInitController
+Purpose:  Initialization of controller global registers and controller
+          structure.
+Call:     sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize,
+                          IRQNum,Frequency,PeriodicOnly)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int CtlNum; Controller number
+          ByteIO_t MudbacIO; Mudbac base I/O address.
+          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
+             This list must be in the order the AIOPs will be found on the
+             controller.  Once an AIOP in the list is not found, it is
+             assumed that there are no more AIOPs on the controller.
+          int AiopIOListSize; Number of addresses in AiopIOList
+          int IRQNum; Interrupt Request number.  Can be any of the following:
+                         0: Disable global interrupts
+                         3: IRQ 3
+                         4: IRQ 4
+                         5: IRQ 5
+                         9: IRQ 9
+                         10: IRQ 10
+                         11: IRQ 11
+                         12: IRQ 12
+                         15: IRQ 15
+          Byte_t Frequency: A flag identifying the frequency
+                   of the periodic interrupt, can be any one of the following:
+                      FREQ_DIS - periodic interrupt disabled
+                      FREQ_137HZ - 137 Hertz
+                      FREQ_69HZ - 69 Hertz
+                      FREQ_34HZ - 34 Hertz
+                      FREQ_17HZ - 17 Hertz
+                      FREQ_9HZ - 9 Hertz
+                      FREQ_4HZ - 4 Hertz
+                   If IRQNum is set to 0 the Frequency parameter is
+                   overidden, it is forced to a value of FREQ_DIS.
+          int PeriodicOnly: TRUE if all interrupts except the periodic
+                               interrupt are to be blocked.
+                            FALSE is both the periodic interrupt and
+                               other channel interrupts are allowed.
+                            If IRQNum is set to 0 the PeriodicOnly parameter is
+                               overidden, it is forced to a value of FALSE.
+Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
+               initialization failed.
+
+Comments:
+          If periodic interrupts are to be disabled but AIOP interrupts
+          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE.
+
+          If interrupts are to be completely disabled set IRQNum to 0.
+
+          Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an
+          invalid combination.
+
+          This function performs initialization of global interrupt modes,
+          but it does not actually enable global interrupts.  To enable
+          and disable global interrupts use functions sEnGlobalInt() and
+          sDisGlobalInt().  Enabling of global interrupts is normally not
+          done until all other initializations are complete.
+
+          Even if interrupts are globally enabled, they must also be
+          individually enabled for each channel that is to generate
+          interrupts.
+
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+
+          After this function all AIOPs on the controller are disabled,
+          they can be enabled with sEnAiop().
+*/
+int sInitController(	CONTROLLER_T *CtlP,
+			int CtlNum,
+			ByteIO_t MudbacIO,
+			ByteIO_t *AiopIOList,
+			int AiopIOListSize,
+			int IRQNum,
+			Byte_t Frequency,
+			int PeriodicOnly)
+{
+	int		i;
+	ByteIO_t	io;
+
+   CtlP->CtlNum = CtlNum;
+   CtlP->CtlID = CTLID_0001;        /* controller release 1 */
+   CtlP->BusType = isISA;     
+   CtlP->MBaseIO = MudbacIO;
+   CtlP->MReg1IO = MudbacIO + 1;
+   CtlP->MReg2IO = MudbacIO + 2;
+   CtlP->MReg3IO = MudbacIO + 3;
+#if 1
+   CtlP->MReg2 = 0;                 /* interrupt disable */
+   CtlP->MReg3 = 0;                 /* no periodic interrupts */
+#else
+   if(sIRQMap[IRQNum] == 0)            /* interrupts globally disabled */
+   {
+      CtlP->MReg2 = 0;                 /* interrupt disable */
+      CtlP->MReg3 = 0;                 /* no periodic interrupts */
+   }
+   else
+   {
+      CtlP->MReg2 = sIRQMap[IRQNum];   /* set IRQ number */
+      CtlP->MReg3 = Frequency;         /* set frequency */
+      if(PeriodicOnly)                 /* periodic interrupt only */
+      {
+         CtlP->MReg3 |= PERIODIC_ONLY;
+      }
+   }
+#endif
+   sOutB(CtlP->MReg2IO,CtlP->MReg2);
+   sOutB(CtlP->MReg3IO,CtlP->MReg3);
+   sControllerEOI(CtlP);               /* clear EOI if warm init */
+   /* Init AIOPs */
+   CtlP->NumAiop = 0;
+   for(i=0; i < AiopIOListSize; i++)
+   {
+      io = AiopIOList[i];
+      CtlP->AiopIO[i] = (WordIO_t)io;
+      CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
+      sOutB(CtlP->MReg2IO,CtlP->MReg2 | (i & 0x03)); /* AIOP index */
+      sOutB(MudbacIO,(Byte_t)(io >> 6));	/* set up AIOP I/O in MUDBAC */
+      sEnAiop(CtlP,i);                         /* enable the AIOP */
+
+      CtlP->AiopID[i] = sReadAiopID(io);       /* read AIOP ID */
+      if(CtlP->AiopID[i] == AIOPID_NULL)       /* if AIOP does not exist */
+      {
+         sDisAiop(CtlP,i);                     /* disable AIOP */
+         break;                                /* done looking for AIOPs */
+      }
+
+      CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */
+      sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE);      /* clock prescaler */
+      sOutB(io + _INDX_DATA,sClockPrescale);
+      CtlP->NumAiop++;                         /* bump count of AIOPs */
+      sDisAiop(CtlP,i);                        /* disable AIOP */
+   }
+
+   if(CtlP->NumAiop == 0)
+      return(-1);
+   else
+      return(CtlP->NumAiop);
+}
+
+/***************************************************************************
+Function: sPCIInitController
+Purpose:  Initialization of controller global registers and controller
+          structure.
+Call:     sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize,
+                          IRQNum,Frequency,PeriodicOnly)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int CtlNum; Controller number
+          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
+             This list must be in the order the AIOPs will be found on the
+             controller.  Once an AIOP in the list is not found, it is
+             assumed that there are no more AIOPs on the controller.
+          int AiopIOListSize; Number of addresses in AiopIOList
+          int IRQNum; Interrupt Request number.  Can be any of the following:
+                         0: Disable global interrupts
+                         3: IRQ 3
+                         4: IRQ 4
+                         5: IRQ 5
+                         9: IRQ 9
+                         10: IRQ 10
+                         11: IRQ 11
+                         12: IRQ 12
+                         15: IRQ 15
+          Byte_t Frequency: A flag identifying the frequency
+                   of the periodic interrupt, can be any one of the following:
+                      FREQ_DIS - periodic interrupt disabled
+                      FREQ_137HZ - 137 Hertz
+                      FREQ_69HZ - 69 Hertz
+                      FREQ_34HZ - 34 Hertz
+                      FREQ_17HZ - 17 Hertz
+                      FREQ_9HZ - 9 Hertz
+                      FREQ_4HZ - 4 Hertz
+                   If IRQNum is set to 0 the Frequency parameter is
+                   overidden, it is forced to a value of FREQ_DIS.
+          int PeriodicOnly: TRUE if all interrupts except the periodic
+                               interrupt are to be blocked.
+                            FALSE is both the periodic interrupt and
+                               other channel interrupts are allowed.
+                            If IRQNum is set to 0 the PeriodicOnly parameter is
+                               overidden, it is forced to a value of FALSE.
+Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
+               initialization failed.
+
+Comments:
+          If periodic interrupts are to be disabled but AIOP interrupts
+          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE.
+
+          If interrupts are to be completely disabled set IRQNum to 0.
+
+          Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an
+          invalid combination.
+
+          This function performs initialization of global interrupt modes,
+          but it does not actually enable global interrupts.  To enable
+          and disable global interrupts use functions sEnGlobalInt() and
+          sDisGlobalInt().  Enabling of global interrupts is normally not
+          done until all other initializations are complete.
+
+          Even if interrupts are globally enabled, they must also be
+          individually enabled for each channel that is to generate
+          interrupts.
+
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+
+          After this function all AIOPs on the controller are disabled,
+          they can be enabled with sEnAiop().
+*/
+int sPCIInitController(	CONTROLLER_T *CtlP,
+			int CtlNum,
+			ByteIO_t *AiopIOList,
+			int AiopIOListSize,
+			int IRQNum,
+			Byte_t Frequency,
+			int PeriodicOnly)
+{
+	int		i;
+	ByteIO_t	io;
+
+   CtlP->CtlNum = CtlNum;
+   CtlP->CtlID = CTLID_0001;        /* controller release 1 */
+   CtlP->BusType = isPCI;        /* controller release 1 */
+
+   CtlP->PCIIO = (WordIO_t)((ByteIO_t)AiopIOList[0] + _PCI_INT_FUNC);
+
+   sPCIControllerEOI(CtlP);               /* clear EOI if warm init */
+   /* Init AIOPs */
+   CtlP->NumAiop = 0;
+   for(i=0; i < AiopIOListSize; i++)
+   {
+      io = AiopIOList[i];
+      CtlP->AiopIO[i] = (WordIO_t)io;
+      CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
+
+      CtlP->AiopID[i] = sReadAiopID(io);       /* read AIOP ID */
+      if(CtlP->AiopID[i] == AIOPID_NULL)       /* if AIOP does not exist */
+         break;                                /* done looking for AIOPs */
+
+      CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */
+      sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE);      /* clock prescaler */
+      sOutB(io + _INDX_DATA,sClockPrescale);
+      CtlP->NumAiop++;                         /* bump count of AIOPs */
+   }
+
+   if(CtlP->NumAiop == 0)
+      return(-1);
+   else
+      return(CtlP->NumAiop);
+}
+
+/***************************************************************************
+Function: sReadAiopID
+Purpose:  Read the AIOP idenfication number directly from an AIOP.
+Call:     sReadAiopID(io)
+          ByteIO_t io: AIOP base I/O address
+Return:   int: Flag AIOPID_XXXX if a valid AIOP is found, where X
+                 is replace by an identifying number.
+          Flag AIOPID_NULL if no valid AIOP is found
+Warnings: No context switches are allowed while executing this function.
+
+*/
+int sReadAiopID(ByteIO_t io)
+{
+   Byte_t AiopID;               /* ID byte from AIOP */
+
+   sOutB(io + _CMD_REG,RESET_ALL);     /* reset AIOP */
+   sOutB(io + _CMD_REG,0x0);
+   AiopID = sInB(io + _CHN_STAT0) & 0x07;
+   if(AiopID == 0x06)
+      return(1);
+   else                                /* AIOP does not exist */
+      return(-1);
+}
+
+/***************************************************************************
+Function: sReadAiopNumChan
+Purpose:  Read the number of channels available in an AIOP directly from
+          an AIOP.
+Call:     sReadAiopNumChan(io)
+          WordIO_t io: AIOP base I/O address
+Return:   int: The number of channels available
+Comments: The number of channels is determined by write/reads from identical
+          offsets within the SRAM address spaces for channels 0 and 4.
+          If the channel 4 space is mirrored to channel 0 it is a 4 channel
+          AIOP, otherwise it is an 8 channel.
+Warnings: No context switches are allowed while executing this function.
+*/
+int sReadAiopNumChan(WordIO_t io)
+{
+   Word_t x;
+
+   sOutDW((DWordIO_t)io + _INDX_ADDR,0x12340000L); /* write to chan 0 SRAM */
+   sOutW(io + _INDX_ADDR,0);       /* read from SRAM, chan 0 */
+   x = sInW(io + _INDX_DATA);
+   sOutW(io + _INDX_ADDR,0x4000);  /* read from SRAM, chan 4 */
+   if(x != sInW(io + _INDX_DATA))  /* if different must be 8 chan */
+      return(8);
+   else
+      return(4);
+}
+
+/***************************************************************************
+Function: sInitChan
+Purpose:  Initialization of a channel and channel structure
+Call:     sInitChan(CtlP,ChP,AiopNum,ChanNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          CHANNEL_T *ChP; Ptr to channel structure
+          int AiopNum; AIOP number within controller
+          int ChanNum; Channel number within AIOP
+Return:   int: TRUE if initialization succeeded, FALSE if it fails because channel
+               number exceeds number of channels available in AIOP.
+Comments: This function must be called before a channel can be used.
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+*/
+int sInitChan(	CONTROLLER_T *CtlP,
+		CHANNEL_T *ChP,
+		int AiopNum,
+		int ChanNum)
+{
+   int i;
+   WordIO_t AiopIO;
+   WordIO_t ChIOOff;
+   Byte_t *ChR;
+   Word_t ChOff;
+   static Byte_t R[4];
+   int brd9600;
+
+   if(ChanNum >= CtlP->AiopNumChan[AiopNum])
+      return(FALSE);                   /* exceeds num chans in AIOP */
+
+   /* Channel, AIOP, and controller identifiers */
+   ChP->CtlP = CtlP;
+   ChP->ChanID = CtlP->AiopID[AiopNum];
+   ChP->AiopNum = AiopNum;
+   ChP->ChanNum = ChanNum;
+
+   /* Global direct addresses */
+   AiopIO = CtlP->AiopIO[AiopNum];
+   ChP->Cmd = (ByteIO_t)AiopIO + _CMD_REG;
+   ChP->IntChan = (ByteIO_t)AiopIO + _INT_CHAN;
+   ChP->IntMask = (ByteIO_t)AiopIO + _INT_MASK;
+   ChP->IndexAddr = (DWordIO_t)AiopIO + _INDX_ADDR;
+   ChP->IndexData = AiopIO + _INDX_DATA;
+
+   /* Channel direct addresses */
+   ChIOOff = AiopIO + ChP->ChanNum * 2;
+   ChP->TxRxData = ChIOOff + _TD0;
+   ChP->ChanStat = ChIOOff + _CHN_STAT0;
+   ChP->TxRxCount = ChIOOff + _FIFO_CNT0;
+   ChP->IntID = (ByteIO_t)AiopIO + ChP->ChanNum + _INT_ID0;
+
+   /* Initialize the channel from the RData array */
+   for(i=0; i < RDATASIZE; i+=4)
+   {
+      R[0] = RData[i];
+      R[1] = RData[i+1] + 0x10 * ChanNum;
+      R[2] = RData[i+2];
+      R[3] = RData[i+3];
+      sOutDW(ChP->IndexAddr,*((DWord_t *)&R[0]));
+   }
+
+   ChR = ChP->R;
+   for(i=0; i < RREGDATASIZE; i+=4)
+   {
+      ChR[i] = RRegData[i];
+      ChR[i+1] = RRegData[i+1] + 0x10 * ChanNum;
+      ChR[i+2] = RRegData[i+2];
+      ChR[i+3] = RRegData[i+3];
+   }
+
+   /* Indexed registers */
+   ChOff = (Word_t)ChanNum * 0x1000;
+
+   if (sClockPrescale == 0x14)
+	   brd9600 = 47;
+   else
+	   brd9600 = 23;
+
+   ChP->BaudDiv[0] = (Byte_t)(ChOff + _BAUD);
+   ChP->BaudDiv[1] = (Byte_t)((ChOff + _BAUD) >> 8);
+   ChP->BaudDiv[2] = (Byte_t)brd9600;
+   ChP->BaudDiv[3] = (Byte_t)(brd9600 >> 8);
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->BaudDiv[0]);
+
+   ChP->TxControl[0] = (Byte_t)(ChOff + _TX_CTRL);
+   ChP->TxControl[1] = (Byte_t)((ChOff + _TX_CTRL) >> 8);
+   ChP->TxControl[2] = 0;
+   ChP->TxControl[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]);
+
+   ChP->RxControl[0] = (Byte_t)(ChOff + _RX_CTRL);
+   ChP->RxControl[1] = (Byte_t)((ChOff + _RX_CTRL) >> 8);
+   ChP->RxControl[2] = 0;
+   ChP->RxControl[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]);
+
+   ChP->TxEnables[0] = (Byte_t)(ChOff + _TX_ENBLS);
+   ChP->TxEnables[1] = (Byte_t)((ChOff + _TX_ENBLS) >> 8);
+   ChP->TxEnables[2] = 0;
+   ChP->TxEnables[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxEnables[0]);
+
+   ChP->TxCompare[0] = (Byte_t)(ChOff + _TXCMP1);
+   ChP->TxCompare[1] = (Byte_t)((ChOff + _TXCMP1) >> 8);
+   ChP->TxCompare[2] = 0;
+   ChP->TxCompare[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxCompare[0]);
+
+   ChP->TxReplace1[0] = (Byte_t)(ChOff + _TXREP1B1);
+   ChP->TxReplace1[1] = (Byte_t)((ChOff + _TXREP1B1) >> 8);
+   ChP->TxReplace1[2] = 0;
+   ChP->TxReplace1[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace1[0]);
+
+   ChP->TxReplace2[0] = (Byte_t)(ChOff + _TXREP2);
+   ChP->TxReplace2[1] = (Byte_t)((ChOff + _TXREP2) >> 8);
+   ChP->TxReplace2[2] = 0;
+   ChP->TxReplace2[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace2[0]);
+
+   ChP->TxFIFOPtrs = ChOff + _TXF_OUTP;
+   ChP->TxFIFO = ChOff + _TX_FIFO;
+
+   sOutB(ChP->Cmd,(Byte_t)ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */
+   sOutB(ChP->Cmd,(Byte_t)ChanNum);  /* remove reset Tx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */
+   sOutW(ChP->IndexData,0);
+   ChP->RxFIFOPtrs = ChOff + _RXF_OUTP;
+   ChP->RxFIFO = ChOff + _RX_FIFO;
+
+   sOutB(ChP->Cmd,(Byte_t)ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */
+   sOutB(ChP->Cmd,(Byte_t)ChanNum);  /* remove reset Rx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */
+   sOutW(ChP->IndexData,0);
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */
+   sOutW(ChP->IndexData,0);
+   ChP->TxPrioCnt = ChOff + _TXP_CNT;
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioCnt);
+   sOutB(ChP->IndexData,0);
+   ChP->TxPrioPtr = ChOff + _TXP_PNTR;
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioPtr);
+   sOutB(ChP->IndexData,0);
+   ChP->TxPrioBuf = ChOff + _TXP_BUF;
+   sEnRxProcessor(ChP);                /* start the Rx processor */
+
+   return(TRUE);
+}
+
+/***************************************************************************
+Function: sStopRxProcessor
+Purpose:  Stop the receive processor from processing a channel.
+Call:     sStopRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+
+Comments: The receive processor can be started again with sStartRxProcessor().
+          This function causes the receive processor to skip over the
+          stopped channel.  It does not stop it from processing other channels.
+
+Warnings: No context switches are allowed while executing this function.
+
+          Do not leave the receive processor stopped for more than one
+          character time.
+
+          After calling this function a delay of 4 uS is required to ensure
+          that the receive processor is no longer processing this channel.
+*/
+void sStopRxProcessor(CHANNEL_T *ChP)
+{
+   Byte_t R[4];
+
+   R[0] = ChP->R[0];
+   R[1] = ChP->R[1];
+   R[2] = 0x0a;
+   R[3] = ChP->R[3];
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&R[0]);
+}
+
+/***************************************************************************
+Function: sFlushRxFIFO
+Purpose:  Flush the Rx FIFO
+Call:     sFlushRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   void
+Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
+          while it is being flushed the receive processor is stopped
+          and the transmitter is disabled.  After these operations a
+          4 uS delay is done before clearing the pointers to allow
+          the receive processor to stop.  These items are handled inside
+          this function.
+Warnings: No context switches are allowed while executing this function.
+*/
+void sFlushRxFIFO(CHANNEL_T *ChP)
+{
+   int i;
+   Byte_t Ch;                   /* channel number within AIOP */
+   int RxFIFOEnabled;                  /* TRUE if Rx FIFO enabled */
+
+   if(sGetRxCnt(ChP) == 0)             /* Rx FIFO empty */
+      return;                          /* don't need to flush */
+
+   RxFIFOEnabled = FALSE;
+   if(ChP->R[0x32] == 0x08) /* Rx FIFO is enabled */
+   {
+      RxFIFOEnabled = TRUE;
+      sDisRxFIFO(ChP);                 /* disable it */
+      for(i=0; i < 2000/200; i++)	/* delay 2 uS to allow proc to disable FIFO*/
+         sInB(ChP->IntChan);		/* depends on bus i/o timing */
+   }
+   sGetChanStatus(ChP);          /* clear any pending Rx errors in chan stat */
+   Ch = (Byte_t)sGetChanNum(ChP);
+   sOutB(ChP->Cmd,Ch | RESRXFCNT);     /* apply reset Rx FIFO count */
+   sOutB(ChP->Cmd,Ch);                 /* remove reset Rx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */
+   sOutW(ChP->IndexData,0);
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */
+   sOutW(ChP->IndexData,0);
+   if(RxFIFOEnabled)
+      sEnRxFIFO(ChP);                  /* enable Rx FIFO */
+}
+
+/***************************************************************************
+Function: sFlushTxFIFO
+Purpose:  Flush the Tx FIFO
+Call:     sFlushTxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   void
+Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
+          while it is being flushed the receive processor is stopped
+          and the transmitter is disabled.  After these operations a
+          4 uS delay is done before clearing the pointers to allow
+          the receive processor to stop.  These items are handled inside
+          this function.
+Warnings: No context switches are allowed while executing this function.
+*/
+void sFlushTxFIFO(CHANNEL_T *ChP)
+{
+   int i;
+   Byte_t Ch;                   /* channel number within AIOP */
+   int TxEnabled;                      /* TRUE if transmitter enabled */
+
+   if(sGetTxCnt(ChP) == 0)             /* Tx FIFO empty */
+      return;                          /* don't need to flush */
+
+   TxEnabled = FALSE;
+   if(ChP->TxControl[3] & TX_ENABLE)
+   {
+      TxEnabled = TRUE;
+      sDisTransmit(ChP);               /* disable transmitter */
+   }
+   sStopRxProcessor(ChP);              /* stop Rx processor */
+   for(i = 0; i < 4000/200; i++)         /* delay 4 uS to allow proc to stop */
+      sInB(ChP->IntChan);	/* depends on bus i/o timing */
+   Ch = (Byte_t)sGetChanNum(ChP);
+   sOutB(ChP->Cmd,Ch | RESTXFCNT);     /* apply reset Tx FIFO count */
+   sOutB(ChP->Cmd,Ch);                 /* remove reset Tx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */
+   sOutW(ChP->IndexData,0);
+   if(TxEnabled)
+      sEnTransmit(ChP);                /* enable transmitter */
+   sStartRxProcessor(ChP);             /* restart Rx processor */
+}
+
+/***************************************************************************
+Function: sWriteTxPrioByte
+Purpose:  Write a byte of priority transmit data to a channel
+Call:     sWriteTxPrioByte(ChP,Data)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Data; The transmit data byte
+
+Return:   int: 1 if the bytes is successfully written, otherwise 0.
+
+Comments: The priority byte is transmitted before any data in the Tx FIFO.
+
+Warnings: No context switches are allowed while executing this function.
+*/
+int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data)
+{
+   Byte_t DWBuf[4];             /* buffer for double word writes */
+   Word_t *WordPtr;          /* must be far because Win SS != DS */
+   register DWordIO_t IndexAddr;
+
+   if(sGetTxCnt(ChP) > 1)              /* write it to Tx priority buffer */
+   {
+      IndexAddr = ChP->IndexAddr;
+      sOutW((WordIO_t)IndexAddr,ChP->TxPrioCnt); /* get priority buffer status */
+      if(sInB((ByteIO_t)ChP->IndexData) & PRI_PEND) /* priority buffer busy */
+         return(0);                    /* nothing sent */
+
+      WordPtr = (Word_t *)(&DWBuf[0]);
+      *WordPtr = ChP->TxPrioBuf;       /* data byte address */
+
+      DWBuf[2] = Data;                 /* data byte value */
+      sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */
+
+      *WordPtr = ChP->TxPrioCnt;       /* Tx priority count address */
+
+      DWBuf[2] = PRI_PEND + 1;         /* indicate 1 byte pending */
+      DWBuf[3] = 0;                    /* priority buffer pointer */
+      sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */
+   }
+   else                                /* write it to Tx FIFO */
+   {
+      sWriteTxByte(sGetTxRxDataIO(ChP),Data);
+   }
+   return(1);                          /* 1 byte sent */
+}
+
+/***************************************************************************
+Function: sEnInterrupts
+Purpose:  Enable one or more interrupts for a channel
+Call:     sEnInterrupts(ChP,Flags)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Flags: Interrupt enable flags, can be any combination
+             of the following flags:
+                TXINT_EN:   Interrupt on Tx FIFO empty
+                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
+                            sSetRxTrigger())
+                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
+                MCINT_EN:   Interrupt on modem input change
+                CHANINT_EN: Allow channel interrupt signal to the AIOP's
+                            Interrupt Channel Register.
+Return:   void
+Comments: If an interrupt enable flag is set in Flags, that interrupt will be
+          enabled.  If an interrupt enable flag is not set in Flags, that
+          interrupt will not be changed.  Interrupts can be disabled with
+          function sDisInterrupts().
+
+          This function sets the appropriate bit for the channel in the AIOP's
+          Interrupt Mask Register if the CHANINT_EN flag is set.  This allows
+          this channel's bit to be set in the AIOP's Interrupt Channel Register.
+
+          Interrupts must also be globally enabled before channel interrupts
+          will be passed on to the host.  This is done with function
+          sEnGlobalInt().
+
+          In some cases it may be desirable to disable interrupts globally but
+          enable channel interrupts.  This would allow the global interrupt
+          status register to be used to determine which AIOPs need service.
+*/
+void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags)
+{
+   Byte_t Mask;                 /* Interrupt Mask Register */
+
+   ChP->RxControl[2] |=
+      ((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
+
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]);
+
+   ChP->TxControl[2] |= ((Byte_t)Flags & TXINT_EN);
+
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]);
+
+   if(Flags & CHANINT_EN)
+   {
+      Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum];
+      sOutB(ChP->IntMask,Mask);
+   }
+}
+
+/***************************************************************************
+Function: sDisInterrupts
+Purpose:  Disable one or more interrupts for a channel
+Call:     sDisInterrupts(ChP,Flags)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Flags: Interrupt flags, can be any combination
+             of the following flags:
+                TXINT_EN:   Interrupt on Tx FIFO empty
+                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
+                            sSetRxTrigger())
+                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
+                MCINT_EN:   Interrupt on modem input change
+                CHANINT_EN: Disable channel interrupt signal to the
+                            AIOP's Interrupt Channel Register.
+Return:   void
+Comments: If an interrupt flag is set in Flags, that interrupt will be
+          disabled.  If an interrupt flag is not set in Flags, that
+          interrupt will not be changed.  Interrupts can be enabled with
+          function sEnInterrupts().
+
+          This function clears the appropriate bit for the channel in the AIOP's
+          Interrupt Mask Register if the CHANINT_EN flag is set.  This blocks
+          this channel's bit from being set in the AIOP's Interrupt Channel
+          Register.
+*/
+void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags)
+{
+   Byte_t Mask;                 /* Interrupt Mask Register */
+
+   ChP->RxControl[2] &=
+         ~((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]);
+   ChP->TxControl[2] &= ~((Byte_t)Flags & TXINT_EN);
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]);
+
+   if(Flags & CHANINT_EN)
+   {
+      Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum];
+      sOutB(ChP->IntMask,Mask);
+   }
+}
diff -u --recursive --new-file v2.1.46/linux/drivers/char/rocket_int.h linux/drivers/char/rocket_int.h
--- v2.1.46/linux/drivers/char/rocket_int.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/rocket_int.h	Mon Jul 21 12:41:21 1997
@@ -0,0 +1,1149 @@
+/*
+ * rocket_int.h --- internal header file for rocket.c
+ *
+ * Written by Theodore Ts'o, Copyright 1997.
+ *
+ * Portions of this file are....
+ * 
+ * Copyright 1994 Comtrol Corporation.    All Rights Reserved.
+ * 
+ * The following source code is subject to Comtrol Corporation's
+ * Developer's License Agreement.
+ * 
+ * This source code is protected by United States copyright law and 
+ * international copyright treaties.
+ * 
+ * This source code may only be used to develop software products that
+ * will operate with Comtrol brand hardware.
+ * 
+ * You may not reproduce nor distribute this source code in its original
+ * form but must produce a derivative work which includes portions of
+ * this source code only.
+ * 
+ * The portions of this source code which you use in your derivative
+ * work must bear Comtrol's copyright notice:
+ * 
+ * 		Copyright 1994 Comtrol Corporation.
+ * 
+ */
+
+/*
+ * Begin Comtrol-provided headers, et. al.
+ */
+
+/*
+	user definitions for Rocket Toolkit
+
+	The following typedefs and defines must be established
+	depending on the platform the toolkit is being used
+	with.
+
+*/
+
+/************************************************************
+The following sets up the world for use with Linux
+************************************************************/
+
+#include <asm/io.h>
+
+typedef unsigned char Byte_t;
+typedef unsigned int ByteIO_t;
+
+typedef unsigned int Word_t;
+typedef unsigned int WordIO_t;
+
+typedef unsigned long DWord_t;
+typedef unsigned int DWordIO_t;
+
+#define sOutB(a, b) outb_p(b, a)
+#define sOutW(a, b) outw_p(b, a)
+#define sOutDW(a, b) outl_p(b, a)
+#define sInB(a) (inb_p(a))
+#define sInW(a) (inw_p(a))
+
+#define sOutStrW(port, addr, count) outsw(port, addr, count)
+
+#define sInStrW(port, addr, count) insw(port, addr, count)
+	
+#define CTL_SIZE 4
+#define AIOP_CTL_SIZE 4
+#define CHAN_AIOP_SIZE 8
+#define MAX_PORTS_PER_AIOP 8
+#define MAX_AIOPS_PER_BOARD 4
+#define MAX_PORTS_PER_BOARD 32
+
+/* Bus type ID */
+#define	isISA	0
+#define	isPCI	1
+#define	isMC	2
+
+/* Controller ID numbers */
+#define CTLID_NULL  -1              /* no controller exists */
+#define CTLID_0001  0x0001          /* controller release 1 */
+
+/* AIOP ID numbers, identifies AIOP type implementing channel */
+#define AIOPID_NULL -1              /* no AIOP or channel exists */
+#define AIOPID_0001 0x0001          /* AIOP release 1 */
+
+#define NULLDEV -1                  /* identifies non-existant device */
+#define NULLCTL -1                  /* identifies non-existant controller */
+#define NULLCTLPTR (CONTROLLER_T *)0 /* identifies non-existant controller */
+#define NULLAIOP -1                 /* identifies non-existant AIOP */
+#define NULLCHAN -1                 /* identifies non-existant channel */
+
+/************************************************************************
+ Global Register Offsets - Direct Access - Fixed values
+************************************************************************/
+
+#define _CMD_REG   0x38   /* Command Register            8    Write */
+#define _INT_CHAN  0x39   /* Interrupt Channel Register  8    Read */
+#define _INT_MASK  0x3A   /* Interrupt Mask Register     8    Read / Write */
+#define _UNUSED    0x3B   /* Unused                      8 */
+#define _INDX_ADDR 0x3C   /* Index Register Address      16   Write */
+#define _INDX_DATA 0x3E   /* Index Register Data         8/16 Read / Write */
+
+/************************************************************************
+ Channel Register Offsets for 1st channel in AIOP - Direct Access
+************************************************************************/
+#define _TD0       0x00  /* Transmit Data               16   Write */
+#define _RD0       0x00  /* Receive Data                16   Read */
+#define _CHN_STAT0 0x20  /* Channel Status              8/16 Read / Write */
+#define _FIFO_CNT0 0x10  /* Transmit/Receive FIFO Count 16   Read */
+#define _INT_ID0   0x30  /* Interrupt Identification    8    Read */
+
+/************************************************************************
+ Tx Control Register Offsets - Indexed - External - Fixed
+************************************************************************/
+#define _TX_ENBLS  0x980    /* Tx Processor Enables Register 8 Read / Write */
+#define _TXCMP1    0x988    /* Transmit Compare Value #1     8 Read / Write */
+#define _TXCMP2    0x989    /* Transmit Compare Value #2     8 Read / Write */
+#define _TXREP1B1  0x98A    /* Tx Replace Value #1 - Byte 1  8 Read / Write */
+#define _TXREP1B2  0x98B    /* Tx Replace Value #1 - Byte 2  8 Read / Write */
+#define _TXREP2    0x98C    /* Transmit Replace Value #2     8 Read / Write */
+
+/************************************************************************
+Memory Controller Register Offsets - Indexed - External - Fixed
+************************************************************************/
+#define _RX_FIFO    0x000    /* Rx FIFO */
+#define _TX_FIFO    0x800    /* Tx FIFO */
+#define _RXF_OUTP   0x990    /* Rx FIFO OUT pointer        16 Read / Write */
+#define _RXF_INP    0x992    /* Rx FIFO IN pointer         16 Read / Write */
+#define _TXF_OUTP   0x994    /* Tx FIFO OUT pointer        8  Read / Write */
+#define _TXF_INP    0x995    /* Tx FIFO IN pointer         8  Read / Write */
+#define _TXP_CNT    0x996    /* Tx Priority Count          8  Read / Write */
+#define _TXP_PNTR   0x997    /* Tx Priority Pointer        8  Read / Write */
+
+#define PRI_PEND    0x80     /* Priority data pending (bit7, Tx pri cnt) */
+#define TXFIFO_SIZE 255      /* size of Tx FIFO */
+#define RXFIFO_SIZE 1023     /* size of Rx FIFO */
+
+/************************************************************************
+Tx Priority Buffer - Indexed - External - Fixed
+************************************************************************/
+#define _TXP_BUF    0x9C0    /* Tx Priority Buffer  32  Bytes   Read / Write */
+#define TXP_SIZE    0x20     /* 32 bytes */
+
+/************************************************************************
+Channel Register Offsets - Indexed - Internal - Fixed
+************************************************************************/
+
+#define _TX_CTRL    0xFF0    /* Transmit Control               16  Write */
+#define _RX_CTRL    0xFF2    /* Receive Control                 8  Write */
+#define _BAUD       0xFF4    /* Baud Rate                      16  Write */
+#define _CLK_PRE    0xFF6    /* Clock Prescaler                 8  Write */
+
+#if 0
+#define CLOCK_PRESC 0x14          /* ?????? new mod 4 (divide by 5) prescale */
+
+#define BRD50             9215
+#define BRD75             6143  
+#define BRD110            4188
+#define BRD134            3438
+#define BRD150            3071
+#define BRD200            2303
+#define BRD300            1535
+#define BRD600            767
+#define BRD1200           383
+#define BRD1800           255
+#define BRD2000           229
+#define BRD2400           191
+#define BRD3600           127
+#define BRD4800           95
+#define BRD7200           63
+#define BRD9600           47
+#define BRD14400          31 
+#define BRD19200          23
+#define BRD38400          11
+#define BRD57600          7
+#define BRD76800          5
+#define BRD115200         3
+#define BRD230400         1
+#define BRD460800	  0
+#endif
+
+#if 0
+
+/* Old clock prescale definition and baud rates associated with it */
+
+#define CLOCK_PRESC 0x19  */        /* mod 9 (divide by 10) prescale */
+#define BRD50             4607
+#define BRD75             3071
+#define BRD110            2094
+#define BRD134            1712
+#define BRD150            1535
+#define BRD200            1151
+#define BRD300            767
+#define BRD600            383
+#define BRD1200           191
+#define BRD1800           127
+#define BRD2000           114
+#define BRD2400           95
+#define BRD3600           64
+#define BRD4800           47
+#define BRD7200           31
+#define BRD9600           23
+#define BRD14400          15
+#define BRD19200          11
+#define BRD38400          5
+#define BRD57600          3
+#define BRD76800          2
+#define BRD115200         1
+#define BRD230400         0
+
+#endif
+
+#define STMBREAK   0x08        /* BREAK */
+#define STMFRAME   0x04        /* framing error */
+#define STMRCVROVR 0x02        /* receiver over run error */
+#define STMPARITY  0x01        /* parity error */
+#define STMERROR   (STMBREAK | STMFRAME | STMPARITY)
+#define STMBREAKH   0x800      /* BREAK */
+#define STMFRAMEH   0x400      /* framing error */
+#define STMRCVROVRH 0x200      /* receiver over run error */
+#define STMPARITYH  0x100      /* parity error */
+#define STMERRORH   (STMBREAKH | STMFRAMEH | STMPARITYH)
+
+#define CTS_ACT   0x20        /* CTS input asserted */
+#define DSR_ACT   0x10        /* DSR input asserted */
+#define CD_ACT    0x08        /* CD input asserted */
+#define TXFIFOMT  0x04        /* Tx FIFO is empty */
+#define TXSHRMT   0x02        /* Tx shift register is empty */
+#define RDA       0x01        /* Rx data available */
+#define DRAINED (TXFIFOMT | TXSHRMT)  /* indicates Tx is drained */
+
+#define STATMODE  0x8000      /* status mode enable bit */
+#define RXFOVERFL 0x2000      /* receive FIFO overflow */
+#define RX2MATCH  0x1000      /* receive compare byte 2 match */
+#define RX1MATCH  0x0800      /* receive compare byte 1 match */
+#define RXBREAK   0x0400      /* received BREAK */
+#define RXFRAME   0x0200      /* received framing error */
+#define RXPARITY  0x0100      /* received parity error */
+#define STATERROR (RXBREAK | RXFRAME | RXPARITY)
+
+#define CTSFC_EN  0x80        /* CTS flow control enable bit */
+#define RTSTOG_EN 0x40        /* RTS toggle enable bit */
+#define TXINT_EN  0x10        /* transmit interrupt enable */
+#define STOP2     0x08        /* enable 2 stop bits (0 = 1 stop) */
+#define PARITY_EN 0x04        /* enable parity (0 = no parity) */
+#define EVEN_PAR  0x02        /* even parity (0 = odd parity) */
+#define DATA8BIT  0x01        /* 8 bit data (0 = 7 bit data) */
+
+#define SETBREAK  0x10        /* send break condition (must clear) */
+#define LOCALLOOP 0x08        /* local loopback set for test */
+#define SET_DTR   0x04        /* assert DTR */
+#define SET_RTS   0x02        /* assert RTS */
+#define TX_ENABLE 0x01        /* enable transmitter */
+
+#define RTSFC_EN  0x40        /* RTS flow control enable */
+#define RXPROC_EN 0x20        /* receive processor enable */
+#define TRIG_NO   0x00        /* Rx FIFO trigger level 0 (no trigger) */
+#define TRIG_1    0x08        /* trigger level 1 char */
+#define TRIG_1_2  0x10        /* trigger level 1/2 */
+#define TRIG_7_8  0x18        /* trigger level 7/8 */
+#define TRIG_MASK 0x18        /* trigger level mask */
+#define SRCINT_EN 0x04        /* special Rx condition interrupt enable */
+#define RXINT_EN  0x02        /* Rx interrupt enable */
+#define MCINT_EN  0x01        /* modem change interrupt enable */
+
+#define RXF_TRIG  0x20        /* Rx FIFO trigger level interrupt */
+#define TXFIFO_MT 0x10        /* Tx FIFO empty interrupt */
+#define SRC_INT   0x08        /* special receive condition interrupt */
+#define DELTA_CD  0x04        /* CD change interrupt */
+#define DELTA_CTS 0x02        /* CTS change interrupt */
+#define DELTA_DSR 0x01        /* DSR change interrupt */
+
+#define REP1W2_EN 0x10        /* replace byte 1 with 2 bytes enable */
+#define IGN2_EN   0x08        /* ignore byte 2 enable */
+#define IGN1_EN   0x04        /* ignore byte 1 enable */
+#define COMP2_EN  0x02        /* compare byte 2 enable */
+#define COMP1_EN  0x01        /* compare byte 1 enable */
+
+#define RESET_ALL 0x80        /* reset AIOP (all channels) */
+#define TXOVERIDE 0x40        /* Transmit software off override */
+#define RESETUART 0x20        /* reset channel's UART */
+#define RESTXFCNT 0x10        /* reset channel's Tx FIFO count register */
+#define RESRXFCNT 0x08        /* reset channel's Rx FIFO count register */
+
+#define INTSTAT0  0x01        /* AIOP 0 interrupt status */
+#define INTSTAT1  0x02        /* AIOP 1 interrupt status */
+#define INTSTAT2  0x04        /* AIOP 2 interrupt status */
+#define INTSTAT3  0x08        /* AIOP 3 interrupt status */
+
+#define INTR_EN   0x08        /* allow interrupts to host */
+#define INT_STROB 0x04        /* strobe and clear interrupt line (EOI) */
+
+/**************************************************************************
+ MUDBAC remapped for PCI
+**************************************************************************/
+
+#define _CFG_INT_PCI  0x40
+#define _PCI_INT_FUNC 0x3A
+
+#define PCI_STROB 0x2000        /* bit 13 of int aiop register */
+#define INTR_EN_PCI   0x0010        /* allow interrupts to host */
+
+
+#define CHAN3_EN  0x08        /* enable AIOP 3 */
+#define CHAN2_EN  0x04        /* enable AIOP 2 */
+#define CHAN1_EN  0x02        /* enable AIOP 1 */
+#define CHAN0_EN  0x01        /* enable AIOP 0 */
+#define FREQ_DIS  0x00
+#define FREQ_274HZ 0x60
+#define FREQ_137HZ 0x50
+#define FREQ_69HZ  0x40
+#define FREQ_34HZ  0x30
+#define FREQ_17HZ  0x20
+#define FREQ_9HZ   0x10
+#define PERIODIC_ONLY 0x80    /* only PERIODIC interrupt */
+
+#define CHANINT_EN 0x0100           /* flags to enable/disable channel ints */
+
+#define RDATASIZE 72
+#define RREGDATASIZE 52
+
+/* Controller level information structure */
+typedef struct
+{
+	int		CtlID;
+	int		CtlNum;
+	int		BusType;
+	WordIO_t	PCIIO;
+	ByteIO_t	MBaseIO;
+	ByteIO_t	MReg1IO;
+	ByteIO_t	MReg2IO;
+	ByteIO_t	MReg3IO;
+	Byte_t		MReg2;
+	Byte_t		MReg3;
+	int		NumAiop;
+	WordIO_t	AiopIO[AIOP_CTL_SIZE];
+	ByteIO_t	AiopIntChanIO[AIOP_CTL_SIZE];
+	int		AiopID[AIOP_CTL_SIZE];
+	int		AiopNumChan[AIOP_CTL_SIZE];
+} CONTROLLER_T;
+
+typedef CONTROLLER_T CONTROLLER_t;
+
+/* Channel level information structure */
+typedef struct
+{
+	CONTROLLER_T	*CtlP;
+	int		AiopNum;
+	int		ChanID;
+	int		ChanNum;
+
+	ByteIO_t	Cmd;
+	ByteIO_t	IntChan;
+	ByteIO_t	IntMask;
+	DWordIO_t	IndexAddr;
+	WordIO_t	IndexData;
+
+	WordIO_t	TxRxData;
+	WordIO_t	ChanStat;
+	WordIO_t	TxRxCount;
+	ByteIO_t	IntID;
+
+	Word_t		TxFIFO;
+	Word_t		TxFIFOPtrs;
+	Word_t		RxFIFO;
+	Word_t		RxFIFOPtrs;
+	Word_t		TxPrioCnt;
+	Word_t		TxPrioPtr;
+	Word_t		TxPrioBuf;
+
+	Byte_t		R[RREGDATASIZE];
+
+	Byte_t		BaudDiv[4];
+	Byte_t		TxControl[4];
+	Byte_t		RxControl[4];
+	Byte_t		TxEnables[4];
+	Byte_t		TxCompare[4];
+	Byte_t		TxReplace1[4];
+	Byte_t		TxReplace2[4];
+} CHANNEL_T;
+
+typedef CHANNEL_T CHANNEL_t;
+typedef CHANNEL_T * CHANPTR_T;
+
+/***************************************************************************
+Function: sClrBreak
+Purpose:  Stop sending a transmit BREAK signal
+Call:     sClrBreak(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrBreak(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~SETBREAK; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sClrDTR
+Purpose:  Clr the DTR output
+Call:     sClrDTR(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrDTR(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~SET_DTR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sClrRTS
+Purpose:  Clr the RTS output
+Call:     sClrRTS(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrRTS(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~SET_RTS; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sClrTxXOFF
+Purpose:  Clear any existing transmit software flow control off condition
+Call:     sClrTxXOFF(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrTxXOFF(ChP) \
+{ \
+   sOutB((ChP)->Cmd,TXOVERIDE | (Byte_t)(ChP)->ChanNum); \
+   sOutB((ChP)->Cmd,(Byte_t)(ChP)->ChanNum); \
+}
+
+/***************************************************************************
+Function: sCtlNumToCtlPtr
+Purpose:  Convert a controller number to controller structure pointer
+Call:     sCtlNumToCtlPtr(CtlNum)
+          int CtlNum; Controller number
+Return:   CONTROLLER_T *: Ptr to controller structure
+*/
+#define sCtlNumToCtlPtr(CTLNUM) &sController[CTLNUM]
+
+/***************************************************************************
+Function: sControllerEOI
+Purpose:  Strobe the MUDBAC's End Of Interrupt bit.
+Call:     sControllerEOI(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+*/
+#define sControllerEOI(CTLP) sOutB((CTLP)->MReg2IO,(CTLP)->MReg2 | INT_STROB)
+
+/***************************************************************************
+Function: sPCIControllerEOI
+Purpose:  Strobe the PCI End Of Interrupt bit.
+Call:     sPCIControllerEOI(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+*/
+#define sPCIControllerEOI(CTLP) sOutW((CTLP)->PCIIO, PCI_STROB)
+
+/***************************************************************************
+Function: sDisAiop
+Purpose:  Disable I/O access to an AIOP
+Call:     sDisAiop(CltP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; Number of AIOP on controller
+*/
+#define sDisAiop(CTLP,AIOPNUM) \
+{ \
+   (CTLP)->MReg3 &= sBitMapClrTbl[AIOPNUM]; \
+   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
+}
+
+/***************************************************************************
+Function: sDisCTSFlowCtl
+Purpose:  Disable output flow control using CTS
+Call:     sDisCTSFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisCTSFlowCtl(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~CTSFC_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: DisParity
+Purpose:  Disable parity
+Call:     sDisParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+*/
+#define sDisParity(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~PARITY_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sDisRxFIFO
+Purpose:  Disable Rx FIFO
+Call:     sDisRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisRxFIFO(ChP) \
+{ \
+   (ChP)->R[0x32] = 0x0a; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x30]); \
+}
+
+/***************************************************************************
+Function: sDisRxStatusMode
+Purpose:  Disable the Rx status mode
+Call:     sDisRxStatusMode(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This takes the channel out of the receive status mode.  All
+          subsequent reads of receive data using sReadRxWord() will return
+          two data bytes.
+*/
+#define sDisRxStatusMode(ChP) sOutW((ChP)->ChanStat,0)
+
+/***************************************************************************
+Function: sDisTransmit
+Purpose:  Disable transmit
+Call:     sDisTransmit(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+          This disables movement of Tx data from the Tx FIFO into the 1 byte
+          Tx buffer.  Therefore there could be up to a 2 byte latency
+          between the time sDisTransmit() is called and the transmit buffer
+          and transmit shift register going completely empty.
+*/
+#define sDisTransmit(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~TX_ENABLE; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sDisTxSoftFlowCtl
+Purpose:  Disable Tx Software Flow Control
+Call:     sDisTxSoftFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisTxSoftFlowCtl(ChP) \
+{ \
+   (ChP)->R[0x06] = 0x8a; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x04]); \
+}
+
+/***************************************************************************
+Function: sEnAiop
+Purpose:  Enable I/O access to an AIOP
+Call:     sEnAiop(CltP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; Number of AIOP on controller
+*/
+#define sEnAiop(CTLP,AIOPNUM) \
+{ \
+   (CTLP)->MReg3 |= sBitMapSetTbl[AIOPNUM]; \
+   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
+}
+
+/***************************************************************************
+Function: sEnCTSFlowCtl
+Purpose:  Enable output flow control using CTS
+Call:     sEnCTSFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnCTSFlowCtl(ChP) \
+{ \
+   (ChP)->TxControl[2] |= CTSFC_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: EnParity
+Purpose:  Enable parity
+Call:     sEnParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: Before enabling parity odd or even parity should be chosen using
+          functions sSetOddParity() or sSetEvenParity().
+*/
+#define sEnParity(ChP) \
+{ \
+   (ChP)->TxControl[2] |= PARITY_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sEnRxFIFO
+Purpose:  Enable Rx FIFO
+Call:     sEnRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnRxFIFO(ChP) \
+{ \
+   (ChP)->R[0x32] = 0x08; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x30]); \
+}
+
+/***************************************************************************
+Function: sEnRxProcessor
+Purpose:  Enable the receive processor
+Call:     sEnRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This function is used to start the receive processor.  When
+          the channel is in the reset state the receive processor is not
+          running.  This is done to prevent the receive processor from
+          executing invalid microcode instructions prior to the
+          downloading of the microcode.
+
+Warnings: This function must be called after valid microcode has been
+          downloaded to the AIOP, and it must not be called before the
+          microcode has been downloaded.
+*/
+#define sEnRxProcessor(ChP) \
+{ \
+   (ChP)->RxControl[2] |= RXPROC_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->RxControl[0]); \
+}
+
+/***************************************************************************
+Function: sEnRxStatusMode
+Purpose:  Enable the Rx status mode
+Call:     sEnRxStatusMode(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This places the channel in the receive status mode.  All subsequent
+          reads of receive data using sReadRxWord() will return a data byte
+          in the low word and a status byte in the high word.
+
+*/
+#define sEnRxStatusMode(ChP) sOutW((ChP)->ChanStat,STATMODE)
+
+/***************************************************************************
+Function: sEnTransmit
+Purpose:  Enable transmit
+Call:     sEnTransmit(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnTransmit(ChP) \
+{ \
+   (ChP)->TxControl[3] |= TX_ENABLE; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sGetAiopIntStatus
+Purpose:  Get the AIOP interrupt status
+Call:     sGetAiopIntStatus(CtlP,AiopNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; AIOP number
+Return:   Byte_t: The AIOP interrupt status.  Bits 0 through 7
+                         represent channels 0 through 7 respectively.  If a
+                         bit is set that channel is interrupting.
+*/
+#define sGetAiopIntStatus(CTLP,AIOPNUM) sInB((CTLP)->AiopIntChanIO[AIOPNUM])
+
+/***************************************************************************
+Function: sGetAiopNumChan
+Purpose:  Get the number of channels supported by an AIOP
+Call:     sGetAiopNumChan(CtlP,AiopNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; AIOP number
+Return:   int: The number of channels supported by the AIOP
+*/
+#define sGetAiopNumChan(CTLP,AIOPNUM) (CTLP)->AiopNumChan[AIOPNUM]
+
+/***************************************************************************
+Function: sGetChanIntID
+Purpose:  Get a channel's interrupt identification byte
+Call:     sGetChanIntID(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The channel interrupt ID.  Can be any
+             combination of the following flags:
+                RXF_TRIG:     Rx FIFO trigger level interrupt
+                TXFIFO_MT:    Tx FIFO empty interrupt
+                SRC_INT:      Special receive condition interrupt
+                DELTA_CD:     CD change interrupt
+                DELTA_CTS:    CTS change interrupt
+                DELTA_DSR:    DSR change interrupt
+*/
+#define sGetChanIntID(ChP) (sInB((ChP)->IntID) & (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR))
+
+/***************************************************************************
+Function: sGetChanNum
+Purpose:  Get the number of a channel within an AIOP
+Call:     sGetChanNum(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   int: Channel number within AIOP, or NULLCHAN if channel does
+               not exist.
+*/
+#define sGetChanNum(ChP) (ChP)->ChanNum
+
+/***************************************************************************
+Function: sGetChanStatus
+Purpose:  Get the channel status
+Call:     sGetChanStatus(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Word_t: The channel status.  Can be any combination of
+             the following flags:
+                LOW BYTE FLAGS
+                CTS_ACT:      CTS input asserted
+                DSR_ACT:      DSR input asserted
+                CD_ACT:       CD input asserted
+                TXFIFOMT:     Tx FIFO is empty
+                TXSHRMT:      Tx shift register is empty
+                RDA:          Rx data available
+
+                HIGH BYTE FLAGS
+                STATMODE:     status mode enable bit
+                RXFOVERFL:    receive FIFO overflow
+                RX2MATCH:     receive compare byte 2 match
+                RX1MATCH:     receive compare byte 1 match
+                RXBREAK:      received BREAK
+                RXFRAME:      received framing error
+                RXPARITY:     received parity error
+Warnings: This function will clear the high byte flags in the Channel
+          Status Register.
+*/
+#define sGetChanStatus(ChP) sInW((ChP)->ChanStat)
+
+/***************************************************************************
+Function: sGetChanStatusLo
+Purpose:  Get the low byte only of the channel status
+Call:     sGetChanStatusLo(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The channel status low byte.  Can be any combination
+             of the following flags:
+                CTS_ACT:      CTS input asserted
+                DSR_ACT:      DSR input asserted
+                CD_ACT:       CD input asserted
+                TXFIFOMT:     Tx FIFO is empty
+                TXSHRMT:      Tx shift register is empty
+                RDA:          Rx data available
+*/
+#define sGetChanStatusLo(ChP) sInB((ByteIO_t)(ChP)->ChanStat)
+
+/***************************************************************************
+Function: sGetControllerIntStatus
+Purpose:  Get the controller interrupt status
+Call:     sGetControllerIntStatus(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+Return:   Byte_t: The controller interrupt status in the lower 4
+                         bits.  Bits 0 through 3 represent AIOP's 0
+                         through 3 respectively.  If a bit is set that
+                         AIOP is interrupting.  Bits 4 through 7 will
+                         always be cleared.
+*/
+#define sGetControllerIntStatus(CTLP) (sInB((CTLP)->MReg1IO) & 0x0f)
+
+/***************************************************************************
+Function: sPCIGetControllerIntStatus
+Purpose:  Get the controller interrupt status
+Call:     sPCIGetControllerIntStatus(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+Return:   unsigned char: The controller interrupt status in the lower 4
+                         bits and bit 4.  Bits 0 through 3 represent AIOP's 0
+                         through 3 respectively. Bit 4 is set if the int 
+			 was generated from periodic. If a bit is set the
+			 AIOP is interrupting.
+*/
+#define sPCIGetControllerIntStatus(CTLP) ((sInW((CTLP)->PCIIO) >> 8) & 0x1f)
+
+/***************************************************************************
+
+Function: sGetRxCnt
+Purpose:  Get the number of data bytes in the Rx FIFO
+Call:     sGetRxCnt(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   int: The number of data bytes in the Rx FIFO.
+Comments: Byte read of count register is required to obtain Rx count.
+
+*/
+#define sGetRxCnt(ChP) sInW((ChP)->TxRxCount)
+
+/***************************************************************************
+Function: sGetTxCnt
+Purpose:  Get the number of data bytes in the Tx FIFO
+Call:     sGetTxCnt(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The number of data bytes in the Tx FIFO.
+Comments: Byte read of count register is required to obtain Tx count.
+
+*/
+#define sGetTxCnt(ChP) sInB((ByteIO_t)(ChP)->TxRxCount)
+
+/*****************************************************************************
+Function: sGetTxRxDataIO
+Purpose:  Get the I/O address of a channel's TxRx Data register
+Call:     sGetTxRxDataIO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   WordIO_t: I/O address of a channel's TxRx Data register
+*/
+#define sGetTxRxDataIO(ChP) (ChP)->TxRxData
+
+/***************************************************************************
+Function: sInitChanDefaults
+Purpose:  Initialize a channel structure to it's default state.
+Call:     sInitChanDefaults(ChP)
+          CHANNEL_T *ChP; Ptr to the channel structure
+Comments: This function must be called once for every channel structure
+          that exists before any other SSCI calls can be made.
+
+*/
+#define sInitChanDefaults(ChP) \
+{ \
+   (ChP)->CtlP = NULLCTLPTR; \
+   (ChP)->AiopNum = NULLAIOP; \
+   (ChP)->ChanID = AIOPID_NULL; \
+   (ChP)->ChanNum = NULLCHAN; \
+}
+
+/***************************************************************************
+Function: sResetAiopByNum
+Purpose:  Reset the AIOP by number
+Call:     sResetAiopByNum(CTLP,AIOPNUM)
+	CONTROLLER_T CTLP; Ptr to controller structure
+	AIOPNUM; AIOP index 
+*/
+#define sResetAiopByNum(CTLP,AIOPNUM) \
+{ \
+   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,RESET_ALL); \
+   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,0x0); \
+}
+
+/***************************************************************************
+Function: sSendBreak
+Purpose:  Send a transmit BREAK signal
+Call:     sSendBreak(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSendBreak(ChP) \
+{ \
+   (ChP)->TxControl[3] |= SETBREAK; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetBaud
+Purpose:  Set baud rate
+Call:     sSetBaud(ChP,Divisor)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Divisor; 16 bit baud rate divisor for channel
+*/
+#define sSetBaud(ChP,DIVISOR) \
+{ \
+   (ChP)->BaudDiv[2] = (Byte_t)(DIVISOR); \
+   (ChP)->BaudDiv[3] = (Byte_t)((DIVISOR) >> 8); \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->BaudDiv[0]); \
+}
+
+/***************************************************************************
+Function: sSetData7
+Purpose:  Set data bits to 7
+Call:     sSetData7(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetData7(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~DATA8BIT; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetData8
+Purpose:  Set data bits to 8
+Call:     sSetData8(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetData8(ChP) \
+{ \
+   (ChP)->TxControl[2] |= DATA8BIT; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetDTR
+Purpose:  Set the DTR output
+Call:     sSetDTR(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetDTR(ChP) \
+{ \
+   (ChP)->TxControl[3] |= SET_DTR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetEvenParity
+Purpose:  Set even parity
+Call:     sSetEvenParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: This function has no effect unless parity is enabled with function
+          sEnParity().
+*/
+#define sSetEvenParity(ChP) \
+{ \
+   (ChP)->TxControl[2] |= EVEN_PAR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetOddParity
+Purpose:  Set odd parity
+Call:     sSetOddParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: This function has no effect unless parity is enabled with function
+          sEnParity().
+*/
+#define sSetOddParity(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~EVEN_PAR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetRTS
+Purpose:  Set the RTS output
+Call:     sSetRTS(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetRTS(ChP) \
+{ \
+   (ChP)->TxControl[3] |= SET_RTS; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetRxTrigger
+Purpose:  Set the Rx FIFO trigger level
+Call:     sSetRxProcessor(ChP,Level)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Level; Number of characters in Rx FIFO at which the
+             interrupt will be generated.  Can be any of the following flags:
+
+             TRIG_NO:   no trigger
+             TRIG_1:    1 character in FIFO
+             TRIG_1_2:  FIFO 1/2 full
+             TRIG_7_8:  FIFO 7/8 full
+Comments: An interrupt will be generated when the trigger level is reached
+          only if function sEnInterrupt() has been called with flag
+          RXINT_EN set.  The RXF_TRIG flag in the Interrupt Idenfification
+          register will be set whenever the trigger level is reached
+          regardless of the setting of RXINT_EN.
+
+*/
+#define sSetRxTrigger(ChP,LEVEL) \
+{ \
+   (ChP)->RxControl[2] &= ~TRIG_MASK; \
+   (ChP)->RxControl[2] |= LEVEL; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->RxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetStop1
+Purpose:  Set stop bits to 1
+Call:     sSetStop1(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetStop1(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~STOP2; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetStop2
+Purpose:  Set stop bits to 2
+Call:     sSetStop2(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetStop2(ChP) \
+{ \
+   (ChP)->TxControl[2] |= STOP2; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sStartRxProcessor
+Purpose:  Start a channel's receive processor
+Call:     sStartRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This function is used to start a Rx processor after it was
+          stopped with sStopRxProcessor() or sStopSWInFlowCtl().  It
+          will restart both the Rx processor and software input flow control.
+
+*/
+#define sStartRxProcessor(ChP) sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0])
+
+/***************************************************************************
+Function: sWriteTxByte
+Purpose:  Write a transmit data byte to a channel.
+          ByteIO_t io: Channel transmit register I/O address.  This can
+                           be obtained with sGetTxRxDataIO().
+          Byte_t Data; The transmit data byte.
+Warnings: This function writes the data byte without checking to see if
+          sMaxTxSize is exceeded in the Tx FIFO.
+*/
+#define sWriteTxByte(IO,DATA) sOutB(IO,DATA)
+
+int sInitController(	CONTROLLER_T *CtlP,
+			int CtlNum,
+			ByteIO_t MudbacIO,
+			ByteIO_t *AiopIOList,
+			int AiopIOListSize,
+			int IRQNum,
+			Byte_t Frequency,
+			int PeriodicOnly);
+
+int sPCIInitController(	CONTROLLER_T *CtlP,
+			int CtlNum,
+			ByteIO_t *AiopIOList,
+			int AiopIOListSize,
+			int IRQNum,
+			Byte_t Frequency,
+			int PeriodicOnly);
+
+int sReadAiopID(ByteIO_t io);
+int sReadAiopNumChan(WordIO_t io);
+int sInitChan(	CONTROLLER_T *CtlP,
+		CHANNEL_T *ChP,
+		int AiopNum,
+		int ChanNum);
+Byte_t sGetRxErrStatus(CHANNEL_T *ChP);
+void sStopRxProcessor(CHANNEL_T *ChP);
+void sStopSWInFlowCtl(CHANNEL_T *ChP);
+void sFlushRxFIFO(CHANNEL_T *ChP);
+void sFlushTxFIFO(CHANNEL_T *ChP);
+int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data);
+void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags);
+void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags);
+
+extern Byte_t R[RDATASIZE];
+extern CONTROLLER_T sController[CTL_SIZE];
+extern Byte_t sIRQMap[16];
+extern Byte_t sBitMapClrTbl[8];
+extern Byte_t sBitMapSetTbl[8];
+extern int sClockPrescale;
+
+
+/*
+ * Begin Linux specific definitions for the Rocketport driver
+ *
+ * This code is Copyright Theodore Ts'o, 1995-1997
+ */
+
+struct r_port {
+	int			magic;
+	int			line;
+	int			flags;
+	int			count;
+	int			blocked_open;
+	struct tty_struct 	*tty;
+	int			board:2;
+	int			aiop:2;
+	int			chan:3;
+	CONTROLLER_t *ctlp;
+	CHANNEL_t 		channel;
+	int			closing_wait;
+	int			close_delay;
+	int			intmask;
+	int			xmit_fifo_room;	/* room in xmit fifo */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	int			session;
+	int			pgrp;
+	int			cd_status;
+	int		        ignore_status_mask;
+	int			read_status_mask;
+	int			cps;
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct tq_struct	tqueue;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+};
+	
+#define RPORT_MAGIC 0x525001
+
+#define NUM_BOARDS 8
+#define MAX_RP_PORTS (32*NUM_BOARDS)
+
+/*
+ * The size of the xmit buffer is 1 page, or 4096 bytes
+ */
+#define XMIT_BUF_SIZE 4096
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Internal flags used only by the rocketport driver */
+#define ROCKET_INITIALIZED	0x80000000 /* Port is active */
+#define ROCKET_CLOSING		0x40000000 /* Serial port is closing */
+#define ROCKET_NORMAL_ACTIVE	0x20000000 /* Normal port is active */
+#define ROCKET_CALLOUT_ACTIVE	0x10000000 /* Callout port is active */
+
+/*
+ * tty subtypes
+ *
+ */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/*
+ * Assigned major numbers for the Comtrol Rocketport
+ */
+#define TTY_ROCKET_MAJOR	46
+#define CUA_ROCKET_MAJOR	47
+
+/*
+ * Utility function.
+ */
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef PCI_VENDOR_ID_RP
+#undef PCI_VENDOR_ID_RP
+#undef PCI_DEVICE_ID_RP8OCTA
+#undef PCI_DEVICE_ID_RP8INTF
+#undef PCI_DEVICE_ID_RP16INTF
+#undef PCI_DEVICE_ID_RP32INTF
+#endif
+
+#define PCI_VENDOR_ID_RP               0x11fe
+#define PCI_DEVICE_ID_RP32INTF         0x0001
+#define PCI_DEVICE_ID_RP8INTF          0x0002
+#define PCI_DEVICE_ID_RP16INTF         0x0003
+#define PCI_DEVICE_ID_RP8OCTA          0x0005
+
+#ifndef RP4QUAD
+#define PCI_DEVICE_ID_RP4QUAD	       0x0004
+#endif
+#ifndef RP8J
+#define PCI_DEVICE_ID_RP8J	       0x0006
+#endif
+
+
diff -u --recursive --new-file v2.1.46/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.46/linux/drivers/char/tty_io.c	Thu Jul 17 10:06:04 1997
+++ linux/drivers/char/tty_io.c	Mon Jul 21 14:12:35 1997
@@ -1829,6 +1829,9 @@
 #ifdef CONFIG_SERIAL
 	rs_init();
 #endif
+#ifdef CONFIG_ROCKETPORT
+	rp_init();
+#endif
 #ifdef CONFIG_CYCLADES
 	cy_init();
 #endif
@@ -1853,4 +1856,3 @@
 #endif
 	return 0;
 }
-
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/BUGS-parport linux/drivers/misc/BUGS-parport
--- v2.1.46/linux/drivers/misc/BUGS-parport	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/BUGS-parport	Wed Jul 23 11:07:12 1997
@@ -0,0 +1,13 @@
+Currently known (or at least suspected) bugs in parport:
+
+o /proc/parport is untested under 2.0.XX
+
+o SCSI aborts for PPA under 2.0.29 [reported by jmr].  Under investigation.
+
+o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
+  the resulting kernel won't link.
+
+o IEEE1284 code does not do the terminating handshake after transfers, which
+  seems to upset some devices.
+
+o lp doesn't allow you to read status while printing is in progress.
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/Makefile linux/drivers/misc/Makefile
--- v2.1.46/linux/drivers/misc/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/Makefile	Wed Jul 23 11:07:12 1997
@@ -0,0 +1,48 @@
+#
+# Makefile for the kernel miscellaneous drivers.
+#
+# 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 definitions are now inherited from the
+# parent makes..
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := misc.a
+MX_OBJS  :=
+LX_OBJS  := 
+MI_OBJS  :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PARPORT),y)
+  L_OBJS += parport_share.o parport_ieee1284.o
+  ifeq ($(CONFIG_PROC_FS),y)
+    L_OBJS += parport_procfs.o
+  endif
+  ifeq ($(CONFIG_PARPORT_PC),y)
+    LX_OBJS += parport_pc.o
+  endif
+  LX_OBJS += parport_init.o
+else
+  ifeq ($(CONFIG_PARPORT),m)
+    MI_OBJS += parport_share.o parport_ieee1284.o
+    ifneq ($(CONFIG_PROC_FS),n) 
+      MI_OBJS += parport_procfs.o
+    endif
+    MIX_OBJS += parport_init.o
+    M_OBJS += parport.o
+  endif
+  ifeq ($(CONFIG_PARPORT_PC),m)
+    MX_OBJS += parport_pc.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+# Special rule to build the composite parport.o module
+parport.o: $(MI_OBJS) $(MIX_OBJS)
+	$(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/TODO-parport linux/drivers/misc/TODO-parport
--- v2.1.46/linux/drivers/misc/TODO-parport	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/TODO-parport	Wed Jul 23 11:07:12 1997
@@ -0,0 +1,21 @@
+Things to be done.
+
+0. Fix the bugs (see BUGS-parport).
+
+1. Proper documentation.
+
+2. Overhaul lp.c:
+
+   a) It's a mess, and there is a lot of code duplication.
+
+   b) ECP support would be nice.  This can only work if both the port and
+      the printer support it.
+
+   c) Errors could do with being handled better.  There's no point logging a
+      message every 10 seconds when the printer is out of paper. 
+
+   d) Handle status readback automatically.  IEEE1284 printers can post status
+      bits when they have something to say.  We should read out and deal 
+      with (maybe just log) whatever the printer wants to tell the world.
+
+3. Assimilate more drivers.
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/parport_arc.c linux/drivers/misc/parport_arc.c
--- v2.1.46/linux/drivers/misc/parport_arc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/parport_arc.c	Wed Jul 23 11:07:12 1997
@@ -0,0 +1,77 @@
+/* $Id$
+ * Parallel-port routines for ARC onboard hardware.
+ *
+ * Author: Phil Blundell <pjb27@cam.ac.uk>
+ */
+
+#include <linux/tasks.h>
+
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/parport.h>
+
+#include <linux/arch/oldlatches.h>
+
+#define DATA_LATCH    0x3350010
+
+/* ARC can't read from the data latch, so we must use a soft copy. */
+static unsigned int data_copy;
+
+static void arc_write_data(struct parport *p, unsigned int data)
+{
+	data_copy = data;
+	outb(data, DATA_LATCH);
+}
+
+static unsigned int arc_read_data(struct parport *p)
+{
+	return data_copy;
+}
+
+static struct parport_operations arc_ops = 
+{
+	arc_write_data,
+	arc_read_data,
+
+	arc_write_control,
+	arc_read_control,
+	arc_frob_control,
+
+	NULL, /* write_econtrol */
+	NULL, /* read_econtrol */
+	NULL, /* frob_econtrol */
+
+	arc_write_status,
+	arc_read_status,
+
+	NULL, /* write_fifo */
+	NULL, /* read_fifo */
+	
+	NULL, /* change_mode */
+	
+	arc_release_resources,
+	arc_claim_resources,
+	
+	NULL, /* epp_write_block */
+	NULL, /* epp_read_block */
+
+	NULL, /* ecp_write_block */
+	NULL, /* epp_write_block */
+	
+	arc_save_state,
+	arc_restore_state,
+
+	arc_enable_irq,
+	arc_disable_irq,
+	arc_examine_irq 
+};
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/parport_ieee1284.c linux/drivers/misc/parport_ieee1284.c
--- v2.1.46/linux/drivers/misc/parport_ieee1284.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/parport_ieee1284.c	Wed Jul 23 11:07:59 1997
@@ -0,0 +1,89 @@
+/* $Id$ 
+ * IEEE-1284 implementation for parport.
+ *
+ * Authors: Philip Blundell <pjb27@cam.ac.uk>
+ *          Carsten Gross <carsten@sol.wohnheim.uni-ulm.de>
+ *	    Jose Renau <renau@acm.org>
+ */
+
+#include <linux/tasks.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+/* The following read functions are an implementation of a status readback
+ * and device id request confirming to IEEE1284-1994.
+ *
+ * These probably ought to go in some seperate file, so people like the SPARC
+ * don't have to pull them in.
+ */
+
+/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
+ * 25 for this. After this time we can create a timeout because the
+ * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
+ * waiting a maximum time of 500 us busy (this is for speed). If there is
+ * not the right answer in this time, we call schedule and other processes
+ * are able "to eat" the time up to 30ms.  So the maximum load avarage can't
+ * get above 5% for a read even if the peripheral is really slow. (but your
+ * read gets very slow then - only about 10 characters per second. This
+ * should be tuneable). Thanks to Andreas who pointed me to this and ordered
+ * the documentation.
+ */ 
+
+int parport_wait_peripheral(struct parport *port, unsigned char mask, 
+	unsigned char result)
+{
+	int counter=0;
+	unsigned char status; 
+	
+	do {
+		status = parport_read_status(port);
+		udelay(25);
+		counter++;
+		if (need_resched)
+			schedule();
+	} while ( ((status & mask) != result) && (counter < 20) );
+	if ( (counter == 20) && ((status & mask) != result) ) { 
+		current->state=TASK_INTERRUPTIBLE;
+		current->timeout=jiffies+4;
+		schedule(); /* wait for 4 scheduler runs (40ms) */
+		status = parport_read_status(port);
+		if ((status & mask) != result) return 1; /* timeout */
+	}
+	return 0; /* okay right response from device */
+}		
+
+/* Test if nibble mode for status readback is okay. Returns the value false
+ * if the printer doesn't support readback at all. If it supports readbacks
+ * and printer data is available the function returns 1, otherwise 2. The
+ * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
+ * 4 is for "request device id using nibble mode". The request for the
+ * device id is best done in an ioctl (or at bootup time).  There is no
+ * check for an invalid value, the only function using this call at the
+ * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
+ * trusted kernel.
+ */
+int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) 
+{
+	parport_write_data(port, mode);
+	udelay(5);		
+	parport_write_control(port, parport_read_control(port) & ~8);  /* SelectIN  low */
+	parport_write_control(port, parport_read_control(port) | 2); /* AutoFeed  high */
+	if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
+		parport_write_control(port, (parport_read_control(port) & ~2) | 8);
+		return 0; /* first stage of negotiation failed, 
+                           * no IEEE1284 compliant device on this port 
+                           */ 
+	}
+	parport_write_control(port, parport_read_control(port) | 1);      /* Strobe  high */
+	udelay(5);				     /* Strobe wait */
+	parport_write_control(port, parport_read_control(port) & ~1);     /* Strobe  low */
+	udelay(5);
+	parport_write_control(port, parport_read_control(port) & ~2);     /*  AutoFeed low */
+	return (parport_wait_peripheral(port, 0x20, 0))?2:1;
+}
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/parport_init.c linux/drivers/misc/parport_init.c
--- v2.1.46/linux/drivers/misc/parport_init.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/parport_init.c	Wed Jul 23 11:07:59 1997
@@ -0,0 +1,114 @@
+/* $Id: parport_init.c,v 1.1.2.2 1997/04/18 15:00:52 phil Exp $
+ * Parallel-port initialisation code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *	    Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tasks.h>
+
+#include <linux/parport.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#ifndef MODULE
+static int io[PARPORT_MAX+1] __initdata = { 0, };
+static int irq[PARPORT_MAX] __initdata = { PARPORT_IRQ_NONE, };
+static int dma[PARPORT_MAX] __initdata = { PARPORT_DMA_NONE, };
+
+extern int parport_pc_init(int *io, int *irq, int *dma);
+
+static int parport_setup_ptr __initdata = 0;
+
+__initfunc(void parport_setup(char *str, int *ints))
+{
+	if (ints[0] == 0 || ints[1] == 0) {
+		/* Disable parport if "parport=" or "parport=0" in cmdline */
+		io[0] = PARPORT_DISABLE; 
+		return;
+	}
+	if (parport_setup_ptr < PARPORT_MAX) {
+		io[parport_setup_ptr] = ints[1];
+		if (ints[0]>1) {
+			irq[parport_setup_ptr] = ints[2];
+			if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
+		}
+		parport_setup_ptr++;
+	} else {
+		printk(KERN_ERR "parport=0x%x", ints[1]);
+		if (ints[0]>1) {
+			printk(",%d", ints[2]);
+			if (ints[0]>2) printk(",%d", ints[3]);
+		}
+		printk(" ignored, too many ports.\n");
+	}
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+{
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	struct parport *port, *next;
+   
+	for (port = parport_enumerate(); port; port = next) {
+		next = port->next;
+		if (!(port->flags & PARPORT_FLAG_COMA))
+			parport_quiesce(port);
+		parport_proc_unregister(port);
+		kfree(port->name);
+		kfree(port);
+	}
+	
+	parport_proc_cleanup();
+}
+#else
+__initfunc(int parport_init(void))
+{
+	struct parport *pb;
+
+	if (io[0] == PARPORT_DISABLE) return 1; 
+#ifdef CONFIG_PARPORT_PC
+	parport_pc_init(io, irq, dma);
+#endif
+	return 0;
+}
+#endif
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(parport_claim);
+EXPORT_SYMBOL(parport_release);
+EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_quiesce);
+EXPORT_SYMBOL(parport_register_device);
+EXPORT_SYMBOL(parport_unregister_device);
+EXPORT_SYMBOL(parport_enumerate);
+EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
+EXPORT_SYMBOL(parport_wait_peripheral);
+
+void inc_parport_count(void)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void dec_parport_count(void)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.46/linux/drivers/misc/parport_pc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/parport_pc.c	Wed Jul 23 11:14:30 1997
@@ -0,0 +1,885 @@
+/* $Id: parport_pc.c,v 1.1.2.3 1997/04/18 15:00:52 phil Exp $ 
+ * Parallel-port routines for PC architecture
+ * 
+ * Authors: Phil Blundell <pjb27@cam.ac.uk>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *	    Jose Renau <renau@acm.org>
+ *          David Campbell <campbell@tirian.che.curtin.edu.au>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/parport.h>
+
+#define ECONTROL 0x402
+#define CONFIGB  0x401
+#define CONFIGA  0x400
+#define EPPREG   0x4
+#define CONTROL  0x2
+#define STATUS   0x1
+#define DATA     0
+
+#define PC_MAX_PORTS  8
+
+static void pc_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* NULL function - Does nothing */
+	return;
+}
+
+static void pc_write_epp(struct parport *p, unsigned int d)
+{
+	outb(d, p->base+EPPREG);
+}
+
+static unsigned int pc_read_epp(struct parport *p)
+{
+	return (unsigned int)inb(p->base+EPPREG);
+}
+
+static unsigned int pc_read_configb(struct parport *p)
+{
+	return (unsigned int)inb(p->base+CONFIGB);
+}
+
+static void pc_write_data(struct parport *p, unsigned int d)
+{
+	outb(d, p->base+DATA);
+}
+
+static unsigned int pc_read_data(struct parport *p)
+{
+	return (unsigned int)inb(p->base+DATA);
+}
+
+static void pc_write_control(struct parport *p, unsigned int d)
+{
+	outb(d, p->base+CONTROL);
+}
+
+static unsigned int pc_read_control(struct parport *p)
+{
+	return (unsigned int)inb(p->base+CONTROL);
+}
+
+static unsigned int pc_frob_control(struct parport *p, unsigned int mask,  unsigned int val)
+{
+	unsigned int old = (unsigned int)inb(p->base+CONTROL);
+	outb(((old & ~mask) ^ val), p->base+CONTROL);
+	return old;
+}
+
+static void pc_write_status(struct parport *p, unsigned int d)
+{
+	outb(d, p->base+STATUS);
+}
+
+static unsigned int pc_read_status(struct parport *p)
+{
+	return (unsigned int)inb(p->base+STATUS);
+}
+
+static void pc_write_econtrol(struct parport *p, unsigned int d)
+{
+	outb(d, p->base+ECONTROL);
+}
+
+static unsigned int pc_read_econtrol(struct parport *p)
+{
+	return (unsigned int)inb(p->base+ECONTROL);
+}
+
+static unsigned int pc_frob_econtrol(struct parport *p, unsigned int mask,  unsigned int val)
+{
+	unsigned int old = (unsigned int)inb(p->base+ECONTROL);
+	outb(((old & ~mask) ^ val), p->base+ECONTROL);
+	return old;
+}
+
+static void pc_change_mode(struct parport *p, int m)
+{
+	/* FIXME */
+}
+
+static void pc_write_fifo(struct parport *p, unsigned int v)
+{
+	/* FIXME */
+}
+
+static unsigned int pc_read_fifo(struct parport *p)
+{
+	return 0; /* FIXME */
+}
+
+static void pc_disable_irq(struct parport *p)
+{
+	/* FIXME */
+}
+
+static void pc_enable_irq(struct parport *p)
+{
+	/* FIXME */
+}
+
+static void pc_release_resources(struct parport *p)
+{
+	if (p->irq != PARPORT_IRQ_NONE)
+		free_irq(p->irq, NULL);
+	release_region(p->base, p->size);
+	if (p->modes & PARPORT_MODE_PCECR)
+		release_region(p->base+0x400, 3);
+}
+
+static int pc_claim_resources(struct parport *p)
+{
+	/* FIXME check that resources are free */
+	if (p->irq != PARPORT_IRQ_NONE)
+		request_irq(p->irq, pc_null_intr_func, 0, p->name, NULL);
+	request_region(p->base, p->size, p->name);
+	if (p->modes & PARPORT_MODE_PCECR)
+		request_region(p->base+0x400, 3, p->name);
+	return 0;
+}
+
+static void pc_save_state(struct parport *p, struct parport_state *s)
+{
+	/* FIXME */
+}
+
+static void pc_restore_state(struct parport *p, struct parport_state *s)
+{
+	/* FIXME */
+}
+
+static unsigned int pc_epp_read_block(struct parport *p, void *buf, unsigned  int length)
+{
+	return 0; /* FIXME */
+}
+
+static unsigned int pc_epp_write_block(struct parport *p, void *buf, unsigned  int length)
+{
+	return 0; /* FIXME */
+}
+
+static unsigned int pc_ecp_read_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle)
+{
+	return 0; /* FIXME */
+}
+
+static unsigned int pc_ecp_write_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle)
+{
+	return 0; /* FIXME */
+}
+
+static int pc_examine_irq(struct parport *p)
+{
+	return 0; /* FIXME */
+}
+
+static struct parport_operations pc_ops = 
+{
+	pc_write_data,
+	pc_read_data,
+
+	pc_write_control,
+	pc_read_control,
+	pc_frob_control,
+
+	pc_write_econtrol,
+	pc_read_econtrol,
+	pc_frob_econtrol,
+
+	pc_write_status,
+	pc_read_status,
+
+	pc_write_fifo,
+	pc_read_fifo,
+	
+	pc_change_mode,
+	
+	pc_release_resources,
+	pc_claim_resources,
+	
+	pc_epp_write_block,
+	pc_epp_read_block,
+
+	pc_ecp_write_block,
+	pc_ecp_read_block,
+	
+	pc_save_state,
+	pc_restore_state,
+
+	pc_enable_irq,
+	pc_disable_irq,
+	pc_examine_irq 
+};
+
+/******************************************************
+ *  DMA detection section:
+ */
+
+/*
+ * Prepare DMA channels from 0-8 to transmit towards buffer
+ */
+static int parport_prepare_dma(char *buff, int size)
+{
+	int tmp = 0;
+	int i,retv;
+	
+	for (i = 0; i < 8; i++) {
+		retv = request_dma(i, "probe");
+		if (retv)
+			continue;
+		tmp |= 1 << i;
+
+		cli();
+		disable_dma(i);
+		clear_dma_ff(i);
+		set_dma_addr(i, virt_to_bus(buff));
+		set_dma_count(i, size);
+		set_dma_mode(i, DMA_MODE_READ);
+		sti();
+	}
+
+	return tmp;
+}
+
+/*
+ * Activate all DMA channels passed in dma
+ */
+static int parport_enable_dma(int dma)
+{
+	int i;
+	
+	for (i = 0; i < 8; i++)
+		if (dma & (1 << i)) {
+		cli();
+		enable_dma(i);
+		sti();
+	    }
+
+	return dma;
+}
+
+static int parport_detect_dma_transfer(int dma, int size)
+{
+	int i,n,retv;
+	int count=0;
+
+	retv = PARPORT_DMA_NONE;
+	for (i = 0; i < 8; i++)
+		if (dma & (1 << i)) {
+			disable_dma(i);
+			clear_dma_ff(i);
+			n = get_dma_residue(i);
+			if (n != size) {
+				retv = i;
+				if (count > 0) {
+					retv = PARPORT_DMA_NONE; /* Multiple DMA's */
+					printk(KERN_ERR "parport: multiple DMA detected.  Huh?\n");
+				}
+				count++;
+			}
+			free_dma(i);
+		}
+
+	return retv;	
+}
+
+/* Only if supports ECP mode */
+static int programmable_dma_support(struct parport *pb)
+{
+	int dma, oldstate = pc_read_econtrol(pb);
+
+	pc_write_econtrol(pb, 0xe0); /* Configuration MODE */
+	
+	dma = pc_read_configb(pb) & 0x07;
+
+	pc_write_econtrol(pb, oldstate);
+	
+	if (dma == 0 || dma == 4) /* Jumper selection */
+		return PARPORT_DMA_NONE;
+	else
+		return dma;
+}
+
+/* Only called if port supports ECP mode.
+ *
+ * The only restriction on DMA channels is that it has to be
+ * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
+ * considered a shared resource and hence they should be registered
+ * when needed and then immediately unregistered.
+ *
+ * DMA autoprobes for ECP mode are known not to work for some
+ * main board BIOS configs. I had to remove everything from the
+ * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
+ * and reboot again, then I got IRQ probes and DMA probes to work.
+ * [Is the BIOS doing a device detection?]
+ *
+ * A value of PARPORT_DMA_NONE is allowed indicating no DMA support.
+ *
+ * if( 0 < DMA < 4 )
+ *    1Byte DMA transfer
+ * else // 4 < DMA < 8
+ *    2Byte DMA transfer
+ *
+ */
+static int parport_dma_probe(struct parport *pb)
+{
+	int dma,retv;
+	int dsr,dsr_read;
+	char *buff;
+
+	retv = programmable_dma_support(pb);
+	if (retv != PARPORT_DMA_NONE)
+		return retv;
+	
+	if (!(buff = kmalloc(2048, GFP_KERNEL | GFP_DMA))) {
+	    printk(KERN_ERR "parport: memory squeeze\n");
+	    return PARPORT_DMA_NONE;
+	}
+	
+ 	dsr = pb->ops->read_control(pb);
+	dsr_read = (dsr & ~(0x20)) | 0x04;    /* Direction == read */
+
+	pb->ops->write_econtrol(pb, 0xc0);	   /* ECP MODE */
+ 	pb->ops->write_control(pb, dsr_read );
+	dma = parport_prepare_dma(buff, 1000);
+	pb->ops->write_econtrol(pb, 0xd8);	   /* ECP FIFO + enable DMA */
+	parport_enable_dma(dma);
+	udelay(500);           /* Give some for DMA tranfer */
+	retv = parport_detect_dma_transfer(dma, 1000);
+	
+	/*
+	 * National Semiconductors only supports DMA tranfers
+	 * in ECP MODE
+	 */
+	if (retv == PARPORT_DMA_NONE) {
+		pb->ops->write_econtrol(pb, 0x60);	   /* ECP MODE */
+		pb->ops->write_control(pb, dsr_read );
+		dma=parport_prepare_dma(buff,1000);
+		pb->ops->write_econtrol(pb, 0x68);	   /* ECP FIFO + enable DMA */
+		parport_enable_dma(dma);
+		udelay(500);           /* Give some for DMA tranfer */
+		retv = parport_detect_dma_transfer(dma, 1000);
+	}
+	
+	kfree(buff);
+	
+	return retv;
+}
+/******************************************************
+ *  MODE detection section:
+ */
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int epp_clear_timeout(struct parport *pb)
+{
+	int r;
+
+	if (!(pc_read_status(pb) & 0x01))
+		return 1;
+
+	/* To clear timeout some chips require double read */
+	pc_read_status(pb);
+	r = pc_read_status(pb);
+	pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
+	pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
+	r = pc_read_status(pb);
+
+	return !(r & 0x01);
+}
+
+
+/*
+ * Checks for port existence, all ports support SPP MODE
+ */
+static int parport_SPP_supported(struct parport *pb)
+{
+	/* Do a simple read-write test to make sure the port exists. */
+	pc_write_control(pb, 0xc);
+	pc_write_data(pb, 0xaa);
+	if (pc_read_data(pb) != 0xaa) return 0;
+	
+	pc_write_data(pb, 0x55);
+	if (pc_read_data(pb) != 0x55) return 0;
+
+	return PARPORT_MODE_PCSPP;
+}
+
+/* Check for ECP
+ *
+ * Old style XT ports alias io ports every 0x400, hence accessing ECR
+ * on these cards actually accesses the CTR.
+ *
+ * Modern cards don't do this but reading from ECR will return 0xff
+ * regardless of what is written here if the card does NOT support
+ * ECP.
+ *
+ * We will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+	int r, octr = pc_read_control(pb), oecr = pc_read_econtrol(pb);
+
+	r= pc_read_control(pb);	
+	if ((pc_read_econtrol(pb) & 0x03) == (r & 0x03)) {
+		pc_write_control(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
+
+		r= pc_read_control(pb);	
+		if ((pc_read_econtrol(pb) & 0x03) == (r & 0x03))
+			return 0; /* Sure that no ECR register exists */
+	}
+	
+	if ((pc_read_econtrol(pb) & 0x03 ) != 0x01)
+		return 0;
+
+	pc_write_econtrol(pb,0x34);
+	if (pc_read_econtrol(pb) != 0x35)
+		return 0;
+
+	pc_write_econtrol(pb, oecr);
+	pc_write_control(pb, octr);
+	
+	return PARPORT_MODE_PCECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+	int i, oecr = pc_read_econtrol(pb);
+	
+	/* If there is no ECR, we have no hope of supporting ECP. */
+	if (!(pb->modes & PARPORT_MODE_PCECR))
+		return 0;
+
+	/*
+	 * Using LGS chipset it uses ECR register, but
+	 * it doesn't support ECP or FIFO MODE
+	 */
+	
+	pc_write_econtrol(pb, 0xc0); /* TEST FIFO */
+	for (i=0; i < 1024 && (pc_read_econtrol(pb) & 0x01); i++)
+		pc_write_fifo(pb, 0xaa);
+
+	pc_write_econtrol(pb, oecr);
+	return (i==1024)?0:PARPORT_MODE_PCECP;
+}
+
+/* EPP mode detection
+ * Theory:
+ *	Bit 0 of STR is the EPP timeout bit, this bit is 0
+ *	when EPP is possible and is set high when an EPP timeout
+ *	occurs (EPP uses the HALT line to stop the CPU while it does
+ *	the byte transfer, an EPP timeout occurs if the attached
+ *	device fails to respond after 10 micro seconds).
+ *
+ *	This bit is cleared by either reading it (National Semi)
+ *	or writing a 1 to the bit (SMC, UMC, WinBond), others ???
+ *	This bit is always high in non EPP modes.
+ */
+static int parport_EPP_supported(struct parport *pb)
+{
+	/* If EPP timeout bit clear then EPP available */
+	if (!epp_clear_timeout(pb))
+		return 0;  /* No way to clear timeout */
+
+	pc_write_control(pb, pc_read_control(pb) | 0x20);
+	pc_write_control(pb, pc_read_control(pb) | 0x10);
+	epp_clear_timeout(pb);
+	
+	pc_read_epp(pb);
+	udelay(30);  /* Wait for possible EPP timeout */
+	
+	if (pc_read_status(pb) & 0x01) {
+		epp_clear_timeout(pb);
+		return PARPORT_MODE_PCEPP;
+	}
+
+	return 0;
+}
+
+static int parport_ECPEPP_supported(struct parport *pb)
+{
+	int mode, oecr = pc_read_econtrol(pb);
+
+	if (!(pb->modes & PARPORT_MODE_PCECR))
+		return 0;
+	
+	/* Search for SMC style EPP+ECP mode */
+	pc_write_econtrol(pb, 0x80);
+	
+	mode = parport_EPP_supported(pb);
+
+	pc_write_econtrol(pb, oecr);
+	
+	return mode?PARPORT_MODE_PCECPEPP:0;
+}
+
+/* Detect PS/2 support.
+ *
+ * Bit 5 (0x20) sets the PS/2 data direction; setting this high
+ * allows us to read data from the data lines.  In theory we would get back
+ * 0xff but any peripheral attached to the port may drag some or all of the
+ * lines down to zero.  So if we get back anything that isn't the contents
+ * of the data register we deem PS/2 support to be present. 
+ *
+ * Some SPP ports have "half PS/2" ability - you can't turn off the line
+ * drivers, but an external peripheral with sufficiently beefy drivers of
+ * its own can overpower them and assert its own levels onto the bus, from
+ * where they can then be read back as normal.  Ports with this property
+ * and the right type of device attached are likely to fail the SPP test,
+ * (as they will appear to have stuck bits) and so the fact that they might
+ * be misdetected here is rather academic. 
+ */
+
+static int parport_PS2_supported(struct parport *pb)
+{
+	int ok = 0, octr = pc_read_control(pb);
+  
+	epp_clear_timeout(pb);
+
+	pc_write_control(pb, octr | 0x20);  /* try to tri-state the buffer */
+	
+	pc_write_data(pb, 0x55);
+	if (pc_read_data(pb) != 0x55) ok++;
+
+	pc_write_data(pb, 0xaa);
+	if (pc_read_data(pb) != 0xaa) ok++;
+	
+	pc_write_control(pb, octr);          /* cancel input mode */
+
+	return ok?PARPORT_MODE_PCPS2:0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+	int mode, oecr = pc_read_econtrol(pb);
+
+	if (!(pb->modes & PARPORT_MODE_PCECR))
+		return 0;
+	
+	pc_write_econtrol(pb, 0x20);
+	
+	mode = parport_PS2_supported(pb);
+
+	pc_write_econtrol(pb, oecr);
+	return mode?PARPORT_MODE_PCECPPS2:0;
+}
+
+/******************************************************
+ *  IRQ detection section:
+ *
+ * This code is for detecting ECP interrupts (due to problems with the
+ * monolithic interrupt probing routines).
+ *
+ * In short this is a voting system where the interrupt with the most
+ * "votes" is the elected interrupt (it SHOULD work...)
+ *
+ * This is horribly x86-specific at the moment.  I'm not convinced it
+ * belongs at all.
+ */
+
+static int intr_vote[16];
+
+static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+	intr_vote[irq]++;
+	return;
+}
+
+static long open_intr_election(void)
+{
+	long tmp = 0;
+	int i;
+
+	/* We ignore the timer - irq 0 */
+	for (i = 1; i < 16; i++) {
+		intr_vote[i] = 0;
+		if (request_irq(i, parport_vote_intr_func,
+		       SA_INTERRUPT, "probe", intr_vote) == 0)
+			tmp |= 1 << i;
+	}
+	return tmp;
+}
+
+static int close_intr_election(long tmp)
+{
+	int irq = PARPORT_IRQ_NONE;
+	int i;
+
+	/* We ignore the timer - irq 0 */
+	for (i = 1; i < 16; i++)
+		if (tmp & (1 << i)) {
+			if (intr_vote[i]) {
+				if (irq != PARPORT_IRQ_NONE)
+					/* More than one interrupt */
+					return PARPORT_IRQ_NONE;
+				irq = i;
+			}
+			free_irq(i, intr_vote);
+		}
+	return irq;
+}
+
+/* Only if supports ECP mode */
+static int programmable_irq_support(struct parport *pb)
+{
+	int irq, oecr = pc_read_econtrol(pb);
+
+	pc_write_econtrol(pb,0xE0); /* Configuration MODE */
+	
+	irq = (pc_read_configb(pb) >> 3) & 0x07;
+
+	switch(irq){
+	case 2:
+		irq = 9;
+		break;
+	case 7:
+		irq = 5;
+		break;
+	case 0:
+		irq = PARPORT_IRQ_NONE;
+		break;
+	default:
+		irq += 7;
+	}
+	
+	pc_write_econtrol(pb, oecr);
+	return irq;
+}
+
+static int irq_probe_ECP(struct parport *pb)
+{
+	int irqs, i, oecr = pc_read_econtrol(pb);
+		
+	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
+	irqs = open_intr_election();
+		
+	pc_write_econtrol(pb, 0x00);	    /* Reset FIFO */
+	pc_write_econtrol(pb, 0xd0);	    /* TEST FIFO + nErrIntrEn */
+
+	/* If Full FIFO sure that WriteIntrThresold is generated */
+	for (i=0; i < 1024 && !(pc_read_econtrol(pb) & 0x02) ; i++) 
+		pc_write_fifo(pb, 0xaa);
+		
+	pb->irq = close_intr_election(irqs);
+	pc_write_econtrol(pb, oecr);
+	return pb->irq;
+}
+
+/*
+ * This detection seems that only works in National Semiconductors
+ * This doesn't work in SMC, LGS, and Winbond 
+ */
+static int irq_probe_EPP(struct parport *pb)
+{
+	int irqs, octr = pc_read_control(pb);
+
+#ifndef ADVANCED_DETECT
+	return PARPORT_IRQ_NONE;
+#endif
+	
+	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
+	irqs = open_intr_election();
+
+	if (pb->modes & PARPORT_MODE_PCECR)
+		pc_write_econtrol(pb, pc_read_econtrol(pb) | 0x10);
+	
+	epp_clear_timeout(pb);
+	pc_write_control(pb, pc_read_control(pb) | 0x20);
+	pc_write_control(pb, pc_read_control(pb) | 0x10);
+	epp_clear_timeout(pb);
+
+	/*  Device isn't expecting an EPP read
+	 * and generates an IRQ.
+	 */
+	pc_read_epp(pb);
+	udelay(20);
+
+	pb->irq = close_intr_election(irqs);
+	pc_write_control(pb, octr);
+	return pb->irq;
+}
+
+static int irq_probe_SPP(struct parport *pb)
+{
+	int irqs, octr = pc_read_control(pb);
+
+#ifndef ADVANCED_DETECT
+	return PARPORT_IRQ_NONE;
+#endif
+
+	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
+	irqs = probe_irq_on();
+
+	if (pb->modes & PARPORT_MODE_PCECR)
+		pc_write_econtrol(pb, 0x10);
+
+	pc_write_data(pb,0x00);
+	pc_write_control(pb,0x00);
+	pc_write_control(pb,0x0c);
+	udelay(5);
+	pc_write_control(pb,0x0d);
+	udelay(5);
+	pc_write_control(pb,0x0c);
+	udelay(25);
+	pc_write_control(pb,0x08);
+	udelay(25);
+	pc_write_control(pb,0x0c);
+	udelay(50);
+
+	pb->irq = probe_irq_off(irqs);
+	if (pb->irq <= 0)
+		pb->irq = PARPORT_IRQ_NONE;	/* No interrupt detected */
+	
+	pc_write_control(pb, octr);
+	return pb->irq;
+}
+
+/* We will attempt to share interrupt requests since other devices
+ * such as sound cards and network cards seem to like using the
+ * printer IRQs.
+ *
+ * When ECP is available we can autoprobe for IRQs.
+ * NOTE: If we can autoprobe it, we can register the IRQ.
+ */
+static int parport_irq_probe(struct parport *pb)
+{
+	if (pb->modes & PARPORT_MODE_PCECR)
+		pb->irq = programmable_irq_support(pb);
+
+	if (pb->modes & PARPORT_MODE_PCECP)
+		pb->irq = irq_probe_ECP(pb);
+			
+	if (pb->irq == PARPORT_IRQ_NONE && 
+	    (pb->modes & PARPORT_MODE_PCECPEPP)) {
+		int oecr = pc_read_econtrol(pb);
+		pc_write_econtrol(pb, 0x80);
+		pb->irq = irq_probe_EPP(pb);
+		pc_write_econtrol(pb, oecr);
+	}
+
+	epp_clear_timeout(pb);
+
+	if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_PCEPP))
+		pb->irq = irq_probe_EPP(pb);
+
+	epp_clear_timeout(pb);
+
+	if (pb->irq == PARPORT_IRQ_NONE)
+		pb->irq = irq_probe_SPP(pb);
+
+	return pb->irq;
+}
+
+static int probe_one_port(unsigned long int base, int irq, int dma)
+{
+	struct parport tmpport, *p;
+	if (check_region(base, 3)) return 0;
+	tmpport.base = base;
+	tmpport.ops = &pc_ops;
+	if (!(parport_SPP_supported(&tmpport))) return 0;
+       	if (!(p = parport_register_port(base, irq, dma, &pc_ops))) return 0;
+	p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p);
+	if (p->base != 0x3bc) {
+		if (!check_region(base+0x400,3)) {
+			p->modes |= parport_ECR_present(p);	
+			p->modes |= parport_ECP_supported(p);
+			p->modes |= parport_ECPPS2_supported(p);
+		}
+		if (!check_region(base+0x3, 5)) {
+			p->modes |= parport_EPP_supported(p);
+			p->modes |= parport_ECPEPP_supported(p);
+		}
+	}
+	p->size = (p->modes & (PARPORT_MODE_PCEPP 
+			       | PARPORT_MODE_PCECPEPP))?8:3;
+	printk(KERN_INFO "%s: PC-style at 0x%x", p->name, p->base);
+	if (p->irq == PARPORT_IRQ_AUTO) {
+		p->irq = PARPORT_IRQ_NONE;
+		parport_irq_probe(p);
+	}
+	if (p->irq != PARPORT_IRQ_NONE)
+		printk(", irq %d", p->irq);
+	if (p->irq != PARPORT_DMA_NONE)
+		printk(", dma %d", p->dma);
+	printk(" [");
+#define printmode(x) {if(p->modes&PARPORT_MODE_PC##x){printk("%s%s",f?",":"",#x);f++;}}
+	{
+		int f = 0;
+		printmode(SPP);
+		printmode(PS2);
+		printmode(EPP);
+		printmode(ECP);
+		printmode(ECPEPP);
+		printmode(ECPPS2);
+	}
+#undef printmode
+	printk("]\n");
+	return 1;
+}
+
+int parport_pc_init(int *io, int *irq, int *dma)
+{
+	int count = 0, i = 0;
+	if (io && *io) {
+		/* Only probe the ports we were given. */
+		do {
+			count += probe_one_port(*(io++), *(irq++), *(dma++));
+		} while (*io && (++i < PC_MAX_PORTS));
+	} else {
+		/* Probe all the likely ports. */
+		count += probe_one_port(0x378, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
+		
+#if defined(__i386__)
+		count += probe_one_port(0x278, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
+		count += probe_one_port(0x3bc, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
+#endif
+	}
+	return count;
+}
+
+#ifdef MODULE
+static int io[PC_MAX_PORTS+1] = { 0, };
+static int dma[PC_MAX_PORTS] = { PARPORT_DMA_AUTO, };
+static int irq[PC_MAX_PORTS] = { PARPORT_IRQ_AUTO, };
+MODULE_PARM(io, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
+
+static int init_module(void)
+{	
+	return (parport_pc_init(NULL, NULL, NULL)?0:1);
+}
+
+static void cleanup_module(void)
+{
+	struct parport *p = parport_enumerate();
+	while (p) {
+		if (p->modes & PARPORT_MODE_PCSPP) { 
+			if (!(p->flags & PARPORT_FLAG_COMA)) 
+				parport_quiesce(p);
+		}
+		p = p->next;
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/parport_procfs.c linux/drivers/misc/parport_procfs.c
--- v2.1.46/linux/drivers/misc/parport_procfs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/parport_procfs.c	Wed Jul 23 11:14:30 1997
@@ -0,0 +1,341 @@
+/* $Id: parport_procfs.c,v 1.1.2.2 1997/04/18 15:00:52 phil Exp $
+ * Parallel port /proc interface code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/proc_fs.h>
+
+#include <linux/parport.h>
+
+#undef PARPORT_INCLUDE_BENCH
+
+struct proc_dir_entry *base=NULL;
+
+extern void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs);
+
+static int irq_write_proc(struct file *file, const char *buffer,
+					  unsigned long count, void *data)
+{
+	int newirq;
+	struct parport *pp = (struct parport *)data;
+	
+	if (count > 4 )  /* more than 4 digits for a irq 0x?? 0?? ??  */
+		return(-EOVERFLOW);
+
+	if (buffer[0] < 32 || !strncmp(buffer, "none", 4)) {
+		newirq = PARPORT_IRQ_NONE;
+	} else {
+		if (buffer[0] == '0') {
+			if( buffer[1] == 'x' )
+				newirq = simple_strtoul(&buffer[2],0,16);
+			else
+				newirq = simple_strtoul(&buffer[1],0,8);
+		} else {
+			newirq = simple_strtoul(buffer,0,10);
+		}
+	}
+
+	if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) 
+		free_irq(pp->irq, pp);
+
+	pp->irq = newirq;
+
+	if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) { 
+		struct pardevice *pd = pp->cad;
+
+		if (pd == NULL) {
+			pd = pp->devices;
+			if (pd != NULL) 
+				request_irq(pp->irq, pd->irq_func ? 
+					    pd->irq_func :
+					    parport_null_intr_func,
+					    SA_INTERRUPT, pd->name, pd->port);
+		} else {
+			request_irq(pp->irq, pd->irq_func ? pd->irq_func :
+				    parport_null_intr_func,
+				    SA_INTERRUPT, pp->name, pd->port);
+		}
+	}
+
+	return count;
+}
+
+static int irq_read_proc(char *page, char **start, off_t off,
+					 int count, int *eof, void *data)
+{
+	struct parport *pp = (struct parport *)data;
+	int len;
+	
+	if (pp->irq == PARPORT_IRQ_NONE)
+		len = sprintf(page, "none\n");
+	else
+		len = sprintf(page, "%d\n", pp->irq);
+	
+	*start = 0;
+	*eof   = 1;
+	return len;
+}
+
+static int devices_read_proc(char *page, char **start, off_t off,
+					 int count, int *eof, void *data)
+{
+	struct parport *pp = (struct parport *)data;
+	struct pardevice *pd1;
+	int len=0;
+
+	for (pd1 = pp->devices; pd1 ; pd1 = pd1->next) {
+		if (pd1 == pp->cad)
+			len += sprintf(page+len, "+");
+		else
+			len += sprintf(page+len, " ");
+
+		len += sprintf(page+len, "%s",pd1->name);
+
+		if (pd1 == pp->lurker)
+			len += sprintf(page+len, " LURK");
+		
+		len += sprintf(page+len,"\n");
+	}
+		
+	*start = 0;
+	*eof   = 1;
+	return len;
+}
+
+static int hardware_read_proc(char *page, char **start, off_t off,
+							  int count, int *eof, void *data)
+{
+	struct parport *pp = (struct parport *)data;
+	int len=0;
+	
+	len += sprintf(page+len, "base:\t0x%x\n",pp->base);
+	if (pp->irq == PARPORT_IRQ_NONE)
+		len += sprintf(page+len, "irq:\tnone\n");
+	else
+		len += sprintf(page+len, "irq:\t%d\n",pp->irq);
+	len += sprintf(page+len, "dma:\t%d\n",pp->dma);
+
+
+#if 0
+	len += sprintf(page+len, "modes:\t");
+	{
+#define printmode(x) {if(pp->modes&PARPORT_MODE_##x){len+=sprintf(page+len,"%s%s",f?",":"",#x);f++;}}
+		int f = 0;
+		printmode(SPP);
+		printmode(PS2);
+		printmode(EPP);
+		printmode(ECP);
+		printmode(ECPEPP);
+		printmode(ECPPS2);
+#undef printmode
+	}
+	len += sprintf(page+len, "\n");
+
+	len += sprintf(page+len, "mode:\t");
+	if (pp->modes & PARPORT_MODE_ECR) {
+		switch (r_ecr(pp) >> 5) {
+		case 0:
+			len += sprintf(page+len, "SPP");
+			if( pp->modes & PARPORT_MODE_PS2 )
+				len += sprintf(page+len, ",PS2");
+			if( pp->modes & PARPORT_MODE_EPP )
+				len += sprintf(page+len, ",EPP");
+			break;
+		case 1:
+			len += sprintf(page+len, "ECPPS2");
+			break;
+		case 2:
+			len += sprintf(page+len, "DATAFIFO");
+			break;
+		case 3:
+			len += sprintf(page+len, "ECP");
+			break;
+		case 4:
+			len += sprintf(page+len, "ECPEPP");
+			break;
+		case 5:
+			len += sprintf(page+len, "Reserved?");
+			break;
+		case 6:
+			len += sprintf(page+len, "TEST");
+			break;
+		case 7:
+			len += sprintf(page+len, "Configuration");
+			break;
+		}
+	} else {
+		len += sprintf(page+len, "SPP");
+		if (pp->modes & PARPORT_MODE_PS2)
+			len += sprintf(page+len, ",PS2");
+		if (pp->modes & PARPORT_MODE_EPP)
+			len += sprintf(page+len, ",EPP");
+	}
+	len += sprintf(page+len, "\n");
+#endif	
+#if 0
+	/* Now no detection, please fix with an external function */
+	len += sprintf(page+len, "chipset:\tunknown\n");
+#endif
+#ifdef PARPORT_INCLUDE_BENCHMARK
+	if (pp->speed)
+		len += sprintf(page+len, "bench:\t%d Bytes/s\n",pp->speed);
+	else
+		len += sprintf(page+len, "bench:\tunknown\n");
+#endif	
+
+	*start = 0;
+	*eof   = 1;
+	return len;
+}
+
+static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode,
+					     struct proc_dir_entry *parent,
+					     unsigned short ino)
+{
+	struct proc_dir_entry *ent;
+
+	ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+	if (!ent)
+		return NULL;
+	memset(ent, 0, sizeof(struct proc_dir_entry));
+	
+	if (mode == S_IFDIR)
+		mode |= S_IRUGO | S_IXUGO;
+	else if (mode == 0)
+		mode = S_IFREG | S_IRUGO;
+
+
+	ent->low_ino = ino;
+	ent->name = name;
+	ent->namelen = strlen(name);
+	ent->mode = mode;
+	if (S_ISDIR(mode)) 
+		ent->nlink = 2;
+	else
+		ent->nlink = 1;
+
+	proc_register(parent, ent);
+	
+	return ent;
+}
+
+
+int parport_proc_init()
+{
+	base = new_proc_entry("parport", S_IFDIR, &proc_root,PROC_PARPORT);
+
+	if (base)
+		return 1;
+	else {
+		printk(KERN_ERR "parport: Error creating proc entry /proc/parport\n");
+		return 0;
+	}
+}
+
+int parport_proc_cleanup()
+{
+	if (base)
+		proc_unregister(&proc_root,base->low_ino);
+
+	base = NULL;
+	
+	return 0;
+}
+
+int parport_proc_register(struct parport *pp)
+{
+	struct proc_dir_entry *ent;
+	static int conta=0;
+	char *name;
+
+	memset(&pp->pdir,0,sizeof(struct parport_dir));
+
+	if (!base) {
+		printk(KERN_ERR "parport: Error entry /proc/parport, not generated?\n");
+		return 1;
+	}
+	
+	name = pp->pdir.name;
+	sprintf(name,"%d",conta++);
+
+	ent = new_proc_entry(name, S_IFDIR, base,0);
+	if (!ent) {
+		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s\n",name);
+		return 1;
+	}
+	pp->pdir.entry = ent;
+
+	ent = new_proc_entry("irq", S_IFREG | S_IRUGO | S_IWUSR, pp->pdir.entry,0);
+	if (!ent) {
+		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/irq\n",name);
+		return 1;
+	}
+	ent->read_proc = irq_read_proc;
+	ent->write_proc= irq_write_proc;
+	ent->data      = pp;
+	pp->pdir.irq   = ent;
+	
+	ent = new_proc_entry("devices", 0, pp->pdir.entry,0);
+	if (!ent) {
+		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/devices\n",name);
+		return 1;
+	}
+	ent->read_proc   = devices_read_proc;
+	ent->data        = pp;
+	pp->pdir.devices = ent;
+	
+	ent = new_proc_entry("hardware", 0, pp->pdir.entry,0);
+	if (!ent) {
+		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/hardware\n",name);
+		return 1;
+	}
+	ent->read_proc    = hardware_read_proc;
+	ent->data         = pp;
+	pp->pdir.hardware = ent;
+	return 0;
+}
+
+int parport_proc_unregister(struct parport *pp)
+{
+	if (pp->pdir.entry) {
+		if (pp->pdir.irq) {
+			proc_unregister(pp->pdir.entry, pp->pdir.irq->low_ino);
+			kfree(pp->pdir.irq);
+		}
+		
+		if (pp->pdir.devices) {
+			proc_unregister(pp->pdir.entry,
+					pp->pdir.devices->low_ino);
+			kfree(pp->pdir.devices);
+		}
+		
+		if (pp->pdir.hardware) {
+			proc_unregister(pp->pdir.entry,
+					pp->pdir.hardware->low_ino);
+			kfree(pp->pdir.hardware);
+		}
+		
+		proc_unregister(base, pp->pdir.entry->low_ino);
+		kfree(pp->pdir.entry);
+	}
+	
+	return 0;
+}
diff -u --recursive --new-file v2.1.46/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.46/linux/drivers/misc/parport_share.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/parport_share.c	Wed Jul 23 11:14:30 1997
@@ -0,0 +1,319 @@
+/* $Id: parport_share.c,v 1.1.2.2 1997/04/18 15:00:52 phil Exp $
+ * Parallel-port resource manager code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *	    Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#undef PARPORT_PARANOID
+
+static struct parport *portlist = NULL, *portlist_tail = NULL;
+static int portcount = 0;
+
+/* Return a list of all the ports we know about. */
+struct parport *parport_enumerate(void)
+{
+	return portlist;
+}
+
+void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* NULL function - Does nothing */
+	return;
+}
+
+struct parport *parport_register_port(unsigned long base, int irq, int dma,
+				      struct parport_operations *ops)
+{
+	struct parport *tmp;
+
+	/* Check for a previously registered port.
+	 * NOTE: we will ignore irq and dma if we find a previously
+	 * registered device.
+	 */
+	for (tmp = portlist; tmp; tmp = tmp->next) {
+		if (tmp->base == base)
+			return tmp;
+	}
+
+	tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
+	if (!tmp) {
+		printk(KERN_WARNING "parport: memory squeeze\n");
+		return NULL;
+	}
+
+	/* Init our structure */
+ 	memset(tmp, 0, sizeof(struct parport));
+	tmp->base = base;
+	tmp->irq = irq;
+	tmp->dma = dma;
+	tmp->modes = 0;
+ 	tmp->next = NULL;
+	tmp->devices = tmp->cad = tmp->lurker = NULL;
+	tmp->flags = 0;
+	tmp->ops = ops; 
+
+	tmp->name = kmalloc(15, GFP_KERNEL);
+	if (!tmp->name) {
+		printk(KERN_ERR "parport: memory squeeze\n");
+		kfree(tmp);
+		return NULL;
+	}
+	sprintf(tmp->name, "parport%d", portcount);
+
+	/* Here we chain the entry to our list. */
+	if (portlist_tail)
+		portlist_tail->next = tmp;
+	portlist_tail = tmp;
+	if (!portlist)
+		portlist = tmp;
+
+	portcount++;
+
+	tmp->probe_info.class = PARPORT_CLASS_LEGACY;  /* assume the worst */
+	return tmp;
+}
+
+void parport_quiesce(struct parport *port)
+{
+	if (port->devices) {
+		printk(KERN_WARNING "%s: attempt to quiesce active port.\n", port->name);
+		return;
+	}
+
+	if (port->flags & PARPORT_FLAG_COMA) {
+		printk(KERN_WARNING "%s: attempt to quiesce comatose port.\n", port->name);
+		return;
+	}
+
+	port->ops->release_resources(port);
+
+	port->flags |= PARPORT_FLAG_COMA; 
+}
+
+struct pardevice *parport_register_device(struct parport *port, const char *name,
+			  int (*pf)(void *), int (*kf)(void *),
+			  void (*irq_func)(int, void *, struct pt_regs *), 
+			  int flags, void *handle)
+{
+	struct pardevice *tmp;
+
+	/* We only allow one lurking device. */
+	if (flags & PARPORT_DEV_LURK) {
+		if (port->lurker) {
+			printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
+				   port->name, name);
+			return NULL;
+		}
+		if (!pf || !kf) {
+			printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n", port->name, name);
+			return NULL;
+		}
+	}
+
+	/* We may need to claw back the port hardware. */
+	if (port->flags & PARPORT_FLAG_COMA) {
+		if (port->ops->claim_resources(port)) {
+			printk(KERN_WARNING "%s: unable to get hardware to register %s.\n", port->name, name);
+			return NULL;
+		}
+		port->flags &= ~PARPORT_FLAG_COMA;
+	}
+
+	tmp = kmalloc(sizeof(struct pardevice), GFP_KERNEL);
+	if (tmp == NULL) {
+		printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name);
+		return NULL;
+	}
+
+	tmp->state = kmalloc(sizeof(struct parport_state), GFP_KERNEL);
+	if (tmp->state == NULL) {
+		printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name);
+		kfree(tmp);
+		return NULL;
+	}
+
+	tmp->name = (char *) name;
+	tmp->port = port;
+	tmp->preempt = pf;
+	tmp->wakeup = kf;
+	tmp->private = handle;
+	tmp->flags = flags;
+	tmp->irq_func = irq_func;
+	port->ops->save_state(port, tmp->state);
+
+	/* Chain this onto the list */
+	tmp->prev = NULL;
+	tmp->next = port->devices;
+	if (port->devices)
+		port->devices->prev = tmp;
+	port->devices = tmp;
+
+	if (flags & PARPORT_DEV_LURK)
+		port->lurker = tmp;
+
+	inc_parport_count();
+
+	return tmp;
+}
+
+void parport_unregister_device(struct pardevice *dev)
+{
+	struct parport *port;
+
+	if (dev == NULL) {
+		printk(KERN_ERR "parport_unregister_device: passed NULL\n");
+		return;
+	}
+
+	port = dev->port;
+
+	if (port->cad == dev) {
+		printk(KERN_WARNING "%s: refused to unregister currently active device %s.\n", port->name, dev->name);
+		return;
+	}
+
+	if (port->lurker == dev)
+		port->lurker = NULL;
+
+	if (dev->next)
+		dev->next->prev = dev->prev;
+	if (dev->prev)
+		dev->prev->next = dev->next;
+	else
+		port->devices = dev->next;
+
+	kfree(dev->state);
+	kfree(dev);
+
+	dec_parport_count();
+
+	/* If there are no more devices, put the port to sleep. */
+	if (!port->devices)
+		parport_quiesce(port);
+
+	return;
+}
+
+int parport_claim(struct pardevice *dev)
+{
+	struct pardevice *pd1;
+
+	if (dev->port->cad == dev) {
+		printk(KERN_INFO "%s: %s already owner\n",
+			   dev->port->name,dev->name);
+		return 0;
+	}
+
+	/* Preempt any current device */
+	pd1 = dev->port->cad;
+	if (dev->port->cad) {
+		if (dev->port->cad->preempt) {
+			/* Now try to preempt */
+			if (dev->port->cad->preempt(dev->port->cad->private))
+				return -EAGAIN;
+			dev->port->ops->save_state(dev->port, dev->state);
+		} else
+			return -EAGAIN;
+	}
+
+	/* Watch out for bad things */
+	if (dev->port->cad != pd1) {
+		printk(KERN_WARNING "%s: death while preempting %s\n",
+		       dev->port->name, dev->name);
+		if (dev->port->cad)
+			return -EAGAIN;
+	}
+
+	/* Now we do the change of devices */
+	dev->port->cad = dev;
+
+	/* Swap the IRQ handlers. */
+	if (dev->port->irq >= 0) {
+		free_irq(dev->port->irq, dev->port);
+		request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
+			    parport_null_intr_func, SA_INTERRUPT, dev->name,
+			    dev->port);
+	}
+
+	/* Restore control registers */
+	dev->port->ops->restore_state(dev->port, dev->state);
+
+	return 0;
+}
+
+void parport_release(struct pardevice *dev)
+{
+	struct pardevice *pd1;
+
+	/* Make sure that dev is the current device */
+	if (dev->port->cad != dev) {
+		printk(KERN_WARNING "%s: %s tried to release parport when not owner\n", dev->port->name, dev->name);
+		return;
+	}
+	dev->port->cad = NULL;
+
+	/* Save control registers */
+	dev->port->ops->save_state(dev->port, dev->state);
+
+	/* Point IRQs somewhere harmless. */
+	if (dev->port->irq >= 0) {
+		free_irq(dev->port->irq, dev->port);
+		request_irq(dev->port->irq, parport_null_intr_func,
+			    SA_INTERRUPT, dev->port->name, dev->port);
+ 	}
+
+	/* Walk the list, offering a wakeup callback to everybody other
+	 * than the lurker and the device that called us.
+	 */
+	for (pd1 = dev->next; pd1; pd1 = pd1->next) {
+		if (!(pd1->flags & PARPORT_DEV_LURK)) {
+			if (pd1->wakeup) {
+				pd1->wakeup(pd1->private);
+				if (dev->port->cad)
+					return;
+			}
+		}
+	}
+
+	for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
+		if (!(pd1->flags & PARPORT_DEV_LURK)) {
+			if (pd1->wakeup) {
+				pd1->wakeup(pd1->private);
+				if (dev->port->cad)
+					return;
+			}
+		}
+	}
+
+	/* Now give the lurker a chance.
+	 * There should be a wakeup callback because we checked for it
+	 * at registration.
+	 */
+	if (dev->port->lurker && (dev->port->lurker != dev)) {
+		if (dev->port->lurker->wakeup) {
+			dev->port->lurker->wakeup(dev->port->lurker->private);
+		} 
+#ifdef PARPORT_PARANOID
+		else {  /* can't happen */
+			printk(KERN_DEBUG
+			  "%s (%s): lurker's wakeup callback went away!\n",
+			       dev->port->name, dev->name);
+		}
+#endif
+	}
+}
diff -u --recursive --new-file v2.1.46/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v2.1.46/linux/drivers/net/eexpress.c	Tue May 13 22:41:09 1997
+++ linux/drivers/net/eexpress.c	Wed Jul 23 11:14:31 1997
@@ -1493,7 +1493,7 @@
 #endif
                 oj = jiffies;
                 while ((SCB_CUstat(scb_status(dev)) == 2) &&
-                       ((jiffies-oj) < 100));
+                       ((jiffies-oj) < 2000));
 		if (SCB_CUstat(scb_status(dev)) == 2)
 			printk("%s: warning, CU didn't stop\n", dev->name);
                 lp->started &= ~(STARTED_CU);
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/BUGS-parport linux/drivers/pnp/BUGS-parport
--- v2.1.46/linux/drivers/pnp/BUGS-parport	Wed Apr 23 19:01:21 1997
+++ linux/drivers/pnp/BUGS-parport	Wed Jul 23 11:14:31 1997
@@ -1,13 +0,0 @@
-Currently known (or at least suspected) bugs in parport:
-
-o /proc/parport is untested under 2.0.XX
-
-o SCSI aborts for PPA under 2.0.29 [reported by jmr].  Under investigation.
-
-o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
-  the resulting kernel won't link.
-
-o IEEE1284 code does not do the terminating handshake after transfers, which
-  seems to upset some devices.
-
-o lp doesn't allow you to read status while printing is in progress.
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/Config.in linux/drivers/pnp/Config.in
--- v2.1.46/linux/drivers/pnp/Config.in	Sun Apr 13 10:18:21 1997
+++ linux/drivers/pnp/Config.in	Wed Jul 23 11:14:31 1997
@@ -8,8 +8,8 @@
 bool 'Plug and Play support' CONFIG_PNP
 
 if [ "$CONFIG_PNP" = "y" ]; then
-  if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
-    bool '  Auto-probe for parallel devices' CONFIG_PNP_PARPORT_AUTOPROBE
+  if [ "$CONFIG_PARPORT" != "n" ]; then
+    dep_tristate '  Auto-probe for parallel devices' CONFIG_PNP_PARPORT $CONFIG_PARPORT
   fi
 fi
 
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/Makefile linux/drivers/pnp/Makefile
--- v2.1.46/linux/drivers/pnp/Makefile	Wed Apr 23 19:01:21 1997
+++ linux/drivers/pnp/Makefile	Wed Jul 23 11:14:31 1997
@@ -23,29 +23,11 @@
 MIX_OBJS :=
 
 ifeq ($(CONFIG_PNP_PARPORT),y)
-  L_OBJS += parport_share.o
-  ifeq ($(CONFIG_PROC_FS),y)
-    L_OBJS += parport_procfs.o
-  endif
-  ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
-    L_OBJS += parport_probe.o
-  endif
-  LX_OBJS += parport_init.o
+  LX_OBJS += parport_probe.o
 else
   ifeq ($(CONFIG_PNP_PARPORT),m)
-    MI_OBJS += parport_share.o
-    ifneq ($(CONFIG_PROC_FS),n) 
-      MI_OBJS += parport_procfs.o
-    endif
-    ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
-      MI_OBJS += parport_probe.o
-    endif
-    MIX_OBJS += parport_init.o
-    M_OBJS += parport.o
+    MX_OBJS += parport_probe.o
   endif
 endif
 
 include $(TOPDIR)/Rules.make
-
-parport.o: $(MI_OBJS) $(MIX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/TODO-parport linux/drivers/pnp/TODO-parport
--- v2.1.46/linux/drivers/pnp/TODO-parport	Wed Apr 23 19:01:21 1997
+++ linux/drivers/pnp/TODO-parport	Wed Jul 23 11:14:31 1997
@@ -1,21 +0,0 @@
-Things to be done.
-
-0. Fix the bugs (see BUGS-parport).
-
-1. Proper documentation.
-
-2. Overhaul lp.c:
-
-   a) It's a mess, and there is a lot of code duplication.
-
-   b) ECP support would be nice.  This can only work if both the port and
-      the printer support it.
-
-   c) Errors could do with being handled better.  There's no point logging a
-      message every 10 seconds when the printer is out of paper. 
-
-   d) Handle status readback automatically.  IEEE1284 printers can post status
-      bits when they have something to say.  We should read out and deal 
-      with (maybe just log) whatever the printer wants to tell the world.
-
-3. Assimilate more drivers.
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/parport_init.c linux/drivers/pnp/parport_init.c
--- v2.1.46/linux/drivers/pnp/parport_init.c	Wed Apr 23 19:01:21 1997
+++ linux/drivers/pnp/parport_init.c	Wed Jul 23 11:14:31 1997
@@ -1,831 +0,0 @@
-/* $Id: parport_init.c,v 1.3.2.4 1997/04/16 21:20:44 phil Exp $
- * Parallel-port initialisation code.
- * 
- * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- *          Tim Waugh <tmw20@cam.ac.uk>
- *	    Jose Renau <renau@acm.org>
- *
- * based on work by Grant Guenther <grant@torque.net>
- *              and Philip Blundell <Philip.Blundell@pobox.com>
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/tasks.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/parport.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-
-#include "parport_ll_io.h"
-
-static int io[PARPORT_MAX] = { 0, };
-static int irq[PARPORT_MAX] = { PARPORT_IRQ_NONE, };
-static int dma[PARPORT_MAX] = { PARPORT_DMA_NONE, };
-
-/******************************************************
- *  DMA detection section:
- */
-
-/*
- * Prepare DMA channels from 0-8 to transmit towards buffer
- */
-static int parport_prepare_dma(char *buff, int size)
-{
-	int tmp = 0;
-	int i,retv;
-	
-	for (i = 0; i < 8; i++) {
-		retv = request_dma(i, "probe");
-		if (retv)
-			continue;
-		tmp |= 1 << i;
-
-		cli();
-		disable_dma(i);
-		clear_dma_ff(i);
-		set_dma_addr(i, virt_to_bus(buff));
-		set_dma_count(i, size);
-		set_dma_mode(i, DMA_MODE_READ);
-		sti();
-	}
-
-	return tmp;
-}
-
-/*
- * Activate all DMA channels passed in dma
- */
-static int parport_enable_dma(int dma)
-{
-	int i;
-	
-	for (i = 0; i < 8; i++)
-		if (dma & (1 << i)) {
-		cli();
-		enable_dma(i);
-		sti();
-	    }
-
-	return dma;
-}
-
-static int parport_detect_dma_transfer(int dma,int size,int *resid)
-{
-	int i,n,retv;
-	int count=0;
-
-	retv = -1;
-	for (i = 0; i < 8; i++)
-		if (dma & (1 << i)) {
-			disable_dma(i);
-			clear_dma_ff(i);
-			n = get_dma_residue(i);
-			if (n != size) {
-				*resid = n;
-				retv = i;
-				if (count > 0) {
-					retv = -1;	/* Multiple DMA's */
-					printk(KERN_ERR "parport: multiple DMA detected.  Huh?\n");
-				}
-				count++;
-			}
-			free_dma(i);
-		}
-
-	return retv;	
-}
-
-/* Only if supports ECP mode */
-static int programmable_dma_support(struct parport *pb)
-{
-	int dma;
-
-	w_ecr(pb,0xE0); /* Configuration MODE */
-	
-	dma = r_cnfgB(pb) & 0x07;
-
-	w_ecr(pb,pb->ecr);
-	
-	if( dma == 0 || dma == 4 ) /* Jumper selection */
-		return -1;
-	else
-		return dma;
-}
-
-/* Only called if port supports ECP mode.
- *
- * The only restriction on DMA channels is that it has to be
- * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
- * considered a shared resource and hence they should be registered
- * when needed and then immediately unregistered.
- *
- * DMA autoprobes for ECP mode are known not to work for some
- * main board BIOS configs. I had to remove everything from the
- * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
- * and reboot again, then I got IRQ probes and DMA probes to work.
- * [Is the BIOS doing a device detection?]
- *
- * A value of -1 is allowed indicating no DMA support.
- *
- * if( 0 < DMA < 4 )
- *    1Byte DMA transfer
- * else // 4 < DMA < 8
- *    2Byte DMA transfer
- *
- */
-static int parport_dma_probe(struct parport *pb)
-{
-	int dma,retv;
-	int dsr,dsr_read;
-	char *buff;
-
-	retv = programmable_dma_support(pb);
-	if (retv != -1)
-		return retv;
-	
-	if (!(buff = kmalloc(2048, GFP_KERNEL | GFP_DMA))) {
-	    printk(KERN_ERR "parport: memory squeeze\n");
-	    return -1;
-	}
-	
- 	dsr = r_ctr(pb);
-	dsr_read = (dsr & ~(0x20)) | 0x04;    /* Direction == read */
-
-	w_ecr(pb, 0xc0);	   /* ECP MODE */
- 	w_ctr(pb, dsr_read );
-	dma=parport_prepare_dma(buff,1000);
-	w_ecr(pb, 0xd8);	   /* ECP FIFO + enable DMA */
-	parport_enable_dma(dma);
-	udelay(500);           /* Give some for DMA tranfer */
-	retv = parport_detect_dma_transfer(dma,1000,&pb->speed);
-	pb->speed = pb->speed * 2000;  /* 500uSec * 2000 = 1sec */
-	
-	/*
-	 * National Semiconductors only supports DMA tranfers
-	 * in ECP MODE
-	 */
-	if (retv == -1) {
-		w_ecr(pb, 0x60);	   /* ECP MODE */
-		w_ctr(pb, dsr_read );
-		dma=parport_prepare_dma(buff,1000);
-		w_ecr(pb, 0x68);	   /* ECP FIFO + enable DMA */
-		parport_enable_dma(dma);
-		udelay(500);           /* Give some for DMA tranfer */
-		retv = parport_detect_dma_transfer(dma,1000,&pb->speed);
-		pb->speed = pb->speed * 2000;  /* 500uSec * 2000 = 1sec */
-	}
-	
-	kfree(buff);
-	
-	w_ctr(pb, pb->ctr);
-	w_ecr(pb, pb->ecr);
-
-	return retv;
-}
-/******************************************************
- *  MODE detection section:
- */
-
-/*
- * Clear TIMEOUT BIT in EPP MODE
- */
-static int epp_clear_timeout(struct parport *pb)
-{
-	int r;
-
-	if (!(r_str(pb) & 0x01))
-		return 1;
-
-	/* To clear timeout some chips require double read */
-	r_str(pb);
-	r = r_str(pb);
-	w_str(pb, r | 0x01); /* Some reset by writing 1 */
-	w_str(pb, r & 0xfe); /* Others by writing 0 */
-	r = r_str(pb);
-
-	return !(r & 0x01);
-}
-
-
-/*
- * Checks for port existence, all ports support SPP MODE
- */
-static int parport_SPP_supported(struct parport *pb)
-{
-	/* Do a simple read-write test to make sure the port exists. */
-
-	w_dtr(pb, 0xaa);
-	if (r_dtr(pb) != 0xaa) return 0;
-	
-	w_dtr(pb, 0x55);
-	if (r_dtr(pb) != 0x55) return 0;
-
-	return PARPORT_MODE_SPP;
-}
-
-/* Check for ECP
- *
- * Old style XT ports alias io ports every 0x400, hence accessing ECR
- * on these cards actually accesses the CTR.
- *
- * Modern cards don't do this but reading from ECR will return 0xff
- * regardless of what is written here if the card does NOT support
- * ECP.
- *
- * We will write 0x2c to ECR and 0xcc to CTR since both of these
- * values are "safe" on the CTR since bits 6-7 of CTR are unused.
- */
-static int parport_ECR_present(struct parport *pb)
-{
-	int r;
-
-	r= r_ctr(pb);	
-	if ((r_ecr(pb) & 0x03) == (r & 0x03)) {
-		w_ctr(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
-
-		r= r_ctr(pb);	
-		if ((r_ecr(pb) & 0x03) == (r & 0x03))
-			return 0; /* Sure that no ECR register exists */
-	}
-	
-	if ((r_ecr(pb) & 0x03 ) != 0x01)
-		return 0;
-
-	w_ecr(pb,0x34);
-	if (r_ecr(pb) != 0x35)
-		return 0;
-
-	w_ecr(pb,pb->ecr);
-	w_ctr(pb,pb->ctr);
-	
-	return PARPORT_MODE_ECR;
-}
-
-static int parport_ECP_supported(struct parport *pb)
-{
-	int i;
-	
-	/* If there is no ECR, we have no hope of supporting ECP. */
-	if (!(pb->modes & PARPORT_MODE_ECR))
-		return 0;
-
-	/*
-	 * Using LGS chipset it uses ECR register, but
-	 * it doesn't support ECP or FIFO MODE
-	 */
-	
-	w_ecr(pb, 0xc0); /* TEST FIFO */
-	for (i=0; i < 1024 && (r_ecr(pb) & 0x01); i++)
-		w_fifo(pb, 0xaa);
-
-	w_ecr(pb, pb->ecr);
-
-	if (i == 1024)
-		return 0;
-	
-	return PARPORT_MODE_ECP;
-}
-
-/* EPP mode detection
- * Theory:
- *	Bit 0 of STR is the EPP timeout bit, this bit is 0
- *	when EPP is possible and is set high when an EPP timeout
- *	occurs (EPP uses the HALT line to stop the CPU while it does
- *	the byte transfer, an EPP timeout occurs if the attached
- *	device fails to respond after 10 micro seconds).
- *
- *	This bit is cleared by either reading it (National Semi)
- *	or writing a 1 to the bit (SMC, UMC, WinBond), others ???
- *	This bit is always high in non EPP modes.
- */
-static int parport_EPP_supported(struct parport *pb)
-{
-	/* If EPP timeout bit clear then EPP available */
-	if (!epp_clear_timeout(pb))
-		return 0;  /* No way to clear timeout */
-
-	w_ctr(pb, r_ctr(pb) | 0x20);
-	w_ctr(pb, r_ctr(pb) | 0x10);
-	epp_clear_timeout(pb);
-	
-	r_epp(pb);
-	udelay(30);  /* Wait for possible EPP timeout */
-	
-	if (r_str(pb) & 0x01) {
-		epp_clear_timeout(pb);
-		return PARPORT_MODE_EPP;
-	}
-
-	return 0;
-}
-
-static int parport_ECPEPP_supported(struct parport *pb)
-{
-	int mode;
-
-	if (!(pb->modes & PARPORT_MODE_ECR))
-		return 0;
-	
-	/* Search for SMC style EPP+ECP mode */
-	w_ecr(pb, 0x80);
-	
-	mode = parport_EPP_supported(pb);
-
-	w_ecr(pb, pb->ecr);
-	
-	if (mode)
-		return PARPORT_MODE_ECPEPP;
-	
-	return 0;
-}
-
-/* Detect PS/2 support.
- *
- * Bit 5 (0x20) sets the PS/2 data direction; setting this high
- * allows us to read data from the data lines.  In theory we would get back
- * 0xff but any peripheral attached to the port may drag some or all of the
- * lines down to zero.  So if we get back anything that isn't the contents
- * of the data register we deem PS/2 support to be present. 
- *
- * Some SPP ports have "half PS/2" ability - you can't turn off the line
- * drivers, but an external peripheral with sufficiently beefy drivers of
- * its own can overpower them and assert its own levels onto the bus, from
- * where they can then be read back as normal.  Ports with this property
- * and the right type of device attached are likely to fail the SPP test,
- * (as they will appear to have stuck bits) and so the fact that they might
- * be misdetected here is rather academic. 
- */
-
-static int parport_PS2_supported(struct parport *pb)
-{
-	int ok = 0;
-  
-	epp_clear_timeout(pb);
-
-	w_ctr(pb, pb->ctr | 0x20);   /* try to tri-state the buffer */
-	
-	w_dtr(pb, 0x55);
-	if (r_dtr(pb) != 0x55) ok++;
-
-	w_dtr(pb, 0xaa);
-	if (r_dtr(pb) != 0xaa) ok++;
-	
-	w_ctr(pb, pb->ctr);          /* cancel input mode */
-
-	return ok?PARPORT_MODE_PS2:0;
-}
-
-static int parport_ECPPS2_supported(struct parport *pb)
-{
-	int mode;
-
-	if (!(pb->modes & PARPORT_MODE_ECR))
-		return 0;
-	
-	w_ecr(pb, 0x20);
-	
-	mode = parport_PS2_supported(pb);
-
-	w_ecr(pb,pb->ecr);
-	
-	if (mode)
-		return PARPORT_MODE_ECPPS2;
-	
-	return 0;
-}
-
-/******************************************************
- *  IRQ detection section:
- *
- * This code is for detecting ECP interrupts (due to problems with the
- * monolithic interrupt probing routines).
- *
- * In short this is a voting system where the interrupt with the most
- * "votes" is the elected interrupt (it SHOULD work...)
- *
- * This is horribly x86-specific at the moment.  I'm not convinced it
- * belongs at all.
- */
-
-static int intr_vote[16];
-
-static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
-{
-	intr_vote[irq]++;
-	return;
-}
-
-static long open_intr_election(void)
-{
-	long tmp = 0;
-	int i;
-
-	/* We ignore the timer - irq 0 */
-	for (i = 1; i < 16; i++) {
-		intr_vote[i] = 0;
-		if (request_irq(i, parport_vote_intr_func,
-		       SA_INTERRUPT, "probe", intr_vote) == 0)
-			tmp |= 1 << i;
-	}
-	return tmp;
-}
-
-static int close_intr_election(long tmp)
-{
-	int irq = PARPORT_IRQ_NONE;
-	int i;
-
-	/* We ignore the timer - irq 0 */
-	for (i = 1; i < 16; i++)
-		if (tmp & (1 << i)) {
-			if (intr_vote[i]) {
-				if (irq != PARPORT_IRQ_NONE)
-					/* More than one interrupt */
-					return PARPORT_IRQ_NONE;
-				irq = i;
-			}
-			free_irq(i, intr_vote);
-		}
-	return irq;
-}
-
-/* Only if supports ECP mode */
-static int programmable_irq_support(struct parport *pb)
-{
-	int irq;
-
-	w_ecr(pb,0xE0); /* Configuration MODE */
-	
-	irq = (r_cnfgB(pb) >> 3) & 0x07;
-
-	switch(irq){
-	  case 2:
-		  irq = 9;
-		  break;
-	  case 7:
-		  irq = 5;
-		  break;
-	  case 0:
-		  irq = -1;
-		  break;
-	  default:
-		  irq += 7;
-	}
-		  
-	w_ecr(pb,pb->ecr);
-	
-	return irq;
-}
-
-static int irq_probe_ECP(struct parport *pb)
-{
-	int irqs,i;
-		
-	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
-	irqs = open_intr_election();
-		
-	w_ecr(pb, 0x00);	    /* Reset FIFO */
-	w_ctr(pb, pb->ctr );	    /* Force direction = 0 */
-	w_ecr(pb, 0xd0);	    /* TEST FIFO + nErrIntrEn */
-
-	/* If Full FIFO sure that WriteIntrThresold is generated */
-	for( i=0 ; i < 1024 && !(r_ecr(pb) & 0x02) ; i++ ){
-		w_fifo(pb, 0xaa);
-	}
-		
-	pb->irq = close_intr_election(irqs);
-	
-	w_ecr(pb, pb->ecr);
-
-	return pb->irq;
-}
-
-/*
- * This detection seems that only works in National Semiconductors
- * This doesn't work in SMC, LGS, and Winbond 
- */
-static int irq_probe_EPP(struct parport *pb)
-{
-	int irqs;
-
-#ifndef ADVANCED_DETECT
-	return -1;
-#endif
-	
-	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
-	irqs = open_intr_election();
-
-	if( pb->modes & PARPORT_MODE_ECR )
-		w_ecr(pb, r_ecr(pb) | 0x10 );
-	
-	epp_clear_timeout(pb);
-	w_ctr(pb, r_ctr(pb) | 0x20);
-	w_ctr(pb, r_ctr(pb) | 0x10);
-	epp_clear_timeout(pb);
-
-	/*  Device isn't expecting an EPP read
-	 * and generates an IRQ.
-	 */
-	r_epp(pb);
-	udelay(20);
-
-	pb->irq = close_intr_election(irqs);
-	
-	w_ctr(pb,pb->ctr);
-	
-	return pb->irq;
-}
-
-static int irq_probe_SPP(struct parport *pb)
-{
-	int irqs;
-
-#ifndef ADVANCED_DETECT
-	return -1;
-#endif
-
-	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
-	irqs = probe_irq_on();
-
-	if( pb->modes & PARPORT_MODE_ECR )
-		w_ecr(pb, 0x10 );
-
-	w_dtr(pb,0x00);
-	w_ctr(pb,0x00);
-	w_ctr(pb,0x0c);
-	udelay(5);
-	w_ctr(pb,0x0d);
-	udelay(5);
-	w_ctr(pb,0x0c);
-	udelay(25);
-	w_ctr(pb,0x08);
-	udelay(25);
-	w_ctr(pb,0x0c);
-	udelay(50);
-
-	pb->irq = probe_irq_off(irqs);
-	if (pb->irq <= 0)
-		pb->irq = PARPORT_IRQ_NONE;	/* No interrupt detected */
-	
-	w_ctr(pb,pb->ctr);
-	
-	return pb->irq;
-}
-
-/* We will attempt to share interrupt requests since other devices
- * such as sound cards and network cards seem to like using the
- * printer IRQs.
- *
- * When ECP is available we can autoprobe for IRQs.
- * NOTE: If we can autoprobe it, we can register the IRQ.
- */
-static int parport_irq_probe(struct parport *pb)
-{
-	if (pb->modes & PARPORT_MODE_ECR)
-		pb->irq = programmable_irq_support(pb);
-
-	if (pb->modes & PARPORT_MODE_ECP)
-		pb->irq = irq_probe_ECP(pb);
-			
-	if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_ECPEPP)) {
-		w_ecr(pb,0x80);
-		pb->irq = irq_probe_EPP(pb);
-		w_ecr(pb,pb->ecr);
-	}
-
-	epp_clear_timeout(pb);
-
-	if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_EPP))
-		pb->irq = irq_probe_EPP(pb);
-
-	epp_clear_timeout(pb);
-
-	if (pb->irq == PARPORT_IRQ_NONE)
-		pb->irq = irq_probe_SPP(pb);
-
-	return pb->irq;
-}
-
-
-int initialize_parport(struct parport *pb, unsigned long base, int irq, int dma, int count)
-{
-	/* Check some parameters */
-	if (dma < -2) {
-		printk(KERN_ERR "parport: Invalid DMA[%d] at base 0x%lx\n",dma,base);
-		return 0;
-	}
-
-	if (irq < -2) {
-		printk(KERN_ERR "parport: Invalid IRQ[%d] at base 0x%lx\n",irq,base);
-		return 0;
-	}
-	
-	/* Init our structure */
- 	memset(pb, 0, sizeof(struct parport));
-	pb->base = base;
-	pb->irq = irq;
-	pb->dma = dma;
-	pb->modes = 0;
- 	pb->next = NULL;
-	pb->devices = pb->cad = pb->lurker = NULL;
-	pb->flags = 0;
-
-	/* Before we start, set the control registers to something sensible. */
-	pb->ecr = 0xc;
-	pb->ctr = 0xc;
-
-	pb->name = kmalloc(15, GFP_KERNEL);
-	if (!pb->name) {
-		printk(KERN_ERR "parport: memory squeeze\n");
-		return 0;
-	}
-	sprintf(pb->name, "parport%d", count);
-
-	if (!parport_SPP_supported(pb)) {
-		epp_clear_timeout(pb);
-		if (!parport_SPP_supported(pb)) {
-			kfree(pb->name);
-			return 0;
-		}
-	}
-
-	pb->modes |= PARPORT_MODE_SPP; 	/* All ports support SPP mode. */
-	pb->modes |= parport_PS2_supported(pb);
-
-	if (pb->base != 0x3bc) {
-		pb->modes |= parport_ECR_present(pb);	
-		pb->modes |= parport_ECP_supported(pb);
-		pb->modes |= parport_ECPPS2_supported(pb);
-		pb->modes |= parport_EPP_supported(pb);
-		pb->modes |= parport_ECPEPP_supported(pb);
-	}
-
-	/* Now register regions */
-	if ((pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) && 
-	    (check_region(pb->base, 8))) {
-		printk(KERN_INFO "%s: EPP disabled due to port conflict at %x.\n", pb->name, pb->base + 3);
-		pb->modes &= ~(PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP);
-	}
-	pb->size = (pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) ? 8 : 3;
-
-	request_region(pb->base, pb->size, pb->name);
-	if (pb->modes & PARPORT_MODE_ECR)
-		request_region(pb->base+0x400, 3, pb->name);
-
-	/* DMA check */
-	if (pb->modes & PARPORT_MODE_ECP) {
-		if (pb->dma == PARPORT_DMA_NONE)
-			pb->dma = parport_dma_probe(pb);
-		else if (pb->dma == -2)
-			pb->dma = PARPORT_DMA_NONE;
-	}
-
-	/* IRQ check */
-	if (pb->irq == PARPORT_IRQ_NONE)
-		pb->irq = parport_irq_probe(pb);
-	else if (pb->irq == -2)
-		pb->irq = PARPORT_IRQ_NONE;
-
-	return 1;
-}
-
-#ifndef MODULE
-static int parport_setup_ptr = 0;
-
-void parport_setup(char *str, int *ints)
-{
-	if (ints[0] == 0 || ints[1] == 0) {
-		/* Disable parport if "parport=" or "parport=0" in cmdline */
-		io[0] = PARPORT_DISABLE; 
-		return;
-	}
-	if (parport_setup_ptr < PARPORT_MAX) {
-		io[parport_setup_ptr] = ints[1];
-		if (ints[0]>1) {
-			irq[parport_setup_ptr] = ints[2];
-			if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
-		}
-		parport_setup_ptr++;
-	} else {
-		printk(KERN_ERR "parport=0x%x", ints[1]);
-		if (ints[0]>1) {
-			printk(",%d", ints[2]);
-			if (ints[0]>2) printk(",%d", ints[3]);
-		}
-		printk(" ignored, too many ports.\n");
-	}
-}
-#endif
-
-#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
-extern void parport_probe_one(struct parport *port);
-#endif
-
-#ifdef MODULE
-MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_MAX) "i");
-MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_MAX) "i");
-MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_MAX) "i");
-
-int init_module(void)
-#else
-int pnp_parport_init(void)
-#endif				/* MODULE */
-{
-	struct parport *pb;
-
-	printk(KERN_INFO "Parallel port sharing: %s\n",
-	       "$Revision: 1.3.2.4 $");
-
-	if (io[0] == PARPORT_DISABLE) return 1; 
-
-#ifdef CONFIG_PROC_FS
-	parport_proc_init();
-#endif
-
-	/* Run probes to ensure parport does exist */
-#define PORT(a,b,c) \
-		if ((pb = parport_register_port((a), (b), (c))))  \
-		        parport_destroy(pb); 
-
-
-	if (io[0]) {
-		/* If the user specified any ports, use them */
-		int i;
-		for (i = 0; io[i] && i < PARPORT_MAX; i++) {
-			PORT(io[i], irq[i], dma[i]);
-		}
-	} else {
-		/* Go for the standard ports. */
-		PORT(0x378, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
-		PORT(0x278, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
-		PORT(0x3bc, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
-#undef PORT
-	}
-
-#if defined(CONFIG_PNP_PARPORT_AUTOPROBE) || defined(CONFIG_PROC_FS)
-	for (pb = parport_enumerate(); pb; pb = pb->next) {
-#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
-		parport_probe_one(pb);
-#endif
-#ifdef CONFIG_PROC_FS
-		parport_proc_register(pb);
-#endif
-	}
-#endif
-
-	return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-	struct parport *port, *next;
-   
-	for (port = parport_enumerate(); port; port = next) {
-		next = port->next;
-		parport_destroy(port);
-		parport_proc_unregister(port);
-		kfree(port->name);
-		kfree(port);
-	}
-	
-	parport_proc_cleanup();
-}
-#endif
-
-/* Exported symbols for modules. */
-
-EXPORT_SYMBOL(parport_claim);
-EXPORT_SYMBOL(parport_release);
-EXPORT_SYMBOL(parport_register_port);
-EXPORT_SYMBOL(parport_destroy);
-EXPORT_SYMBOL(parport_register_device);
-EXPORT_SYMBOL(parport_unregister_device);
-EXPORT_SYMBOL(parport_enumerate);
-EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
-
-#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
-EXPORT_SYMBOL(parport_probe);
-EXPORT_SYMBOL(parport_probe_one);
-#endif
-
-void inc_parport_count(void)
-{
-#ifdef MODULE
-	MOD_INC_USE_COUNT;
-#endif
-}
-
-void dec_parport_count(void)
-{
-#ifdef MODULE
-	MOD_DEC_USE_COUNT;
-#endif
-}
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/parport_ll_io.h linux/drivers/pnp/parport_ll_io.h
--- v2.1.46/linux/drivers/pnp/parport_ll_io.h	Sun Apr 13 10:18:21 1997
+++ linux/drivers/pnp/parport_ll_io.h	Wed Jul 23 11:14:31 1997
@@ -1,21 +0,0 @@
-/* $Id: parport_ll_io.h,v 1.1.2.1 1997/03/26 13:01:09 phil Exp $ 
- * David Campbell's "favourite IO routines" for parallel ports
- */
-
-#define r_dtr(x)	inb((x)->base)
-#define r_str(x)	inb((x)->base+1)
-#define r_ctr(x)	inb((x)->base+2)
-#define r_epp(x)	inb((x)->base+4)
-#define r_fifo(x)	inb((x)->base+0x400)
-#define r_ecr(x)	inb((x)->base+0x402)
-#define r_cnfgA(x)	inb((x)->base+0x400)
-#define r_cnfgB(x)	inb((x)->base+0x401)
-
-#define w_dtr(x,y)	outb((y), (x)->base)
-#define w_str(x,y)	outb((y), (x)->base+1)
-#define w_ctr(x,y)	outb((y), (x)->base+2)
-#define w_epp(x,y)	outb((y), (x)->base+4)
-#define w_fifo(x,y)	outb((y), (x)->base+0x400)
-#define w_ecr(x,y)	outb((y), (x)->base+0x402)
-#define w_cnfgA(x,y)	outb((y), (x)->base+0x400)
-#define w_cnfgB(x,y)	outb((y), (x)->base+0x401)
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/parport_probe.c linux/drivers/pnp/parport_probe.c
--- v2.1.46/linux/drivers/pnp/parport_probe.c	Mon Apr 14 16:28:12 1997
+++ linux/drivers/pnp/parport_probe.c	Wed Jul 23 11:14:31 1997
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/ctype.h>
+#include <linux/module.h>
 
 #include <linux/lp.h>
 
@@ -26,23 +27,23 @@
 static inline int read_nibble(struct parport *port) 
 {
 	unsigned char i;
-	i = parport_r_status(port)>>3;
+	i = parport_read_status(port)>>3;
 	i&=~8;
 	if ( ( i & 0x10) == 0) i|=8;
 	return(i & 0x0f);
 }
 
 static void read_terminate(struct parport *port) {
-	parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+	parport_write_control(port, (parport_read_control(port) & ~2) | 8);
 	/* SelectIN high, AutoFeed low */
 	if (parport_wait_peripheral(port, 0x80, 0)) 
 		/* timeout, SelectIN high, Autofeed low */
 		return;
-	parport_w_ctrl(port, parport_r_ctrl(port) | 2);
+	parport_write_control(port, parport_read_control(port) | 2);
 	/* AutoFeed high */
 	parport_wait_peripheral(port, 0x80, 0x80);
 	/* no timeout possible, Autofeed low, SelectIN high */
-	parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+	parport_write_control(port, (parport_read_control(port) & ~2) | 8);
 	return;
 }
 
@@ -56,14 +57,14 @@
 	unsigned char Byte=0;
 
 	for (i=0; ; i++) {
-		parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+		parport_write_control(port, parport_read_control(port) | 2); /* AutoFeed high */
 		if (parport_wait_peripheral(port, 0x40, 0)) {
 			printk("%s: read1 timeout.\n", port->name);
-			parport_w_ctrl(port, parport_r_ctrl(port) & ~2);
+			parport_write_control(port, parport_read_control(port) & ~2);
 			break;
 		}
 		z = read_nibble(port);
-		parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+		parport_write_control(port, parport_read_control(port) & ~2); /* AutoFeed low */
 		if (parport_wait_peripheral(port, 0x40, 0x40)) {
 			printk("%s: read2 timeout.\n", port->name);
 			break;
@@ -75,7 +76,7 @@
 			if (count++ == length)
 				temp = NULL;
 			/* Does the error line indicate end of data? */
-			if ((parport_r_status(port) & LP_PERRORP) == LP_PERRORP) 
+			if ((parport_read_status(port) & LP_PERRORP) == LP_PERRORP) 
 				break;
 		} else Byte=z;
 	}
@@ -87,7 +88,7 @@
 
 static int wakeup(void *ref)
 {
-	struct ppd **dev = (struct ppd **)ref;
+	struct pardevice **dev = (struct pardevice **)ref;
 	
 	if (!wait_q || parport_claim(*dev))
 		return 1;
@@ -98,9 +99,7 @@
 
 int parport_probe(struct parport *port, char *buffer, int len)
 {
-	struct ppd *dev = parport_register_device(port, "IEEE 1284 probe",
-						  NULL, wakeup, NULL,
-						  PARPORT_DEV_TRAN, &dev);
+	struct pardevice *dev = parport_register_device(port, "IEEE 1284 probe", NULL, wakeup, NULL, PARPORT_DEV_TRAN, &dev);
 
 	int result = 0;
 
@@ -264,3 +263,17 @@
 	}
 	kfree(buffer);
 }
+
+#if MODULE
+int init_module(void)
+{
+	struct parport *p;
+	for (p = parport_enumerate(); p; p = p->next) 
+		parport_probe_one(p);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/parport_procfs.c linux/drivers/pnp/parport_procfs.c
--- v2.1.46/linux/drivers/pnp/parport_procfs.c	Mon Jul  7 08:24:28 1997
+++ linux/drivers/pnp/parport_procfs.c	Wed Jul 23 11:14:31 1997
@@ -1,339 +0,0 @@
-/* $Id: parport_procfs.c,v 1.3.2.6 1997/04/16 21:30:38 phil Exp $
- * Parallel port /proc interface code.
- * 
- * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- *          Tim Waugh <tmw20@cam.ac.uk>
- *
- * based on work by Grant Guenther <grant@torque.net>
- *              and Philip Blundell <Philip.Blundell@pobox.com>
- */
-
-#include <linux/tasks.h>
-#include <asm/ptrace.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-
-#include <linux/proc_fs.h>
-
-#include <linux/parport.h>
-#include "parport_ll_io.h"
-
-#undef PARPORT_INCLUDE_BENCH
-
-struct proc_dir_entry *base=NULL;
-
-void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs);
-
-static int irq_write_proc(struct file *file, const char *buffer,
-					  unsigned long count, void *data)
-{
-	int newirq;
-	struct parport *pp = (struct parport *)data;
-	
-	if (count > 4 )  /* more than 4 digits for a irq 0x?? 0?? ??  */
-		return(-EOVERFLOW);
-
-	if (buffer[0] < 32 || !strncmp(buffer, "none", 4)) {
-		newirq = PARPORT_IRQ_NONE;
-	} else {
-		if (buffer[0] == '0') {
-			if( buffer[1] == 'x' )
-				newirq = simple_strtoul(&buffer[2],0,16);
-			else
-				newirq = simple_strtoul(&buffer[1],0,8);
-		} else {
-			newirq = simple_strtoul(buffer,0,10);
-		}
-	}
-
-	if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) 
-		free_irq(pp->irq, pp);
-
-	pp->irq = newirq;
-
-	if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) { 
-		struct ppd *pd = pp->cad;
-
-		if (pd == NULL) {
-			pd = pp->devices;
-			if (pd != NULL) 
-				request_irq(pp->irq, pd->irq_func ? 
-					    pd->irq_func :
-					    parport_null_intr_func,
-					    SA_INTERRUPT, pd->name, pd->port);
-		} else {
-			request_irq(pp->irq, pd->irq_func ? pd->irq_func :
-				    parport_null_intr_func,
-				    SA_INTERRUPT, pp->name, pd->port);
-		}
-	}
-
-	return count;
-}
-
-static int irq_read_proc(char *page, char **start, off_t off,
-					 int count, int *eof, void *data)
-{
-	struct parport *pp = (struct parport *)data;
-	int len;
-	
-	if (pp->irq == PARPORT_IRQ_NONE)
-		len = sprintf(page, "none\n");
-	else
-		len = sprintf(page, "%d\n", pp->irq);
-	
-	*start = 0;
-	*eof   = 1;
-	return len;
-}
-
-static int devices_read_proc(char *page, char **start, off_t off,
-					 int count, int *eof, void *data)
-{
-	struct parport *pp = (struct parport *)data;
-	struct ppd *pd1;
-	int len=0;
-
-	for (pd1 = pp->devices; pd1 ; pd1 = pd1->next) {
-		if (pd1 == pp->cad)
-			len += sprintf(page+len, "+");
-		else
-			len += sprintf(page+len, " ");
-
-		len += sprintf(page+len, "%s",pd1->name);
-
-		if (pd1 == pp->lurker)
-			len += sprintf(page+len, " LURK");
-		
-		len += sprintf(page+len,"\n");
-	}
-		
-	*start = 0;
-	*eof   = 1;
-	return len;
-}
-
-static int hardware_read_proc(char *page, char **start, off_t off,
-							  int count, int *eof, void *data)
-{
-	struct parport *pp = (struct parport *)data;
-	int len=0;
-	
-	len += sprintf(page+len, "base:\t0x%x\n",pp->base);
-	if (pp->irq == PARPORT_IRQ_NONE)
-		len += sprintf(page+len, "irq:\tnone\n");
-	else
-		len += sprintf(page+len, "irq:\t%d\n",pp->irq);
-	len += sprintf(page+len, "dma:\t%d\n",pp->dma);
-
-	len += sprintf(page+len, "modes:\t");
-	{
-#define printmode(x) {if(pp->modes&PARPORT_MODE_##x){len+=sprintf(page+len,"%s%s",f?",":"",#x);f++;}}
-		int f = 0;
-		printmode(SPP);
-		printmode(PS2);
-		printmode(EPP);
-		printmode(ECP);
-		printmode(ECPEPP);
-		printmode(ECPPS2);
-#undef printmode
-	}
-	len += sprintf(page+len, "\n");
-
-	len += sprintf(page+len, "mode:\t");
-	if (pp->modes & PARPORT_MODE_ECR) {
-		switch (r_ecr(pp) >> 5) {
-		case 0:
-			len += sprintf(page+len, "SPP");
-			if( pp->modes & PARPORT_MODE_PS2 )
-				len += sprintf(page+len, ",PS2");
-			if( pp->modes & PARPORT_MODE_EPP )
-				len += sprintf(page+len, ",EPP");
-			break;
-		case 1:
-			len += sprintf(page+len, "ECPPS2");
-			break;
-		case 2:
-			len += sprintf(page+len, "DATAFIFO");
-			break;
-		case 3:
-			len += sprintf(page+len, "ECP");
-			break;
-		case 4:
-			len += sprintf(page+len, "ECPEPP");
-			break;
-		case 5:
-			len += sprintf(page+len, "Reserved?");
-			break;
-		case 6:
-			len += sprintf(page+len, "TEST");
-			break;
-		case 7:
-			len += sprintf(page+len, "Configuration");
-			break;
-		}
-	} else {
-		len += sprintf(page+len, "SPP");
-		if (pp->modes & PARPORT_MODE_PS2)
-			len += sprintf(page+len, ",PS2");
-		if (pp->modes & PARPORT_MODE_EPP)
-			len += sprintf(page+len, ",EPP");
-	}
-	len += sprintf(page+len, "\n");
-	
-#if 0
-	/* Now no detection, please fix with an external function */
-	len += sprintf(page+len, "chipset:\tunknown\n");
-#endif
-#ifdef PARPORT_INCLUDE_BENCHMARK
-	if (pp->speed)
-		len += sprintf(page+len, "bench:\t%d Bytes/s\n",pp->speed);
-	else
-		len += sprintf(page+len, "bench:\tunknown\n");
-#endif	
-
-	*start = 0;
-	*eof   = 1;
-	return len;
-}
-
-static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode,
-					     struct proc_dir_entry *parent,
-					     unsigned short ino)
-{
-	struct proc_dir_entry *ent;
-
-	ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
-	if (!ent)
-		return NULL;
-	memset(ent, 0, sizeof(struct proc_dir_entry));
-	
-	if (mode == S_IFDIR)
-		mode |= S_IRUGO | S_IXUGO;
-	else if (mode == 0)
-		mode = S_IFREG | S_IRUGO;
-
-
-	ent->low_ino = ino;
-	ent->name = name;
-	ent->namelen = strlen(name);
-	ent->mode = mode;
-	if (S_ISDIR(mode)) 
-		ent->nlink = 2;
-	else
-		ent->nlink = 1;
-
-	proc_register(parent, ent);
-	
-	return ent;
-}
-
-
-int parport_proc_init()
-{
-	base = new_proc_entry("parport", S_IFDIR, &proc_root,PROC_PARPORT);
-
-	if (base)
-		return 1;
-	else {
-		printk(KERN_ERR "parport: Error creating proc entry /proc/parport\n");
-		return 0;
-	}
-}
-
-int parport_proc_cleanup()
-{
-	if (base)
-		proc_unregister(&proc_root,base->low_ino);
-
-	base = NULL;
-	
-	return 0;
-}
-
-int parport_proc_register(struct parport *pp)
-{
-	struct proc_dir_entry *ent;
-	static int conta=0;
-	char *name;
-
-	memset(&pp->pdir,0,sizeof(struct parport_dir));
-
-	if (!base) {
-		printk(KERN_ERR "parport: Error entry /proc/parport, not generated?\n");
-		return 1;
-	}
-	
-	name = pp->pdir.name;
-	sprintf(name,"%d",conta++);
-
-	ent = new_proc_entry(name, S_IFDIR, base,0);
-	if (!ent) {
-		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s\n",name);
-		return 1;
-	}
-	pp->pdir.entry = ent;
-
-	ent = new_proc_entry("irq", S_IFREG | S_IRUGO | S_IWUSR, pp->pdir.entry,0);
-	if (!ent) {
-		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/irq\n",name);
-		return 1;
-	}
-	ent->read_proc = irq_read_proc;
-	ent->write_proc= irq_write_proc;
-	ent->data      = pp;
-	pp->pdir.irq   = ent;
-	
-	ent = new_proc_entry("devices", 0, pp->pdir.entry,0);
-	if (!ent) {
-		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/devices\n",name);
-		return 1;
-	}
-	ent->read_proc   = devices_read_proc;
-	ent->data        = pp;
-	pp->pdir.devices = ent;
-	
-	ent = new_proc_entry("hardware", 0, pp->pdir.entry,0);
-	if (!ent) {
-		printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/hardware\n",name);
-		return 1;
-	}
-	ent->read_proc    = hardware_read_proc;
-	ent->data         = pp;
-	pp->pdir.hardware = ent;
-	return 0;
-}
-
-int parport_proc_unregister(struct parport *pp)
-{
-	if (pp->pdir.entry) {
-		if (pp->pdir.irq) {
-			proc_unregister(pp->pdir.entry, pp->pdir.irq->low_ino);
-			kfree(pp->pdir.irq);
-		}
-		
-		if (pp->pdir.devices) {
-			proc_unregister(pp->pdir.entry,
-					pp->pdir.devices->low_ino);
-			kfree(pp->pdir.devices);
-		}
-		
-		if (pp->pdir.hardware) {
-			proc_unregister(pp->pdir.entry,
-					pp->pdir.hardware->low_ino);
-			kfree(pp->pdir.hardware);
-		}
-		
-		proc_unregister(base, pp->pdir.entry->low_ino);
-		kfree(pp->pdir.entry);
-	}
-	
-	return 0;
-}
diff -u --recursive --new-file v2.1.46/linux/drivers/pnp/parport_share.c linux/drivers/pnp/parport_share.c
--- v2.1.46/linux/drivers/pnp/parport_share.c	Wed May 28 10:51:32 1997
+++ linux/drivers/pnp/parport_share.c	Wed Jul 23 11:14:31 1997
@@ -1,470 +0,0 @@
-/* $Id: parport_share.c,v 1.3.2.5 1997/04/16 21:20:44 phil Exp $
- * Parallel-port resource manager code.
- * 
- * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- *          Tim Waugh <tmw20@cam.ac.uk>
- *	    Jose Renau <renau@acm.org>
- *
- * based on work by Grant Guenther <grant@torque.net>
- *              and Philip Blundell <Philip.Blundell@pobox.com>
- */
-
-#include <linux/tasks.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/parport.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-
-#undef PARPORT_PARANOID
-
-#include "parport_ll_io.h"
-
-static struct parport *portlist = NULL, *portlist_tail = NULL;
-static int portcount = 0;
-
-/* from parport_init.c */
-extern int initialize_parport(struct parport *, unsigned long base, 
-			      int irq, int dma, int count);
-
-/* Return a list of all the ports we know about. */
-struct parport *parport_enumerate(void)
-{
-	return portlist;
-}
-
-void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
-{
-	/* NULL function - Does nothing */
-	return;
-}
-
-struct parport *parport_register_port(unsigned long base, int irq, int dma)
-{
-	struct parport new, *tmp;
-
-	/* Check for a previously registered port.
-	 * NOTE: we will ignore irq and dma if we find a previously
-	 * registered device.
-	 */
-	for (tmp = portlist; tmp; tmp = tmp->next) {
-		if (tmp->base == base)
-			return tmp;
-	}
-
-	/* Has someone grabbed the address yet? */
-	if (check_region(base, 3))
-		return NULL;
-
-	if (!initialize_parport(&new,base,irq,dma,portcount))
-		return NULL;
-					   
-	if (new.dma >= 0) {
-		if (request_dma(new.dma, new.name)) {
-			printk(KERN_INFO "%s: unable to claim DMA %d\n", 
-			       new.name, new.dma);
-			release_region(new.base, new.size);
-			if( new.modes & PARPORT_MODE_ECR )
-				release_region(new.base+0x400, 3);
-			kfree(new.name);
-			return NULL;
-		}
-	}
-
-	tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
-	if (!tmp) {
-		printk(KERN_WARNING "parport: memory squeeze\n");
-		release_region(new.base, new.size);
-		if( new.modes & PARPORT_MODE_ECR )
-			release_region(new.base+0x400, 3);
-		kfree(new.name);
-		return NULL;
-	}
-	memcpy(tmp, &new, sizeof(struct parport));
-
-	if (new.irq != PARPORT_IRQ_NONE) {
-		if (request_irq(new.irq, parport_null_intr_func,
-			  SA_INTERRUPT, new.name, tmp) != 0) {
-			printk(KERN_INFO "%s: unable to claim IRQ %d\n", 
-			       new.name, new.irq);
-			kfree(tmp);
-			release_region(new.base, new.size);
-			if( new.modes & PARPORT_MODE_ECR )
-				release_region(new.base+0x400, 3);
-			kfree(new.name);
-			return NULL;
-		}
-	}
-
-	/* Here we chain the entry to our list. */
-	if (portlist_tail)
-		portlist_tail->next = tmp;
-	portlist_tail = tmp;
-	if (!portlist)
-		portlist = tmp;
-
-	printk(KERN_INFO "%s at 0x%x", tmp->name, tmp->base);
-	if (tmp->irq >= 0)
-		printk(", irq %d", tmp->irq);
-	if (tmp->dma >= 0)
-		printk(", dma %d", tmp->dma);
-	printk(" [");
-	{
-		/* Ugh! */
-#define printmode(x) {if(tmp->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
-		int f = 0;
-		printmode(SPP);
-		printmode(PS2);
-		printmode(EPP);
-		printmode(ECP);
-		printmode(ECPEPP);
-		printmode(ECPPS2);
-#undef printmode
-	}
-	printk("]\n");
-	portcount++;
-
-	/* Restore device back to default conditions */
-	if (tmp->modes & PARPORT_MODE_ECR)
-		w_ecr(tmp, tmp->ecr);
-	w_ctr(tmp, tmp->ctr);
-
-	tmp->probe_info.class = PARPORT_CLASS_LEGACY;  /* assume the worst */
-	return tmp;
-}
-
-void parport_destroy(struct parport *port)
-{
-	/* Dangerous to try destroying a port if its friends are nearby. */
-	if (port->devices) {
-		printk("%s: attempt to release active port\n", port->name);
-		return;		/* Devices still present */
-	}
-
-	/* No point in further destroying a port that already lies in ruins. */
-	if (port->flags & PARPORT_FLAG_COMA) 
-		return;
-
-	/* Now clean out the port entry */
-	if (port->irq >= 0)
-		free_irq(port->irq, port);
-	if (port->dma >= 0)
-		free_dma(port->dma);
-	release_region(port->base, port->size);
-	if( port->modes & PARPORT_MODE_ECR )
-		release_region(port->base+0x400, 3);
-	port->flags |= PARPORT_FLAG_COMA; 
-}
-
-struct ppd *parport_register_device(struct parport *port, const char *name,
-				   callback_func pf, callback_func kf,
-				   irq_handler_func irq_func, int flags,
-				   void *handle)
-{
-	struct ppd *tmp;
-
-	/* We only allow one lurker device (eg PLIP) */
-	if (flags & PARPORT_DEV_LURK) {
-		if (port->lurker) {
-			printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
-				   port->name, name);
-			return NULL;
-		}
-		if (!pf || !kf) {
-			printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n"
-				   ,port->name, name);
-			return NULL;
-		}
-	}
-
-	/* We may need to claw back the port hardware. */
-	if (port->flags & PARPORT_FLAG_COMA) {
-		if (check_region(port->base, 3)) {
-			return NULL;
-		}
-		request_region(port->base, port->size, port->name);
-		if( port->modes & PARPORT_MODE_ECR )
-			request_region(port->base+0x400, 3,port->name);
-			
-		if (port->dma >= 0) {
-			if (request_dma(port->dma, port->name)) {
-				release_region(port->base, port->size);
-				if( port->modes & PARPORT_MODE_ECR )
-					release_region(port->base+0x400, 3);
-				return NULL;
-			}
-		}
-		if (port->irq != PARPORT_IRQ_NONE) {
-			if (request_irq(port->irq, parport_null_intr_func,
-					SA_INTERRUPT, port->name,
-					port) != 0) {
-				release_region(port->base, port->size);
-				if( port->modes & PARPORT_MODE_ECR )
-					release_region(port->base+0x400, 3);
-				if (port->dma >= 0)
-					free_dma(port->dma);
-				return NULL;
-			}
-		}
-		port->flags &= ~PARPORT_FLAG_COMA;
-	}
-
-	tmp = kmalloc(sizeof(struct ppd), GFP_KERNEL);
-	tmp->name = (char *) name;
-	tmp->port = port;
-	tmp->preempt = pf;
-	tmp->wakeup = kf;
-	tmp->private = handle;
-	tmp->flags = flags;
-	tmp->irq_func = irq_func;
-	tmp->ctr = port->ctr;
-	tmp->ecr = port->ecr;
-
-	/* Chain this onto the list */
-	tmp->prev = NULL;
-	tmp->next = port->devices;
-	if (port->devices)
-		port->devices->prev = tmp;
-	port->devices = tmp;
-
-	if (flags & PARPORT_DEV_LURK)
-		port->lurker = tmp;
-
-	inc_parport_count();
-
-	return tmp;
-}
-
-void parport_unregister_device(struct ppd *dev)
-{
-	struct parport *port;
-
-	if (!dev) {
-		printk(KERN_ERR "parport_unregister_device: passed NULL\n");
-		return;
-	}
-
-	port = dev->port;
-
-	if (port->cad == dev) {
-		printk(KERN_INFO "%s: refused to unregister currently active device %s\n", port->name, dev->name);
-		return;
-	}
-
-	if (port->lurker == dev)
-		port->lurker = NULL;
-
-	if (dev->next)
-		dev->next->prev = dev->prev;
-	if (dev->prev)
-		dev->prev->next = dev->next;
-	else
-		port->devices = dev->next;
-
-	kfree(dev);
-
-	dec_parport_count();
-
-	/* If there are no more devices, put the port to sleep. */
-	if (!port->devices)
-		parport_destroy(port);
-
-	return;
-}
-
-int parport_claim(struct ppd *dev)
-{
-	struct ppd *pd1;
-
-	if (dev->port->cad == dev) {
-		printk(KERN_INFO "%s: %s already owner\n",
-			   dev->port->name,dev->name);
-		return 0;
-	}
-
-	/* Preempt any current device */
-	pd1 = dev->port->cad;
-	if (dev->port->cad) {
-		if (dev->port->cad->preempt) {
-			/* Now try to preempt */
-			if (dev->port->cad->preempt(dev->port->cad->private))
-				return -EAGAIN;
-
-			/* Save control registers */
-			if (dev->port->modes & PARPORT_MODE_ECR)
-				dev->port->cad->ecr = dev->port->ecr = 
-					r_ecr(dev->port);
-			if (dev->port->modes & PARPORT_MODE_SPP)
-				dev->port->cad->ctr = dev->port->ctr =
-					r_ctr(dev->port);
-		} else
-			return -EAGAIN;
-	}
-
-	/* Watch out for bad things */
-	if (dev->port->cad != pd1) {
-		printk(KERN_WARNING "%s: death while preempting %s\n",
-		       dev->port->name, dev->name);
-		if (dev->port->cad)
-			return -EAGAIN;
-	}
-
-	/* Now we do the change of devices */
-	dev->port->cad = dev;
-
-	if (dev->port->irq >= 0) {
-		free_irq(dev->port->irq, dev->port);
-		request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
-			    parport_null_intr_func, SA_INTERRUPT, dev->name,
-			    dev->port);
-	}
-
-	/* Restore control registers */
-	if (dev->port->modes & PARPORT_MODE_ECR)
-		if (dev->ecr != dev->port->ecr) w_ecr(dev->port, dev->ecr);
-	if (dev->port->modes & PARPORT_MODE_SPP)
-		if (dev->ctr != dev->port->ctr) w_ctr(dev->port, dev->ctr);
-
-	return 0;
-}
-
-void parport_release(struct ppd *dev)
-{
-	struct ppd *pd1;
-
-	/* Make sure that dev is the current device */
-	if (dev->port->cad != dev) {
-		printk(KERN_WARNING "%s: %s tried to release parport when not owner\n", dev->port->name, dev->name);
-		return;
-	}
-	dev->port->cad = NULL;
-
-	/* Save control registers */
-	if (dev->port->modes & PARPORT_MODE_ECR)
-		dev->ecr = dev->port->ecr = r_ecr(dev->port);
-	if (dev->port->modes & PARPORT_MODE_SPP)
-		dev->ctr = dev->port->ctr = r_ctr(dev->port);
-	
-	if (dev->port->irq >= 0) {
-		free_irq(dev->port->irq, dev->port);
-		request_irq(dev->port->irq, parport_null_intr_func,
-			    SA_INTERRUPT, dev->port->name, dev->port);
- 	}
-
-	/* Walk the list, offering a wakeup callback to everybody other
-	 * than the lurker and the device that called us.
-	 */
-	for (pd1 = dev->next; pd1; pd1 = pd1->next) {
-		if (!(pd1->flags & PARPORT_DEV_LURK)) {
-			if (pd1->wakeup) {
-				pd1->wakeup(pd1->private);
-				if (dev->port->cad)
-					return;
-			}
-		}
-	}
-
-	for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
-		if (!(pd1->flags & PARPORT_DEV_LURK)) {
-			if (pd1->wakeup) {
-				pd1->wakeup(pd1->private);
-				if (dev->port->cad)
-					return;
-			}
-		}
-	}
-
-	/* Now give the lurker a chance.
-	 * There should be a wakeup callback because we checked for it
-	 * at registration.
-	 */
-	if (dev->port->lurker && (dev->port->lurker != dev)) {
-		if (dev->port->lurker->wakeup) {
-			dev->port->lurker->wakeup(dev->port->lurker->private);
-		} 
-#ifdef PARPORT_PARANOID
-		else {  /* can't happen */
-			printk(KERN_DEBUG
-			  "%s (%s): lurker's wakeup callback went away!\n",
-			       dev->port->name, dev->name);
-		}
-#endif
-	}
-}
-
-/* The following read funktions are an implementation of a status readback
- * and device id request confirming to IEEE1284-1994.
- *
- * These probably ought to go in some seperate file, so people like the SPARC
- * don't have to pull them in.
- */
-
-/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
- * 25 for this. After this time we can create a timeout because the
- * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
- * waiting a maximum time of 500 us busy (this is for speed). If there is
- * not the right answer in this time, we call schedule and other processes
- * are able "to eat" the time up to 30ms.  So the maximum load avarage can't
- * get above 5% for a read even if the peripheral is really slow. (but your
- * read gets very slow then - only about 10 characters per second. This
- * should be tuneable). Thanks to Andreas who pointed me to this and ordered
- * the documentation.
- */ 
-
-int parport_wait_peripheral(struct parport *port, unsigned char mask, 
-	unsigned char result)
-{
-	int counter=0;
-	unsigned char status; 
-	
-	do {
-		status = parport_r_status(port);
-		udelay(25);
-		counter++;
-		if (need_resched)
-			schedule();
-	} while ( ((status & mask) != result) && (counter < 20) );
-	if ( (counter == 20) && ((status & mask) != result) ) { 
-		current->state=TASK_INTERRUPTIBLE;
-		current->timeout=jiffies+4;
-		schedule(); /* wait for 4 scheduler runs (40ms) */
-		status = parport_r_status(port);
-		if ((status & mask) != result) return 1; /* timeout */
-	}
-	return 0; /* okay right response from device */
-}		
-
-/* Test if nibble mode for status readback is okay. Returns the value false
- * if the printer doesn't support readback at all. If it supports readbacks
- * and printer data is available the function returns 1, otherwise 2. The
- * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
- * 4 is for "request device id using nibble mode". The request for the
- * device id is best done in an ioctl (or at bootup time).  There is no
- * check for an invalid value, the only function using this call at the
- * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
- * trusted kernel.
- */
-int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) 
-{
-	parport_w_data(port, mode);
-	udelay(5);		
-	parport_w_ctrl(port, parport_r_ctrl(port) & ~8);  /* SelectIN low */
-	parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
-	if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
-		parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
-		return 0; /* first stage of negotiation failed, 
-                           * no IEEE1284 compliant device on this port 
-                           */ 
-	}
-	parport_w_ctrl(port, parport_r_ctrl(port) | 1);      /* Strobe high */
-	udelay(5);				     /* Strobe wait */
-	parport_w_ctrl(port, parport_r_ctrl(port) & ~1);     /* Strobe low */
-	udelay(5);
-	parport_w_ctrl(port, parport_r_ctrl(port) & ~2);     /* AutoFeed low */
-	return (parport_wait_peripheral(port, 0x20, 0))?2:1;
-}
diff -u --recursive --new-file v2.1.46/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.46/linux/fs/dcache.c	Thu Jul 17 10:06:06 1997
+++ linux/fs/dcache.c	Wed Jul 23 11:01:00 1997
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/malloc.h>
+#include <linux/init.h>
 
 /*
  * This is the single most critical data structure when it comes
@@ -229,6 +230,46 @@
 	return __dlookup(d_hash(dir, name->hash), dir, name);
 }
 
+/*
+ * An insecure source has sent us a dentry, here we verify it.
+ *
+ * This is just to make knfsd able to have the dentry pointer
+ * in the NFS file handle.
+ *
+ * NOTE! Do _not_ dereference the pointers before we have
+ * validated them. We can test the pointer values, but we
+ * must not actually use them until we have found a valid
+ * copy of the pointer in kernel space..
+ */
+int d_validate(struct dentry *dentry, struct dentry *dparent,
+	       unsigned int hash, unsigned int len)
+{
+	struct list_head *base = d_hash(dparent, hash);
+	struct list_head *lhp = base;
+
+	while ((lhp = lhp->next) != base) {
+		if (dentry == list_entry(lhp, struct dentry, d_hash))
+			goto found_it;
+	}
+
+	/* Special case, local mount points don't live in the hashes.
+	 * So if we exhausted the chain, search the super blocks.
+	 */
+	if (dentry && dentry == dparent) {
+		struct super_block *sb;
+
+		for (sb = super_blocks + 0; sb < super_blocks + NR_SUPER; sb++) {
+			if (sb->s_root == dentry)
+				goto found_it;
+		}
+	}
+	return 0;
+found_it:
+	return	(dentry->d_parent == dparent) &&
+		(dentry->d_name.hash == hash) &&
+		(dentry->d_name.len == len);
+}
+
 static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent)
 {
 	list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash));
@@ -313,13 +354,17 @@
 	d_insert_to_parent(dentry, newdir);
 }
 
+/*
+ * This is broken in more ways than one. Unchecked recursion,
+ * unchecked buffer size. Get rid of it.
+ */
 int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
 {
 	if (IS_ROOT(entry) || (chroot && entry == chroot)) {
 		*buf = '/';
 		return 1;
 	} else {
-		int len = d_path(entry->d_parent, chroot, buf);
+		int len = d_path(entry->d_covers->d_parent, chroot, buf);
 
 		buf += len;
 		if (len > 1) {
@@ -331,7 +376,7 @@
 	}
 }
 
-void dcache_init(void)
+__initfunc(void dcache_init(void))
 {
 	int i;
 	struct list_head *d = dentry_hashtable;
diff -u --recursive --new-file v2.1.46/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.1.46/linux/fs/fat/inode.c	Thu Jul 17 10:06:06 1997
+++ linux/fs/fat/inode.c	Wed Jul 23 10:29:42 1997
@@ -44,8 +44,18 @@
 			MSDOS_I(inode)->i_linked = NULL;
 		}
 		if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
-		return;
 	}
+}
+
+void fat_delete_inode(struct inode *inode)
+{
+	struct inode *depend, *linked;
+	struct super_block *sb;
+
+	depend = MSDOS_I(inode)->i_depend;
+	linked = MSDOS_I(inode)->i_linked;
+	sb = inode->i_sb;
+
 	inode->i_size = 0;
 	fat_truncate(inode);
 	if (depend) {
diff -u --recursive --new-file v2.1.46/linux/fs/fat/misc.c linux/fs/fat/misc.c
--- v2.1.46/linux/fs/fat/misc.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/fat/misc.c	Wed Jul 23 10:29:42 1997
@@ -40,7 +40,7 @@
 
 	not_ro = !(s->s_flags & MS_RDONLY);
 	if (not_ro) s->s_flags |= MS_RDONLY;
-	printk("Filesystem panic (dev %s).", kdevname(s->s_dev));
+	printk("Filesystem panic (dev %s).\n  %s\n", kdevname(s->s_dev), msg);
 	if (not_ro)
 		printk("  File system has been set read-only\n");
 }
diff -u --recursive --new-file v2.1.46/linux/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c
--- v2.1.46/linux/fs/hpfs/hpfs_fs.c	Mon Jun 16 16:35:57 1997
+++ linux/fs/hpfs/hpfs_fs.c	Thu Jul 24 11:25:07 1997
@@ -128,15 +128,16 @@
 
 static void hpfs_read_inode(struct inode *);
 static void hpfs_put_super(struct super_block *);
-static void hpfs_statfs(struct super_block *, struct statfs *, int);
+static int hpfs_statfs(struct super_block *, struct statfs *, int);
 static int hpfs_remount_fs(struct super_block *, int *, char *);
 
 static const struct super_operations hpfs_sops =
 {
 	hpfs_read_inode,		/* read_inode */
-	NULL,				/* notify_change */
 	NULL,				/* write_inode */
 	NULL,				/* put_inode */
+	NULL,				/* delete_inode */
+	NULL,				/* notify_change */
 	hpfs_put_super,			/* put_super */
 	NULL,				/* write_super */
 	hpfs_statfs,			/* statfs */
@@ -175,6 +176,7 @@
 	NULL,				/* mknod */
 	NULL,				/* rename */
 	NULL,				/* readlink */
+	NULL,				/* follow_link */
 	generic_readpage,		/* readpage */
 	NULL,				/* writepage */
 	(int (*)(struct inode *, int))
@@ -189,7 +191,7 @@
 			  char *buf, unsigned long count);
 static int hpfs_readdir(struct inode *inode, struct file *filp,
 			void *dirent, filldir_t filldir);
-static int hpfs_lookup(struct inode *, const char *, int, struct inode **);
+static int hpfs_lookup(struct inode *, struct dentry *);
 
 static const struct file_operations hpfs_dir_ops =
 {
@@ -485,10 +487,10 @@
 	 * all set.  try it out.
 	 */
 
-	s->s_mounted = iget(s, s->s_hpfs_root);
+	s->s_root = d_alloc_root(iget(s, s->s_hpfs_root), NULL);
 	unlock_super(s);
 
-	if (!s->s_mounted) {
+	if (!s->s_root) {
 		printk("HPFS: hpfs_read_super: inode get failed\n");
 		s->s_dev = 0;
 		MOD_DEC_USE_COUNT;
@@ -501,7 +503,8 @@
 
 	root_dno = fnode_dno(dev, s->s_hpfs_root);
 	if (root_dno)
-		de = map_dirent(s->s_mounted, root_dno, "\001\001", 2, &qbh);
+		de = map_dirent(s->s_root->d_inode, root_dno,
+				"\001\001", 2, &qbh);
 	if (!root_dno || !de) {
 		printk("HPFS: "
 		       "hpfs_read_super: root dir isn't in the root dir\n");
@@ -510,9 +513,9 @@
 		return 0;
 	}
 
-	s->s_mounted->i_atime = local_to_gmt(de->read_date);
-	s->s_mounted->i_mtime = local_to_gmt(de->write_date);
-	s->s_mounted->i_ctime = local_to_gmt(de->creation_date);
+	s->s_root->d_inode->i_atime = local_to_gmt(de->read_date);
+	s->s_root->d_inode->i_mtime = local_to_gmt(de->write_date);
+	s->s_root->d_inode->i_ctime = local_to_gmt(de->creation_date);
 
 	brelse4(&qbh);
 	return s;
@@ -739,7 +742,7 @@
  * directory band -- not exactly right but pretty analogous.
  */
 
-static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+static int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
 {
 	struct statfs tmp;
 
@@ -763,7 +766,8 @@
 	tmp.f_files = s->s_hpfs_dirband_size;
 	tmp.f_ffree = s->s_hpfs_n_free_dnodes;
 	tmp.f_namelen = 254;
-	copy_to_user(buf, &tmp, bufsiz);
+
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
 /*
@@ -1115,17 +1119,17 @@
  * the boondocks.)
  */
 
-static int hpfs_lookup(struct inode *dir, const char *name, int len,
-		       struct inode **result)
+static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
 {
 	struct quad_buffer_head qbh;
 	struct hpfs_dirent *de;
 	struct inode *inode;
 	ino_t ino;
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
 
 	/* In case of madness */
 
-	*result = 0;
 	if (dir == 0)
 		return -ENOENT;
 	if (!S_ISDIR(dir->i_mode))
@@ -1197,7 +1201,7 @@
 	 * Made it.
 	 */
 
-	*result = inode;
+	d_instantiate(dentry, inode);
 	iput(dir);
 	return 0;
 
@@ -1697,7 +1701,7 @@
 	if (!data)
 		goto bail;
 
-	qbh->bh[0] = bh = breada(dev, secno, 512, 0, UINT_MAX);
+	qbh->bh[0] = bh = bread(dev, secno, 512);
 	if (!bh)
 		goto bail0;
 	memcpy(data, bh->b_data, 512);
diff -u --recursive --new-file v2.1.46/linux/fs/inode.c linux/fs/inode.c
--- v2.1.46/linux/fs/inode.c	Sun Jul 20 20:41:58 1997
+++ linux/fs/inode.c	Mon Jul 21 14:25:47 1997
@@ -141,32 +141,31 @@
 		inode->i_sb->s_op->write_inode(inode);
 }
 
+static inline void sync_one(struct list_head *head, struct list_head *clean,
+			    struct list_head *placement, struct inode *inode)
+{
+	list_del(placement);
+	if (test_bit(I_LOCK, &inode->i_state)) {
+		list_add(placement, head);
+		spin_unlock(&inode_lock);
+		__wait_on_inode(inode);
+	} else {
+		list_add(placement, clean);
+		clear_bit(I_DIRTY, &inode->i_state);
+		set_bit(I_LOCK, &inode->i_state);
+		spin_unlock(&inode_lock);
+		write_inode(inode);
+		unlock_inode(inode);
+	}
+	spin_lock(&inode_lock);
+}
+
 static inline void sync_list(struct list_head *head, struct list_head *clean)
 {
 	struct list_head * tmp;
 
-	while ((tmp = head->prev) != head) {
-		struct inode *inode = list_entry(tmp, struct inode, i_list);
-		list_del(tmp);
-
-		/*
-		 * If the inode is locked, it's already being written out.
-		 * We have to wait for it, though.
-		 */
-		if (test_bit(I_LOCK, &inode->i_state)) {
-			list_add(tmp, head);
-			spin_unlock(&inode_lock);
-			__wait_on_inode(inode);
-		} else {
-			list_add(tmp, clean);
-			clear_bit(I_DIRTY, &inode->i_state);
-			set_bit(I_LOCK, &inode->i_state);
-			spin_unlock(&inode_lock);
-			write_inode(inode);
-			unlock_inode(inode);
-		}
-		spin_lock(&inode_lock);
-	}	
+	while ((tmp = head->prev) != head)
+		sync_one(head, clean, tmp, list_entry(tmp, struct inode, i_list));
 }
 
 /*
@@ -178,6 +177,17 @@
 {
 	spin_lock(&inode_lock);
 	sync_list(&inode_dirty, &inode_in_use);
+	spin_unlock(&inode_lock);
+}
+
+/*
+ * Needed by knfsd
+ */
+void write_inode_now(struct inode *inode)
+{
+	spin_lock(&inode_lock);
+	if (test_bit(I_DIRTY, &inode->i_state))
+		sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode);
 	spin_unlock(&inode_lock);
 }
 
diff -u --recursive --new-file v2.1.46/linux/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c
--- v2.1.46/linux/fs/lockd/svcsubs.c	Tue May 13 22:41:14 1997
+++ linux/fs/lockd/svcsubs.c	Mon Jul 21 09:53:20 1997
@@ -24,7 +24,7 @@
  * Global file hash table
  */
 #define FILE_NRHASH		32
-#define FILE_HASH(dev, ino)	(((dev) + (ino)) & FILE_NRHASH)
+#define FILE_HASH(dhash)	((dhash) & FILE_NRHASH)
 static struct nlm_file *	nlm_files[FILE_NRHASH];
 static struct semaphore		nlm_file_sema = MUTEX;
 
@@ -43,21 +43,21 @@
 {
 	struct nlm_file	*file;
 	struct knfs_fh	*fh = (struct knfs_fh *) f;
-	unsigned int	hash = FILE_HASH(fh->fh_dev, fh->fh_ino);
+	unsigned int	hash = FILE_HASH(fh->fh_dhash);
 	u32		nfserr;
 
-	dprintk("lockd: nlm_file_lookup(%04x/%ld)\n", fh->fh_dev, fh->fh_ino);
+	dprintk("lockd: nlm_file_lookup(%p)\n", fh->fh_dentry);
 
 	/* Lock file table */
 	down(&nlm_file_sema);
 
 	for (file = nlm_files[hash]; file; file = file->f_next) {
-		if (file->f_handle.fh_ino == fh->fh_ino
+		if (file->f_handle.fh_dentry == fh->fh_dentry
 		 && !memcmp(&file->f_handle, fh, sizeof(*fh)))
 			goto found;
 	}
 
-	dprintk("lockd: creating file for %04x/%ld\n", fh->fh_dev, fh->fh_ino);
+	dprintk("lockd: creating file for %p\n", fh->fh_dentry);
 	if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) {
 		up(&nlm_file_sema);
 		return nlm_lck_denied_nolocks;
@@ -93,11 +93,11 @@
 static inline void
 nlm_delete_file(struct nlm_file *file)
 {
-	struct inode	*inode = nlmsvc_file_inode(file);
+	struct dentry *dentry = file->f_file.f_dentry;
 	struct nlm_file	**fp, *f;
 
-	dprintk("lockd: closing file %04x/%ld\n", inode->i_dev, inode->i_ino);
-	fp = nlm_files + FILE_HASH(inode->i_dev, inode->i_ino);
+	dprintk("lockd: closing file %p\n", dentry);
+	fp = nlm_files + FILE_HASH(dentry->d_name.hash);
 	while ((f = *fp) != NULL) {
 		if (f == file) {
 			*fp = file->f_next;
diff -u --recursive --new-file v2.1.46/linux/fs/minix/bitmap.c linux/fs/minix/bitmap.c
--- v2.1.46/linux/fs/minix/bitmap.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/minix/bitmap.c	Wed Jul 23 13:02:08 1997
@@ -191,7 +191,7 @@
 		printk("free_inode: inode has no device\n");
 		return;
 	}
-	if (inode->i_count != 1) {
+	if (inode->i_count > 1) {
 		printk("free_inode: inode has count=%d\n",inode->i_count);
 		return;
 	}
@@ -251,7 +251,6 @@
 		iput(inode);
 		return NULL;
 	}
-	inode->i_count = 1;
 	inode->i_nlink = 1;
 	inode->i_dev = sb->s_dev;
 	inode->i_uid = current->fsuid;
diff -u --recursive --new-file v2.1.46/linux/fs/minix/dir.c linux/fs/minix/dir.c
--- v2.1.46/linux/fs/minix/dir.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/minix/dir.c	Wed Jul 23 13:02:08 1997
@@ -92,5 +92,6 @@
 		} while (offset < 1024 && filp->f_pos < inode->i_size);
 		brelse(bh);
 	}
+	UPDATE_ATIME(inode);
 	return 0;
 }
diff -u --recursive --new-file v2.1.46/linux/fs/minix/file.c linux/fs/minix/file.c
--- v2.1.46/linux/fs/minix/file.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/minix/file.c	Wed Jul 23 13:02:08 1997
@@ -108,7 +108,13 @@
 			}
 		}
 		p = (pos % BLOCK_SIZE) + bh->b_data;
-		copy_from_user(p,buf,c);
+		c -= copy_from_user(p,buf,c);
+		if (!c) {
+			brelse(bh);
+			if (!written)
+				written = -EFAULT;
+			break;
+		}
 		update_vm_cache(inode, pos, p, c);
 		mark_buffer_uptodate(bh, 1);
 		mark_buffer_dirty(bh, 0);
diff -u --recursive --new-file v2.1.46/linux/fs/minix/namei.c linux/fs/minix/namei.c
--- v2.1.46/linux/fs/minix/namei.c	Sun Jul 20 20:41:58 1997
+++ linux/fs/minix/namei.c	Wed Jul 23 13:02:08 1997
@@ -109,7 +109,7 @@
 
 	bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
 	if (bh) {
-		unsigned long ino = le32_to_cpu(de->inode);
+		int ino = de->inode;
 		brelse (bh);
 		inode = iget(dir->i_sb, ino);
  
@@ -593,6 +593,7 @@
 	inode->i_nlink++;
 	inode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(inode);
+	inode->i_count++;
 	d_instantiate(dentry, inode);
 	return 0;
 }
diff -u --recursive --new-file v2.1.46/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.1.46/linux/fs/msdos/namei.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/msdos/namei.c	Wed Jul 23 10:31:03 1997
@@ -53,9 +53,10 @@
 
 struct super_operations msdos_sops = { 
 	msdos_read_inode,
-	fat_notify_change,
 	fat_write_inode,
 	fat_put_inode,
+	fat_delete_inode,
+	fat_notify_change,
 	msdos_put_super,
 	NULL, /* added in 0.96c */
 	fat_statfs,
@@ -190,79 +191,56 @@
 }
 
 /***** Get inode using directory and name */
-int msdos_lookup(struct inode *dir,const char *name,int len,
-    struct inode **result)
+int msdos_lookup(struct inode *dir,struct dentry *dentry)
 {
 	struct super_block *sb = dir->i_sb;
 	int ino,res;
 	struct msdos_dir_entry *de;
 	struct buffer_head *bh;
-	struct inode *next;
+	struct inode *next, *inode;
 	
 	PRINTK (("msdos_lookup\n"));
 
-	*result = NULL;
-	if (!dir) return -ENOENT;
-	if (!S_ISDIR(dir->i_mode)) {
-		iput(dir);
-		return -ENOENT;
-	}
-	PRINTK (("msdos_lookup 2\n"));
-	if (len == 1 && name[0] == '.') {
-		*result = dir;
-		return 0;
-	}
-	if (len == 2 && name[0] == '.' && name[1] == '.') {
-		ino = fat_parent_ino(dir,0);
-		iput(dir);
-		if (ino < 0) return ino;
-		if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
+	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
+		d_add(dentry, NULL);
 		return 0;
 	}
-	PRINTK (("msdos_lookup 3\n"));
-	if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
-		iput(dir);
-		return res;
-	}
 	PRINTK (("msdos_lookup 4\n"));
 	if (bh)
 		fat_brelse(sb, bh);
 	PRINTK (("msdos_lookup 4.5\n"));
-	if (!(*result = iget(dir->i_sb,ino))) {
-		iput(dir);
+	if (!(inode = iget(dir->i_sb,ino)))
 		return -EACCES;
-	}
 	PRINTK (("msdos_lookup 5\n"));
-	if (!(*result)->i_sb ||
-	    ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+	if (!inode->i_sb ||
+	    (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
 		/* crossed a mount point into a non-msdos fs */
-		iput(dir);
+		d_add(dentry, inode);
 		return 0;
 	}
-	if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
-		iput(*result);
-		iput(dir);
-		return -ENOENT;
+	if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
+		iput(inode);
+		d_add(dentry, NULL);
+		return 0;
 	}
 	PRINTK (("msdos_lookup 6\n"));
-	while (MSDOS_I(*result)->i_old) {
-		next = MSDOS_I(*result)->i_old;
-		iput(*result);
-		if (!(*result = iget(next->i_sb,next->i_ino))) {
+	while (MSDOS_I(inode)->i_old) {
+		next = MSDOS_I(inode)->i_old;
+		iput(inode);
+		if (!(inode = iget(next->i_sb,next->i_ino))) {
 			fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
-			iput(dir);
 			return -ENOENT;
 		}
 	}
 	PRINTK (("msdos_lookup 7\n"));
-	iput(dir);
+	d_add(dentry, inode);
 	PRINTK (("msdos_lookup 8\n"));
 	return 0;
 }
 
 
 /***** Creates a directory entry (name is already formatted). */
-static int msdos_create_entry(struct inode *dir, const char *name,int len,
+static int msdos_create_entry(struct inode *dir, const char *name,
     int is_dir, int is_hid, struct inode **result)
 {
 	struct super_block *sb = dir->i_sb;
@@ -270,6 +248,7 @@
 	struct msdos_dir_entry *de;
 	int res,ino;
 
+	*result = NULL;
 	if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
 		if (res != -ENOENT) return res;
 		if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
@@ -300,23 +279,22 @@
 }
 
 /***** Create a file or directory */
-int msdos_create(struct inode *dir,const char *name,int len,int mode,
-	struct inode **result)
+int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
 {
 	struct super_block *sb = dir->i_sb;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
+	struct inode *inode;
 	char msdos_name[MSDOS_NAME];
 	int ino,res,is_hid;
 
 	if (!dir) return -ENOENT;
 	if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
-				     name,len,msdos_name,0,
-				     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
-		iput(dir);
+				     dentry->d_name.name,dentry->d_name.len,
+				     msdos_name,0,
+				     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
 		return res;
-	}
-	is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
 	fat_lock_creation();
 	/* Scan for existing file twice, so that creating a file fails
 	 * with -EINVAL if the other (dotfile/nondotfile) exists.
@@ -325,19 +303,17 @@
 	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
 		fat_unlock_creation();
 		fat_brelse(sb, bh);
-		iput(dir);
 		return is_hid ? -EEXIST : -EINVAL;
  	}
 	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
 		fat_unlock_creation();
 		fat_brelse(sb, bh);
-		iput(dir);
 		return is_hid ? -EINVAL : -EEXIST;
  	}
-	res = msdos_create_entry(dir,msdos_name,len,S_ISDIR(mode),is_hid,
-				 result);
+	res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
+				 &inode);
 	fat_unlock_creation();
-	iput(dir);
+	d_instantiate(dentry, inode);
 	return res;
 }
 
@@ -388,7 +364,7 @@
 }
 
 /***** Remove a directory */
-int msdos_rmdir(struct inode *dir,const char *name,int len)
+int msdos_rmdir(struct inode *dir,struct dentry *dentry)
 {
 	struct super_block *sb = dir->i_sb;
 	int res,ino;
@@ -399,11 +375,10 @@
 	bh = NULL;
 	inode = NULL;
 	res = -EPERM;
-	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
+	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
+			      &bh,&de,&ino)) < 0)
 		goto rmdir_done;
-	if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done;
-	res = -ENOENT;
-	if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done;
+	inode = dentry->d_inode;
 	res = -ENOTDIR;
 	if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
 	res = -EBUSY;
@@ -419,16 +394,15 @@
 	mark_inode_dirty(dir);
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
+	d_delete(dentry);
 	res = 0;
 rmdir_done:
 	fat_brelse(sb, bh);
-	iput(dir);
-	iput(inode);
 	return res;
 }
 
 /***** Make a directory */
-int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
+int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
 {
 	struct super_block *sb = dir->i_sb;
 	struct buffer_head *bh;
@@ -438,37 +412,34 @@
 	int ino,res,is_hid;
 
 	if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
-				     name,len,msdos_name,0,
-				     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
-		iput(dir);
+				     dentry->d_name.name,dentry->d_name.len,
+				     msdos_name,0,
+				     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
 		return res;
-	}
-	is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
 	fat_lock_creation();
 	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
 		fat_unlock_creation();
 		fat_brelse(sb, bh);
-		iput(dir);
 		return -EEXIST;
  	}
-	if ((res = msdos_create_entry(dir,msdos_name,len,1,is_hid,
+	if ((res = msdos_create_entry(dir,msdos_name,1,is_hid,
 				      &inode)) < 0) {
 		fat_unlock_creation();
-		iput(dir);
 		return res;
 	}
 	dir->i_nlink++;
 	inode->i_nlink = 2; /* no need to mark them dirty */
 	MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
 	if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
-	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,1,0,&dot)) < 0)
+	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
 		goto mkdir_error;
 	dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
 	MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
 	dot->i_nlink = inode->i_nlink;
 	mark_inode_dirty(dot);
 	iput(dot);
-	if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0)
+	if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
 		goto mkdir_error;
 	fat_unlock_creation();
 	dot->i_size = dir->i_size;
@@ -477,12 +448,10 @@
 	mark_inode_dirty(dot);
 	MSDOS_I(inode)->i_busy = 0;
 	iput(dot);
-	iput(inode);
-	iput(dir);
+	d_instantiate(dentry, inode);
 	return 0;
 mkdir_error:
-	iput(inode);
-	if (msdos_rmdir(dir,name,len) < 0)
+	if (msdos_rmdir(dir,dentry) < 0)
 		fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
 	fat_unlock_creation();
 	return res;
@@ -491,8 +460,7 @@
 /***** Unlink a file */
 static int msdos_unlinkx(
 	struct inode *dir,
-	const char *name,
-	int len,
+	struct dentry *dentry,
 	int nospc)	/* Flag special file ? */
 {
 	struct super_block *sb = dir->i_sb;
@@ -503,12 +471,10 @@
 
 	bh = NULL;
 	inode = NULL;
-	if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0)
-		goto unlink_done;
-	if (!(inode = iget(dir->i_sb,ino))) {
-		res = -ENOENT;
+	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
+			      &bh,&de,&ino)) < 0)
 		goto unlink_done;
-	}
+	inode = dentry->d_inode;
 	if (!S_ISREG(inode->i_mode) && nospc){
 		res = -EPERM;
 		goto unlink_done;
@@ -524,28 +490,28 @@
 	mark_inode_dirty(dir);
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
+	d_delete(dentry);	/* This also frees the inode */
 unlink_done:
 	fat_brelse(sb, bh);
-	iput(inode);
-	iput(dir);
 	return res;
 }
 
 /***** Unlink, as called for msdosfs */
-int msdos_unlink(struct inode *dir,const char *name,int len)
+int msdos_unlink(struct inode *dir,struct dentry *dentry)
 {
-	return msdos_unlinkx (dir,name,len,1);
+	return msdos_unlinkx (dir,dentry,1);
 }
 
 /***** Unlink, as called for umsdosfs */
-int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
+int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
 {
-	return msdos_unlinkx (dir,name,len,0);
+	return msdos_unlinkx (dir,dentry,0);
 }
 
 /***** Rename within a directory */
-static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
-    struct inode *new_dir,char *new_name,int new_len,
+static int rename_same_dir(struct inode *old_dir,char *old_name,
+    struct dentry *old_dentry,
+    struct inode *new_dir,char *new_name,struct dentry *new_dentry,
     struct buffer_head *old_bh,
     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 {
@@ -563,10 +529,7 @@
 		return -ENOENT;
 	}
 	if (exists) {
-		if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
-			fat_brelse(sb, new_bh);
-			return -EIO;
-		}
+		new_inode = new_dentry->d_inode;
 		error = S_ISDIR(new_inode->i_mode)
 			? (old_de->attr & ATTR_DIR)
 				? msdos_empty(new_inode)
@@ -576,7 +539,6 @@
 				: 0;
 		if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
 		if (error) {
-			iput(new_inode);
 			fat_brelse(sb, new_bh);
 			return error;
 		}
@@ -589,50 +551,49 @@
 		mark_inode_dirty(new_inode);
 		new_de->name[0] = DELETED_FLAG;
 		fat_mark_buffer_dirty(sb, new_bh, 1);
-		iput(new_inode);
 		fat_brelse(sb, new_bh);
 	}
 	memcpy(old_de->name,new_name,MSDOS_NAME);
+	/* Update the dcache */
+	d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+	d_delete(new_dentry);
 set_hid:
 	old_de->attr = is_hid
 		? (old_de->attr | ATTR_HIDDEN)
 		: (old_de->attr &~ ATTR_HIDDEN);
 	fat_mark_buffer_dirty(sb, old_bh, 1);
 	/* update binary info for conversion, i_attrs */
-	if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
-		MSDOS_I(old_inode)->i_attrs = is_hid
-			? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
-			: (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
-		iput(old_inode);
-	}
+	old_inode = old_dentry->d_inode;
+	MSDOS_I(old_inode)->i_attrs = is_hid
+		? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
+		: (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
 	return 0;
 }
 
 /***** Rename across directories - a nonphysical move */
-static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
-    struct inode *new_dir,char *new_name,int new_len,
+static int rename_diff_dir(struct inode *old_dir,char *old_name,
+    struct dentry *old_dentry,
+    struct inode *new_dir,char *new_name,struct dentry *new_dentry,
     struct buffer_head *old_bh,
     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 {
 	struct super_block *sb = old_dir->i_sb;
 	struct buffer_head *new_bh,*free_bh,*dotdot_bh;
 	struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
-	struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk;
+	struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
+	struct dentry *walk;
 	int new_ino,free_ino,dotdot_ino;
 	int error,exists,ino;
 
 	if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
 	if (old_ino == new_dir->i_ino) return -EINVAL;
-	if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+	walk = new_dentry;
 	/* prevent moving directory below itself */
-	while (walk->i_ino != MSDOS_ROOT_INO) {
-		ino = fat_parent_ino(walk,1);
-		iput(walk);
-		if (ino < 0) return ino;
-		if (ino == old_ino) return -EINVAL;
-		if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
+	for (;;) {
+		if (walk == old_dentry) return -EINVAL;
+		if (walk == walk->d_parent) break;
+		walk = walk->d_parent;
 	}
-	iput(walk);
 	/* find free spot */
 	while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
 	    SCAN_ANY)) < 0) {
@@ -641,14 +602,8 @@
 		if (error) return error;
 	}
 	exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
-	if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
-		fat_brelse(sb, free_bh);
-		if (exists)
-			fat_brelse(sb, new_bh);
-		return -EIO;
-	}
+	old_inode = old_dentry->d_inode;
 	if (*(unsigned char *) old_de->name == DELETED_FLAG) {
-		iput(old_inode);
 		fat_brelse(sb, free_bh);
 		if (exists)
 			fat_brelse(sb, new_bh);
@@ -656,11 +611,7 @@
 	}
 	new_inode = NULL; /* to make GCC happy */
 	if (exists) {  /* Trash the old file! */
-		if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
-			iput(old_inode);
-			fat_brelse(sb, new_bh);
-			return -EIO;
-		}
+		new_inode = new_dentry->d_inode;
 		error = S_ISDIR(new_inode->i_mode)
 			? (old_de->attr & ATTR_DIR)
 				? msdos_empty(new_inode)
@@ -670,8 +621,6 @@
 				: 0;
 		if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
 		if (error) {
-			iput(new_inode);
-			iput(old_inode);
 			fat_brelse(sb, new_bh);
 			return error;
 		}
@@ -690,10 +639,8 @@
 		free_de->name[0] = DELETED_FLAG;
 /*  Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
 		fat_brelse(sb, free_bh);
-		if (exists) {
-			iput(new_inode);
+		if (exists)
 			fat_brelse(sb, new_bh);
-		}
 		return -EIO;
 	}
 	if (exists && S_ISDIR(new_inode->i_mode)) {
@@ -715,7 +662,6 @@
 		/* Two references now exist to free_inode so increase count */
 		free_inode->i_count++;
 		/* free_inode is put after putting new_inode and old_inode */
-		iput(new_inode);
 		fat_brelse(sb, new_bh);
 	}
 	if (S_ISDIR(old_inode->i_mode)) {
@@ -737,16 +683,18 @@
 		iput(dotdot_inode);
 		fat_brelse(sb, dotdot_bh);
 	}
+	/* Update the dcache */
+	d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+	d_delete(new_dentry);
 	error = 0;
 rename_done:
 	fat_brelse(sb, free_bh);
-	iput(old_inode);
 	return error;
 }
 
 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
-int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
-		 struct inode *new_dir,const char *new_name,int new_len)
+int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
+		 struct inode *new_dir,struct dentry *new_dentry)
 {
 	struct super_block *sb = old_dir->i_sb;
 	char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
@@ -756,28 +704,30 @@
 	int is_hid,old_hid; /* if new file and old file are hidden */
 
 	if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
-				       old_name,old_len,old_msdos_name,1,
+				       old_dentry->d_name.name,
+				       old_dentry->d_name.len,old_msdos_name,1,
 				       MSDOS_SB(old_dir->i_sb)->options.dotsOK))
 	    < 0) goto rename_done;
 	if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
-				       new_name,new_len,new_msdos_name,0,
+				       new_dentry->d_name.name,
+				       new_dentry->d_name.len,new_msdos_name,0,
 				       MSDOS_SB(new_dir->i_sb)->options.dotsOK))
 	    < 0) goto rename_done;
-	is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
-	old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
+	is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
+	old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
 	if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
 	    &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
 	fat_lock_creation();
 	if (old_dir == new_dir)
-		error = rename_same_dir(old_dir,old_msdos_name,old_len,new_dir,
-		    new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
-	else error = rename_diff_dir(old_dir,old_msdos_name,old_len,new_dir,
-		    new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
+		error = rename_same_dir(old_dir,old_msdos_name,old_dentry,
+		    new_dir,new_msdos_name,new_dentry,
+		    old_bh,old_de,old_ino,is_hid);
+	else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry,
+		     new_dir,new_msdos_name,new_dentry,
+		     old_bh,old_de,old_ino,is_hid);
 	fat_unlock_creation();
 	fat_brelse(sb, old_bh);
 rename_done:
-	iput(old_dir);
-	iput(new_dir);
 	return error;
 }
 
diff -u --recursive --new-file v2.1.46/linux/fs/namei.c linux/fs/namei.c
--- v2.1.46/linux/fs/namei.c	Sun Jul 20 20:41:58 1997
+++ linux/fs/namei.c	Thu Jul 24 11:54:11 1997
@@ -338,26 +338,39 @@
 	return result;
 }
 
-/*
- * This should check "link_count", but doesn't do that yet..
- */
 static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
 {
 	struct inode * inode = dentry->d_inode;
 
 	if (inode && inode->i_op && inode->i_op->follow_link) {
-		struct dentry *result;
+		if (current->link_count < 5) {
+			struct dentry * result;
 
-		/* This eats the base */
-		result = inode->i_op->follow_link(inode, base);
-		base = dentry;
-		dentry = result;
+			current->link_count++;
+			/* This eats the base */
+			result = inode->i_op->follow_link(inode, base);
+			current->link_count--;
+			dput(dentry);
+			return result;
+		}
+		dput(dentry);
+		dentry = ERR_PTR(-ELOOP);
 	}
 	dput(base);
 	return dentry;
 }
 
 /*
+ * Allow a filesystem to translate the character set of
+ * a file name. This allows for filesystems that are not
+ * case-sensitive, for example.
+ *
+ * This is only a dummy define right now, but eventually
+ * it might become something like "(parent)->d_charmap[c]"
+ */
+#define name_translate_char(parent, c) (c)
+
+/*
  * Name resolution.
  *
  * This is the basic name resolution function, turning a pathname
@@ -397,6 +410,7 @@
 		c = *name;
 		do {
 			len++; name++;
+			c = name_translate_char(base, c);
 			hash = partial_name_hash(c, hash);
 			c = *name;
 		} while (c && (c != '/'));
diff -u --recursive --new-file v2.1.46/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.46/linux/fs/nfs/write.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/nfs/write.c	Wed Jul 23 12:53:04 1997
@@ -424,7 +424,6 @@
 
 /*
  * Update and possibly write a cached page of an NFS file.
- * The page is already locked when we get here.
  *
  * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad
  * things with a page scheduled for an RPC call (e.g. invalidate it).
@@ -441,6 +440,7 @@
 				inode->i_dev, inode->i_ino,
 				count, page->offset+offset, sync);
 
+	set_bit(PG_locked, &page->flags);
 	page_addr = (u8 *) page_address(page);
 
 	/* If wsize is smaller than page size, update and write
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/export.c linux/fs/nfsd/export.c
--- v2.1.46/linux/fs/nfsd/export.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/nfsd/export.c	Mon Jul 21 09:53:20 1997
@@ -90,9 +90,25 @@
 }
 
 /*
+ * Look up the root inode of the parent fs.
+ * We have to go through iget in order to allow for wait_on_inode.
+ */
+static inline int
+nfsd_parentdev(dev_t *devp)
+{
+	struct super_block	*sb;
+
+	if (!(sb = get_super(*devp)) || !sb->s_root->d_covers)
+		return 0;
+	if (*devp == sb->s_root->d_covers->d_inode->i_dev)
+		return 0;
+	*devp = sb->s_root->d_covers->d_inode->i_dev;
+	return 1;
+}
+
+/*
  * Find the parent export entry for a given fs. This function is used
  * only by the export syscall to keep the export tree consistent.
- * nfsd_parentdev(dev) returns the device on which dev is mounted.
  */
 static svc_export *
 exp_parent(svc_client *clp, dev_t dev)
@@ -118,6 +134,7 @@
 	svc_client	*clp;
 	svc_export	*exp, *parent;
 	svc_export	**head;
+	struct dentry	*dentry = NULL;
 	struct inode	*inode = NULL;
 	int		i, err;
 	dev_t		dev;
@@ -128,7 +145,7 @@
 	    !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
 		return -EINVAL;
 
-	dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
+	dprintk("exp_export called for %s:%s (%x/%d fl %x).\n",
 			nxp->ex_client, nxp->ex_path,
 			nxp->ex_dev, nxp->ex_ino, nxp->ex_flags);
 	dev = nxp->ex_dev;
@@ -161,8 +178,19 @@
 		goto finish;
 	}
 
-	/* Look up the inode */
-	if (!(inode = nfsd_iget(nxp->ex_dev, nxp->ex_ino))) {
+	/* Look up the dentry */
+	dentry = lookup_dentry(nxp->ex_path, NULL, 0);
+	if (IS_ERR(dentry)) {
+		err = -EINVAL;
+		goto finish;
+	}
+	inode = dentry->d_inode;
+	if(!inode) {
+		err = -ENOENT;
+		goto finish;
+	}
+	if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) {
+		/* I'm just being paranoid... */
 		err = -EINVAL;
 		goto finish;
 	}
@@ -175,9 +203,9 @@
 
 	/* If this is a sub-export, must be root of FS */
 	if ((parent = exp_parent(clp, dev)) != NULL) {
-		struct super_block *sb;
+		struct super_block *sb = inode->i_sb;
 
-		if ((sb = inode->i_sb) && (inode != sb->s_mounted)) {
+		if (sb && (inode != sb->s_root->d_inode)) {
 			err = -EINVAL;
 			goto finish;
 		}
@@ -192,7 +220,7 @@
 	strcpy(exp->ex_path, nxp->ex_path);
 	exp->ex_client = clp;
 	exp->ex_parent = parent;
-	exp->ex_inode = inode;
+	exp->ex_dentry = dentry;
 	exp->ex_flags = nxp->ex_flags;
 	exp->ex_dev = dev;
 	exp->ex_ino = ino;
@@ -219,9 +247,10 @@
 	err = 0;
 
 finish:
-	/* Release inode */
-	if (err < 0 && inode)
-		iput(inode);
+	/* Release dentry */
+	if (err < 0 && dentry)
+		dput(dentry);
+
 	/* Unlock hashtable */
 	exp_unlock();
 	return err;
@@ -236,6 +265,7 @@
 {
 	svc_export	*exp;
 	svc_client	*clp;
+	struct dentry	*dentry;
 	struct inode	*inode;
 	int		i;
 
@@ -247,11 +277,12 @@
 				exp->ex_parent = unexp->ex_parent;
 	}
 
-	inode = unexp->ex_inode;
+	dentry = unexp->ex_dentry;
+	inode = dentry->d_inode;
 	if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
-		printk(KERN_WARNING "nfsd: bad inode in unexport!\n");
+		printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
 	else
-		iput(unexp->ex_inode);
+		dput(dentry);
 
 	kfree(unexp);
 }
@@ -326,13 +357,28 @@
 {
 	struct svc_export	*exp = NULL;
 	struct svc_fh		fh;
+	struct dentry		*dentry;
+	struct inode		*inode;
 
-	dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
+	dprintk("nfsd: exp_rootfh(%s:%x/%d)\n", clp->cl_ident, dev, ino);
 
 	if (!(exp = exp_get(clp, dev, ino)))
 		return -EPERM;
-	exp->ex_inode->i_count++;
-	fh_compose(&fh, exp, exp->ex_inode);
+
+	dentry = exp->ex_dentry;
+	inode = dentry->d_inode;
+	if(!inode) {
+		printk("exp_rootfh: Aieee, NULL d_inode\n");
+		return -EPERM;
+	}
+	if(inode->i_dev != dev || inode->i_ino != ino) {
+		printk("exp_rootfh: Aieee, ino/dev mismatch\n");
+		printk("exp_rootfh: arg[dev(%x):ino(%d)] inode[dev(%x):ino(%ld)]\n",
+		       dev, ino, inode->i_dev, inode->i_ino);
+	}
+
+	dget(dentry);
+	fh_compose(&fh, exp, dentry);
 	memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
 	fh_put(&fh);
 
@@ -613,25 +659,6 @@
 	return 0;
 }
 
-#if 0
-/*
- * Get the inode associated with a pathname. Used by exp_export.
- */
-struct inode *
-exp_lnamei(char *pathname, int *errp)
-{
-	struct inode	*inode;
-	unsigned long	oldfs;
-
-	oldfs = get_fs();
-	set_fs(KERNEL_DS);
-	*errp = lnamei(pathname, &inode);
-	set_fs(oldfs);
-
-	return inode;
-}
-#endif
-
 /*
  * Initialize the exports module.
  */
@@ -648,7 +675,6 @@
 	clients = NULL;
 
 	initialized = 1;
-	return;
 }
 
 /*
@@ -672,6 +698,4 @@
 	}
 	exp_unlock();
 	dprintk("nfsd: export shutdown complete.\n");
-
-	return;
 }
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/lockd.c linux/fs/nfsd/lockd.c
--- v2.1.46/linux/fs/nfsd/lockd.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/lockd.c	Mon Jul 21 09:53:20 1997
@@ -24,7 +24,7 @@
 
 	fh.fh_handle = *f;
 	fh.fh_export = NULL;
-	fh.fh_inode  = NULL;
+	fh.fh_dverified = 0;
 
 	nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
 	fh_put(&fh);
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/nfs3proc.c linux/fs/nfsd/nfs3proc.c
--- v2.1.46/linux/fs/nfsd/nfs3proc.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/nfs3proc.c	Mon Jul 21 09:53:20 1997
@@ -58,7 +58,7 @@
 				SVCFH_INO(&argp->fh));
 
 	resp->fh = argp->fh;
-	nfserr = fh_lookup(rqstp, &resp->fh, 0, MAY_NOP);
+	nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
 	RETURN(nfserr);
 }
 
@@ -227,7 +227,7 @@
 	attr   = &argp->attrs;
 
 	/* Get the directory inode */
-	nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
+	nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
 	if (nfserr)
 		RETURN(nfserr);
 
@@ -241,8 +241,7 @@
 
 	/* Now create the file and set attributes */
 	nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
-				attr, S_IFREG, 0, newfhp,
-				argp->createmode);
+				attr, S_IFREG, 0, newfhp);
 
 	RETURN(nfserr);
 }
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c
--- v2.1.46/linux/fs/nfsd/nfsctl.c	Mon Jun 16 16:35:58 1997
+++ linux/fs/nfsd/nfsctl.c	Mon Jul 21 09:53:20 1997
@@ -133,9 +133,10 @@
 #endif
 
 int
-asmlinkage handle_sys_nfsservctl(int cmd, struct nfsctl_arg *argp,
-			         union nfsctl_res *resp)
+asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
 {
+	struct nfsctl_arg *	argp = opaque_argp;
+	union nfsctl_res *	resp = opaque_resp;
 	struct nfsctl_arg *	arg = NULL;
 	union nfsctl_res *	res = NULL;
 	int			err;
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.1.46/linux/fs/nfsd/nfsfh.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/nfsfh.c	Mon Jul 21 09:53:20 1997
@@ -18,44 +18,23 @@
 #define NFSDDBG_FACILITY		NFSDDBG_FH
 
 /*
- * Get the inode version number
- */
-static inline int
-nfsd_iversion(struct inode *inode)
-{
-	if (inode->i_sb->s_magic == EXT2_SUPER_MAGIC)
-		return inode->u.ext2_i.i_version;
-	return 0;
-}
-
-/*
- * Get the inode given a file handle.
+ * Perform sanity checks on the dentry in a client's file handle.
  */
 u32
-fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
+fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
 {
 	struct svc_export *exp;
+	struct dentry	*dentry;
 	struct inode	*inode;
 	struct knfs_fh	*fh = &fhp->fh_handle;
 
-	/* Already checked */
-	if (fhp->fh_inode)
+	if(fhp->fh_dverified)
 		return 0;
 
-	dprintk("nfsd: fh_lookup(exp %x/%ld fh %x/%ld)\n",
-			fh->fh_xdev, fh->fh_xino, fh->fh_dev, fh->fh_ino);
+	dprintk("nfsd: fh_lookup(exp %x/%d fh %p)\n",
+			fh->fh_xdev, fh->fh_xino, fh->fh_dentry);
 
-	/* Make sure that clients don't cheat */
-	if (fh->fh_dev != fh->fh_xdev) {
-		printk(KERN_NOTICE "nfsd: fh with bad dev fields "
-				"(%x != %x) from %08lx:%d\n",
-				fh->fh_dev, fh->fh_xdev,
-				ntohl(rqstp->rq_addr.sin_addr.s_addr),
-				ntohs(rqstp->rq_addr.sin_port));
-		return nfserr_perm;
-	}
-
-	/* Look up the export entry */
+	/* Look up the export entry. */
 	exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino);
 	if (!exp) {
 		/* nfsdstats.fhstale++; */
@@ -71,22 +50,26 @@
 		return nfserr_perm;
 	}
 
-	/* Set user creds if we haven't done so already */
+	/* Set user creds if we haven't done so already. */
 	nfsd_setuser(rqstp, exp);
 
-	/* Get the inode */
-	if (!(inode = nfsd_iget(fh->fh_dev, fh->fh_ino))
-	 || !inode->i_nlink || fh->fh_version != nfsd_iversion(inode)) {
-		if (inode)
-			iput(inode);
+	dentry = fh->fh_dentry;
+
+	if(!d_validate(dentry, fh->fh_dparent, fh->fh_dhash, fh->fh_dlen) ||
+	   !(inode = dentry->d_inode) ||
+	   !inode->i_nlink) {
+		/* Currently we cannot tell the difference between
+		 * a bogus pointer and a true unlink between fh
+		 * uses.  But who cares about accurate error reporting
+		 * to buggy/malicious clients... -DaveM
+		 */
+
 		/* nfsdstats.fhstale++; */
-		return nfserr_stale;	/* unlinked in the meanwhile */
+		return nfserr_stale;
 	}
 
-	/* This is basically what wait_on_inode does */
-	while (inode->i_lock)
-		sleep_on(&inode->i_wait);
-	fhp->fh_inode  = inode;
+	dget(dentry);
+	fhp->fh_dverified = 1;
 	fhp->fh_export = exp;
 
 	/* Type check. The correct error return for type mismatches
@@ -100,25 +83,28 @@
 	if (type < 0 && (inode->i_mode & S_IFMT) == -type)
 		return (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir;
 
-	/* Finally, check access permissions */
-	return nfsd_permission(fhp->fh_export, inode, access);
+	/* Finally, check access permissions. */
+	return nfsd_permission(fhp->fh_export, dentry, access);
 }
 
 /*
  * Compose file handle for NFS reply.
  */
 void
-fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct inode *inode)
+fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
 {
-	dprintk("nfsd: fh_compose(exp %x/%ld fh %x/%ld)\n",
-			exp->ex_dev, exp->ex_ino, inode->i_dev, inode->i_ino);
+	dprintk("nfsd: fh_compose(exp %x/%d dentry %p)\n",
+			exp->ex_dev, exp->ex_ino, dentry);
 
 	fh_init(fhp);			/* initialize empty fh */
-	fhp->fh_inode = inode;
-	fhp->fh_export = exp;
-	fhp->fh_handle.fh_dev = inode->i_dev;
-	fhp->fh_handle.fh_ino = inode->i_ino;
+	fhp->fh_handle.fh_dentry = dentry;
+	fhp->fh_handle.fh_dparent = dentry->d_parent;
+	fhp->fh_handle.fh_dhash = dentry->d_name.hash;
+	fhp->fh_handle.fh_dlen = dentry->d_name.len;
 	fhp->fh_handle.fh_xdev = exp->ex_dev;
 	fhp->fh_handle.fh_xino = exp->ex_ino;
-	fhp->fh_handle.fh_version = nfsd_iversion(inode);
+	fhp->fh_export = exp;
+
+	/* We stuck it there, we know it's good. */
+	fhp->fh_dverified = 1;
 }
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- v2.1.46/linux/fs/nfsd/nfsproc.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/nfsproc.c	Mon Jul 21 09:53:20 1997
@@ -55,12 +55,10 @@
 nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
 					  struct nfsd_attrstat *resp)
 {
-	dprintk("nfsd: GETATTR  %x/%ld\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+	dprintk("nfsd: GETATTR  %p\n", SVCFH_DENTRY(&argp->fh));
 
 	fh_copy(&resp->fh, &argp->fh);
-	RETURN(fh_lookup(rqstp, &resp->fh, 0, MAY_NOP));
+	RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP));
 }
 
 /*
@@ -70,9 +68,7 @@
 nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
 					  struct nfsd_attrstat  *resp)
 {
-	dprintk("nfsd: SETATTR  %x/%ld\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+	dprintk("nfsd: SETATTR  %p\n", SVCFH_DENTRY(&argp->fh));
 
 	fh_copy(&resp->fh, &argp->fh);
 	RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
@@ -87,10 +83,7 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: LOOKUP   %x/%ld %s\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->name);
+	dprintk("nfsd: LOOKUP   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
 	nfserr = nfsd_lookup(rqstp, &argp->fh,
 				    argp->name,
@@ -111,9 +104,7 @@
 	u32		*path;
 	int		dummy, nfserr;
 
-	dprintk("nfsd: READLINK %x/%ld\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+	dprintk("nfsd: READLINK %p\n", SVCFH_DENTRY(&argp->fh));
 
 	/* Reserve room for status and path length */
 	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 2);
@@ -136,10 +127,9 @@
 	u32 *	buffer;
 	int	nfserr, avail;
 
-	dprintk("nfsd: READ %x/%ld %d bytes at %d\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->count, argp->offset);
+	dprintk("nfsd: READ %p %d bytes at %d\n",
+		SVCFH_DENTRY(&argp->fh),
+		argp->count, argp->offset);
 
 	/* Obtain buffer pointer for payload. 19 is 1 word for
 	 * status, 17 words for fattr, and 1 word for the byte count.
@@ -173,10 +163,9 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: WRITE    %x/%ld %d bytes at %d\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->len, argp->offset);
+	dprintk("nfsd: WRITE    %p %d bytes at %d\n",
+		SVCFH_DENTRY(&argp->fh),
+		argp->len, argp->offset);
 
 	nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
 				   argp->offset,
@@ -204,23 +193,22 @@
 	int		rdonly = 0, exists;
 	dev_t		rdev = NODEV;
 
-	dprintk("nfsd: CREATE   %x/%ld %s\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->name);
+	dprintk("nfsd: CREATE   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
 	dirfhp = &argp->fh;
 	newfhp = &resp->fh;
 	attr = &argp->attrs;
 
 	/* Get the directory inode */
-	nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
+	nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
 	if (nfserr)
 		RETURN(nfserr);
-	dirp = dirfhp->fh_inode;
+	dirp = dirfhp->fh_handle.fh_dentry->d_inode;
 
 	/* Check for MAY_WRITE separately. */
-	nfserr = nfsd_permission(dirfhp->fh_export, dirp, MAY_WRITE);
+	nfserr = nfsd_permission(dirfhp->fh_export,
+				 dirfhp->fh_handle.fh_dentry,
+				 MAY_WRITE);
 	if (nfserr == nfserr_rofs) {
 		rdonly = 1;	/* Non-fatal error for echo > /dev/null */
 	} else if (nfserr) {
@@ -230,7 +218,16 @@
 
 	/* First, check if the file already exists.  */
 	exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
-	inode = newfhp->fh_inode;
+
+	if (newfhp->fh_dverified)
+		inode = newfhp->fh_handle.fh_dentry->d_inode;
+
+	/* Get rid of this soon... */
+	if (exists && !inode) {
+		printk("nfsd_proc_create: Wheee... exists but d_inode==NULL\n");
+		nfserr = nfserr_rofs;
+		goto done;
+	}		
 
 	/* Unfudge the mode bits */
 	if (attr->ia_valid & ATTR_MODE) { 
@@ -311,10 +308,7 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: REMOVE   %x/%ld %s\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->name);
+	dprintk("nfsd: REMOVE   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
 	/* Unlink. -SIFDIR means file must not be a directory */
 	nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
@@ -328,13 +322,9 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: RENAME   %x/%ld %s -> %x/%ld %s\n",
-				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				argp->fname,
-				SVCFH_DEV(&argp->tfh),
-				SVCFH_INO(&argp->tfh),
-				argp->tname);
+	dprintk("nfsd: RENAME   %p %s -> %p %s\n",
+		SVCFH_DENTRY(&argp->ffh), argp->fname,
+		SVCFH_DENTRY(&argp->tfh), argp->tname);
 
 	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
 				    &argp->tfh, argp->tname, argp->tlen);
@@ -349,12 +339,10 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: LINK     %x/%ld -> %x/%ld %s\n",
-				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				SVCFH_DEV(&argp->tfh),
-				SVCFH_INO(&argp->tfh),
-				argp->tname);
+	dprintk("nfsd: LINK     %p -> %p %s\n",
+		SVCFH_DENTRY(&argp->ffh),
+		SVCFH_DENTRY(&argp->tfh),
+		argp->tname);
 
 	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
 				  &argp->ffh);
@@ -370,10 +358,9 @@
 	struct svc_fh	newfh;
 	int		nfserr;
 
-	dprintk("nfsd: SYMLINK  %x/%ld %s -> %s\n",
-				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				argp->fname, argp->tname);
+	dprintk("nfsd: SYMLINK  %p %s -> %s\n",
+		SVCFH_DENTRY(&argp->ffh),
+		argp->fname, argp->tname);
 
 	/*
 	 * Create the link, look up new file and set attrs.
@@ -381,7 +368,7 @@
 	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
 						 argp->tname, argp->tlen,
 						 &newfh);
-	if (nfserr)
+	if (!nfserr)
 		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
 
 	fh_put(&argp->ffh);
@@ -398,11 +385,11 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: MKDIR    %x/%ld %s\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->name);
+	dprintk("nfsd: MKDIR    %p %s\n",
+		SVCFH_DENTRY(&argp->fh),
+		argp->name);
 
+	resp->fh.fh_dverified = 0; /* paranoia */
 	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
 				    &argp->attrs, S_IFDIR, 0, &resp->fh);
 	fh_put(&argp->fh);
@@ -418,10 +405,7 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: RMDIR    %x/%ld %s\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->name);
+	dprintk("nfsd: RMDIR    %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
 	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
 	fh_put(&argp->fh);
@@ -438,10 +422,9 @@
 	u32 *	buffer;
 	int	nfserr, count;
 
-	dprintk("nfsd: READDIR  %x/%ld %d bytes at %d\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->count, argp->cookie);
+	dprintk("nfsd: READDIR  %p %d bytes at %d\n",
+		SVCFH_DENTRY(&argp->fh),
+		argp->count, argp->cookie);
 
 	/* Reserve buffer space for status */
 	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
@@ -470,9 +453,7 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: STATFS   %x/%ld\n",
-				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+	dprintk("nfsd: STATFS   %p\n", SVCFH_DENTRY(&argp->fh));
 
 	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
 	fh_put(&argp->fh);
@@ -488,34 +469,34 @@
 struct nfsd_void { int dummy; };
 
 #define PROC(name, argt, rest, relt, cache)	\
- { (svc_procfunc) nfsd_proc_##name,	\
-   (kxdrproc_t) nfssvc_decode_##argt,	\
-   (kxdrproc_t) nfssvc_encode_##rest,	\
-   (kxdrproc_t) nfssvc_release_##relt,	\
-   sizeof(struct nfsd_##argt),		\
-   sizeof(struct nfsd_##rest),		\
-   0,					\
-   cache				\
+ { (svc_procfunc) nfsd_proc_##name,		\
+   (kxdrproc_t) nfssvc_decode_##argt,		\
+   (kxdrproc_t) nfssvc_encode_##rest,		\
+   (kxdrproc_t) nfssvc_release_##relt,		\
+   sizeof(struct nfsd_##argt),			\
+   sizeof(struct nfsd_##rest),			\
+   0,						\
+   cache					\
  }
 struct svc_procedure		nfsd_procedures2[18] = {
-  PROC(null,	 void,		void,		none,	 RC_NOCACHE),
-  PROC(getattr,	 fhandle,	attrstat,	fhandle, RC_NOCACHE),
-  PROC(setattr,  sattrargs,	attrstat,	fhandle, RC_REPLBUFF),
-  PROC(none,	 void,		void,		none,	 RC_NOCACHE),
-  PROC(lookup,	 diropargs,	diropres,	fhandle, RC_NOCACHE),
-  PROC(readlink, fhandle,	readlinkres,	none,	 RC_NOCACHE),
-  PROC(read,	 readargs,	readres,	fhandle, RC_NOCACHE),
-  PROC(none,	 void,		void,		none,	 RC_NOCACHE),
-  PROC(write,	 writeargs,	attrstat,	fhandle, RC_REPLBUFF),
-  PROC(create,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
-  PROC(remove,	 diropargs,	void,		none,	 RC_REPLSTAT),
-  PROC(rename,	 renameargs,	void,		none,	 RC_REPLSTAT),
-  PROC(link,	 linkargs,	void,		none,	 RC_REPLSTAT),
-  PROC(symlink,	 symlinkargs,	void,		none,	 RC_REPLSTAT),
-  PROC(mkdir,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
-  PROC(rmdir,	 diropargs,	void,		none,	 RC_REPLSTAT),
-  PROC(readdir,	 readdirargs,	readdirres,	none,	 RC_REPLSTAT),
-  PROC(statfs,	 fhandle,	statfsres,	none,	 RC_NOCACHE),
+  PROC(null,	 void,		void,		none,		RC_NOCACHE),
+  PROC(getattr,	 fhandle,	attrstat,	fhandle,	RC_NOCACHE),
+  PROC(setattr,  sattrargs,	attrstat,	fhandle,	RC_REPLBUFF),
+  PROC(none,	 void,		void,		none,		RC_NOCACHE),
+  PROC(lookup,	 diropargs,	diropres,	fhandle,	RC_NOCACHE),
+  PROC(readlink, fhandle,	readlinkres,	none,		RC_NOCACHE),
+  PROC(read,	 readargs,	readres,	fhandle,	RC_NOCACHE),
+  PROC(none,	 void,		void,		none,		RC_NOCACHE),
+  PROC(write,	 writeargs,	attrstat,	fhandle,	RC_REPLBUFF),
+  PROC(create,	 createargs,	diropres,	fhandle,	RC_REPLBUFF),
+  PROC(remove,	 diropargs,	void,		none,		RC_REPLSTAT),
+  PROC(rename,	 renameargs,	void,		none,		RC_REPLSTAT),
+  PROC(link,	 linkargs,	void,		none,		RC_REPLSTAT),
+  PROC(symlink,	 symlinkargs,	void,		none,		RC_REPLSTAT),
+  PROC(mkdir,	 createargs,	diropres,	fhandle,	RC_REPLBUFF),
+  PROC(rmdir,	 diropargs,	void,		none,		RC_REPLSTAT),
+  PROC(readdir,	 readdirargs,	readdirres,	none,		RC_REPLSTAT),
+  PROC(statfs,	 fhandle,	statfsres,	none,		RC_NOCACHE),
 };
 
 
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/nfsxdr.c linux/fs/nfsd/nfsxdr.c
--- v2.1.46/linux/fs/nfsd/nfsxdr.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/nfsxdr.c	Mon Jul 21 09:53:20 1997
@@ -363,7 +363,7 @@
 nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd_attrstat *resp)
 {
-	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
 		return 0;
 	return xdr_ressize_check(rqstp, p);
 }
@@ -373,7 +373,7 @@
 					struct nfsd_diropres *resp)
 {
 	if (!(p = encode_fh(p, &resp->fh))
-	 || !(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+	 || !(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
 		return 0;
 	return xdr_ressize_check(rqstp, p);
 }
@@ -391,7 +391,7 @@
 nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd_readres *resp)
 {
-	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
 		return 0;
 	*p++ = htonl(resp->count);
 	p += XDR_QUADLEN(resp->count);
diff -u --recursive --new-file v2.1.46/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.1.46/linux/fs/nfsd/vfs.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/nfsd/vfs.c	Mon Jul 21 09:53:20 1997
@@ -5,12 +5,11 @@
  * other parts of the kernel because they weren't in ksyms.c, others
  * are partial duplicates with added or changed functionality.
  *
- * Note that several functions lock the inode upon which they want
- * to act, most notably those that create directory entries. The
- * unlock operation can take place either by calling fh_unlock within
- * the function directly, or at a later time in fh_put(). So if you
- * notice code paths that apparently fail to unlock the inode, don't
- * worry--they have been taken care of.
+ * Note that several functions dget() the dentry upon which they want
+ * to act, most notably those that create directory entries. Response
+ * dentry's are dput()'d if necessary in the release callback.
+ * So if you notice code paths that apparently fail to dput() the
+ * dentry, don't worry--they have been taken care of.
  *
  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
@@ -59,10 +58,9 @@
  * add a hash table here.
  */
 struct raparms {
-	struct raparms *	p_next;
+	struct raparms		*p_next;
 	unsigned int		p_count;
-	dev_t			p_dev;
-	ino_t			p_ino;
+	struct dentry		*p_dentry;
 	unsigned long		p_reada,
 				p_ramax,
 				p_raend,
@@ -85,12 +83,14 @@
 }
 
 /*
- * Check whether directory is a mount point
+ * Check whether directory is a mount point, but it is alright if
+ * this is precisely the local mount point being exported.
  */
 static inline int
-nfsd_iscovered(struct inode *inode)
+nfsd_iscovered(struct dentry *dentry, struct svc_export *exp)
 {
-	return inode->i_mount != NULL;
+	return (dentry != dentry->d_covers &&
+		dentry != exp->ex_dentry);
 }
 
 /*
@@ -101,72 +101,40 @@
 					int len, struct svc_fh *resfh)
 {
 	struct svc_export	*exp;
-	struct super_block	*sb;
-	struct inode		*dirp, *inode;
-	int			perm, err, dotdot = 0;
+	struct dentry		*dparent;
+	int			err;
 
-	dprintk("nfsd: nfsd_lookup(fh %x/%ld, %s)\n",
-			SVCFH_DEV(fhp), SVCFH_INO(fhp), name);
+	dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name);
 
-	/* Obtain inode and export */
-	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
+	/* Obtain dentry and export. */
+	if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
 		return err;
-	dirp = fhp->fh_inode;
+
+	dparent = fhp->fh_handle.fh_dentry;
 	exp  = fhp->fh_export;
 
-	/* check permissions before traversing mount-points */
-	perm = nfsd_permission(exp, dirp, MAY_EXEC);
+	/* Fast path... */
+	err = nfsd_permission(exp, dparent, MAY_EXEC);
+	if ((err == 0)					&&
+	    !fs_off_limits(dparent->d_inode->i_sb)	&&
+	    !nfsd_iscovered(dparent, exp)) {
+		struct dentry *dchild;
+
+		dchild = lookup_dentry(name, dget(dparent), 1);
+		err = PTR_ERR(dchild);
+		if (IS_ERR(dchild))
+			return nfserrno(-err);
 
-	dotdot = (len == 2 && name[0] == '.' && name[1] == '.');
-	if (dotdot) {
-		if (dirp == current->fs->root) {
-			dirp->i_count++;
-			*resfh = *fhp;
-			return 0;
-		}
+		fh_compose(resfh, exp, dchild);
+		return (dchild->d_inode ? 0 : nfserr_noent);
+	}
 
-		if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) {
-			dirp->i_count++;
-			*resfh = *fhp;
-			return 0;
-		}
-	} else if (len == 1 && name[0] == '.') {
-		len = 0;
-	} else if (fs_off_limits(dirp->i_sb)) {
-		/* No lookups on NFS mounts and procfs */
+	/* Slow path... */
+	if (fs_off_limits(dparent->d_inode->i_sb))
 		return nfserr_noent;
-	} else if (nfsd_iscovered(dirp)) {
-		/* broken NFS client */
+	if (nfsd_iscovered(dparent, exp))
 		return nfserr_acces;
-	}
-	if (!dirp->i_op || !dirp->i_op->lookup)
-		return nfserr_notdir;
- 	if (perm != 0)
-		return perm;
-	if (!len) {
-		dirp->i_count++;
-		*resfh = *fhp;
-		return 0;
-	}
-
-	dirp->i_count++;		/* lookup eats the dirp inode */
-	err = dirp->i_op->lookup(dirp, name, len, &inode);
-
-	if (err)
-		return nfserrno(-err);
-
-	/* Note that lookup() has already done a call to iget() so that
-	 * the inode returned never refers to an inode covered by a mount.
-	 * When this has happened, return the covered inode.
-	 */
-	if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) {
-		iput(inode);
-		inode = sb->s_covered;
-		inode->i_count++;
-	}
-
-	fh_compose(resfh, exp, inode);
-	return 0;
+	return err;
 }
 
 /*
@@ -175,6 +143,7 @@
 int
 nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
 {
+	struct dentry	*dentry;
 	struct inode	*inode;
 	int		accmode = MAY_SATTR;
 	int		ftype = 0;
@@ -187,16 +156,16 @@
 		ftype = S_IFREG;
 
 	/* Get inode */
-	if ((err = fh_lookup(rqstp, fhp, ftype, accmode)) != 0)
+	if ((err = fh_verify(rqstp, fhp, ftype, accmode)) != 0)
 		return err;
 
-	fh_lock(fhp);			/* lock inode */
-	inode = fhp->fh_inode;
+	dentry = fhp->fh_handle.fh_dentry;
+	inode = dentry->d_inode;
 
 	/* The size case is special... */
 	if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
 		if (iap->ia_size < inode->i_size) {
-			err = nfsd_permission(fhp->fh_export, inode, MAY_TRUNC);
+			err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
 			if (err != 0)
 				return err;
 		}
@@ -205,7 +174,7 @@
 		inode->i_size = iap->ia_size;
 		if (inode->i_op && inode->i_op->truncate)
 			inode->i_op->truncate(inode);
-		inode->i_dirt = 1;
+		mark_inode_dirty(inode);
 		put_write_access(inode);
 		iap->ia_valid &= ATTR_SIZE;
 		iap->ia_valid |= ATTR_MTIME;
@@ -234,11 +203,11 @@
 	if (iap->ia_valid) {
 		iap->ia_valid |= ATTR_CTIME;
 		iap->ia_ctime = CURRENT_TIME;
-		err = nfsd_notify_change(inode, iap);
+		err = notify_change(inode, iap);
 		if (err)
 			return nfserrno(-err);
 		if (EX_ISSYNC(fhp->fh_export))
-			nfsd_write_inode(inode);
+			write_inode_now(inode);
 	}
 
 	return 0;
@@ -252,13 +221,16 @@
 nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
 			int wflag, struct file *filp)
 {
+	struct dentry	*dentry;
 	struct inode	*inode;
 	int		access, err;
 
 	access = wflag? MAY_WRITE : MAY_READ;
-	if ((err = fh_lookup(rqstp, fhp, type, access)) != 0)
+	if ((err = fh_verify(rqstp, fhp, type, access)) != 0)
 		return err;
-	inode = fhp->fh_inode;
+
+	dentry = fhp->fh_handle.fh_dentry;
+	inode = dentry->d_inode;
 
 	/* Disallow access to files with the append-only bit set or
 	 * with mandatory locking enabled */
@@ -275,7 +247,7 @@
 	filp->f_count = 1;
 	filp->f_flags = wflag? O_WRONLY : O_RDONLY;
 	filp->f_mode  = wflag? FMODE_WRITE : FMODE_READ;
-	filp->f_inode = inode;
+	filp->f_dentry = dentry;
 
 	if (filp->f_op->open) {
 		err = filp->f_op->open(inode, filp);
@@ -291,7 +263,6 @@
 		}
 	}
 
-	inode->i_count++;
 	return 0;
 }
 
@@ -301,16 +272,17 @@
 void
 nfsd_close(struct file *filp)
 {
-	struct inode	*inode;
+	struct dentry	*dentry = filp->f_dentry;
+	struct inode	*inode = dentry->d_inode;
 
-	inode = filp->f_inode;
 	if (!inode->i_count)
 		printk(KERN_WARNING "nfsd: inode count == 0!\n");
+	if (!dentry->d_count)
+		printk(KERN_WARNING "nfsd: wheee, dentry count == 0!\n");
 	if (filp->f_op && filp->f_op->release)
 		filp->f_op->release(inode, filp);
 	if (filp->f_mode & FMODE_WRITE)
 		put_write_access(inode);
-	iput(inode);
 }
 
 /*
@@ -326,12 +298,12 @@
  * Obtain the readahead parameters for the given file
  */
 static inline struct raparms *
-nfsd_get_raparms(dev_t dev, ino_t ino)
+nfsd_get_raparms(struct dentry *dentry)
 {
 	struct raparms	*ra, **rap, **frap = NULL;
 
 	for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) {
-		if (ra->p_dev != dev || ra->p_ino != ino) {
+		if (ra->p_dentry != dentry) {
 			if (ra->p_count == 0)
 				frap = rap;
 		} else
@@ -342,6 +314,7 @@
 	rap = frap;
 	ra = *frap;
 	memset(ra, 0, sizeof(*ra));
+	ra->p_dentry = dentry;
 found:
 	if (rap != &raparm_cache) {
 		*rap = ra->p_next;
@@ -361,6 +334,7 @@
 						unsigned long *count)
 {
 	struct raparms	*ra;
+	struct dentry	*dentry;
 	struct inode	*inode;
 	struct file	file;
 	unsigned long	oldfs;
@@ -368,14 +342,15 @@
 
 	if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file)) != 0)
 		return err;
-	inode = file.f_inode;
+	dentry = file.f_dentry;
+	inode = dentry->d_inode;
 	if (!file.f_op->read) {
 		nfsd_close(&file);
 		return nfserr_perm;
 	}
 
 	/* Get readahead parameters */
-	if ((ra = nfsd_get_raparms(inode->i_dev, inode->i_ino)) != NULL) {
+	if ((ra = nfsd_get_raparms(dentry)) != NULL) {
 		file.f_reada = ra->p_reada;
 		file.f_ramax = ra->p_ramax;
 		file.f_raend = ra->p_raend;
@@ -385,7 +360,7 @@
 	file.f_pos = offset;
 
 	oldfs = get_fs(); set_fs(KERNEL_DS);
-	err = file.f_op->read(file.f_inode, &file, buf, *count);
+	err = file.f_op->read(inode, &file, buf, *count);
 	set_fs(oldfs);
 
 	/* Write back readahead params */
@@ -419,6 +394,7 @@
 {
 	struct svc_export	*exp;
 	struct file		file;
+	struct dentry		*dentry;
 	struct inode		*inode;
 	unsigned long		oldfs;
 	int			err;
@@ -432,7 +408,8 @@
 		return nfserr_perm;
 	}
 
-	inode = fhp->fh_inode;
+	dentry = file.f_dentry;
+	inode = dentry->d_inode;
 	exp   = fhp->fh_export;
 
 	/*
@@ -459,7 +436,7 @@
 
 		ia.ia_valid = ATTR_MODE;
 		ia.ia_mode  = inode->i_mode & ~(S_ISUID | S_ISGID);
-		nfsd_notify_change(inode, &ia);
+		notify_change(inode, &ia);
 	}
 
 	fh_unlock(fhp);			/* unlock inode */
@@ -494,10 +471,10 @@
 #endif
 		}
 
-		if (inode->i_dirt) {
+		if (test_bit(I_DIRTY, &inode->i_state)) {
 			dprintk("nfsd: write sync %d\n", current->pid);
 			nfsd_sync(inode, &file);
-			nfsd_write_inode(inode);
+			write_inode_now(inode);
 		}
 		wake_up(&inode->i_wait);
 		last_ino = inode->i_ino;
@@ -519,110 +496,120 @@
 		char *fname, int flen, struct iattr *iap,
 		int type, dev_t rdev, struct svc_fh *resfhp)
 {
-	struct inode	*dirp, *inode = NULL;
+	struct dentry	*dentry, *dchild;
+	struct inode	*dirp;
 	int		err;
 
 	if (!flen)
 		return nfserr_perm;
-
 	if (!(iap->ia_valid & ATTR_MODE))
 		iap->ia_mode = 0;
-
-	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+	if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
 		return err;
 
-	fh_lock(fhp);			/* lock directory */
-	dirp = fhp->fh_inode;
-	dirp->i_count++;	/* dirop eats the inode */
+	dentry = fhp->fh_handle.fh_dentry;
+	dirp = dentry->d_inode;
 
+	/* Get all the sanity checks out of the way before we lock the parent. */
+	if(!dirp->i_op || !dirp->i_op->lookup) {
+		return nfserrno(-ENOTDIR);
+	} else if(type == S_IFREG) {
+		if(!dirp->i_op->create)
+			return nfserr_perm;
+	} else if(type == S_IFDIR) {
+		if(!dirp->i_op->mkdir)
+			return nfserr_perm;
+	} else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) {
+		if(!dirp->i_op->mknod)
+			return nfserr_perm;
+	} else {
+		return nfserr_perm;
+	}
+
+	if(!resfhp->fh_dverified) {
+		dchild = lookup_dentry(fname, dget(dentry), 0);
+		err = PTR_ERR(dchild);
+		if(IS_ERR(dchild))
+			return nfserrno(-err);
+	} else
+		dchild = resfhp->fh_handle.fh_dentry;
+
+	/* Looks good, lock the directory. */
+	fh_lock(fhp);
 	switch (type) {
 	case S_IFREG:
-		if (!dirp->i_op || !dirp->i_op->create)
-			return nfserr_perm;
-		err = dirp->i_op->create(dirp, fname, flen,
-					 iap->ia_mode, &inode);
+		err = dirp->i_op->create(dirp, dchild, iap->ia_mode);
 		break;
 	case S_IFDIR:
-		if (!dirp->i_op || !dirp->i_op->mkdir)
-			return nfserr_perm;
-		err = dirp->i_op->mkdir(dirp, fname, flen, iap->ia_mode);
+		err = dirp->i_op->mkdir(dirp, dchild, iap->ia_mode);
 		break;
 	case S_IFCHR:
 	case S_IFBLK:
 	case S_IFIFO:
-		if (!dirp->i_op || !dirp->i_op->mknod)
-			return nfserr_perm;
-		err = dirp->i_op->mknod(dirp, fname, flen, iap->ia_mode, rdev);
+		err = dirp->i_op->mknod(dirp, dchild, iap->ia_mode, rdev);
 		break;
-	default:
-		iput(dirp);
-		err = -EACCES;
 	}
-
 	fh_unlock(fhp);
 
 	if (err < 0)
 		return nfserrno(-err);
-
-	/*
-	 * If the VFS call doesn't return the inode, look it up now.
-	 */
-	if (inode == NULL) {
-		dirp->i_count++;
-		err = dirp->i_op->lookup(dirp, fname, flen, &inode);
-		if (err < 0)
-			return -nfserrno(err);	/* Huh?! */
-	}
-
 	if (EX_ISSYNC(fhp->fh_export))
-		nfsd_write_inode(dirp);
+		write_inode_now(dirp);
 
-	/* Assemble the file handle for the newly created file */
-	fh_compose(resfhp, fhp->fh_export, inode);
+	/* If needed, assemble the file handle for the newly created file.  */
+	if(!resfhp->fh_dverified)
+		fh_compose(resfhp, fhp->fh_export, dchild);
 
 	/* Set file attributes. Mode has already been set and
 	 * setting uid/gid works only for root. Irix appears to
 	 * send along the gid when it tries to implement setgid
 	 * directories via NFS.
 	 */
-	if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) {
-		if ((err = nfsd_setattr(rqstp, resfhp, iap)) != 0) {
-			fh_put(resfhp);
-			return err;
-		}
-	}
+	err = 0;
+	if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+		err = nfsd_setattr(rqstp, resfhp, iap);
 
-	return 0;
+	return err;
 }
 
 /*
  * Truncate a file.
  * The calling routines must make sure to update the ctime
  * field and call notify_change.
+ *
+ * XXX Nobody calls this thing? -DaveM
  */
 int
 nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
 {
+	struct dentry	*dentry;
 	struct inode	*inode;
+	struct iattr	newattrs;
 	int		err;
 
-	if ((err = fh_lookup(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
+	if ((err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
 		return err;
 
-	fh_lock(fhp);			/* lock inode if not yet locked */
-	inode = fhp->fh_inode;
+	dentry = fhp->fh_handle.fh_dentry;
+	inode = dentry->d_inode;
 
 	if ((err = get_write_access(inode)) != 0)
-		return nfserrno(-err);
-	inode->i_size = size;
-	if (inode->i_op && inode->i_op->truncate)
-		inode->i_op->truncate(inode);
-	inode->i_dirt = 1;
-	put_write_access(inode);
+		goto out;
 
+	/* Things look sane, lock and do it. */
+	fh_lock(fhp);
+	newattrs.ia_size = size;
+	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+	err = notify_change(inode, &newattrs);
+	if (!err) {
+		vmtruncate(inode, size);
+		if (inode->i_op && inode->i_op->truncate)
+			inode->i_op->truncate(inode);
+	}
+	put_write_access(inode);
 	fh_unlock(fhp);
-
-	return 0;
+out:
+	return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -632,18 +619,21 @@
 int
 nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
 {
+	struct dentry	*dentry;
 	struct inode	*inode;
 	unsigned long	oldfs;
 	int		err;
 
-	if ((err = fh_lookup(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
+	if ((err = fh_verify(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
 		return err;
-	inode = fhp->fh_inode;
+
+	dentry = fhp->fh_handle.fh_dentry;
+	inode = dentry->d_inode;
 
 	if (!inode->i_op || !inode->i_op->readlink)
 		return nfserr_io;
 
-	inode->i_count++;
+	UPDATE_ATIME(inode);
 	oldfs = get_fs(); set_fs(KERNEL_DS);
 	err = inode->i_op->readlink(inode, buf, *lenp);
 	set_fs(oldfs);
@@ -664,42 +654,45 @@
 				char *path,  int plen,
 				struct svc_fh *resfhp)
 {
-	struct inode	*dirp, *inode;
+	struct dentry	*dentry, *dnew;
+	struct inode	*dirp;
 	int		err;
 
 	if (!flen || !plen)
 		return nfserr_noent;
 
-	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+	if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
 		return err;
 
-	dirp = fhp->fh_inode;
-	if (nfsd_iscovered(dirp))
-		return nfserr_perm;
-	if (!dirp->i_op || !dirp->i_op->symlink)
-		return nfserr_perm;
+	dentry = fhp->fh_handle.fh_dentry;
+	dirp = dentry->d_inode;
 
-	fh_lock(fhp);			/* lock inode */
-	dirp->i_count++;
-	err = dirp->i_op->symlink(dirp, fname, flen, path);
-	fh_unlock(fhp);			/* unlock inode */
-
-	if (err)
-		return nfserrno(-err);
+	if (nfsd_iscovered(dentry, fhp->fh_export)	||
+	    !dirp->i_op					||
+	    !dirp->i_op->symlink)
+		return nfserr_perm;
 
-	if (EX_ISSYNC(fhp->fh_export))
-		nfsd_write_inode(dirp);
+	dnew = lookup_dentry(fname, dget(dentry), 0);
+	err = PTR_ERR(dnew);
+	if (IS_ERR(dnew))
+		goto out;
+
+	err = -EEXIST;
+	if(dnew->d_inode)
+		goto compose_and_out;
 
-	/*
-	 * Okay, now look up the inode of the new symlink.
-	 */
-	dirp->i_count++;	/* lookup eats the dirp inode */
-	err = dirp->i_op->lookup(dirp, fname, flen, &inode);
-	if (err)
-		return nfserrno(-err);
+	fh_lock(fhp);
+	err = dirp->i_op->symlink(dirp, dnew, path);
+	fh_unlock(fhp);
 
-	fh_compose(resfhp, fhp->fh_export, inode);
-	return 0;
+	if (!err) {
+		if (EX_ISSYNC(fhp->fh_export))
+			write_inode_now(dirp);
+	}
+compose_and_out:
+	fh_compose(resfhp, fhp->fh_export, dnew);
+out:
+	return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -709,37 +702,69 @@
 nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
 				char *fname, int len, struct svc_fh *tfhp)
 {
+	struct dentry	*ddir, *dnew;
 	struct inode	*dirp, *dest;
 	int		err;
 
-	if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) ||
-	    (err = fh_lookup(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
+	if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) ||
+	    (err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
 		return err;
-	dirp = ffhp->fh_inode;
-	dest = tfhp->fh_inode;
 
+	ddir = ffhp->fh_handle.fh_dentry;
+	dirp = ddir->d_inode;
+
+	dnew = lookup_dentry(fname, dget(ddir), 1);
+	err = PTR_ERR(dnew);
+	if (IS_ERR(dnew))
+		return nfserrno(-err);
+
+	err = -EEXIST;
+	if (dnew->d_inode)
+		goto dput_and_out;
+	dest = tfhp->fh_handle.fh_dentry->d_inode;
+
+	err = -EPERM;
 	if (!len)
-		return nfserr_perm;
-	if (nfsd_iscovered(dirp))
-		return nfserr_acces;
+		goto dput_and_out;
+
+	err = -EACCES;
+	if (nfsd_iscovered(ddir, ffhp->fh_export))
+		goto dput_and_out;
 	if (dirp->i_dev != dest->i_dev)
-		return nfserr_acces;	/* FIXME: nxdev for NFSv3 */
+		goto dput_and_out;	/* FIXME: nxdev for NFSv3 */
+
+	err = -EPERM;
 	if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ )
-		return nfserr_perm;
+		goto dput_and_out;
 	if (!dirp->i_op || !dirp->i_op->link)
-		return nfserr_perm;
+		goto dput_and_out;
 
-	fh_lock(ffhp);			/* lock directory inode */
-	dirp->i_count++;
-	err = dirp->i_op->link(dest, dirp, fname, len);
-	fh_unlock(ffhp);		/* unlock inode */
+	fh_lock(ffhp);
+	err = dirp->i_op->link(dest, dirp, dnew);
+	fh_unlock(ffhp);
 
 	if (!err && EX_ISSYNC(ffhp->fh_export)) {
-		nfsd_write_inode(dirp);
-		nfsd_write_inode(dest);
+		write_inode_now(dirp);
+		write_inode_now(dest);
+	}
+dput_and_out:
+	dput(dnew);
+	return (err ? nfserrno(-err) : 0);
+}
+
+/* More "hidden treasure" from the generic VFS. -DaveM */
+static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
+{
+	if((unsigned long) s1 < (unsigned long) s2) {
+		down(s1);
+		down(s2);
+	} else if(s1 == s2) {
+		down(s1);
+		atomic_dec(&s1->count);
+	} else {
+		down(s2);
+		down(s1);
 	}
-
-	return err? nfserrno(-err) : 0;
 }
 
 /*
@@ -749,14 +774,19 @@
 nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
 			    struct svc_fh *tfhp, char *tname, int tlen)
 {
+	struct dentry	*fdentry, *tdentry, *odentry, *ndentry;
 	struct inode	*fdir, *tdir;
 	int		err;
 
-	if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0)
-	 || (err = fh_lookup(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
+	if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0)
+	 || (err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
 		return err;
-	fdir = ffhp->fh_inode;
-	tdir = tfhp->fh_inode;
+
+	fdentry = ffhp->fh_handle.fh_dentry;
+	fdir = fdentry->d_inode;
+
+	tdentry = tfhp->fh_handle.fh_dentry;
+	tdir = tdentry->d_inode;
 
 	if (!flen || (fname[0] == '.' && 
 	    (flen == 1 || (flen == 2 && fname[1] == '.'))) ||
@@ -764,23 +794,37 @@
 	    (tlen == 1 || (tlen == 2 && tname[1] == '.'))))
 		return nfserr_perm;
 
+	odentry = lookup_dentry(fname, dget(fdentry), 1);
+	err = PTR_ERR(odentry);
+	if (IS_ERR(odentry))
+		goto out_no_unlock;
+
+	ndentry = lookup_dentry(tname, dget(tdentry), 1);
+	err = PTR_ERR(ndentry);
+	if (IS_ERR(ndentry))
+		goto out_dput_old;
+
+	nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
+	err = -EXDEV;
 	if (fdir->i_dev != tdir->i_dev)
-		return nfserr_acces;	/* nfserr_nxdev */
+		goto out_unlock;
+	err = -EPERM;
 	if (!fdir->i_op || !fdir->i_op->rename)
-		return nfserr_perm;
-
-	fh_lock(tfhp);			/* lock destination directory */
-	tdir->i_count++;
-	fdir->i_count++;
-	err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen);
-	fh_unlock(tfhp);		/* unlock inode */
-
+		goto out_unlock;
+	err = fdir->i_op->rename(fdir, odentry, tdir, ndentry);
 	if (!err && EX_ISSYNC(tfhp->fh_export)) {
-		nfsd_write_inode(fdir);
-		nfsd_write_inode(tdir);
+		write_inode_now(fdir);
+		write_inode_now(tdir);
 	}
-
-	return err? nfserrno(-err) : 0;
+out_unlock:
+	up(&tdir->i_sem);
+	up(&fdir->i_sem);
+
+	dput(ndentry);
+out_dput_old:
+	dput(odentry);
+out_no_unlock:
+	return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -790,35 +834,40 @@
 nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
 				char *fname, int flen)
 {
+	struct dentry	*dentry, *rdentry;
 	struct inode	*dirp;
 	int		err;
 
 	if (!flen || isdotent(fname, flen))
 		return nfserr_acces;
-
-	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
+	if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
 		return err;
 
-	fh_lock(fhp);			/* lock inode */
-	dirp = fhp->fh_inode;
+	dentry = fhp->fh_handle.fh_dentry;
+	dirp = dentry->d_inode;
 
+	rdentry = lookup_dentry(fname, dget(dentry), 0);
+	err = PTR_ERR(rdentry);
+	if (IS_ERR(rdentry))
+		goto out;
+
+	fh_lock(fhp);
 	if (type == S_IFDIR) {
-		if (!dirp->i_op || !dirp->i_op->rmdir)
-			return nfserr_notdir;
-		dirp->i_count++;
-		err = dirp->i_op->rmdir(dirp, fname, flen);
-	} else {	/* other than S_IFDIR */
-		if (!dirp->i_op || !dirp->i_op->unlink)
-			return nfserr_perm;
-		dirp->i_count++;
-		err = dirp->i_op->unlink(dirp, fname, flen);
+		err = -ENOTDIR;
+		if (dirp->i_op && dirp->i_op->rmdir)
+			err = dirp->i_op->rmdir(dirp, rdentry);
+	} else {
+		err = -EPERM;
+		if (dirp->i_op && dirp->i_op->unlink)
+			err = dirp->i_op->unlink(dirp, rdentry);
 	}
+	fh_unlock(fhp);
 
-	fh_unlock(fhp);			/* unlock inode */
+	dput(rdentry);
 	if (!err && EX_ISSYNC(fhp->fh_export))
-		nfsd_write_inode(dirp);
-
-	return err? nfserrno(-err) : 0;
+		write_inode_now(dirp);
+out:
+	return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -829,6 +878,7 @@
 			encode_dent_fn func, u32 *buffer, int *countp)
 {
 	struct readdir_cd cd;
+	struct inode	*inode;
 	struct file	file;
 	u32		*p;
 	int		oldlen, eof, err;
@@ -856,6 +906,7 @@
 	 * readdir() is not guaranteed to fill up the entire buffer, but
 	 * may choose to do less.
 	 */
+	inode = file.f_dentry->d_inode;
 	do {
 		oldlen = cd.buflen;
 
@@ -864,7 +915,7 @@
 			file.f_inode->i_dev, file.f_inode->i_ino,
 			(int) file.f_pos, (int) oldlen, (int) cd.buflen);
 		 */
-		err = file.f_op->readdir(file.f_inode, &file,
+		err = file.f_op->readdir(inode, &file,
 					 &cd, (filldir_t) func);
 
 		if (err < 0) {
@@ -904,14 +955,16 @@
 int
 nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
 {
+	struct dentry		*dentry;
 	struct inode		*inode;
 	struct super_block	*sb;
 	unsigned long		oldfs;
 	int			err;
 
-	if ((err = fh_lookup(rqstp, fhp, 0, MAY_NOP)) != 0)
+	if ((err = fh_verify(rqstp, fhp, 0, MAY_NOP)) != 0)
 		return err;
-	inode = fhp->fh_inode;
+	dentry = fhp->fh_handle.fh_dentry;
+	inode = dentry->d_inode;
 
 	if (!(sb = inode->i_sb) || !sb->s_op->statfs)
 		return nfserr_io;
@@ -928,8 +981,9 @@
  * Check for a user's access permissions to this inode.
  */
 int
-nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
+nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
 {
+	struct inode	*inode = dentry->d_inode;
 	int		err;
 
 	if (acc == MAY_NOP)
@@ -954,7 +1008,7 @@
 	if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
 		if (EX_RDONLY(exp) || IS_RDONLY(inode))
 			return nfserr_rofs;
-		if (S_ISDIR(inode->i_mode) && nfsd_iscovered(inode))
+		if (S_ISDIR(inode->i_mode) && nfsd_iscovered(dentry, exp))
 			return nfserr_perm;
 		if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
 			return nfserr_perm;
@@ -981,80 +1035,6 @@
 		err = permission(inode, MAY_EXEC);
 
 	return err? nfserrno(-err) : 0;
-}
-
-/*
- * Look up the inode for a given FH.
- */
-struct inode *
-nfsd_iget(dev_t dev, ino_t ino)
-{
-	struct super_block	*sb;
-
-	if (!(sb = get_super(dev)) || fs_off_limits(sb))
-		return NULL;
-	return __iget(sb, ino, 0);
-}
-
-/*
- * Write the inode if dirty (copy of fs/inode.c:write_inode)
- */
-void
-nfsd_write_inode(struct inode *inode)
-{
-	struct super_block	*sb = inode->i_sb;
-
-	if (!inode->i_dirt)
-		return;
-	while (inode->i_lock) {
-		sleep_on(&inode->i_wait);
-		if (!inode->i_dirt)
-			return;
-	}
-	if (!sb || !sb->s_op || !sb->s_op->write_inode) {
-		inode->i_dirt = 0;
-		return;
-	}
-	inode->i_lock = 1;
-	sb->s_op->write_inode(inode);
-	inode->i_lock = 0;
-	wake_up(&inode->i_wait);
-}
-
-/*
- * Look up the root inode of the parent fs.
- * We have to go through iget in order to allow for wait_on_inode.
- */
-int
-nfsd_parentdev(dev_t* devp)
-{
-	struct super_block	*sb;
-
-	if (!(sb = get_super(*devp)) || !sb->s_covered)
-		return 0;
-	if (*devp == sb->s_covered->i_dev)
-		return 0;
-	*devp = sb->s_covered->i_dev;
-	return 1;
-}
-
-/*
- * This is a copy from fs/inode.c because it wasn't exported.
- */
-int
-nfsd_notify_change(struct inode *inode, struct iattr *attr)
-{
-	int retval;
-
-	if (inode->i_sb && inode->i_sb->s_op &&
-	    inode->i_sb->s_op->notify_change)
-		return inode->i_sb->s_op->notify_change(inode, attr);
-
-	if ((retval = inode_change_ok(inode, attr)) != 0)
-		return retval;
-
-	inode_setattr(inode, attr);
-	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.46/linux/fs/open.c linux/fs/open.c
--- v2.1.46/linux/fs/open.c	Sun Jul 20 20:41:58 1997
+++ linux/fs/open.c	Wed Jul 23 09:48:11 1997
@@ -543,44 +543,21 @@
 	return error;
 }
 
-asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group)
-{
-	struct dentry * dentry;
-	int error;
-
-	lock_kernel();
-	dentry = lnamei(filename);
-
-	error = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
-		goto out;
-
-	error = chown_common(dentry, user, group);
-
-	dput(dentry);
-out:
-	unlock_kernel();
-	return(error);
-}
-
 asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
 {
 	struct dentry * dentry;
 	int error;
 
 	lock_kernel();
-	dentry = namei(filename);
+	dentry = lnamei(filename);
 
 	error = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
-		goto out;
-
-	error = chown_common(dentry, user, group);
-
-	dput(dentry);
-out:
+	if (!IS_ERR(dentry)) {
+		error = chown_common(dentry, user, group);
+		dput(dentry);
+	}
 	unlock_kernel();
-	return(error);
+	return error;
 }
 
 asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
diff -u --recursive --new-file v2.1.46/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.1.46/linux/fs/romfs/inode.c	Thu Jun 26 12:33:39 1997
+++ linux/fs/romfs/inode.c	Thu Jul 24 11:25:44 1997
@@ -18,12 +18,19 @@
  * Changes
  *					Changed for 2.1.19 modules
  *	Jan 1997			Initial release
+ *	Jun 1997			2.1.43+ changes
+ *	Jul 1997			proper page locking in readpage
+ *					Changed to work with 2.1.45+ fs
+ *					Fixed follow_link
  */
 
 /* todo:
- *	use malloced memory for file names?
- *	considering write access...
- *	network (tftp) files?
+ *	- see Documentation/filesystems/romfs.txt
+ *	- use malloced memory for file names?
+ *	- considering write access...
+ *	- network (tftp) files?
+ *	- in the ancient times something leaked to made umounts
+ *	  impossible, but I've not seen it in the last months
  */
 
 /*
@@ -74,7 +81,7 @@
 
 	MOD_INC_USE_COUNT;
 
-	/* I would parse the options, but there are none.. :) */
+	/* I would parse the options here, but there are none.. :) */
 
 	lock_super(s);
 	set_blocksize(dev, ROMBSIZE);
@@ -82,6 +89,7 @@
 	s->s_blocksize_bits = ROMBSBITS;
 	bh = bread(dev, 0, ROMBSIZE);
 	if (!bh) {
+		/* XXX merge with other printk? */
                 printk ("romfs: unable to read superblock\n");
 		goto outnobh;
 	}
@@ -113,10 +121,11 @@
 	brelse(bh);
 
 	s->s_op	= &romfs_ops;
+	s->s_root = d_alloc_root(iget(s, sz), NULL);
 
 	unlock_super(s);
 
-	if (!(s->s_mounted = iget(s, sz)))
+	if (!s->s_root)
 		goto outnobh;
 
 	/* Ehrhm; sorry.. :)  And thanks to Hans-Joachim Widmaier  :) */
@@ -145,10 +154,9 @@
 	return;
 }
 
-
 /* That's simple too. */
 
-static void
+static int
 romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
 {
 	struct statfs tmp;
@@ -157,9 +165,12 @@
 	tmp.f_type = ROMFS_MAGIC;
 	tmp.f_bsize = ROMBSIZE;
 	tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
-	copy_to_user(buf, &tmp, bufsize);
+	/* XXX tmp.f_namelen = relevant? */
+	return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
 }
 
+/* some helper routines */
+
 static int
 romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
 {
@@ -238,8 +249,6 @@
 	return res;
 }
 
-/* Directory operations */
-
 static int
 romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir)
 {
@@ -295,14 +304,16 @@
 }
 
 static int
-romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
+romfs_lookup(struct inode *dir, struct dentry *dentry)
 {
 	unsigned long offset, maxoff;
 	int fslen, res;
+	struct inode *inode;
 	char fsname[ROMFS_MAXFN];	/* XXX dynamic? */
 	struct romfs_inode ri;
+	const char *name;		/* got from dentry */
+	int len;
 
-	*result = NULL;
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		res = -EBADF;
 		goto out;
@@ -317,6 +328,12 @@
 	maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
 	offset = ntohl(ri.spec) & ROMFH_MASK;
 
+	/* ok, now find the file, whose name is in "dentry", in the
+	 * directory specified by "dir".  */
+
+	name = dentry->d_name.name;
+	len = dentry->d_name.len;
+
 	for(;;) {
 		if (!offset || offset >= maxoff
 		    || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
@@ -350,20 +367,19 @@
 	if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
 		offset = ntohl(ri.spec) & ROMFH_MASK;
 
-	res = 0;
-	if (!(*result = iget(dir->i_sb, offset)))
-		res = -EACCES;
+	res = -EACCES;
+	if ((inode = iget(dir->i_sb, offset))!=NULL) {
+		res = 0;
+		d_add(dentry, inode);
+	}
 
 out:
-	iput(dir);
 	return res;
 }
 
 /*
  * Ok, we do readpage, to be able to execute programs.  Unfortunately,
- * bmap is not applicable, since we have looser alignments.
- *
- * XXX I'm not quite sure that I need to muck around the PG_xx bits..
+ * we can't use bmap, since we have looser alignments.
  */
 
 static int
@@ -373,8 +389,12 @@
 	unsigned long offset, avail, readlen;
 	int result = -EIO;
 
-	buf = page_address(page);
 	atomic_inc(&page->count);
+	set_bit(PG_locked, &page->flags);
+
+	buf = page_address(page);
+	clear_bit(PG_uptodate, &page->flags);
+	clear_bit(PG_error, &page->flags);
 	offset = page->offset;
 	if (offset < inode->i_size) {
 		avail = inode->i_size-offset;
@@ -383,13 +403,19 @@
 			if (readlen < PAGE_SIZE) {
 				memset((void *)(buf+readlen),0,PAGE_SIZE-readlen);
 			}
-			result = 0;
 			set_bit(PG_uptodate, &page->flags);
-		} else {
-			memset((void *)buf, 0, PAGE_SIZE);
+			result = 0;
 		}
 	}
+	if (result) {
+		set_bit(PG_error, &page->flags);
+		memset((void *)buf, 0, PAGE_SIZE);
+	}
+
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
 	free_page(buf);
+
 	return result;
 }
 
@@ -417,6 +443,46 @@
 	return mylen;
 }
 
+static struct dentry *romfs_follow_link(struct inode *inode, struct dentry *base)
+{
+	char *link;
+	int len, cnt;
+	struct dentry *dentry;
+
+	/* Note: 2.1.46+ calls this for our strange directories...
+	 * What I do is not really right, but I like it better for now,
+	 * than a separate i_op table.  Anyway, our directories won't
+	 * have multiple "real" links to them, so it maybe loses nothing.  */
+	if (!S_ISLNK(inode->i_mode)) {
+		dentry = dget(i_dentry(inode));
+		goto outnobuf;
+	}
+
+	len = inode->i_size;
+
+	dentry = ERR_PTR(-EAGAIN);			/* correct? */
+	if (!(link = kmalloc(len+1, GFP_KERNEL)))
+		goto outnobuf;
+
+	cnt = romfs_copyfrom(inode, link, inode->u.romfs_i.i_dataoffset, len);
+	if (len != cnt) {
+		dentry = ERR_PTR(-EIO);
+		goto out;
+	} else
+		link[len] = 0;
+
+	dentry = lookup_dentry(link, base, 1);
+	kfree(link);
+
+	if (0) {
+out:
+		kfree(link);
+outnobuf:
+		dput(base);
+	}
+	return dentry;
+}
+
 /* Mapping from our types to the kernel */
 
 static struct file_operations romfs_file_operations = {
@@ -447,6 +513,7 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	NULL,			/* readlink */
+	NULL,			/* follow_link */
 	romfs_readpage,		/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap -- not really */
@@ -471,7 +538,7 @@
 	NULL			/* revalidate */
 };
 
-/* Merged dir/symlink op table.  readdir/lookup/readlink
+/* Merged dir/symlink op table.  readdir/lookup/readlink/follow_link
  * will protect from type mismatch.
  */
 
@@ -487,6 +554,7 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	romfs_readlink,		/* readlink */
+	romfs_follow_link,	/* follow_link */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
@@ -519,9 +587,9 @@
 	int nextfh, ino;
 	struct romfs_inode ri;
 
-	i->i_op = NULL;
-
 	ino = i->i_ino & ROMFH_MASK;
+	i->i_op = NULL;
+	i->i_mode = 0;
 
 	/* Loop for finding the real hard link */
 	for(;;) {
@@ -549,6 +617,7 @@
 		ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
 	else
 		ino = 0;
+
 	i->u.romfs_i.i_metasize = ino;
 	i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
 
@@ -573,9 +642,10 @@
 
 static struct super_operations romfs_ops = {
 	romfs_read_inode,	/* read inode */
-	NULL,			/* notify change */
 	NULL,			/* write inode */
 	NULL,			/* put inode */
+	NULL,			/* delete inode */
+	NULL,			/* notify change */
 	romfs_put_super,	/* put super */
 	NULL,			/* write super */
 	romfs_statfs,		/* statfs */
diff -u --recursive --new-file v2.1.46/linux/fs/super.c linux/fs/super.c
--- v2.1.46/linux/fs/super.c	Thu Jul 17 10:06:08 1997
+++ linux/fs/super.c	Wed Jul 23 11:34:23 1997
@@ -637,6 +637,7 @@
 	if (MAJOR(dev) >= MAX_BLKDEV)
 		return -ENXIO;
 
+	fsync_dev(dev);
 	retval = do_umount(dev,0);
 	if (!retval) {
 		fsync_dev(dev);
diff -u --recursive --new-file v2.1.46/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.1.46/linux/include/linux/dcache.h	Thu Jul 17 10:06:08 1997
+++ linux/include/linux/dcache.h	Mon Jul 21 09:53:20 1997
@@ -98,6 +98,10 @@
 /* appendix may either be NULL or be used for transname suffixes */
 extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name);
 
+/* validate "insecure" dentry pointer */
+extern int d_validate(struct dentry *dentry, struct dentry *dparent,
+		      unsigned int hash, unsigned int len);
+
 /* write full pathname into buffer and return length */
 extern int d_path(struct dentry * entry, struct dentry * chroot, char * buf);
 
diff -u --recursive --new-file v2.1.46/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.46/linux/include/linux/fs.h	Sun Jul 20 20:41:59 1997
+++ linux/include/linux/fs.h	Thu Jul 24 11:54:23 1997
@@ -677,6 +677,7 @@
 extern void invalidate_buffers(kdev_t dev);
 extern int floppy_is_wp(int minor);
 extern void sync_inodes(kdev_t dev);
+extern void write_inode_now(struct inode *inode);
 extern void sync_dev(kdev_t dev);
 extern int fsync_dev(kdev_t dev);
 extern void sync_supers(kdev_t dev);
@@ -812,6 +813,7 @@
 
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern void inode_setattr(struct inode *, struct iattr *);
+extern int notify_change(struct inode * inode, struct iattr * attr);
 
 /* kludge to get SCSI modules working */
 #include <linux/minix_fs.h>
diff -u --recursive --new-file v2.1.46/linux/include/linux/lp.h linux/include/linux/lp.h
--- v2.1.46/linux/include/linux/lp.h	Sun Apr 13 10:18:22 1997
+++ linux/include/linux/lp.h	Wed Jul 23 11:14:31 1997
@@ -113,7 +113,7 @@
 };
 
 struct lp_struct {
-	struct ppd *dev;
+	struct pardevice *dev;
 	int flags;
 	unsigned int chars;
 	unsigned int time;
diff -u --recursive --new-file v2.1.46/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v2.1.46/linux/include/linux/msdos_fs.h	Thu Jul 17 10:06:08 1997
+++ linux/include/linux/msdos_fs.h	Wed Jul 23 10:29:42 1997
@@ -211,6 +211,7 @@
 extern int fat_bmap(struct inode *inode,int block);
 extern int fat_notify_change(struct inode *,struct iattr *);
 extern void fat_put_inode(struct inode *inode);
+extern void fat_delete_inode(struct inode *inode);
 extern void fat_put_super(struct super_block *sb);
 extern void fat_read_inode(struct inode *inode, struct inode_operations *dir_ops);
 extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent);
@@ -252,34 +253,30 @@
 
 /* msdos.c - these are for Umsdos */
 extern void msdos_read_inode(struct inode *inode);
-extern int msdos_lookup(struct inode *dir,const char *name,int len, 
-			struct inode **result);
-extern int msdos_create(struct inode *dir,const char *name,int len,int mode,
-			struct inode **result);
-extern int msdos_rmdir(struct inode *dir,const char *name,int len);
-extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
-extern int msdos_unlink(struct inode *dir,const char *name,int len);
-extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len);
-extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
-			struct inode *new_dir,const char *new_name,int new_len);
+extern int msdos_lookup(struct inode *dir,struct dentry *);
+extern int msdos_create(struct inode *dir,struct dentry *dentry,int mode);
+extern int msdos_rmdir(struct inode *dir,struct dentry *dentry);
+extern int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode);
+extern int msdos_unlink(struct inode *dir,struct dentry *dentry);
+extern int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry);
+extern int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
+			struct inode *new_dir,struct dentry *new_dentry);
 
 /* fatfs_syms.c */
 extern int init_fat_fs(void);
 
 /* vfat/namei.c - these are for dmsdos */
-extern int vfat_create(struct inode *dir,const char *name,int len,int mode,
-		       struct inode **result);
-extern int vfat_unlink(struct inode *dir,const char *name,int len);
-extern int vfat_mkdir(struct inode *dir,const char *name,int len,int mode);
-extern int vfat_rmdir(struct inode *dir,const char *name,int len);
-extern int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
-		       struct inode *new_dir,const char *new_name,int new_len);
+extern int vfat_create(struct inode *dir,struct dentry *dentry,int mode);
+extern int vfat_unlink(struct inode *dir,struct dentry *dentry);
+extern int vfat_mkdir(struct inode *dir,struct dentry *dentry,int mode);
+extern int vfat_rmdir(struct inode *dir,struct dentry *dentry);
+extern int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
+		       struct inode *new_dir,struct dentry *new_dentry);
 extern void vfat_put_super(struct super_block *sb);
 extern struct super_block *vfat_read_super(struct super_block *sb,void *data,
 					   int silent);
 extern void vfat_read_inode(struct inode *inode);
-extern int vfat_lookup(struct inode *dir,const char *name,int len,
-		       struct inode **result);
+extern int vfat_lookup(struct inode *dir,struct dentry *);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.46/linux/include/linux/nfsd/export.h linux/include/linux/nfsd/export.h
--- v2.1.46/linux/include/linux/nfsd/export.h	Mon Jul  7 16:03:34 1997
+++ linux/include/linux/nfsd/export.h	Tue Jul 22 11:42:18 1997
@@ -59,7 +59,7 @@
 	struct svc_export *	ex_parent;
 	struct svc_client *	ex_client;
 	int			ex_flags;
-	struct inode *		ex_inode;
+	struct dentry *		ex_dentry;
 	dev_t			ex_dev;
 	ino_t			ex_ino;
 	uid_t			ex_anon_uid;
diff -u --recursive --new-file v2.1.46/linux/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h
--- v2.1.46/linux/include/linux/nfsd/nfsd.h	Mon Jul  7 16:03:34 1997
+++ linux/include/linux/nfsd/nfsd.h	Tue Jul 22 11:42:18 1997
@@ -109,10 +109,7 @@
 int		nfsd_statfs(struct svc_rqst *, struct svc_fh *,
 				struct statfs *);
 int		nfsd_notify_change(struct inode *, struct iattr *);
-int		nfsd_permission(struct svc_export *, struct inode *, int);
-void		nfsd_write_inode(struct inode *);
-struct inode *	nfsd_iget(dev_t dev, ino_t ino);
-int		nfsd_parentdev(dev_t *devp);
+int		nfsd_permission(struct svc_export *, struct dentry *, int);
 
 /*
  * lockd binding
diff -u --recursive --new-file v2.1.46/linux/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- v2.1.46/linux/include/linux/nfsd/nfsfh.h	Thu Jul 17 10:06:08 1997
+++ linux/include/linux/nfsd/nfsfh.h	Tue Jul 22 11:42:18 1997
@@ -21,18 +21,18 @@
 #include <linux/nfsd/debug.h>
 
 /*
- * This is our NFSv2 file handle.
+ * This is the new "dentry style" Linux NFSv2 file handle.
  *
- * The xdev and xino fields are currently used to transport the dev/ino
- * of the exported inode. The xdev field is redundant, though, because
- * we do not allow mount point crossing.
+ * The xino and xdev fields are currently used to transport the
+ * ino/dev of the exported inode.
  */
 struct nfs_fhbase {
-	dev_t			fb_dev;
-	dev_t			fb_xdev;
-	ino_t			fb_ino;
+	struct dentry		*fb_dentry;
+	struct dentry		*fb_dparent;
+	unsigned int		fb_dhash;
+	unsigned int		fb_dlen;
 	ino_t			fb_xino;
-	__u32			fb_version;
+	dev_t			fb_xdev;
 };
 
 #define NFS_FH_PADDING		(NFS_FHSIZE - sizeof(struct nfs_fhbase))
@@ -41,11 +41,12 @@
 	__u8			fh_cookie[NFS_FH_PADDING];
 };
 
-#define fh_dev			fh_base.fb_dev
-#define fh_xdev			fh_base.fb_xdev
-#define fh_ino			fh_base.fb_ino
+#define fh_dentry		fh_base.fb_dentry
+#define fh_dparent		fh_base.fb_dparent
+#define fh_dhash		fh_base.fb_dhash
+#define fh_dlen			fh_base.fb_dlen
 #define fh_xino			fh_base.fb_xino
-#define fh_version		fh_base.fb_version
+#define fh_xdev			fh_base.fb_xdev
 
 
 #ifdef __KERNEL__
@@ -57,26 +58,24 @@
 typedef struct svc_fh {
 	struct knfs_fh		fh_handle;	/* FH data */
 	struct svc_export *	fh_export;	/* export pointer */
-	struct dentry *		fh_dentry;	/* file */
 	size_t			fh_pre_size;	/* size before operation */
 	time_t			fh_pre_mtime;	/* mtime before oper */
 	time_t			fh_pre_ctime;	/* ctime before oper */
 	unsigned long		fh_post_version;/* inode version after oper */
 	unsigned char		fh_locked;	/* inode locked by us */
+	unsigned char		fh_dverified;	/* dentry has been checked */
 } svc_fh;
 
 /*
- * Shorthands for dprintk()'s
+ * Shorthand for dprintk()'s
  */
-#define SVCFH_INO(f)		((f)->fh_handle.fh_ino)
-#define SVCFH_DEV(f)		((f)->fh_handle.fh_dev)
+#define SVCFH_DENTRY(f)		((f)->fh_handle.fh_dentry)
 
 /*
  * Function prototypes
  */
-u32             fh_lookup(struct svc_rqst *, struct svc_fh *, int, int);
-void            fh_compose(struct svc_fh *, struct svc_export *,
-                                struct inode *);
+u32             fh_verify(struct svc_rqst *, struct svc_fh *, int, int);
+void            fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
 
 static __inline__ struct svc_fh *
 fh_copy(struct svc_fh *dst, struct svc_fh *src)
@@ -98,7 +97,7 @@
 static inline void
 fh_lock(struct svc_fh *fhp)
 {
-	struct inode	*inode = fhp->fh_dentry->d_inode;
+	struct inode	*inode = fhp->fh_handle.fh_dentry->d_inode;
 
 	/*
 	dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
@@ -118,7 +117,7 @@
 static inline void
 fh_unlock(struct svc_fh *fhp)
 {
-	struct inode	*inode = fhp->fh_dentry->d_inode;
+	struct inode	*inode = fhp->fh_handle.fh_dentry->d_inode;
 
 	if (fhp->fh_locked) {
 		if (!fhp->fh_post_version)
@@ -135,9 +134,9 @@
 static inline void
 fh_put(struct svc_fh *fhp)
 {
-	if (fhp->fh_dentry) {
+	if (fhp->fh_dverified) {
 		fh_unlock(fhp);
-		dput(fhp->fh_dentry);
+		dput(fhp->fh_handle.fh_dentry);
 	}
 }
 #else
@@ -148,9 +147,10 @@
 {
 	struct dentry	*dentry;
 
-	if (!(dentry = fhp->fh_dentry))
+	if (!fhp->fh_dverified)
 		return;
 
+	dentry = fhp->fh_handle.fh_dentry;
 	if (!dentry->d_count) {
 		printk("nfsd: trying to free free dentry in %s:%d\n"
 		       "      file %s/%s\n",
diff -u --recursive --new-file v2.1.46/linux/include/linux/nfsd/syscall.h linux/include/linux/nfsd/syscall.h
--- v2.1.46/linux/include/linux/nfsd/syscall.h	Mon Apr  7 11:35:32 1997
+++ linux/include/linux/nfsd/syscall.h	Mon Jul 21 09:53:20 1997
@@ -111,11 +111,11 @@
 /*
  * Kernel syscall implementation.
  */
-#ifdef CONFIG_NFSD
+#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
 extern asmlinkage int	sys_nfsservctl(int, struct nfsctl_arg *,
 					    union nfsctl_res *);
 #else
-# define sys_nfsservctl	sys_ni_syscall
+#define sys_nfsservctl		sys_ni_syscall
 #endif
 extern int		exp_addclient(struct nfsctl_client *ncp);
 extern int		exp_delclient(struct nfsctl_client *ncp);
diff -u --recursive --new-file v2.1.46/linux/include/linux/nfsd/xdr.h linux/include/linux/nfsd/xdr.h
--- v2.1.46/linux/include/linux/nfsd/xdr.h	Mon Apr  7 11:35:32 1997
+++ linux/include/linux/nfsd/xdr.h	Mon Jul 21 09:53:20 1997
@@ -150,9 +150,10 @@
 int nfssvc_encode_readres(struct svc_rqst *, u32 *, struct nfsd_readres *);
 int nfssvc_encode_statfsres(struct svc_rqst *, u32 *, struct nfsd_statfsres *);
 int nfssvc_encode_readdirres(struct svc_rqst *, u32 *, struct nfsd_readdirres *);
-int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *);
 
 int nfssvc_encode_entry(struct readdir_cd *, const char *name,
 				int namlen, off_t offset, ino_t ino);
+
+int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *);
 
 #endif /* LINUX_NFSD_H */
diff -u --recursive --new-file v2.1.46/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.1.46/linux/include/linux/parport.h	Mon Jul  7 16:02:51 1997
+++ linux/include/linux/parport.h	Wed Jul 23 11:15:54 1997
@@ -1,4 +1,4 @@
-/* $Id: parport.h,v 1.2.6.3 1997/04/16 21:21:03 phil Exp $ */
+/* $Id: parport.h,v 1.2.6.3.2.2 1997/04/18 15:03:53 phil Exp $ */
 
 #ifndef _PARPORT_H_
 #define _PARPORT_H_
@@ -11,12 +11,92 @@
 #define PARPORT_MAX  8 
 
 /* Magic numbers */
-#define PARPORT_IRQ_NONE  -1
-#define PARPORT_DMA_NONE  -1
+#define PARPORT_IRQ_NONE  -2
+#define PARPORT_DMA_NONE  -2
+#define PARPORT_IRQ_AUTO  -1
+#define PARPORT_DMA_AUTO  -1
 #define PARPORT_DISABLE   -2
 
-/* Type classes for Plug-and-Play probe */
+/* Define this later. */
+struct parport;
+
+struct pc_parport_state {
+	unsigned int ctr;
+	unsigned int ecr;
+};
+
+struct parport_state {
+	union {
+		struct pc_parport_state pc;
+		/* ARC has no state. */
+		void *misc; 
+	} u;
+};
 
+/* Generic operations vector through the dispatch table. */
+#define parport_write_data(p,x)            (p)->ops->write_data(p,x)
+#define parport_read_data(p)               (p)->ops->read_data(p)
+#define parport_write_control(p,x)         (p)->ops->write_control(p,x)
+#define parport_read_control(p)            (p)->ops->read_control(p)
+#define parport_frob_control(p,m,v)        (p)->ops->frob_control(p,m,v)
+#define parport_write_econtrol(p,x)        (p)->ops->write_econtrol(p,x)
+#define parport_read_econtrol(p)           (p)->ops->read_econtrol(p)
+#define parport_frob_econtrol(p,m,v)       (p)->ops->frob_econtrol(p,m,v)
+#define parport_write_status(p,v)          (p)->ops->write_status(p,v)
+#define parport_read_status(p)             (p)->ops->read_status(p)
+#define parport_write_fifo(p,v)            (p)->ops->write_fifo(p,v)
+#define parport_read_fifo(p)               (p)->ops->read_fifo(p)
+#define parport_change_mode(p,m)           (p)->ops->change_mode(p,m)
+#define parport_release_resources(p)       (p)->ops->release_resources(p)
+#define parport_claim_resources(p)         (p)->ops->claim_resources(p)
+
+struct parport_operations {
+	void (*write_data)(struct parport *, unsigned int);
+	unsigned int (*read_data)(struct parport *);
+	void (*write_control)(struct parport *, unsigned int);
+	unsigned int (*read_control)(struct parport *);
+	unsigned int (*frob_control)(struct parport *, unsigned int mask, unsigned int val);
+	void (*write_econtrol)(struct parport *, unsigned int);
+	unsigned int (*read_econtrol)(struct parport *);
+	unsigned int (*frob_econtrol)(struct parport *, unsigned int mask, unsigned int val);
+	void (*write_status)(struct parport *, unsigned int);
+	unsigned int (*read_status)(struct parport *);
+	void (*write_fifo)(struct parport *, unsigned int);
+	unsigned int (*read_fifo)(struct parport *);
+
+	void (*change_mode)(struct parport *, int);
+
+	void (*release_resources)(struct parport *);
+	int (*claim_resources)(struct parport *);
+
+	unsigned int (*epp_write_block)(struct parport *, void *, unsigned int);
+	unsigned int (*epp_read_block)(struct parport *, void *, unsigned int);
+
+	unsigned int (*ecp_write_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *);
+	unsigned int (*ecp_read_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *);
+
+	void (*save_state)(struct parport *, struct parport_state *);
+	void (*restore_state)(struct parport *, struct parport_state *);
+
+	void (*enable_irq)(struct parport *);
+	void (*disable_irq)(struct parport *);
+	int (*examine_irq)(struct parport *);
+};
+
+#define PARPORT_CONTROL_STROBE    0x1
+#define PARPORT_CONTROL_AUTOFD    0x2
+#define PARPORT_CONTROL_INIT      0x4
+#define PARPORT_CONTROL_SELECT    0x8
+#define PARPORT_CONTROL_INTEN     0x10
+#define PARPORT_CONTROL_DIRECTION 0x20
+
+#define PARPORT_STATUS_ERROR      0x8
+#define PARPORT_STATUS_SELECT     0x10
+#define PARPORT_STATUS_PAPEROUT   0x20
+#define PARPORT_STATUS_ACK        0x40
+#define PARPORT_STATUS_BUSY       0x80
+
+/* Type classes for Plug-and-Play probe.  */
 typedef enum {
 	PARPORT_CLASS_LEGACY = 0,       /* Non-IEEE1284 device */
 	PARPORT_CLASS_PRINTER,
@@ -41,12 +121,6 @@
 	char *description;
 };
 
-/* Definitions for parallel port sharing */
-
-/* Forward declare some stuff so we can use mutually circular structures */
-struct ppd;
-struct parport;
-
 /* Each device can have two callback functions:
  *  1) a preemption function, called by the resource manager to request
  *     that the driver relinquish control of the port.  The driver should
@@ -59,37 +133,27 @@
  *     the port, it should call parport_claim() here.  The return value from
  *     this function is ignored.
  */
-typedef int (*callback_func) (void *);
-
-/* This is an ordinary kernel IRQ handler routine.
- * The dev_id field (void *) will point the the port structure
- * associated with the interrupt request (to allow IRQ sharing)
- * Please make code IRQ sharing as this function may be called
- * when it isn't meant for you...
- */
-typedef void (*irq_handler_func) (int, void *, struct pt_regs *);
 
 /* A parallel port device */
-struct ppd {
+struct pardevice {
 	char *name;
-	struct parport *port;	/* The port this is associated with */
-	callback_func preempt;	/* preemption function */
-	callback_func wakeup;	/* kick function */
+	struct parport *port;
+	int (*preempt)(void *);
+	void (*wakeup)(void *);
 	void *private;
-	irq_handler_func irq_func;
+	void (*irq_func)(int, void *, struct pt_regs *);
 	int flags;
-	unsigned char ctr;	/* SPP CTR register */
-	unsigned char ecr;	/* ECP ECR register */
-	struct ppd *next;
-	struct ppd *prev;
+	struct pardevice *next;
+	struct pardevice *prev;
+	struct parport_state *state;     /* saved status over preemption */
 };
 
-struct parport_dir{
+struct parport_dir {
 	struct proc_dir_entry *entry;    /* Directory /proc/parport/X     */
 	struct proc_dir_entry *irq;      /* IRQ entry /proc/parport/X/irq */
 	struct proc_dir_entry *devices;  /* /proc/parport/X/devices       */
 	struct proc_dir_entry *hardware; /* /proc/parport/X/hardware      */
-    char name[4]; /* /proc/parport/"XXXX" */
+	char name[4];                    /* /proc/parport/"XXXX" */
 };
 
 /* A parallel port */
@@ -100,16 +164,19 @@
 	int irq;		/* interrupt (or -1 for none) */
 	int dma;
 	unsigned int modes;
-	struct ppd *devices;
-	struct ppd *cad;	/* port owner */
-	struct ppd *lurker;
-	unsigned int ctr;	/* SPP CTR register */
-	unsigned int ecr;	/* ECP ECR register */
+
+	struct pardevice *devices;
+	struct pardevice *cad;	/* port owner */
+	struct pardevice *lurker;
+	
 	struct parport *next;
-        unsigned int flags; 
+	unsigned int flags; 
+
 	struct parport_dir pdir;
 	struct parport_device_info probe_info; 
-	int speed;  /* Max Write in Bytes/s */
+
+	struct parport_operations *ops;
+	void *private_data;     /* for lowlevel driver */
 };
 
 /* parport_register_port registers a new parallel port at the given address (if
@@ -117,15 +184,15 @@
  * claiming the I/O region, IRQ and DMA.
  * NULL is returned if initialisation fails. 
  */
-struct parport *parport_register_port(unsigned long base, int irq, int dma);
+struct parport *parport_register_port(unsigned long base, int irq, int dma,
+				      struct parport_operations *ops);
 
 /* parport_in_use returns nonzero if there are devices attached to a port. */
 #define parport_in_use(x)  ((x)->devices != NULL)
 
-/* parport_destroy blows away a parallel port.  This fails if any devices are
- * registered.
- */
-void parport_destroy(struct parport *);
+/* Put a parallel port to sleep; release its hardware resources.  Only possible
+ * if no devices are registered.  */
+void parport_quiesce(struct parport *);
 
 /* parport_enumerate returns a pointer to the linked list of all the ports
  * in this machine.
@@ -140,19 +207,20 @@
  * Only one lurking driver can be used on a given port. 
  * handle is a user pointer that gets handed to callback functions. 
  */
-struct ppd *parport_register_device(struct parport *port, const char *name,
-				    callback_func pf, callback_func kf,
-				    irq_handler_func irq_func, int flags,
-				    void *handle);
+struct pardevice *parport_register_device(struct parport *port, 
+			  const char *name,
+			  int (*pf)(void *), int (*kf)(void *),
+			  void (*irq_func)(int, void *, struct pt_regs *), 
+			  int flags, void *handle);
 
-/* parport_deregister causes the kernel to forget about a device */
-void parport_unregister_device(struct ppd *dev);
+/* parport_unregister unlinks a device from the chain. */
+void parport_unregister_device(struct pardevice *dev);
 
 /* parport_claim tries to gain ownership of the port for a particular driver.
  * This may fail (return non-zero) if another driver is busy.  If this
  * driver has registered an interrupt handler, it will be enabled. 
  */
-int parport_claim(struct ppd *dev);
+int parport_claim(struct pardevice *dev);
 
 /* parport_release reverses a previous parport_claim.  This can never fail, 
  * though the effects are undefined (except that they are bad) if you didn't
@@ -162,25 +230,24 @@
  * If you mess with the port state (enabling ECP for example) you should
  * clean up before releasing the port. 
  */
-void parport_release(struct ppd *dev);
+void parport_release(struct pardevice *dev);
 
 /* The "modes" entry in parport is a bit field representing the following
  * modes.
  * Note that LP_ECPEPP is for the SMC EPP+ECP mode which is NOT
  * 100% compatible with EPP.
  */
-#define PARPORT_MODE_SPP	        0x0001
-#define PARPORT_MODE_PS2		0x0002
-#define PARPORT_MODE_EPP		0x0004
-#define PARPORT_MODE_ECP		0x0008
-#define PARPORT_MODE_ECPEPP		0x0010
-#define PARPORT_MODE_ECR		0x0020  /* ECR Register Exists */
-#define PARPORT_MODE_ECPPS2		0x0040
-
-/* Flags used to identify what a device does
- */
-#define PARPORT_DEV_TRAN	        0x0000
-#define PARPORT_DEV_LURK	        0x0001
+#define PARPORT_MODE_PCSPP	        0x0001
+#define PARPORT_MODE_PCPS2		0x0002
+#define PARPORT_MODE_PCEPP		0x0004
+#define PARPORT_MODE_PCECP		0x0008
+#define PARPORT_MODE_PCECPEPP		0x0010
+#define PARPORT_MODE_PCECR		0x0020  /* ECR Register Exists */
+#define PARPORT_MODE_PCECPPS2		0x0040
+
+/* Flags used to identify what a device does. */
+#define PARPORT_DEV_TRAN	        0x0000  /* We're transient. */
+#define PARPORT_DEV_LURK	        0x0001  /* We lurk. */
 
 #define PARPORT_FLAG_COMA		1
 
@@ -200,31 +267,5 @@
 
 extern int parport_probe(struct parport *port, char *buffer, int len);
 extern void parport_probe_one(struct parport *port);
-
-/* Primitive port access functions */
-extern inline void parport_w_ctrl(struct parport *port, int val) 
-{
-	outb(val, port->base+2);
-}
-
-extern inline int parport_r_ctrl(struct parport *port)
-{
-	return inb(port->base+2);
-}
-
-extern inline void parport_w_data(struct parport *port, int val)
-{
-	outb(val, port->base);
-}
-
-extern inline int parport_r_data(struct parport *port)
-{
-	return inb(port->base);
-}
-
-extern inline int parport_r_status(struct parport *port)
-{
-	return inb(port->base+1);
-}
 
 #endif /* _PARPORT_H_ */
diff -u --recursive --new-file v2.1.46/linux/include/linux/rocket.h linux/include/linux/rocket.h
--- v2.1.46/linux/include/linux/rocket.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/rocket.h	Mon Jul 21 12:41:21 1997
@@ -0,0 +1,55 @@
+/*
+ * This file contains the exported interface of the rocket driver to
+ * its configuration program.
+ */
+
+struct rocket_config {
+	int	line;
+	int	flags;
+	int	closing_wait;
+	int	close_delay;
+	int	port;
+	int	reserved[32];
+};
+
+struct rocket_ports {
+	int	tty_major;
+	int	callout_major;
+	int	port_bitmap[4];
+	int	reserved[32];
+};
+
+/*
+ * Rocketport flags
+ */
+#define ROCKET_CALLOUT_NOHUP    0x00000001
+#define ROCKET_FORCE_CD		0x00000002
+#define ROCKET_HUP_NOTIFY	0x00000004
+#define ROCKET_SPLIT_TERMIOS	0x00000008
+#define ROCKET_SPD_MASK		0x00000070
+#define ROCKET_SPD_HI		0x00000010 /* Use 56000 instead of 38400 bps */
+#define ROCKET_SPD_VHI		0x00000020 /* Use 115200 instead of 38400 bps*/
+#define ROCKET_SPD_SHI		0x00000030 /* Use 230400 instead of 38400 bps*/
+#define ROCKET_SPD_WARP	        0x00000040 /* Use 460800 instead of 38400 bps*/
+#define ROCKET_SAK		0x00000080
+#define ROCKET_SESSION_LOCKOUT	0x00000100
+#define ROCKET_PGRP_LOCKOUT	0x00000200
+	
+#define ROCKET_FLAGS		0x000003FF
+
+#define ROCKET_USR_MASK 0x0071	/* Legal flags that non-privileged
+				 * users can set or reset */
+
+/*
+ * For closing_wait and closing_wait2
+ */
+#define ROCKET_CLOSING_WAIT_NONE	65535
+#define ROCKET_CLOSING_WAIT_INF		0
+
+/*
+ * Rocketport ioctls -- "RP"
+ */
+#define RCKP_GET_STRUCT		0x00525001
+#define RCKP_GET_CONFIG		0x00525002
+#define RCKP_SET_CONFIG		0x00525003
+#define RCKP_GET_PORTS		0x00525004
diff -u --recursive --new-file v2.1.46/linux/include/linux/timer.h linux/include/linux/timer.h
--- v2.1.46/linux/include/linux/timer.h	Tue Apr  2 22:20:45 1996
+++ linux/include/linux/timer.h	Mon Jul 21 12:41:21 1997
@@ -54,6 +54,7 @@
 
 #define HD_TIMER2	24
 #define GSCD_TIMER	25
+#define COMTROL_TIMER	26
 
 #define DIGI_TIMER	29
 
diff -u --recursive --new-file v2.1.46/linux/init/main.c linux/init/main.c
--- v2.1.46/linux/init/main.c	Thu Jul 17 10:06:09 1997
+++ linux/init/main.c	Wed Jul 23 10:41:21 1997
@@ -62,6 +62,7 @@
 static int init(void *);
 extern int bdflush(void *);
 extern int kswapd(void *);
+extern void kswapd_setup(void);
 
 extern void init_IRQ(void);
 extern void init_modules(void);
@@ -966,6 +967,7 @@
 	/* Launch bdflush from here, instead of the old syscall way. */
 	kernel_thread(bdflush, NULL, 0);
 	/* Start the background pageout daemon. */
+	kswapd_setup();
 	kernel_thread(kswapd, NULL, 0);
 
 #if CONFIG_AP1000
diff -u --recursive --new-file v2.1.46/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.46/linux/kernel/ksyms.c	Sun Jul 20 20:41:59 1997
+++ linux/kernel/ksyms.c	Mon Jul 21 09:53:21 1997
@@ -136,6 +136,7 @@
 EXPORT_SYMBOL(num_physpages);
 EXPORT_SYMBOL(high_memory);
 EXPORT_SYMBOL(update_vm_cache);
+EXPORT_SYMBOL(vmtruncate);
 
 /* filesystem internal functions */
 EXPORT_SYMBOL(getname);
@@ -150,6 +151,7 @@
 EXPORT_SYMBOL(close_fp);
 EXPORT_SYMBOL(d_alloc_root);
 EXPORT_SYMBOL(d_delete);
+EXPORT_SYMBOL(d_validate);
 EXPORT_SYMBOL(d_add);
 EXPORT_SYMBOL(d_move);
 EXPORT_SYMBOL(d_instantiate);
@@ -163,6 +165,8 @@
 EXPORT_SYMBOL(permission);
 EXPORT_SYMBOL(inode_setattr);
 EXPORT_SYMBOL(inode_change_ok);
+EXPORT_SYMBOL(write_inode_now);
+EXPORT_SYMBOL(notify_change);
 EXPORT_SYMBOL(get_hardblocksize);
 EXPORT_SYMBOL(set_blocksize);
 EXPORT_SYMBOL(getblk);
diff -u --recursive --new-file v2.1.46/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.46/linux/mm/filemap.c	Thu Jul 17 10:06:09 1997
+++ linux/mm/filemap.c	Wed Jul 23 12:53:04 1997
@@ -43,18 +43,7 @@
  * Simple routines for both non-shared and shared mappings.
  */
 
-/*
- * This is a special fast page-free routine that _only_ works
- * on page-cache pages that we are currently using. We can
- * just decrement the page count, because we know that the page
- * has a count > 1 (the page cache itself counts as one, and
- * we're currently using it counts as one). So we don't need
- * the full free_page() stuff..
- */
-static inline void release_page(struct page * page)
-{
-	atomic_dec(&page->count);
-}
+#define release_page(page) __free_page((page))
 
 /*
  * Invalidate the pages of an inode, removing all pages that aren't
@@ -767,6 +756,9 @@
  * The goto's are kind of ugly, but this streamlines the normal case of having
  * it in the page cache, and handles the special cases reasonably without
  * having a lot of duplicated code.
+ *
+ * WSH 06/04/97: fixed a memory leak and moved the allocation of new_page
+ * ahead of the wait if we're sure to need it.
  */
 static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share)
 {
@@ -791,8 +783,15 @@
 found_page:
 	/*
 	 * Ok, found a page in the page cache, now we need to check
-	 * that it's up-to-date
+	 * that it's up-to-date.  First check whether we'll need an
+	 * extra page -- better to overlap the allocation with the I/O.
 	 */
+	if (no_share && !new_page) {
+		new_page = __get_free_page(GFP_KERNEL);
+		if (!new_page)
+			goto failure;
+	}
+
 	if (PageLocked(page))
 		goto page_locked_wait;
 	if (!PageUptodate(page))
@@ -817,13 +816,8 @@
 	}
 
 	/*
-	 * Check that we have another page to copy it over to..
+	 * No sharing ... copy to the new page.
 	 */
-	if (!new_page) {
-		new_page = __get_free_page(GFP_KERNEL);
-		if (!new_page)
-			goto failure;
-	}
 	copy_page(new_page, old_page);
 	flush_page_to_ram(new_page);
 	release_page(page);
@@ -887,6 +881,8 @@
 	 */
 failure:
 	release_page(page);
+	if (new_page)
+		free_page(new_page);
 no_page:
 	return 0;
 }
@@ -935,6 +931,10 @@
 		/* whee.. just mark the buffer heads dirty */
 		struct buffer_head * tmp = bh;
 		do {
+			/*
+			 * WSH: There's a race here: mark_buffer_dirty()
+			 * could block, and the buffers aren't pinned down.
+			 */
 			mark_buffer_dirty(tmp, 0);
 			tmp = tmp->b_this_page;
 		} while (tmp != bh);
@@ -953,6 +953,9 @@
 	file.f_pos = offset;
 	file.f_reada = 0;
 
+	/*
+	 * WSH: could vm_area struct (and inode) be released while writing?
+	 */
 	down(&inode->i_sem);
 	result = do_write_page(inode, &file, (const char *) page, offset);
 	up(&inode->i_sem);
@@ -1295,7 +1298,7 @@
 	unsigned long	ppos, offset;
 	unsigned int	bytes, written;
 	unsigned long	pos;
-	int		status, sync, didread = 0;
+	int		status, sync, didread;
 
 	if (!inode->i_op || !inode->i_op->updatepage)
 		return -EIO;
@@ -1334,9 +1337,14 @@
 			page_cache = 0;
 		}
 
-lockit:
-		while (test_and_set_bit(PG_locked, &page->flags))
-			wait_on_page(page);
+		/*
+		 * WSH 06/05/97: restructured slightly to make sure we release
+		 * the page on an error exit.  Removed explicit setting of
+		 * PG_locked, as that's handled below the i_op->xxx interface.
+		 */
+		didread = 0;
+page_wait:
+		wait_on_page(page);
 
 		/*
 		 * If the page is not uptodate, and we're writing less
@@ -1345,28 +1353,24 @@
 		 * after the current end of file.
 		 */
 		if (!PageUptodate(page)) {
-			/* Already tried to read it twice... too bad */
-			if (didread > 1) {
-				status = -EIO;
-				break;
-			}
 			if (bytes < PAGE_SIZE && ppos < inode->i_size) {
-				/* readpage implicitly unlocks the page */
-				status = inode->i_op->readpage(inode, page);
+				if (didread < 2)
+				    status = inode->i_op->readpage(inode, page);
+				else 
+				    status = -EIO; /* two tries ... error out */
 				if (status < 0)
-					break;
+					goto done_with_page;
 				didread++;
-				goto lockit;
+				goto page_wait;
 			}
 			set_bit(PG_uptodate, &page->flags);
 		}
-		didread = 0;
 
-		/* Alright, the page is there, and we've locked it. Now
-		 * update it. */
+		/* Alright, the page is there.  Now update it. */
 		status = inode->i_op->updatepage(inode, page, buf,
 							offset, bytes, sync);
-		free_page(page_address(page));
+done_with_page:
+		__free_page(page);
 		if (status < 0)
 			break;
 
diff -u --recursive --new-file v2.1.46/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.46/linux/mm/vmscan.c	Thu Jul 17 10:06:09 1997
+++ linux/mm/vmscan.c	Wed Jul 23 10:41:21 1997
@@ -406,14 +406,30 @@
 }
 
 /*
+ * Before we start the kernel thread, print out the 
+ * kswapd initialization message (otherwise the init message 
+ * may be printed in the middle of another driver's init 
+ * message).  It looks very bad when that happens.
+ */
+void kswapd_setup(void)
+{
+       int i;
+       char *revision="$Revision: 1.23 $", *s, *e;
+
+       if ((s = strchr(revision, ':')) &&
+           (e = strchr(s, '$')))
+               s++, i = e - s;
+       else
+               s = revision, i = -1;
+       printk ("Starting kswapd v%.*s\n", i, s);
+}
+
+/*
  * The background pageout daemon.
  * Started as a kernel thread from the init process.
  */
 int kswapd(void *unused)
 {
-	int i;
-	char *revision="$Revision: 1.23 $", *s, *e;
-	
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "kswapd");
@@ -434,13 +450,6 @@
 
 	init_swap_timer();
 	
-	if ((s = strchr(revision, ':')) &&
-	    (e = strchr(s, '$')))
-		s++, i = e - s;
-	else
-		s = revision, i = -1;
-	printk ("Started kswapd v%.*s\n", i, s);
-
 	while (1) {
 		kswapd_awake = 0;
 		current->signal = 0;
@@ -496,7 +505,6 @@
 	}
 	timer_active |= (1<<SWAP_TIMER);
 }
-
 
 /* 
  * Initialise the swap timer
