## Automatically generated incremental diff
## From:   linux-2.2.18-pre8
## To:     linux-2.2.18-pre9
## Robot:  $Id: make-incremental-diff,v 1.5 2001/12/01 08:32:36 hpa Exp $

diff -urN linux-2.2.18-pre8/Documentation/ARM-README linux/Documentation/ARM-README
--- linux-2.2.18-pre8/Documentation/ARM-README	Thu Apr 29 11:53:41 1999
+++ linux/Documentation/ARM-README	Wed Dec 31 16:00:00 1969
@@ -1,181 +0,0 @@
-			   ARM Linux 2.1.99
-			   ================
-
-  Since this is a development kernel, it will not be as stable as the 2.0
-  series, and can cause very nasty problems (eg, trashing your hard disk).
-  When running one of these kernels, I advise you to back up the complete
-  contents of all your hard disks.
-
-
-Contributors
-------------
-
-  Here is a list of people actively working on the project (If you
-  wish to be added to the list, please email me):
-
-  Name: Russell King
-  Mail: linux@arm.uk.linux.org
-  Desc: Original developer of ARM Linux, project co-ordinator.
-
-  Name: Dave Gilbert
-  Mail: linux@treblig.org
-  Desc: A3/4/5xx floppy and hard disk code maintainer.
-
-  Name: Philip Blundell
-  Mail: Philip.Blundell@pobox.com
-  Desc: Architecture and processor selection during make config.
-
-
-Todo list
----------
-
-  This is the list of changes to be done (roughly prioritised):
-
-  * fully test new MEMC translation code
-  * fully test new AcornSCSI driver.
-  * reply to email ;)
-
-
-			     Notes
-			     =====
-
-Compilation of kernel
----------------------
-
-  In order to compile ARM Linux, you will need a compiler capable of
-  generating ARM ELF code with GNU extensions.  GCC-2.7.2.2 is good.
-
-  To build ARM Linux natively, you shouldn't have to alter the ARCH = line in
-  the top level Makefile.  However, if you don't have the ARM Linux ELF tools
-  installed as default, then you should change the CROSS_COMPILE line as
-  detailed below.
-
-  If you wish to cross-compile, then alter the following lines in the top
-  level make file:
-
-    ARCH = <whatever>
-	with
-    ARCH = arm
-
-	and
-
-    CROSS_COMPILE=
-	to
-    CROSS_COMPILE=<your-path-to-your-compiler-without-gcc>
-	eg.
-    CROSS_COMPILE=/usr/bin/arm-unknown-linuxelf-
-
-  Do a 'make config', followed by 'make dep', and finally 'make all' to
-  build the kernel (vmlinux).  A compressed image can be built by doing
-  a 'make zImage' instead of 'make all'.
-
-
-Bug reports etc.
-----------------
-
-  Please send patches, bug reports and code for the ARM Linux project
-  to linux@arm.uk.linux.org.  Patches will not be included into future
-  kernels unless they come to me (or the relevant person concerned).
-
-  When sending bug reports, please ensure that they contain all relevant
-  information, eg. the kernel messages that were printed before/during
-  the problem, what you were doing, etc.
-
-  For patches, please include some explanation as to what the patch does
-  and why (if relevant).
-
-
-Modules
--------
-
-  Although modularisation is supported (and required for the FP emulator),
-  each module on an arm2/arm250/arm3 machine when is loaded will take
-  memory up to the next 32k boundary due to the size of the pages.  Hence is
-  modularisation on these machines really worth it?
-
-  However, arm6 and up machines allow modules to take multiples of 4k, and
-  as such Acorn RiscPCs and other architectures using these processors can
-  make good use of modularisation.
-
-
-ADFS Image files
-----------------
-
-  You can access image files on your ADFS partitions by mounting the ADFS
-  partition, and then using the loopback device driver.  You must have
-  losetup installed.
-
-  Please note that the PCEmulator DOS partitions have a partition table at
-  the start, and as such, you will have to give '-o offset' to losetup.
-
-
-Kernel initialisation abort codes
----------------------------------
-
-  When the kernel is unable to boot, it will if possible display a colour
-  at the top of the screen.  The colours have the following significance
-  when run in a 16 colour mode with the default palette:
-
-    Stripes of white, red, yellow, and green:
-       Kernel does not support the processor architecture detected.
-
-
-Request to developers
----------------------
-
-  When writing device drivers which include a separate assembler file, please
-  include it in with the C file, and not the arch/arm/lib directory.  This
-  allows the driver to be compiled as a loadable module without requiring
-  half the code to be compiled into the kernel image.
-
-  In general, try to avoid using assembler unless it is really necessary.  It
-  makes drivers far less easy to port to other hardware.
-
-
-ST506 hard drives
------------------
-
-  The ST506 hard drive controllers seem to be working fine (if a little
-  slowly).  At the moment they will only work off the controllers on an
-  A4x0's motherboard, but for it to work off a Podule just requires
-  someone with a podule to add the addresses for the IRQ mask and the
-  HDC base to the source.
-
-  As of 31/3/96 it works with two drives (you should get the ADFS
-  *configure hard drive set to 2). I've got an internal 20 MB and a great
-  big external 5.25" FH 64 MB drive (who could ever want more :-) ).
-
-  I've just got 240 K/s off it (a dd with bs=128k); that's about half of what
-  RiscOS gets, but it's a heck of a lot better than the 50 K/s I was getting
-  last week :-)
-
-  Known bug: Drive data errors can cause a hang; including cases where
-  the controller has fixed the error using ECC. (Possibly ONLY
-  in that case...hmm).
-
-
-1772 Floppy
------------
-  This also seems to work OK, but hasn't been stressed much lately.  It
-  hasn't got any code for disc change detection in there at the moment which
-  could be a bit of a problem!  Suggestions on the correct way to do this
-  are welcome.
-
-
-Kernel entry (head-armv.S)
---------------------------
-  The initial entry into the kernel made via head-armv.S uses architecture
-  independent code.  The architecture is selected by the value of 'r1' on
-  entry, which must be kept unique.  You can register a new architecture
-  by mailing the following details to rmk@arm.uk.linux.org.  Please give
-  the mail a subject of 'Register new architecture':
-
-    Name: <name of your architecture>
-    ARCHDIR: <name of include/asm-arm/arch-* directory>
-    Description:
-    <description of your architecture>
-
-  Please follow this format - it is an automated system.  You should
-  receive a reply the next day.
----
-Russell King (03/05/1998)
diff -urN linux-2.2.18-pre8/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.2.18-pre8/Documentation/Configure.help	Mon Dec  3 20:24:36 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 20:25:13 2001
@@ -4474,6 +4474,23 @@
   The module will be called qlogicfc.o. If you want to compile it as
   a module, say M here and read Documentation/modules.txt.
 
+Compaq 64-bit / 66MHz PCI Fibre Channel Host Adapter
+CONFIG_SCSI_CPQFCTS
+  This driver supports Compaq HBA part #120186-B21, the non-
+  intelligent Tachyon TL/TS (HPFC-5166A/1.2) based adapter.
+  Kernel linked or kernel module, SMP support.  Information at
+  /proc/scsi/cpqfcTS/*
+  
+  Only supports FC-AL (non-loop mode not supported). Supports Fabric 
+  (FL_Port, public device); tested on Brocade switches.  Supports hot-
+  plug of new devices and movement of devices across ports (i.e.,
+  dynamic re-assignment of 24-bit FC port_id).
+
+  Tested on Compaq RA4x00 (f/w ver 2.40 or newer), RA8000, with Vixel
+  Rapport 1000 (7-port non-managed), Gadzoox 12-port, Gadzoox Capellix
+  3000, Brocade 2010, 2400, 2800.  Selective Storage 
+  Presentation (SSP) on RA4x00 f/w 2.54 (no redundancy).
+
 Seagate ST-02 and Future Domain TMC-8xx SCSI support
 CONFIG_SCSI_SEAGATE
   These are 8-bit SCSI controllers; the ST-01 is also supported by
diff -urN linux-2.2.18-pre8/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- linux-2.2.18-pre8/Documentation/ioctl-number.txt	Fri Feb 20 17:54:35 1998
+++ linux/Documentation/ioctl-number.txt	Mon Dec  3 20:25:13 2001
@@ -85,6 +85,7 @@
 'W'	20-27	linux/octal-relay.h	in development
 'W'	28-2F	linux/iso16-relay.h	in development
 'Y'	all	linux/cyclades.h
+'Z'     all     linux/drivers/scsi/cpqfcTSioctl.h
 'a'	all	various, see http://lrcwww.epfl.ch/linux-atm/magic.html
 'b'	00-FF	bit3 vme host bridge	in development:
 					<mailto:natalia@nikhefk.nikhef.nl>
diff -urN linux-2.2.18-pre8/MAINTAINERS linux/MAINTAINERS
--- linux-2.2.18-pre8/MAINTAINERS	Mon Dec  3 20:24:36 2001
+++ linux/MAINTAINERS	Mon Dec  3 20:25:13 2001
@@ -183,6 +183,13 @@
 L:	linux-fbdev@vuser.vu.union.edu
 S:	Maintained
 
+COMPAQ FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA
+P:      Amy Vanzant-Hodge 
+M:      Amy Vanzant-Hodge (fibrechannel@compaq.com)
+L:	compaqandlinux@cpqlin.van-dijk.net
+W:	ftp.compaq.com/pub/products/drivers/linux
+S:      Supported
+
 COMPUTONE INTELLIPORT MULTIPORT CARD
 P:     Doug McNash
 P:     Michael H. Warfield
diff -urN linux-2.2.18-pre8/Makefile linux/Makefile
--- linux-2.2.18-pre8/Makefile	Mon Dec  3 20:24:36 2001
+++ linux/Makefile	Mon Dec  3 20:25:13 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 18
-EXTRAVERSION = pre8
+EXTRAVERSION = pre9
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -urN linux-2.2.18-pre8/arch/arm/Makefile linux/arch/arm/Makefile
--- linux-2.2.18-pre8/arch/arm/Makefile	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/Makefile	Mon Dec  3 20:25:13 2001
@@ -10,217 +10,172 @@
 # License.  See the file "COPYING" in the main directory of this archive
 # for more details.
 #
-# Copyright (C) 1995, 1996 by Russell King
+# Copyright (C) 1995-1999 by Russell King
 
-CFLAGS_PROC		:=
-ASFLAGS_PROC		:=
+LD		:= $(CROSS_COMPILE)ld
+OBJCOPY		:= $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
+OBJDUMP		 = $(CROSS_COMPILE)objdump
+CPP		:= $(CC) -E
+PERL		:= perl
+LINKFLAGS	:= -p -X -T arch/arm/vmlinux.lds
+ARCHCC		:= $(word 1,$(CC))
+
+AFLAGS		+= -mno-fpu
+CFLAGS_PIPE	:= -pipe
+CFLAGS		:= $(CFLAGS) $(CFLAGS_PIPE)
+
+ifdef CONFIG_FRAME_POINTER
+CFLAGS		:= $(CFLAGS:-fomit-frame-pointer=)
+endif
+
+ifdef CONFIG_DEBUG_INFO
+CFLAGS		+= -g
+endif
 
-# All processors get `-mshort-load-bytes' for now, to work around alignment
-# problems.  This is more of a hack that just happens to work than a real fix
-# but it will do for now.
+# Ensure this is ld "2.9.4" or later
+NEW_LINKER	:= $(shell if $(LD) --gc-sections --version >/dev/null 2>&1; then echo y; else echo n; fi)
+
+ifneq ($(NEW_LINKER),y)
+dummy:;		@echo '*** 2.2 kernels no longer build correctly with old versions of binutils.'
+		@echo '*** Please upgrade your binutils to 2.9.5.'
+		@false
+endif
+
+
+# GCC 2.7 uses different options to later compilers; sort out which we have
+NEW_GCC		:= $(shell if $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; then echo n; else echo y; fi)
+
+#
+# select flags depending on the compiler
+#
+ifeq ($(NEW_GCC),y)
+CFLAGS			+= -mshort-load-bytes -msoft-float
+CFLAGS_PROC_CPU_26	:= -mcpu=arm3 -Os
+CFLAGS_PROC_CPU_32v3	:= -march=armv3
+CFLAGS_PROC_CPU_32v4	:= -march=armv4
+CFLAGS_ARM6		:= -mtune=arm6
+CFLAGS_ARM7		:= -mtune=arm7
+CFLAGS_SA110		:= -mtune=strongarm110
+else
+CFLAGS_PROC_CPU_26	:= -m3
+CFLAGS_PROC_CPU_32v3	:=
+CFLAGS_PROC_CPU_32v4	:=
+CFLAGS_ARM6		:= -m6
+CFLAGS_ARM7		:= -m6
+CFLAGS_SA110		:= -m6
+endif
 
+#
+# Select CPU dependent flags
+#
 ifeq ($(CONFIG_CPU_26),y)
-  PROCESSOR		 = armo
-  TEXTADDR		 = 0x02080000
-  ZTEXTADDR		 = 0x01800000
-  ZRELADDR		 = 0x02080000
-  ifeq ($(CONFIG_BINUTILS_NEW),y)
-    CFLAGS_PROC		+= -mapcs-26 -mshort-load-bytes
-    ifeq ($(CONFIG_CPU_ARM2),y)
-      CFLAGS_PROC	+= -mcpu=arm2
-      ASFLAGS_PROC	+= -m2
-    endif
-    ifeq ($(CONFIG_CPU_ARM3),y)
-      CFLAGS_PROC	+= -mcpu=arm3
-      ASFLAGS_PROC	+= -m3
-    endif
-  else
-    ifeq ($(CONFIG_CPU_ARM2),y)
-      CFLAGS_PROC	+= -m2
-      ASFLAGS_PROC	+= -m2
-    endif
-    ifeq ($(CONFIG_CPU_ARM3),y)
-      CFLAGS_PROC	+= -m3
-      ASFLAGS_PROC	+= -m3
-    endif
-  endif
+ PROCESSOR	 = armo
+ TEXTADDR	 = 0x02080000
+ CFLAGS		+= $(CFLAGS_PROC_CPU_26)
+ AFLAGS		+= -mapcs-26
 endif
 
 ifeq ($(CONFIG_CPU_32),y)
-  PROCESSOR		 = armv
-  TEXTADDR		 = 0xC0008000
-  ifeq ($(CONFIG_BINUTILS_NEW),y)
-    CFLAGS_PROC		+= -mapcs-32 -mshort-load-bytes
-    ifeq ($(CONFIG_CPU_ARM6),y)
-      CFLAGS_PROC	+= -mcpu=arm6
-    endif
-    ifeq ($(CONFIG_CPU_ARM7),y)
-      CFLAGS_PROC	+= -mcpu=arm7
-    endif
-    ifeq ($(CONFIG_CPU_SA110),y)
-      CFLAGS_PROC	+= -mcpu=strongarm110
-    endif
-  else
-    CFLAGS_PROC		+= -m6
-  endif
-  ASFLAGS_PROC		+= -m6
-endif
-
-# Processor Architecture
-# CFLAGS_PROC - processor dependent CFLAGS
-# PROCESSOR - processor type
-# TEXTADDR - Uncompressed kernel link text address
-# ZTEXTADDR - Compressed kernel link text address
-# ZRELADDR - Compressed kernel relocating address (point at which uncompressed kernel is loaded).
-#
+ PROCESSOR	 = armv
+ TEXTADDR	 = 0xC0008000
+ ifeq ($(CONFIG_CPU_32v4),y)
+  CFLAGS	+= $(CFLAGS_PROC_CPU_32v4)
+  AFLAGS	+= -mapcs-32 -marmv4
+ else
+  CFLAGS	+= $(CFLAGS_PROC_CPU_32v3)
+  AFLAGS	+= -mapcs-32 -marmv3m
+ endif
+ #
+ # Exactly one of the following must be selected
+ #
+ ifeq ($(CONFIG_CPU_ARM6),y)
+  CFLAGS	+= $(CFLAGS_ARM6)
+ else
+ ifeq ($(CONFIG_CPU_ARM7),y)
+  CFLAGS	+= $(CFLAGS_ARM7)
+ else
+ ifeq ($(CONFIG_CPU_SA110),y)
+  CFLAGS	+= $(CFLAGS_SA110)
+ endif
+ endif
+ endif
+endif
 
-COMPRESSED_HEAD	 = head.o
+GCCLIB		:= $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
 
 ifeq ($(CONFIG_ARCH_A5K),y)
 MACHINE		 = a5k
 ARCHDIR		 = arc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
 endif
 
 ifeq ($(CONFIG_ARCH_ARC),y)
 MACHINE		 = arc
 ARCHDIR		 = arc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
 endif
 
 ifeq ($(CONFIG_ARCH_RPC),y)
 MACHINE		 = rpc
 ARCHDIR		 = rpc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
-ZTEXTADDR	 = 0x10008000
-ZRELADDR	 = 0x10008000
 endif
 
 ifeq ($(CONFIG_ARCH_EBSA110),y)
 MACHINE		 = ebsa110
 ARCHDIR		 = ebsa110
-ZTEXTADDR	 = 0x00008000
-ZRELADDR	 = 0x00008000
 endif
 
-ifeq ($(CONFIG_ARCH_EBSA285),y)
-MACHINE		 = ebsa285
+ifeq ($(CONFIG_FOOTBRIDGE),y)
+MACHINE		 = footbridge
 ARCHDIR		 = ebsa285
-ZTEXTADDR	 = 0x00008000
-ZRELADDR	 = 0x00008000
+endif
+
+ifeq ($(CONFIG_ARCH_CO285),y)
+TEXTADDR	 = 0x60008000
 endif
 
 ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
 MACHINE		 = nexuspci
 ARCHDIR		 = nexuspci
-ZTEXTADDR	 = 0x40200000
-ZRELADDR	 = 0x40008000
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o
-COMPRESSED_HEAD	 = head-nexuspci.o
-endif
-
-ifeq ($(CONFIG_ARCH_VNC),y)
-TEXTADDR	 = 0xC000C000
-MACHINE		 = vnc
-ARCHDIR		 = vnc
-endif
-
-ifeq ($(CONFIG_ARCH_TBOX),y)
-MACHINE		 = tbox
-ARCHDIR		 = tbox
-ZTEXTADDR	 = 0x80008000
-ZRELDIR		 = 0x80008000
 endif
 
-PERL		 = perl
-ifeq ($(CONFIG_BINUTILS_NEW),y)
-LD		 = $(CROSS_COMPILE)ld -m elf32arm
-else
-LD		 = $(CROSS_COMPILE)ld -m elf_arm
-endif
-OBJCOPY		 = $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
-OBJDUMP		 = $(CROSS_COMPILE)objdump
-CPP		 = $(CC) -E
-ARCHCC		:= $(word 1,$(CC))
-GCCLIB		:= `$(CC) $(CFLAGS_PROC) --print-libgcc-file-name`
-#GCCARCH		:= -B/usr/bin/arm-linuxelf- 
-HOSTCFLAGS	:= $(CFLAGS:-fomit-frame-pointer=)
-ifeq ($(CONFIG_FRAME_POINTER),y)
-CFLAGS		:= $(CFLAGS:-fomit-frame-pointer=)
-endif
-CFLAGS		:= $(CFLAGS_PROC) $(CFLAGS) -pipe
-ASFLAGS		:= $(ASFLAGS_PROC) $(ASFLAGS)
-LINKFLAGS	 = -T $(TOPDIR)/arch/arm/vmlinux-$(PROCESSOR).lds -e stext -Ttext $(TEXTADDR)
-ZLINKFLAGS	 = -Ttext $(ZTEXTADDR)
-
-SUBDIRS		:= $(SUBDIRS:drivers=arch/arm/drivers) arch/arm/lib arch/arm/kernel arch/arm/mm
-HEAD		:= arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o
+HEAD		:= arch/arm/kernel/head-$(PROCESSOR).o \
+		   arch/arm/kernel/init_task.o
+SUBDIRS		+= arch/arm/kernel arch/arm/mm arch/arm/lib \
+		   arch/arm/special arch/arm/nwfpe
 CORE_FILES	:= arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
-LIBS		:= arch/arm/lib/lib.a $(LIBS) $(GCCLIB)
+LIBS		:= arch/arm/lib/lib.o arch/arm/lib/lib.a $(LIBS) $(GCCLIB)
+DRIVERS		+= arch/arm/special/special.a
 
-BLOCK_DRIVERS	:= drivers/block/block.a
-CDROM_DRIVERS	:= drivers/cdrom/cdrom.a
-ifeq ($(CONFIG_FB),y)
-CHAR_DRIVERS	:= arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a
-else
-ifeq ($(CONFIG_VGA_CONSOLE),y)
-CHAR_DRIVERS	:= arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a
-else
-CHAR_DRIVERS	:= arch/arm/drivers/char/char.a
+ifeq ($(CONFIG_NWFPE),y)
+LIBS		:= arch/arm/nwfpe/math-emu.a $(LIBS)
 endif
-endif
-MISC_DRIVERS	:= drivers/misc/misc.a
-NET_DRIVERS	:= drivers/net/net.a
-PARIDE_DRIVERS	:= drivers/block/paride/paride.a
-PCI_DRIVERS	:= drivers/pci/pci.a
-SCSI_DRIVERS	:= drivers/scsi/scsi.a
-SOUND_DRIVERS	:= drivers/sound/sound.a
-VIDEO_DRIVERS	:= drivers/video/video.a
-PNP_DRIVERS	:= drivers/pnp/pnp.a
 
 ifeq ($(CONFIG_ARCH_ACORN),y)
-BLOCK_DRIVERS	+= drivers/acorn/block/acorn-block.a
-CHAR_DRIVERS	+= drivers/acorn/char/acorn-char.a
-NET_DRIVERS	+= drivers/acorn/net/acorn-net.a drivers/net/net.a
-SCSI_DRIVERS	+= drivers/acorn/scsi/acorn-scsi.a
+SUBDIRS		+= drivers/acorn
+DRIVERS		+= drivers/acorn/block/acorn-block.a
+DRIVERS		+= drivers/acorn/char/acorn-char.a
+DRIVERS		+= drivers/acorn/net/acorn-net.a
+DRIVERS		+= drivers/acorn/scsi/acorn-scsi.a
 endif
 
-DRIVERS		:= $(BLOCK_DRIVERS) $(CHAR_DRIVERS) $(MISC_DRIVERS) $(NET_DRIVERS)
+MAKEBOOT	 = $(MAKE) -C arch/$(ARCH)/boot
 
-ifeq ($(CONFIG_FB),y)
-DRIVERS		:= $(DRIVERS) $(VIDEO_DRIVERS)
-else
-ifeq ($(CONFIG_VGA_CONSOLE),y)
-DRIVERS		:= $(DRIVERS) $(VIDEO_DRIVERS)
-endif
-endif
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS		:= $(DRIVERS) $(SCSI_DRIVERS)
-endif
-ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
-DRIVERS		:= $(DRIVERS) $(CDROM_DRIVERS)
-endif
-ifdef CONFIG_PCI
-DRIVERS		:= $(DRIVERS) $(PCI_DRIVERS)
-endif
-ifeq ($(CONFIG_SOUND),y)
-DRIVERS		:= $(DRIVERS) $(SOUND_DRIVERS)
-endif
-ifeq ($(CONFIG_PARIDE),y)
-DRIVERS		:= $(DRIVERS) $(PARIDE_DRIVERS)
-endif
-ifdef CONFIG_PNP
-DRIVERS		:= $(DRIVERS) $(PNP_DRIVERS)
-endif
+# The following is a hack to get 'constants.h' up
+# to date before starting compilation
+CONSTANTS	:= constants
+
+constants: $(TOPDIR)/include/asm-arm/proc-fns.h dummy
+	@$(MAKE) -C arch/arm/lib constants.h
+
+symlinks: archsymlinks
 
-symlinks::
+archsymlinks:
 	$(RM) include/asm-arm/arch include/asm-arm/proc
 	(cd include/asm-arm; ln -sf arch-$(ARCHDIR) arch; ln -sf proc-$(PROCESSOR) proc)
 
-# Once we've finished integrating the sources, the @$(MAKE) will disappear
-archmrproper:
-	rm -f include/asm-arm/arch include/asm-arm/proc
-	@$(MAKE) -C arch/$(ARCH)/drivers mrproper
+vmlinux: arch/arm/vmlinux.lds
+
+arch/arm/vmlinux.lds: arch/arm/vmlinux-$(PROCESSOR).lds.in dummy
+	@sed 's/TEXTADDR/$(TEXTADDR)/' <$< >$@
 
 arch/arm/kernel: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel
@@ -231,19 +186,19 @@
 arch/arm/lib: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/lib
 
-MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+zImage zinstall Image install: vmlinux
+	@$(MAKEBOOT) $@
 
-zImage: vmlinux
-	@$(MAKEBOOT) zImage
-
-zinstall: vmlinux
-	@$(MAKEBOOT) zinstall
+archmrproper:
+	@$(MAKE) -C arch/$(ARCH)/special mrproper
+	$(RM) include/asm-arm/arch include/asm-arm/proc
 
-Image: vmlinux
-	@$(MAKEBOOT) Image
+archclean:
+	@$(MAKEBOOT) clean
+	$(RM) arch/arm/lib/constants.h arch/arm/vmlinux.lds
 
-install: vmlinux
-	@$(MAKEBOOT) install
+archdep:
+	@$(MAKEBOOT) dep
 
 # My testing targets (that short circuit a few dependencies)
 zImg:;	@$(MAKEBOOT) zImage
@@ -251,10 +206,23 @@
 i:;	@$(MAKEBOOT) install
 zi:;	@$(MAKEBOOT) zinstall
 
-archclean:
-	@$(MAKEBOOT) clean
-	$(RM) arch/arm/lib/constants.h
+#
+# Configuration targets.  Use these to select a
+# configuration for your architecture
+#
+a5k_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/a5k arch/arm/defconfig
+
+ebsa110_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/ebsa110 arch/arm/defconfig
+
+footbridge_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/footbridge arch/arm/defconfig
+
+rpc_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/rpc arch/arm/defconfig
 
-archdep:
-	@$(MAKEBOOT) dep
-sed -e /^MACHINE..*=/s,= .*,= rpc,;/^PROCESSOR..*=/s,= .*,= armv, linux/arch/arm/Makefile.normal
diff -urN linux-2.2.18-pre8/arch/arm/boot/Makefile linux/arch/arm/boot/Makefile
--- linux-2.2.18-pre8/arch/arm/boot/Makefile	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/boot/Makefile	Mon Dec  3 20:25:13 2001
@@ -20,10 +20,10 @@
 	@$(MAKE) -C compressed vmlinux
 
 install: $(CONFIGURE) Image
-	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL).$(EXTRAVERSION) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
 
 zinstall: $(CONFIGURE) zImage
-	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL).$(EXTRAVERSION) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
 
 clean:
 	rm -f Image zImage
diff -urN linux-2.2.18-pre8/arch/arm/boot/compressed/Makefile linux/arch/arm/boot/compressed/Makefile
--- linux-2.2.18-pre8/arch/arm/boot/compressed/Makefile	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/boot/compressed/Makefile	Mon Dec  3 20:25:13 2001
@@ -2,32 +2,97 @@
 # linux/arch/arm/boot/compressed/Makefile
 #
 # create a compressed vmlinuz image from the original vmlinux
+
+HEAD		 = head.o
+OBJS		 = misc.o
+SYSTEM		 = $(TOPDIR)/vmlinux
+CFLAGS		 = -O2 -DSTDC_HEADERS $(CFLAGS_PROC)
+FONTC		 = $(TOPDIR)/drivers/video/font_acorn_8x8.c
+ZLDFLAGS	 = -p -X -T vmlinux.lds
+
 #
-# With this config, max compressed image size = 640k
-# Uncompressed image size = 1.3M (text+data)
+# Architecture dependencies
+#
+ifeq ($(CONFIG_ARCH_ACORN),y)
+OBJS		+= ll_char_wr.o font.o
+endif
+
+ifeq ($(CONFIG_CPU_26),y)
+ZTEXTADDR	 = 0x02080000
+endif
+
+ifeq ($(CONFIG_ARCH_RPC),y)
+ZTEXTADDR	 = 0x10008000
+endif
+
+ifeq ($(CONFIG_ARCH_EBSA110),y)
+ZTEXTADDR	 = 0x00008000
+endif
+
+ifeq ($(CONFIG_FOOTBRIDGE),y)
+ZTEXTADDR	 = 0x00008000
+endif
+
+ifeq ($(CONFIG_ARCH_NETWINDER),y)
+OBJS		+= head-netwinder.o
+endif
+
+ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
+HEAD		 = head-nexuspci.o
+OBJS		+= $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o
+ZTEXTADDR	 = 0x40200000
+ZRELADDR	 = 0x40008000
+endif
+
+ifeq ($(CONFIG_ARCH_SA1100),y)
+OBJS		+= head-sa1100.o
+ifeq ($(CONFIG_SA1100_VICTOR),y)
+ZTEXTADDR	 = 0x00002000
+ZBSSADDR	 = 0xc0100000
+else
+ZTEXTADDR	 = 0xc0008000
+endif
+ZRELADDR	 = 0xc0008000
+endif
 
-SYSTEM	=$(TOPDIR)/vmlinux
-HEAD	=$(COMPRESSED_HEAD)
-OBJS	=$(HEAD) misc.o $(COMPRESSED_EXTRA)
-CFLAGS	=-O2 -DSTDC_HEADERS $(CFLAGS_PROC)
-ARFLAGS =rc
+#
+# If you don't define ZRELADDR above,
+# then it defaults to ZTEXTADDR
+#
+ifeq ($(ZRELADDR),)
+ZRELADDR	= $(ZTEXTADDR)
+endif
+
+SEDFLAGS	= s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;
+
+ifneq ($(ZBSSADDR),)
+SEDFLAGS	+= s/BSS_START/$(ZBSSADDR)/
+else
+SEDFLAGS	+= s/BSS_START/ALIGN(4)/
+endif
 
 all:		vmlinux
 
-vmlinux:	piggy.o $(OBJS)
-		$(LD) $(ZLINKFLAGS) -o vmlinux $(OBJS) piggy.o
+vmlinux:	$(HEAD) $(OBJS) piggy.o vmlinux.lds
+		$(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(GCCLIB) -o vmlinux
 
 $(HEAD): 	$(HEAD:.o=.S)
-		$(CC) -traditional -DLOADADDR=$(ZRELADDR) -c $(HEAD:.o=.S)
+		$(CC) $(AFLAGS) -traditional -c $(HEAD:.o=.S)
 
 piggy.o:	$(SYSTEM)
-		tmppiggy=_tmp_$$$$piggy; \
-		rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \
-		$(OBJCOPY) $(SYSTEM) $$tmppiggy; \
-		gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \
-		echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \
-		$(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \
-		rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk;
+		$(OBJCOPY) $(SYSTEM) piggy
+		gzip -9 < piggy > piggy.gz
+		$(LD) -r -o $@ -b binary piggy.gz
+		rm -f piggy piggy.gz
+
+font.o:		$(FONTC)
+		$(CC) $(CFLAGS) -Dstatic= -c -o $@ $(FONTC)
+
+vmlinux.lds:	vmlinux.lds.in
+		@sed "$(SEDFLAGS)" < vmlinux.lds.in > $@
+
+clean:;		rm -f vmlinux core piggy* vmlinux.lds
 
-clean:;		rm -f vmlinux core
+.PHONY:	vmlinux.lds clean
 
+misc.o: misc.c $(TOPDIR)/include/asm/arch/uncompress.h $(TOPDIR)/lib/inflate.c
diff -urN linux-2.2.18-pre8/arch/arm/boot/compressed/head-netwinder.S linux/arch/arm/boot/compressed/head-netwinder.S
--- linux-2.2.18-pre8/arch/arm/boot/compressed/head-netwinder.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/boot/compressed/head-netwinder.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,31 @@
+		.section	".start", #alloc, #execinstr
+
+		adr	r2, 1f
+		ldmdb	r2, {r7, r8}
+		and	r3, r2, #0xc000
+		teq	r3, #0x8000
+		beq	2f
+		bic	r3, r2, #0xc000
+		orr	r3, r3, #0x8000
+		mov	r0, r3
+		mov	r4, #64
+		sub	r5, r8, r7
+		b	1f
+
+		.word	_start
+		.word	__bss_start
+
+1:
+		.rept	4
+		ldmia	r2!, {r6, r7, r8, r9}
+		stmia	r3!, {r6, r7, r8, r9}
+		.endr
+		subs	r4, r4, #64
+		bcs	1b
+		movs	r4, r5
+		mov	r5, #0
+		mov	r1, #5		@ only here to fix NeTTroms which dont set r1
+		movne	pc, r0
+
+		mov	r0, #0
+2:
diff -urN linux-2.2.18-pre8/arch/arm/boot/compressed/head-sa1100.S linux/arch/arm/boot/compressed/head-sa1100.S
--- linux-2.2.18-pre8/arch/arm/boot/compressed/head-sa1100.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/boot/compressed/head-sa1100.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,65 @@
+/* 
+ * linux/arch/arm/boot/compressed/head-sa1100.S
+ * 
+ * Copyright (C) 1999 Nicolas Pitre <nico@cam.org>
+ * 
+ * SA1100 specific tweaks.  This is merged with head.S by the linker.
+ */
+
+#include <linux/config.h>
+
+
+		.section        ".start", #alloc, #execinstr
+
+#ifndef CONFIG_ARCH_SA1100
+#error What am I doing here...
+#endif
+
+#ifdef CONFIG_SA1100_BRUTUS
+@ need to enter SVC mode
+#define angel_SWIreason_EnterSVC 0x17   /* from arm.h, in angel source */
+#define angel_SWI_ARM (0xEF123456 & 0xffffff)
+		mov	r0, #angel_SWIreason_EnterSVC
+		swi	#angel_SWI_ARM
+
+		@ turn off interrupts to prevent the angel from running
+		mrs	r0, cpsr
+		orr	r0, r0, #0xc0
+		msr	cpsr, r0
+#endif
+
+#ifdef CONFIG_SA1100_VICTOR
+		@ Copy cmdline to 0xc0000000
+		mov	r1, #0xc0000000
+		cmp	r0, #0
+		moveq	r2, #0
+1:		ldrneb	r2, [r0], #1
+		cmpne	r2, #0
+		strb	r2, [r1], #1
+		bne	1b
+#endif
+
+		@ Data cache might be active.
+		@ Be sure to flush kernel binary out of the cache,
+		@ whatever state it is, before it is turned off.
+		@ This is done by fetching through currently executed
+		@ memory to be sure we hit the same cache.
+		bic	r2, pc, #0x1f
+		add	r3, r2, #0x4000		@ 16 kb is quite enough...
+1:		ldr	r0, [r2], #32
+		teq	r2, r3
+		bne	1b
+		mcr	p15, 0, r0, c7, c10, 4	@ drain WB
+		mcr	p15, 0, r0, c7, c7, 0	@ flush I & D caches
+
+		@ disabling MMU, enabling I cache
+		mrc	p15, 0, r0, c1, c0, 0	@ read control reg
+		bic	r0, r0, #0x0d		@ clear WB, DC, MMU
+		orr	r0, r0, #0x1000		@ set Icache
+		mcr	p15, 0, r0, c1, c0, 0
+
+		@ set registers for entry
+		mov	r0, #0
+		mov	r1, #16
+
+
diff -urN linux-2.2.18-pre8/arch/arm/boot/compressed/head.S linux/arch/arm/boot/compressed/head.S
--- linux-2.2.18-pre8/arch/arm/boot/compressed/head.S	Sat Jul 18 11:55:22 1998
+++ linux/arch/arm/boot/compressed/head.S	Mon Dec  3 20:25:13 2001
@@ -1,129 +1,253 @@
 /*
  * linux/arch/arm/boot/compressed/head.S
  *
- * Copyright (C) 1996,1997,1998 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
 #include <linux/linkage.h>
 
-		.text
+		.section ".start", #alloc, #execinstr
 /*
  * sort out different calling conventions
  */
 		.align
-		.globl	_start
-_start:
-start:		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
+start:
+		.type	start,#function
+		.rept	8
 		mov	r0, r0
+		.endr
+
 		b	1f
-		.word	0x016f2818			@ Magic numbers to help the loader
-		.word	_start	
+		.word	0x016f2818		@ Magic numbers to help the loader
+		.word	start	
+1:
+
+		/*
+		 * some architecture specific code can
+		 * be inserted by the linker here
+		 */
+
+		.text
 1:		teq	r0, #0
-		beq	2f
-		mov	r4, #0x02000000
-		add	r4, r4, #0x7C000
-		mov	r3, #0x4000
-		sub	r3, r3, #4
-1:		ldmia	r0!, {r5 - r12}
-		stmia	r4!, {r5 - r12}
-		subs	r3, r3, #32
-		bpl	1b
-2:		adr	r2, LC0
-		ldmia	r2, {r2, r3, r4, r5, r6, sp}
-		add	r2, r2, #3
-		add	r3, r3, #3
-		add	sp, sp, #3
-		bic	r2, r2, #3
-		bic	r3, r3, #3
-		bic	sp, sp, #3
-		adr	r7, start
-		sub	r6, r7, r6
-/*
- * Relocate pointers
- */
-		add	r2, r2, r6
-		add	r3, r3, r6
-		add	r5, r5, r6
-		add	sp, sp, r6
-/*
- * Clear zero-init
- */
-		mov	r6, #0
-1:		str	r6, [r2], #4
+		bne	1b
+		mov	r7, r1			@ save architecture ID
+		mrc	p15, 0, r6, c0, c0	@ get processor ID
+		adr	r2, LC0
+		ldmia	r2, {r2, r3, r4, r5, sp}
+
+		mov	r0, #0
+1:		str	r0, [r2], #4		@ clear bss
+		str	r0, [r2], #4
+		str	r0, [r2], #4
+		str	r0, [r2], #4
 		cmp	r2, r3
 		blt	1b
-		str	r1, [r5]		@ save architecture
-/*
- * Uncompress the kernel
- */
-		mov	r1, #0x8000
-		add	r3, r2, r1, lsl #1	@ Add 64k for malloc
-		sub	r1, r1, #1
-		add	r3, r3, r1
-		bic	r5, r3, r1		@ decompress kernel to after end of the compressed
+
+		mov	r1, sp			@ malloc space above stack
+		add	r2, sp, #0x10000	@ 64k max
+
+		teq	r4, r5			@ will we overwrite ourselves?
+		moveq	r5, r2
+		movne	r5, r4
+
 		mov	r0, r5
-		mov	r1, r2
-		mov	r2, r0
+		mov	r3, r7
 		bl	SYMBOL_NAME(decompress_kernel)
-		add	r0, r0, #7
-		bic	r2, r0, #7
+
+		teq	r4, r5			@ do we need to relocate
+		beq	call_kernel		@ the kernel?
+
+		add	r0, r0, #127
+		bic	r0, r0, #127		@ align the kernel length
 /*
- * Now move the kernel to the correct location (r5 -> r4, len r0)
- */
-		mov	r0, r4			@ r0 = start of real kernel
-		mov	r1, r5			@ r1 = start of kernel image
-		add	r3, r5, r2		@ r3 = end of kernel
-		adr	r4, movecode
-		adr	r5, movecodeend
-1:		ldmia	r4!, {r6 - r12, lr}
-		stmia	r3!, {r6 - r12, lr}
-		cmp	r4, r5
+ * r0     = decompressed kernel length
+ * r1-r3  = unused
+ * r4     = kernel execution address
+ * r5     = decompressed kernel start
+ * r6     = processor ID
+ * r7     = architecture ID
+ * r8-r14 = unused
+ */
+		add	r1, r5, r0		@ end of decompressed kernel
+		adr	r2, reloc_start
+		adr	r3, reloc_end
+1:		ldmia	r2!, {r8 - r13}		@ copy relocation code
+		stmia	r1!, {r8 - r13}
+		ldmia	r2!, {r8 - r13}
+		stmia	r1!, {r8 - r13}
+		cmp	r2, r3
 		blt	1b
-		mrc	p15, 0, r5, c0, c0
-		eor	r5, r5, #0x44 << 24
-		eor	r5, r5, #0x01 << 16
-		eor	r5, r5, #0xa1 << 8
-		movs	r5, r5, lsr #4
-		mov	r5, #0
-		mcreq	p15, 0, r5, c7, c5, 0	@ flush I cache
-		ldr	r5, LC0 + 12		@ get architecture
-		ldr	r5, [r5]
-		add	pc, r1, r2		@ Call move code
+
+		eor	r1, r6, #0x44 << 24	@ SA-110?
+		eor	r1, r1, #0x01 << 16
+		eor	r1, r1, #0xa1 << 8
+		movs	r1, r1, lsr #4
+		mcreq	p15, 0, r1, c7, c7, 0	@ flush I & D-cache
+		mcreq	p15, 0, r1, c7, c10, 4	@ drain WB
+		add	pc, r5, r0		@ call relocation code
 
 /*
- * r0 = length, r1 = to, r2 = from
- */
-movecode:	add	r3, r1, r2
-		mov	r4, r0
-1:		ldmia	r1!, {r6 - r12, lr}
-		stmia	r0!, {r6 - r12, lr}
-		cmp	r1, r3
+ * r0     = decompressed kernel length
+ * r1-r3  = unused
+ * r4     = kernel execution address
+ * r5     = decompressed kernel start
+ * r6     = processor ID
+ * r7     = architecture ID
+ * r8-r14 = unused
+ */
+reloc_start:	add	r8, r5, r0
+#if 0
+	mov r0, #'\n'
+	bl putc
+	mov r0, r6
+	mov r1, #8
+	bl phex
+	mov r0, #':'
+	bl putc
+	mov r0, r5
+	mov r1, #8
+	bl phex
+	mov r0, #'-'
+	bl putc
+	mov r0, r8
+	mov r1, #8
+	bl phex
+	mov r0, #'>'
+	bl putc
+	mov r0, r4
+	mov r1, #8
+	bl phex
+	mov r0, #'\n'
+	bl putc
+#endif
+		mov	r0, r8
+		mov	r1, r4
+1:
+		.rept	4
+		ldmia	r5!, {r2, r3, r8 - r13}	@ relocate kernel
+		stmia	r1!, {r2, r3, r8 - r13}
+		.endr
+
+		cmp	r5, r0
 		blt	1b
-		mrc	p15, 0, r0, c0, c0
-		eor	r0, r0, #0x44 << 24
+#if 0
+	mov r8, r0
+	mov r0, r5
+	mov r1, #8
+	bl phex
+	mov r0, #'-'
+	bl putc
+	mov r0, r8
+	mov r1, #8
+	bl phex
+	mov r0, #'\n'
+	bl putc
+	mov r0, r4
+	bl  memdump
+#endif
+		eor	r0, r6, #0x44 << 24	@ SA-110?
 		eor	r0, r0, #0x01 << 16
 		eor	r0, r0, #0xa1 << 8
 		movs	r0, r0, lsr #4
+		mcreq	p15, 0, r0, c7, c7, 0	@ flush I cache
+		mcreq	p15, 0, r1, c7, c10, 4	@ drain WB
+
+call_kernel:	mov	r0, #0
+		mov	r1, r7			@ restore architecture number
+		mov	pc, r4			@ call kernel
+
+phexbuf:	.space	12
+
+#if 0
+		.macro	loadsp,	rb
+		mov	\rb, #0x7c000000
+		.endm
+
+		.macro	writeb,	rb
+		strb	\rb, [r3, #0x3f8]
+		.endm
+#else
+		.macro	loadsp,	rb
+		mov	\rb, #0x03000000
+		orr	\rb, \rb, #0x00010000
+		.endm
+
+		.macro	writeb,	rb
+		strb	\rb, [r3, #0x3f8 << 2]
+		.endm
+#endif
+
+phex:		adr	r3, phexbuf
+		mov	r2, #0
+		strb	r2, [r3, r1]
+1:		subs	r1, r1, #1
+		movmi	r0, r3
+		bmi	puts
+		and	r2, r0, #15
+		mov	r0, r0, lsr #4
+		cmp	r2, #10
+		addge	r2, r2, #7
+		add	r2, r2, #'0'
+		strb	r2, [r3, r1]
+		b	1b
+
+puts:		loadsp	r3
+1:		ldrb	r2, [r0], #1
+		teq	r2, #0
+		moveq	pc, lr
+2:		writeb	r2
+		mov	r1, #0x00020000
+3:		subs	r1, r1, #1
+		bne	3b
+		teq	r2, #'\n'
+		moveq	r2, #'\r'
+		beq	2b
+		teq	r0, #0
+		bne	1b
+		mov	pc, lr
+putc:
+		mov	r2, r0
 		mov	r0, #0
-		mcreq	p15, 0, r0, c7, c5, 0	@ flush I cache
-		mov	r1, r5			@ call kernel correctly
-		mov	pc, r4			@ call via EXEC entry
-movecodeend:
-
-LC0:		.word	SYMBOL_NAME(_edata)
-		.word	SYMBOL_NAME(_end)
-		.word	LOADADDR
-		.word	SYMBOL_NAME(architecture)
-		.word	start
-		.word	SYMBOL_NAME(user_stack)+4096
-		.align
+		loadsp	r3
+		b	2b
 
-		.bss
-SYMBOL_NAME(architecture):
-		.space	4
+memdump:	mov	r12, r0
+		mov	r10, lr
+		mov	r1, #8
+		bl	phex
+		mov	r0, #'\n'
+		bl	putc
+		mov	r11, #0
+2:		mov	r0, r11, lsl #2
+		mov	r1, #4
+		bl	phex
+		mov	r0, #':'
+		bl	putc
+1:		mov	r0, #' '
+		bl	putc
+		ldr	r0, [r12, r11, lsl #2]
+		mov	r1, #8
+		bl	phex
+		and	r0, r11, #7
+		teq	r0, #3
+		moveq	r0, #' '
+		bleq	putc
+		and	r0, r11, #7
+		add	r11, r11, #1
+		teq	r0, #7
+		bne	1b
+		mov	r0, #'\n'
+		bl	putc
+		cmp	r11, #64
+		blt	2b
+		mov	pc, r10
+reloc_end:
+
+LC0:		.word	__bss_start
+		.word	_end
+		.word	_load_addr
+		.word	_start
+		.word	user_stack+4096
 		.align
+
+		.section	".stack"
+user_stack:	.space	4096
diff -urN linux-2.2.18-pre8/arch/arm/boot/compressed/ll_char_wr.S linux/arch/arm/boot/compressed/ll_char_wr.S
--- linux-2.2.18-pre8/arch/arm/boot/compressed/ll_char_wr.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/boot/compressed/ll_char_wr.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,158 @@
+/*
+ * linux/arch/arm/lib/ll_char_wr.S
+ *
+ * Copyright (C) 1995, 1996 Russell King.
+ *
+ * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King.
+ *
+ * 10-04-96	RMK	Various cleanups & reduced register usage.
+ * 08-04-98	RMK	Shifts re-ordered
+ */
+
+@ Regs: [] = corruptible
+@       {} = used
+@       () = do not use
+#define __ASSEMBLY__
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+		.text
+
+#define BOLD            0x01
+#define ITALIC          0x02
+#define UNDERLINE       0x04
+#define FLASH           0x08
+#define INVERSE         0x10
+
+LC0:		.word	SYMBOL_NAME(bytes_per_char_h)
+		.word	SYMBOL_NAME(video_size_row)
+		.word	SYMBOL_NAME(acorndata_8x8)
+		.word	SYMBOL_NAME(con_charconvtable)
+
+ENTRY(ll_write_char)
+		stmfd	sp!, {r4 - r7, lr}
+@
+@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc)
+@
+		eor	ip, r1, #UNDERLINE << 9
+/*
+ * calculate colours
+ */
+		tst	r1, #INVERSE << 9
+		moveq	r2, r1, lsr #16
+		moveq	r3, r1, lsr #24
+		movne	r2, r1, lsr #24
+		movne	r3, r1, lsr #16
+		and	r3, r3, #255
+		and	r2, r2, #255
+/*
+ * calculate offset into character table
+ */
+		mov	r1, r1, lsl #23
+		mov	r1, r1, lsr #20
+/*
+ * calculate offset required for each row [maybe I should make this an argument to this fn.
+ * Have to see what the register usage is like in the calling routines.
+ */
+		adr	r4, LC0
+		ldmia	r4, {r4, r5, r6, lr}
+		ldr	r4, [r4]
+		ldr	r5, [r5]
+/*
+ * Go to resolution-dependent routine...
+ */
+		cmp	r4, #4
+		blt	Lrow1bpp
+		eor	r2, r3, r2			@ Create eor mask to change colour from bg
+		orr	r3, r3, r3, lsl #8		@ to fg.
+		orr	r3, r3, r3, lsl #16
+		add	r0, r0, r5, lsl #3		@ Move to bottom of character
+		add	r1, r1, #7
+		ldrb	r7, [r6, r1]
+		tst	ip, #UNDERLINE << 9
+		eoreq	r7, r7, #255
+		teq	r4, #8
+		beq	Lrow8bpplp
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
+@
+		orr	r3, r3, r3, lsl #4
+Lrow4bpplp:	ldr	r7, [lr, r7, lsl #2]
+		mul	r7, r2, r7
+		tst	r1, #7				@ avoid using r7 directly after
+		eor	ip, r3, r7
+		str	ip, [r0, -r5]!
+		LOADREGS(eqfd, sp!, {r4 - r7, pc})
+		sub	r1, r1, #1
+		ldrb	r7, [r6, r1]
+		ldr	r7, [lr, r7, lsl #2]
+		mul	r7, r2, r7
+		tst	r1, #7				@ avoid using r7 directly after
+		eor	ip, r3, r7
+		str	ip, [r0, -r5]!
+		subne	r1, r1, #1
+		ldrneb	r7, [r6, r1]
+		bne	Lrow4bpplp
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
+@
+Lrow8bpplp:	mov	ip, r7, lsr #4
+		ldr	ip, [lr, ip, lsl #2]
+		mul	r4, r2, ip
+		and	ip, r7, #15			@ avoid r4
+		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
+		mul	ip, r2, ip			@ avoid r4
+		eor	r4, r3, r4			@ avoid ip
+		tst	r1, #7				@ avoid ip
+		sub	r0, r0, r5			@ avoid ip
+		eor	ip, r3, ip
+		stmia	r0, {r4, ip}
+		LOADREGS(eqfd, sp!, {r4 - r7, pc})
+		sub	r1, r1, #1
+		ldrb	r7, [r6, r1]
+		mov	ip, r7, lsr #4
+		ldr	ip, [lr, ip, lsl #2]
+		mul	r4, r2, ip
+		and	ip, r7, #15			@ avoid r4
+		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
+		mul	ip, r2, ip			@ avoid r4
+		eor	r4, r3, r4			@ avoid ip
+		tst	r1, #7				@ avoid ip
+		sub	r0, r0, r5			@ avoid ip
+		eor	ip, r3, ip
+		stmia	r0, {r4, ip}
+		subne	r1, r1, #1
+		ldrneb	r7, [r6, r1]
+		bne	Lrow8bpplp
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc)
+@
+Lrow1bpp:	add	r6, r6, r1
+		ldmia	r6, {r4, r7}
+		tst	ip, #INVERSE << 9
+		mvnne	r4, r4
+		mvnne	r7, r7
+		strb	r4, [r0], r5
+		mov	r4, r4, lsr #8
+		strb	r4, [r0], r5
+		mov	r4, r4, lsr #8
+		strb	r4, [r0], r5
+		mov	r4, r4, lsr #8
+		strb	r4, [r0], r5
+		strb	r7, [r0], r5
+		mov	r7, r7, lsr #8
+		strb	r7, [r0], r5
+		mov	r7, r7, lsr #8
+		strb	r7, [r0], r5
+		mov	r7, r7, lsr #8
+		tst	ip, #UNDERLINE << 9
+		mvneq	r7, r7
+		strb	r7, [r0], r5
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+		.bss
+ENTRY(con_charconvtable)
+		.space	1024
diff -urN linux-2.2.18-pre8/arch/arm/boot/compressed/misc.c linux/arch/arm/boot/compressed/misc.c
--- linux-2.2.18-pre8/arch/arm/boot/compressed/misc.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/boot/compressed/misc.c	Mon Dec  3 20:25:13 2001
@@ -7,8 +7,17 @@
  * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
  *
  * Modified for ARM Linux by Russell King
+ *
+ * Nicolas Pitre <nico@visuaide.com>  1999/04/14 :
+ *  For this code to run directly from Flash, all constant variables must
+ *  be marked with 'const' and all other variables initialized at run-time 
+ *  only.  This way all non constant variables will end up in the bss segment,
+ *  which should point to addresses in RAM and cleared to 0 on start.
+ *  This allows for a much quicker boot time.
  */
 
+unsigned int __machine_arch_type;
+
 #include <asm/uaccess.h>
 #include <asm/arch/uncompress.h>
 #include <asm/proc/uncompress.h>
@@ -22,7 +31,7 @@
 /*
  * Optimised C version of memzero for the ARM.
  */
-extern __inline__ __ptr_t __memzero (__ptr_t s, size_t n)
+void __memzero (__ptr_t s, __kernel_size_t n)
 {
 	union { void *vp; unsigned long *ulp; unsigned char *ucp; } u;
 	int i;
@@ -62,11 +71,8 @@
 
 	if (n & 1)
 		*u.ucp++ = 0;
-	return s;
 }
 
-#define memzero(s,n) __memzero(s,n)
-
 extern __inline__ __ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
 			    size_t __n)
 {
@@ -157,11 +163,11 @@
 static void gzip_release(void **);
 
 extern char input_data[];
-extern int  input_len;
+extern char input_data_end[];
 
 static uch *output_data;
 static ulg output_ptr;
-static ulg bytes_out = 0;
+static ulg bytes_out;
 
 static void *malloc(int size);
 static void free(void *where);
@@ -226,13 +232,14 @@
  * Fill the input buffer. This is called only when the buffer is empty
  * and at least one byte is really needed.
  */
-int fill_inbuf()
+int fill_inbuf(void)
 {
 	if (insize != 0)
 		error("ran out of input data\n");
 
 	inbuf = input_data;
-	insize = input_len;
+	insize = &input_data_end[0] - &input_data[0];
+
 	inptr = 1;
 	return inbuf[0];
 }
@@ -241,7 +248,7 @@
  * Write the output window window[0..outcnt-1] and update crc and bytes_out.
  * (Used for the decompressed data only.)
  */
-void flush_window()
+void flush_window(void)
 {
 	ulg c = crc;
 	unsigned n;
@@ -257,6 +264,7 @@
 	bytes_out += (ulg)outcnt;
 	output_ptr += (ulg)outcnt;
 	outcnt = 0;
+	puts(".");
 }
 
 static void error(char *x)
@@ -270,26 +278,24 @@
 	while(1);	/* Halt */
 }
 
-#define STACK_SIZE (4096)
-
-ulg user_stack [STACK_SIZE];
-
 #ifndef STANDALONE_DEBUG
 
-ulg decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p)
-{
-	free_mem_ptr = free_mem_ptr_p;
-	free_mem_ptr_end = free_mem_ptr_end_p;
-
-	proc_decomp_setup ();
-	arch_decomp_setup ();
+ulg
+decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
+		  int arch_id)
+{
+	output_data		= (uch *)output_start;	/* Points to kernel start */
+	free_mem_ptr		= free_mem_ptr_p;
+	free_mem_ptr_end	= free_mem_ptr_end_p;
+	__machine_arch_type	= arch_id;
 
-	output_data = (uch *)output_start;	/* Points to kernel start */
+	proc_decomp_setup();
+	arch_decomp_setup();
 
 	makecrc();
 	puts("Uncompressing Linux...");
 	gunzip();
-	puts("done.\nNow booting the kernel\n");
+	puts(" done, booting the kernel.\n");
 	return output_ptr;
 }
 #else
diff -urN linux-2.2.18-pre8/arch/arm/boot/compressed/vmlinux.lds.in linux/arch/arm/boot/compressed/vmlinux.lds.in
--- linux-2.2.18-pre8/arch/arm/boot/compressed/vmlinux.lds.in	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/boot/compressed/vmlinux.lds.in	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,52 @@
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+  . = LOAD_ADDR;
+  _load_addr = .;
+
+  . = TEXT_START;
+  _text = .;
+
+  .text : {
+    _start = .;
+    head.o(.start)
+    *(.start)
+    head.o(.text)
+    *(.text)
+    *(.fixup)
+    *(.gnu.warning)
+    input_data = .;
+    piggy.o
+    input_data_end = .;
+    . = ALIGN(4);
+  }
+
+  _etext = .;
+
+  .data : {
+    *(.data)
+  }
+
+  _edata = .;
+
+  . = BSS_START;
+  __bss_start = .;
+  .bss : {
+    *(.bss)
+  }
+  _end = .;
+
+  .stack : {
+    *(.stack)
+  }
+
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
+
diff -urN linux-2.2.18-pre8/arch/arm/config.in linux/arch/arm/config.in
--- linux-2.2.18-pre8/arch/arm/config.in	Wed Jun  7 14:26:42 2000
+++ linux/arch/arm/config.in	Mon Dec  3 20:25:13 2001
@@ -7,6 +7,11 @@
 define_bool CONFIG_ARM y
 
 mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
 comment 'System and processor type'
 
 choice 'ARM system type'	\
@@ -14,96 +19,111 @@
 	 A5000			CONFIG_ARCH_A5K \
 	 RiscPC			CONFIG_ARCH_RPC \
 	 EBSA-110		CONFIG_ARCH_EBSA110 \
-	 EBSA-285		CONFIG_ARCH_EBSA285 \
-	 NexusPCI		CONFIG_ARCH_NEXUSPCI \
-	 Corel-VNC		CONFIG_ARCH_VNC \
-	 Tbox			CONFIG_ARCH_TBOX" RiscPC
+	 FootBridge-based	CONFIG_FOOTBRIDGE" RiscPC
 
-if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then
-  bool '  Include support for CATS boards' CONFIG_CATS
+if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+   bool 'FootBridge in HOST mode' CONFIG_HOST_FOOTBRIDGE
+   if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+      define_bool CONFIG_ADDIN_FOOTBRIDGE n
+   else
+      define_bool CONFIG_ADDIN_FOOTBRIDGE y
+   fi
+fi
+
+if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+   bool '  Include support for Intel EBSA285' CONFIG_ARCH_EBSA285
+   bool '  Include support for Chalice CATS boards' CONFIG_CATS
+   bool '  Include support for Corel NetWinder' CONFIG_ARCH_NETWINDER
+fi
+
+if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then
+   # If we get any other footbridge-based plug-in boards, then
+   # add your architecture options here
+   define_bool CONFIG_ARCH_CO285 y
 fi
 
+#
 # Select various configuration options depending on the machine type
 #  Easy check for Acorn-style architectures
-
+#
 if [ "$CONFIG_ARCH_ARC" = "y" -o \
      "$CONFIG_ARCH_A5K" = "y" -o \
      "$CONFIG_ARCH_RPC" = "y" ]; then
-  define_bool CONFIG_ARCH_ACORN y
-else
-  define_bool CONFIG_ARCH_ACORN n
-fi
-
-if [ "$CONFIG_ARCH_TBOX" = "y" ]; then
-  define_bool CONFIG_BUS_I2C y
-fi
-
-#  These machines always have PCI
-
-if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \
-     "$CONFIG_ARCH_VNC" = "y" ]; then
-  define_bool CONFIG_PCI y
-fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then
-  bool "PCI support" CONFIG_PCI
-fi
-
-# These machines have ISA-DMA
-if [ "$CONFIG_CATS" = "y" -o \
-     "$CONFIG_ARCH_VNC" = "y" ]; then
-  define_bool CONFIG_ISA_DMA y
+   define_bool CONFIG_ARCH_ACORN y
 else
-  define_bool CONFIG_ISA_DMA n
+   define_bool CONFIG_ARCH_ACORN n
 fi
 
+#
 # Figure out whether this system uses 26-bit or 32-bit CPUs.  Nobody has
 # ever built a machine that can take both, and now that ARM3 is obsolete
 # nobody is likely to either.
-
+#
 if [ "$CONFIG_ARCH_ARC" = "y" -o \
      "$CONFIG_ARCH_A5K" = "y" ]; then
-  define_bool CONFIG_CPU_32 n
-  define_bool CONFIG_CPU_26 y
-else
-  define_bool CONFIG_CPU_32 y
-  define_bool CONFIG_CPU_26 n
-fi
-
-# Now allow the user to choose a more precise CPU.  This is only used to set
-# the flags we pass to GCC, not in any code.
+   define_bool CONFIG_CPU_32 n
+   define_bool CONFIG_CPU_26 y
 
-choice 'Optimise for CPU'				\
-	"ARM2		CONFIG_CPU_ARM2 \
-	 ARM3		CONFIG_CPU_ARM3 \
-	 ARM6		CONFIG_CPU_ARM6 \
-	 ARM7		CONFIG_CPU_ARM7 \
-	 SA110		CONFIG_CPU_SA110" ARM6
+   #
+   # Select memory size
+   #
+   bool '2MB physical memory' CONFIG_PAGESIZE_16
+else
+   define_bool CONFIG_CPU_32 y
+   define_bool CONFIG_CPU_26 n
 
-if [ "$CONFIG_CPU_26" = "y" ]; then
+   #
+   # Select CPU and optimisation dependent on architecture
+   #
+   if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
+        "$CONFIG_FOOTBRIDGE" = "y" -o \
+        "$CONFIG_ARCH_NEXUSPCI" = "y" ]; then
+      define_bool CONFIG_CPU_32v4 y
+      define_bool CONFIG_CPU_SA110 y
+   else
+      if [ "$CONFIG_ARCH_RPC" = "y" ]; then
+	 define_bool CONFIG_CPU_32v3 y
+	 bool 'Support ARM610' CONFIG_CPU_ARM6
+	 bool 'Support ARM710' CONFIG_CPU_ARM7
+	 bool 'Support StrongARM110' CONFIG_CPU_SA110
+      fi
+   fi
+fi
+
+#if [ "$CONFIG_ARCH_TBOX" = "y" ]; then
+#  define_bool CONFIG_BUS_I2C y
+#fi
 
-# For 26-bit CPUs, the page size changes with the amount of physical RAM!
-# The default is 4MB but if the user has less they have to own up to it here.
+#
+#  These machines always have PCI
+#
+if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \
+     "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+   define_bool CONFIG_PCI y
+fi
 
-  choice 'Physical memory size'		\
-	"4MB+		CONFIG_PAGESIZE_32	\
-	 2MB		CONFIG_PAGESIZE_16	\
-	 1MB/512K	CONFIG_PAGESIZE_8" 4MB+
+#
+# These machines have ISA-DMA
+#
+if [ "$CONFIG_CATS" = "y" -o \
+     "$CONFIG_ARCH_NETWINDER" = "y" ]; then
+   define_bool CONFIG_ISA_DMA y
+else
+   define_bool CONFIG_ISA_DMA n
 fi
-endmenu
 
-mainmenu_option next_comment
-comment 'Code maturity level options'
-bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
-bool 'Use new compilation options (for GCC 2.8)' CONFIG_BINUTILS_NEW
-bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
+if [ "$CONFIG_CPU_32" = "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
+   bool 'Kernel-mode alignment trap handler' CONFIG_ALIGNMENT_TRAP
+fi
+#bool 'Split text into discardable sections' CONFIG_TEXT_SECTIONS
 endmenu
 
 mainmenu_option next_comment
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
-  bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel module loader' CONFIG_KMOD
+   bool '  Set version information on all symbols for modules' CONFIG_MODVERSIONS
+   bool '  Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
@@ -113,13 +133,19 @@
 bool 'System V IPC' CONFIG_SYSVIPC
 bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
+tristate 'Math emulation' CONFIG_NWFPE
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
+if [ "$CONFIG_CPU_32" = "y" ]; then
+   tristate 'RISC OS personality' CONFIG_ARTHUR
+fi
 
 tristate 'Parallel port support' CONFIG_PARPORT
 if [ "$CONFIG_PARPORT" != "n" ]; then
-  dep_tristate '  Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
+  if [ "$CONFIG_ARCH_ARC" = "y" ]; then
+    dep_tristate '  Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
+  fi
   dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
 # If exactly one hardware type is selected then parport will optimise away
 # support for loading any others.  Defeat this if the user is keen.
@@ -129,10 +155,24 @@
     fi
   fi
 fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" -o \
-     "$CONFIG_ARCH_EBSA110" = "y" -o \
-     "$CONFIG_ARCH_VNC" = "y" ]; then
-  string 'Initial kernel command string' CONFIG_CMDLINE ""
+if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
+     "$CONFIG_ARCH_NETWINDER" = "y" -o \
+     "$CONFIG_CATS" = "y" ]; then
+   string 'Initial kernel command string' CONFIG_CMDLINE ""
+fi
+if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
+     "$CONFIG_ARCH_EBSA110" = "y" -o   \
+     "$CONFIG_ARCH_EBSA285" = "y" -o   \
+     "$CONFIG_ARCH_CO285" = "y" ]; then
+   bool 'Timer and CPU usage LEDs' CONFIG_LEDS
+   if [ "$CONFIG_LEDS" = "y" ]; then
+      if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
+	   "$CONFIG_ARCH_EBSA285" = "y" -o   \
+	   "$CONFIG_ARCH_CO285" = "y" ]; then
+         bool '  Timer LED' CONFIG_LEDS_TIMER
+         bool '  CPU usage LED' CONFIG_LEDS_CPU
+      fi
+   fi
 fi
 endmenu
 
@@ -141,80 +181,100 @@
 source drivers/block/Config.in
 
 if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-  source drivers/acorn/block/Config.in
+   source drivers/acorn/block/Config.in
 fi
 
-if [ "$CONFIG_VGA_CONSOLE" = "n" -a "$CONFIG_FB" = "n" ]; then
-  source arch/arm/drivers/char/Config.in
-else
-  source drivers/char/Config.in
-fi
+source drivers/char/Config.in
 if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-  source drivers/acorn/char/Config.in
+   if [ "$CONFIG_BUSMOUSE" = "y" ]; then
+      if [ "$CONFIG_ARCH_RPC" != "y" ]; then
+         define_bool CONFIG_KBDMOUSE y
+      else
+         define_bool CONFIG_RPCMOUSE y
+      fi
+   fi
 fi
 
 if [ "$CONFIG_VT" = "y" ]; then
-  mainmenu_option next_comment
-  comment 'Console drivers'
-  if [ "$CONFIG_ARCH_ACORN" != "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
-    bool 'VGA text console' CONFIG_VGA_CONSOLE
-  fi
-  bool 'Support Frame buffer devices' CONFIG_FB
-  source drivers/video/Config.in
-  endmenu
+   mainmenu_option next_comment
+   comment 'Console drivers'
+   if [ "$CONFIG_ARCH_ACORN" != "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
+      bool 'VGA text console' CONFIG_VGA_CONSOLE
+   fi
+   bool 'Support Frame buffer devices' CONFIG_FB
+   source drivers/video/Config.in
+   endmenu
 fi
 
 if [ "$CONFIG_NET" = "y" ]; then
-  source net/Config.in
-fi
+   source net/Config.in
 
-if [ "$CONFIG_NET" = "y" ]; then
-  mainmenu_option next_comment
-  comment 'Network device support'
+   source net/ax25/Config.in
 
-  bool 'Network device support?' CONFIG_NETDEVICES
-  if [ "$CONFIG_NETDEVICES" = "y" ]; then
-    source drivers/net/Config.in
-  fi
-  endmenu
+   source net/irda/Config.in
+
+   mainmenu_option next_comment
+   comment 'Network device support'
+
+   bool 'Network device support?' CONFIG_NETDEVICES
+   if [ "$CONFIG_NETDEVICES" = "y" ]; then
+      source drivers/net/Config.in
+   fi
+   endmenu
 fi
 
+# mainmenu_option next_comment
+# comment 'ISDN subsystem'
+#
+# tristate 'ISDN support' CONFIG_ISDN
+# if [ "$CONFIG_ISDN" != "n" ]; then
+#    source drivers/isdn/Config.in
+# fi
+# endmenu
+
 mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support?' CONFIG_SCSI
 
 if [ "$CONFIG_SCSI" != "n" ]; then
-  source drivers/scsi/Config.in
+   source drivers/scsi/Config.in
 fi
 endmenu
 
 if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_PCI" = "y" ]; then
-  mainmenu_option next_comment
-  comment 'Sound'
+   mainmenu_option next_comment
+   comment 'Sound'
 
-  tristate 'Sound support' CONFIG_SOUND
-  if [ "$CONFIG_SOUND" != "n" ]; then
-    source drivers/sound/Config.in
-  fi
-  endmenu
+   tristate 'Sound support' CONFIG_SOUND
+   if [ "$CONFIG_SOUND" != "n" ]; then
+      source drivers/sound/Config.in
+   fi
+   endmenu
 fi
 
-# mainmenu_option next_comment
-# comment 'ISDN subsystem'
-#
-# tristate 'ISDN support' CONFIG_ISDN
-# if [ "$CONFIG_ISDN" != "n" ]; then
-#   source drivers/isdn/Config.in
-# fi
-# endmenu
-
 source fs/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
-bool 'Debug kernel errors' CONFIG_DEBUG_ERRORS
+bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
+bool 'Verbose kernel error messages' CONFIG_DEBUG_ERRORS
+bool 'Verbose user fault messages' CONFIG_DEBUG_USER
+bool 'Include debugging information in kernel binary' CONFIG_DEBUG_INFO
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+if [ "$CONFIG_CPU_26" = "y" ]; then
+   bool 'Disable pgtable cache' CONFIG_NO_PGT_CACHE
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   # These options are only for real kernel hackers
+   # who want to get their hands dirty. 
+   bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL
+   if [ "$CONFIG_DEBUG_LL" = "y" ]; then
+      if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+         bool 'Kernel low-level debugging messages via DC21285 port' CONFIG_DEBUG_DC21285_PORT
+      fi
+   fi
+fi
 endmenu
diff -urN linux-2.2.18-pre8/arch/arm/def-configs/a5k linux/arch/arm/def-configs/a5k
--- linux-2.2.18-pre8/arch/arm/def-configs/a5k	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/def-configs/a5k	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,425 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+CONFIG_ARCH_A5K=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_FOOTBRIDGE is not set
+CONFIG_ARCH_ACORN=y
+# CONFIG_ISA_DMA is not set
+# CONFIG_CPU_32 is not set
+CONFIG_CPU_26=y
+# CONFIG_CPU_ARM2 is not set
+CONFIG_CPU_ARM3=y
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+# CONFIG_CPU_SA110 is not set
+CONFIG_PAGESIZE_32=y
+# CONFIG_PAGESIZE_16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_NWFPE is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=m
+# CONFIG_BINFMT_MISC is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Acorn-specific block devices
+#
+CONFIG_BLK_DEV_IDE_CARDS=y
+CONFIG_BLK_DEV_IDE_ICSIDE=y
+# CONFIG_BLK_DEV_IDE_RAPIDE is not set
+# CONFIG_BLK_DEV_FD1772 is not set
+CONFIG_BLK_DEV_MFM=m
+CONFIG_BLK_DEV_MFM_AUTODETECT=y
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_ATOMWIDE_SERIAL=y
+CONFIG_DUALSP_SERIAL=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=y
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_PSMOUSE is not set
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+CONFIG_KBDMOUSE=y
+
+#
+# Console drivers
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FB_ACORN=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_MFB=y
+CONFIG_FBCON_CFB2=y
+CONFIG_FBCON_CFB4=y
+CONFIG_FBCON_CFB8=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+# CONFIG_FBCON_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_FONT_ACORN_8x8=y
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+# CONFIG_SKB_LARGE is not set
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+# CONFIG_IRDA is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_ARM_ETHER1=y
+CONFIG_ARM_ETHER3=y
+# CONFIG_ARM_ETHERH is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+# CONFIG_CHR_DEV_SG is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+CONFIG_SCSI_PPA=m
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_ACORNSCSI_3=y
+CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y
+CONFIG_SCSI_ACORNSCSI_SYNC=y
+CONFIG_SCSI_ARXESCSI=m
+# CONFIG_SCSI_CUMANA_2 is not set
+CONFIG_SCSI_EESOXSCSI=y
+# CONFIG_SCSI_POWERTECSCSI is not set
+
+#
+# The following drivers are not fully supported
+#
+# CONFIG_SCSI_CUMANA_1 is not set
+# CONFIG_SCSI_ECOSCSI is not set
+# CONFIG_SCSI_OAK1 is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFSD=y
+# CONFIG_NFSD_SUN is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+CONFIG_ACORN_PARTITION=y
+CONFIG_ACORN_PARTITION_ADFS=y
+CONFIG_ACORN_PARTITION_ICS=y
+CONFIG_ACORN_PARTITION_POWERTEC=y
+CONFIG_ACORN_PARTITION_RISCIX=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Kernel hacking
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_NO_PGT_CACHE=y
+# CONFIG_DEBUG_LL is not set
diff -urN linux-2.2.18-pre8/arch/arm/def-configs/ebsa110 linux/arch/arm/def-configs/ebsa110
--- linux-2.2.18-pre8/arch/arm/def-configs/ebsa110	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/def-configs/ebsa110	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,340 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+CONFIG_ARCH_EBSA110=y
+# CONFIG_FOOTBRIDGE is not set
+# CONFIG_ARCH_ACORN is not set
+# CONFIG_ISA_DMA is not set
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_NWFPE is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_CMDLINE="root=/dev/nfs rw mem=16M console=ttyS1,38400n8"
+CONFIG_LEDS=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_ONLY is not set
+
+#
+# Additional Block Devices
+#
+# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+CONFIG_SERIAL_EXTENDED=y
+# CONFIG_SERIAL_MANY_PORTS is not set
+# CONFIG_SERIAL_SHARE_IRQ is not set
+# CONFIG_SERIAL_DETECT_IRQ is not set
+# CONFIG_SERIAL_MULTIPORT is not set
+# CONFIG_HUB6 is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_PSMOUSE is not set
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_NETLINK=y
+CONFIG_RTNETLINK=y
+# CONFIG_NETLINK_DEV is not set
+CONFIG_FIREWALL=y
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+CONFIG_IP_FIREWALL=y
+# CONFIG_IP_FIREWALL_NETLINK is not set
+CONFIG_IP_ALWAYS_DEFRAG=y
+# CONFIG_IP_TRANSPARENT_PROXY is not set
+CONFIG_IP_MASQUERADE=y
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+CONFIG_IP_MASQUERADE_ICMP=y
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+# CONFIG_IP_MASQUERADE_MOD is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+# CONFIG_SKB_LARGE is not set
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+# CONFIG_IRDA is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_ARM_AM79C961A=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=y
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_DLCI is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_EFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SGI_DISKLABEL is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_FRAME_POINTER is not set
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_LL is not set
diff -urN linux-2.2.18-pre8/arch/arm/def-configs/footbridge linux/arch/arm/def-configs/footbridge
--- linux-2.2.18-pre8/arch/arm/def-configs/footbridge	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/def-configs/footbridge	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,549 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+CONFIG_FOOTBRIDGE=y
+CONFIG_HOST_FOOTBRIDGE=y
+# CONFIG_ADDIN_FOOTBRIDGE is not set
+CONFIG_ARCH_EBSA285=y
+# CONFIG_CATS is not set
+CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_PCI=y
+CONFIG_ISA_DMA=y
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_ALIGNMENT_TRAP is not set
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune"
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+# CONFIG_LEDS_CPU is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_OFFBOARD=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_STRIPED=m
+CONFIG_MD_MIRRORING=m
+CONFIG_MD_RAID5=m
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+CONFIG_PARIDE_PARPORT=y
+CONFIG_PARIDE=m
+
+#
+# Parallel IDE high-level drivers
+#
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+
+#
+# Parallel IDE protocol modules
+#
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+CONFIG_DS1620=y
+CONFIG_NWBUTTON=y
+CONFIG_NWBUTTON_REBOOT=y
+CONFIG_NWFLASH=m
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_CYBER2000=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA_PLANES is not set
+CONFIG_FBCON_VGA=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+CONFIG_FONT_ACORN_8x8=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRCOMM=m
+CONFIG_IRLPT=m
+CONFIG_IRLPT_CLIENT=m
+CONFIG_IRLPT_SERVER=m
+# CONFIG_IRDA_OPTIONS is not set
+CONFIG_IRDA_COMPRESSION=y
+
+#
+#    IrDA compressors
+#
+CONFIG_IRDA_DEFLATE=m
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+CONFIG_IRPORT_SIR=m
+
+#
+# FIR device drivers
+#
+# CONFIG_NSC_FIR is not set
+CONFIG_WINBOND_FIR=m
+# CONFIG_SHARP_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+CONFIG_VORTEX=y
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_ACENIC is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DE4X5 is not set
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_NE3210 is not set
+CONFIG_NE2K_PCI=y
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_ES3210 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+# CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_DLCI is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+CONFIG_SOUND_SB=m
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_VIDC is not set
+CONFIG_SOUND_WAVEARTIST=m
+CONFIG_WAVEARTIST_BASE=250
+CONFIG_WAVEARTIST_IRQ=12
+CONFIG_WAVEARTIST_DMA=3
+CONFIG_WAVEARTIST_DMA2=7
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_EFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_SUN is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SGI_DISKLABEL is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_CODEPAGE_437=m
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=m
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Kernel hacking
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_LL is not set
diff -urN linux-2.2.18-pre8/arch/arm/def-configs/netwinder linux/arch/arm/def-configs/netwinder
--- linux-2.2.18-pre8/arch/arm/def-configs/netwinder	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/def-configs/netwinder	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,677 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+CONFIG_FOOTBRIDGE=y
+CONFIG_HOST_FOOTBRIDGE=y
+# CONFIG_ADDIN_FOOTBRIDGE is not set
+# CONFIG_ARCH_EBSA285 is not set
+# CONFIG_CATS is not set
+CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_PCI=y
+CONFIG_ISA_DMA=y
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+CONFIG_CPU_ARMV4=y
+CONFIG_TUNE_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_CMDLINE="mem=32M root=/dev/hda1 ide0=autotune ide1=autotune"
+CONFIG_LEDS=y
+# CONFIG_LEDS_TIMER is not set
+# CONFIG_LEDS_CPU is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=m
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_BLK_DEV_DAC960=m
+CONFIG_PARIDE_PARPORT=m
+CONFIG_PARIDE=m
+
+#
+# Parallel IDE high-level drivers
+#
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+
+#
+# Parallel IDE protocol modules
+#
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
+CONFIG_BLK_CPQ_DA=m
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# USB drivers - not for the faint of heart
+#
+# CONFIG_USB is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=m
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+CONFIG_PRINTER=m
+# CONFIG_PRINTER_READBACK is not set
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=m
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+CONFIG_21285_WATCHDOG=m
+CONFIG_977_WATCHDOG=m
+CONFIG_DS1620=y
+CONFIG_NWBUTTON=y
+CONFIG_NWBUTTON_REBOOT=y
+CONFIG_NWFLASH=m
+# CONFIG_21285_FLASH is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_BUZ is not set
+CONFIG_NVRAM=m
+CONFIG_RTC=y
+
+#
+# Video For Linux
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_RADIO_RTRACK=m
+CONFIG_RADIO_RTRACK2=m
+CONFIG_RADIO_AZTECH=m
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_MIROPCM20 is not set
+CONFIG_RADIO_GEMTEK=m
+CONFIG_VIDEO_BT848=m
+CONFIG_VIDEO_BWQCAM=m
+CONFIG_VIDEO_CQCAM=m
+CONFIG_VIDEO_PMS=m
+CONFIG_VIDEO_SAA5249=m
+CONFIG_RADIO_SF16FMI=m
+CONFIG_RADIO_TYPHOON=m
+# CONFIG_RADIO_TYPHOON_PROC_FS is not set
+CONFIG_RADIO_ZOLTRIX=m
+CONFIG_TUNER_3036=m
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_CYBER2000=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA_PLANES is not set
+CONFIG_FBCON_VGA=y
+CONFIG_FBCON_FONTWIDTH8_ONLY=y
+CONFIG_FBCON_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_NETLINK=y
+CONFIG_RTNETLINK=y
+# CONFIG_NETLINK_DEV is not set
+CONFIG_FIREWALL=y
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+CONFIG_IP_FIREWALL=y
+# CONFIG_IP_FIREWALL_NETLINK is not set
+CONFIG_IP_ALWAYS_DEFRAG=y
+CONFIG_IP_TRANSPARENT_PROXY=y
+CONFIG_IP_MASQUERADE=y
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+# CONFIG_IP_MASQUERADE_ICMP is not set
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+CONFIG_IP_MASQUERADE_MOD=y
+CONFIG_IP_MASQUERADE_IPAUTOFW=m
+CONFIG_IP_MASQUERADE_IPPORTFW=m
+CONFIG_IP_MASQUERADE_MFW=m
+# CONFIG_IP_ROUTER is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_NET_IPGRE_BROADCAST is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+CONFIG_INET_RARP=m
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+CONFIG_SPX=m
+CONFIG_ATALK=m
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRCOMM=m
+CONFIG_IRLPT=m
+CONFIG_IRLPT_CLIENT=m
+CONFIG_IRLPT_SERVER=m
+CONFIG_IRDA_OPTIONS=y
+
+#
+#    IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRDA_DEBUG=y
+# CONFIG_IRDA_COMPRESSION is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+CONFIG_IRPORT_SIR=m
+
+#
+# FIR device drivers
+#
+# CONFIG_NSC_FIR is not set
+CONFIG_WINBOND_FIR=m
+# CONFIG_SHARP_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+CONFIG_DUMMY=m
+CONFIG_EQUALIZER=m
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
+CONFIG_YELLOWFIN=m
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_ACENIC is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DE4X5 is not set
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_NE3210 is not set
+CONFIG_NE2K_PCI=y
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_ES3210 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
+CONFIG_PLIP=m
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+# CONFIG_SLIP_SMART is not set
+# CONFIG_SLIP_MODE_SLIP6 is not set
+# CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+CONFIG_SHAPER=m
+
+#
+# Wan interfaces
+#
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_DLCI is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=m
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+CONFIG_SCSI_PPA=m
+CONFIG_SCSI_IMM=m
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+CONFIG_SCSI_SYM53C8XX=m
+CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
+CONFIG_SCSI_NCR53C8XX_SYNC=20
+# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
+# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+CONFIG_SOUND_SB=m
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+CONFIG_SOUND_OPL3SA1=m
+# CONFIG_SOUND_SOFTOSS is not set
+CONFIG_SOUND_YM3812=m
+CONFIG_SOUND_VMIDI=m
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_VIDC is not set
+CONFIG_SOUND_WAVEARTIST=m
+CONFIG_WAVEARTIST_BASE=250
+CONFIG_WAVEARTIST_IRQ=12
+CONFIG_WAVEARTIST_DMA=3
+CONFIG_WAVEARTIST_DMA2=7
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
+
+#
+# Filesystems
+#
+CONFIG_QUOTA=y
+CONFIG_AUTOFS_FS=m
+CONFIG_ADFS_FS=m
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_UMSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=m
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_EFS_FS=m
+CONFIG_SGI_PARTITION=y
+
+#
+# Network File Systems
+#
+CONFIG_CODA_FS=m
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_SUN is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+CONFIG_SMB_FS=m
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_SGI_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SGI_DISKLABEL is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_ACORN_PARTITION is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_ISO8859_9=m
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+
+#
+# Kernel hacking
+#
+# CONFIG_FRAME_POINTER is not set
+CONFIG_DEBUG_ERRORS=y
+CONFIG_DEBUG_USER=y
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_LL is not set
diff -urN linux-2.2.18-pre8/arch/arm/def-configs/rpc linux/arch/arm/def-configs/rpc
--- linux-2.2.18-pre8/arch/arm/def-configs/rpc	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/def-configs/rpc	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,471 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+CONFIG_ARCH_RPC=y
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_FOOTBRIDGE is not set
+CONFIG_ARCH_ACORN=y
+# CONFIG_ISA_DMA is not set
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_ALIGNMENT_TRAP is not set
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_NWFPE is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=y
+CONFIG_BLK_DEV_IDEFLOPPY=y
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Acorn-specific block devices
+#
+CONFIG_BLK_DEV_IDE_CARDS=y
+CONFIG_BLK_DEV_IDE_ICSIDE=y
+CONFIG_BLK_DEV_IDE_RAPIDE=y
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_ATOMWIDE_SERIAL=y
+CONFIG_DUALSP_SERIAL=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_PSMOUSE is not set
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+CONFIG_RPCMOUSE=y
+
+#
+# Console drivers
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FB_ACORN=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+CONFIG_FBCON_MFB=y
+CONFIG_FBCON_CFB2=y
+CONFIG_FBCON_CFB4=y
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+CONFIG_FBCON_CFB32=y
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA is not set
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+CONFIG_FONT_ACORN_8x8=y
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+# CONFIG_IRDA is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_ARM_ETHER1=m
+CONFIG_ARM_ETHER3=m
+CONFIG_ARM_ETHERH=m
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+CONFIG_SCSI_PPA=m
+CONFIG_SCSI_IMM=m
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_ACORNSCSI_3=m
+CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y
+CONFIG_SCSI_ACORNSCSI_SYNC=y
+CONFIG_SCSI_ARXESCSI=m
+CONFIG_SCSI_CUMANA_2=m
+CONFIG_SCSI_EESOXSCSI=m
+CONFIG_SCSI_POWERTECSCSI=m
+
+#
+# The following drivers are not fully supported
+#
+CONFIG_SCSI_CUMANA_1=m
+CONFIG_SCSI_OAK1=m
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+# CONFIG_SOUND_SB is not set
+# CONFIG_SOUND_ADLIB is not set
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+CONFIG_SOUND_VIDC=m
+# CONFIG_SOUND_WAVEARTIST is not set
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+CONFIG_ACORN_PARTITION=y
+CONFIG_ACORN_PARTITION_ADFS=y
+CONFIG_ACORN_PARTITION_ICS=y
+CONFIG_ACORN_PARTITION_POWERTEC=y
+CONFIG_ACORN_PARTITION_RISCIX=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_ISO8859_9=m
+# CONFIG_NLS_ISO8859_15 is not set
+CONFIG_NLS_KOI8_R=m
+
+#
+# Kernel hacking
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_ARTHUR is not set
+# CONFIG_DEBUG_LL is not set
diff -urN linux-2.2.18-pre8/arch/arm/defconfig linux/arch/arm/defconfig
--- linux-2.2.18-pre8/arch/arm/defconfig	Thu Feb 25 10:46:46 1999
+++ linux/arch/arm/defconfig	Mon Dec  3 20:25:13 2001
@@ -4,47 +4,71 @@
 CONFIG_ARM=y
 
 #
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+CONFIG_FOOTBRIDGE=y
+CONFIG_HOST_FOOTBRIDGE=y
+# CONFIG_ADDIN_FOOTBRIDGE is not set
+CONFIG_ARCH_EBSA285=y
+# CONFIG_CATS is not set
+CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_PCI=y
+CONFIG_ISA_DMA=y
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
 # Code maturity level options
 #
 CONFIG_EXPERIMENTAL=y
+# CONFIG_ALIGNMENT_TRAP is not set
+# CONFIG_TEXT_SECTIONS is not set
 
 #
 # Loadable module support
 #
 CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
+# CONFIG_MODVERSIONS is not set
 CONFIG_KMOD=y
 
 #
 # General setup
 #
-# CONFIG_ARCH_ARC is not set
-# CONFIG_ARCH_A5K is not set
-CONFIG_ARCH_RPC=y
-# CONFIG_ARCH_EBSA110 is not set
-# CONFIG_ARCH_NEXUSPCI is not set
-CONFIG_ARCH_ACORN=y
-# CONFIG_PCI is not set
-# CONFIG_CPU_ARM2 is not set
-# CONFIG_CPU_ARM3 is not set
-# CONFIG_CPU_ARM6 is not set
-CONFIG_CPU_SA110=y
-CONFIG_FRAME_POINTER=y
-# CONFIG_BINUTILS_NEW is not set
-CONFIG_DEBUG_ERRORS=y
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
 CONFIG_BINFMT_AOUT=y
-CONFIG_BINFMT_ELF=m
-# CONFIG_BINFMT_JAVA is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
 CONFIG_PARPORT=y
 CONFIG_PARPORT_PC=y
+CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune"
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+# CONFIG_LEDS_CPU is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
 
 #
-# Floppy, IDE, and other block devices
+# Block devices
 #
-CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_FD is not set
 CONFIG_BLK_DEV_IDE=y
 
 #
@@ -52,32 +76,165 @@
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDECD is not set
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_BLK_DEV_IDE_PCMCIA is not set
-CONFIG_BLK_DEV_IDE_CARDS=y
-CONFIG_BLK_DEV_IDE_ICSIDE=y
-# CONFIG_BLK_DEV_IDE_RAPIDE is not set
-# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_OFFBOARD=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
 
 #
 # Additional Block Devices
 #
 CONFIG_BLK_DEV_LOOP=m
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_STRIPED=m
+CONFIG_MD_MIRRORING=m
+CONFIG_MD_RAID5=m
 CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_DEV_XD is not set
 CONFIG_PARIDE_PARPORT=y
-# CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_PART=y
+CONFIG_PARIDE=m
+
+#
+# Parallel IDE high-level drivers
+#
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+
+#
+# Parallel IDE protocol modules
+#
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
 # CONFIG_BLK_DEV_HD is not set
 
 #
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+CONFIG_DS1620=y
+CONFIG_NWBUTTON=y
+CONFIG_NWBUTTON_REBOOT=y
+CONFIG_NWFLASH=m
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_CYBER2000=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+CONFIG_FBCON_VGA=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+CONFIG_FONT_ACORN_8x8=y
+
+#
 # Networking options
 #
-# CONFIG_PACKET is not set
+CONFIG_PACKET=y
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
 # CONFIG_FILTER is not set
@@ -85,21 +242,20 @@
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
 # CONFIG_IP_ADVANCED_ROUTER is not set
-# CONFIG_IP_PNP is not set
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
-# CONFIG_IP_ALIAS is not set
+CONFIG_IP_ALIAS=y
 # CONFIG_SYN_COOKIES is not set
 
 #
 # (it is safe to leave these untouched)
 #
 # CONFIG_INET_RARP is not set
-CONFIG_IP_NOSR=y
-# CONFIG_SKB_LARGE is not set
+CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
 
 #
@@ -111,107 +267,198 @@
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
+# CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
-# CONFIG_NET_PROFILE is not set
 
 #
-# SCSI support
+# Amateur Radio support
 #
-CONFIG_SCSI=y
+# CONFIG_HAMRADIO is not set
 
 #
-# SCSI support type (disk, tape, CD-ROM)
+# IrDA subsystem support
 #
-CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
-CONFIG_BLK_DEV_SR=y
-# CONFIG_BLK_DEV_SR_VENDOR is not set
-# CONFIG_CHR_DEV_SG is not set
+# CONFIG_IRDA is not set
 
 #
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+# Network device support
 #
-# CONFIG_SCSI_MULTI_LUN is not set
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_LOGGING=y
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+CONFIG_VORTEX=y
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DE4X5 is not set
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_NE3210 is not set
+CONFIG_NE2K_PCI=y
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_ES3210 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
 
 #
-# SCSI low-level drivers
+# CCP compressors for PPP are only built as modules.
 #
-CONFIG_SCSI_ACORNSCSI_3=m
-CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y
-CONFIG_SCSI_ACORNSCSI_SYNC=y
-CONFIG_SCSI_CUMANA_2=m
-CONFIG_SCSI_POWERTECSCSI=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
 
 #
-# The following drives are not fully supported
+# SCSI support
 #
-CONFIG_SCSI_CUMANA_1=m
-CONFIG_SCSI_OAK1=m
-CONFIG_SCSI_PPA=m
-CONFIG_SCSI_PPA_HAVE_PEDANTIC=2
+# CONFIG_SCSI is not set
 
 #
-# Network device support
+# Sound
 #
-CONFIG_NETDEVICES=y
-# CONFIG_DUMMY is not set
-# CONFIG_EQUALIZER is not set
-CONFIG_PPP=m
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+CONFIG_SOUND_SB=m
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_VIDC is not set
+CONFIG_SOUND_WAVEARTIST=m
+CONFIG_WAVEARTIST_BASE=250
+CONFIG_WAVEARTIST_IRQ=12
+CONFIG_WAVEARTIST_DMA=3
+CONFIG_WAVEARTIST_DMA2=7
 
 #
-# CCP compressors for PPP are only built as modules.
+# Additional low level sound drivers
 #
-# CONFIG_SLIP is not set
-CONFIG_ETHER1=m
-CONFIG_ETHER3=m
-CONFIG_ETHERH=m
+# CONFIG_LOWLEVEL_SOUND is not set
 
 #
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-CONFIG_JOLIET=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
-CONFIG_VFAT_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
-CONFIG_NFSD=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_UFS_FS is not set
-CONFIG_ADFS_FS=y
-CONFIG_ADFS_FS=y
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_OSF_PARTITION is not set
 # CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+CONFIG_ACORN_PARTITION=y
+CONFIG_ACORN_PARTITION_ADFS=y
+# CONFIG_ACORN_PARTITION_ICS is not set
+# CONFIG_ACORN_PARTITION_POWERTEC is not set
+# CONFIG_ACORN_PARTITION_RISCIX is not set
 CONFIG_NLS=y
 
 #
 # Native Language Support
 #
-# CONFIG_NLS_CODEPAGE_437 is not set
+CONFIG_NLS_CODEPAGE_437=m
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
 # CONFIG_NLS_CODEPAGE_855 is not set
 # CONFIG_NLS_CODEPAGE_857 is not set
 # CONFIG_NLS_CODEPAGE_860 is not set
@@ -223,8 +470,8 @@
 # CONFIG_NLS_CODEPAGE_866 is not set
 # CONFIG_NLS_CODEPAGE_869 is not set
 # CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
 # CONFIG_NLS_ISO8859_3 is not set
 # CONFIG_NLS_ISO8859_4 is not set
 # CONFIG_NLS_ISO8859_5 is not set
@@ -232,34 +479,15 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+CONFIG_NLS_ISO8859_15=m
 # CONFIG_NLS_KOI8_R is not set
 
 #
-# Character devices
-#
-CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=y
-# CONFIG_SERIAL_CONSOLE is not set
-# CONFIG_SERIAL_EXTENDED is not set
-CONFIG_ATOMWIDE_SERIAL=y
-CONFIG_DUALSP_SERIAL=y
-CONFIG_MOUSE=y
-CONFIG_PRINTER=m
-CONFIG_PRINTER_READBACK=y
-# CONFIG_UMISC is not set
-# CONFIG_WATCHDOG is not set
-CONFIG_RPCMOUSE=y
-
-#
-# Sound
-#
-CONFIG_SOUND=m
-CONFIG_VIDC=y
-CONFIG_AUDIO=y
-DSP_BUFFSIZE=65536
-
-#
 # Kernel hacking
 #
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
 CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_LL is not set
diff -urN linux-2.2.18-pre8/arch/arm/kernel/Makefile linux/arch/arm/kernel/Makefile
--- linux-2.2.18-pre8/arch/arm/kernel/Makefile	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/Makefile	Mon Dec  3 20:25:13 2001
@@ -9,66 +9,73 @@
 ENTRY_OBJ = entry-$(PROCESSOR).o
 
 O_TARGET := kernel.o
-O_OBJS   := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o setup.o \
-	    signal.o sys_arm.o time.o traps.o
+O_OBJS   := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o \
+	    setup.o signal.o sys_arm.o time.o traps.o
 
-DMA_OBJS_arc      = dma-arc.o
-DMA_OBJS_a5k      = dma-a5k.o
-DMA_OBJS_rpc      = dma-rpc.o
-DMA_OBJS_ebsa110  = dma-dummy.o
-DMA_OBJS_ebsa285  = dma-ebsa285.o
-DMA_OBJS_nexuspci =
-DMA_OBJS_vnc      = dma-vnc.o
-
-O_OBJS_arc        = ecard.o iic.o fiq.o oldlatches.o
-O_OBJS_a5k        = ecard.o iic.o fiq.o
-O_OBJS_rpc        = ecard.o iic.o fiq.o
-O_OBJS_ebsa110    = leds-ebsa110.o
-O_OBJS_ebsa285    = leds-ebsa285.o hw-ebsa285.o
-O_OBJS_nexuspci   =
-O_OBJS_vnc        = leds-ebsa285.o hw-vnc.o
+ifeq ($(CONFIG_ISA_DMA),y)
+  ISA_DMA_OBJS += dma-isa.o
+endif
+
+O_OBJS_arc        = dma-arc.o iic.o fiq.o oldlatches.o
+O_OBJS_a5k        = dma-a5k.o iic.o fiq.o
+O_OBJS_rpc        = dma-rpc.o iic.o fiq.o
+O_OBJS_ebsa110    = dma-dummy.o
+O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS) isa.o
+O_OBJS_nexuspci   = dma-dummy.o
+
+OX_OBJS_arc	  = dma.o
+OX_OBJS_a5k	  = dma.o
+OX_OBJS_rpc	  = dma.o
+OX_OBJS_ebsa110	  = 
+OX_OBJS_footbridge= dma.o hw-footbridge.o
+OX_OBJS_nexuspci  =
 
-all: lib kernel.o $(HEAD_OBJ) init_task.o
+all: kernel.o $(HEAD_OBJ) init_task.o
+
+O_OBJS += $(O_OBJS_$(MACHINE))
 
 ifeq ($(CONFIG_MODULES),y)
   OX_OBJS = armksyms.o
-else
-  O_OBJS += armksyms.o
 endif
 
-ifeq ($(MACHINE),nexuspci)
-  ifdef CONFIG_PCI
+ifeq ($(CONFIG_ARCH_ACORN),y)
+  OX_OBJS += ecard.o
+endif
+
+ifeq ($(CONFIG_PCI),y)
+  ifeq ($(MACHINE),nexuspci)
     O_OBJS += plx9080.o
-  endif
-else
-  ifdef CONFIG_PCI
+  else
     O_OBJS += dec21285.o
   endif
 endif
 
-ifneq ($(DMA_OBJS_$(MACHINE)),)
-  OX_OBJS += dma.o
-  O_OBJS  += $(DMA_OBJS_$(MACHINE))
-  ifeq ($(CONFIG_ISA_DMA),y)
-    O_OBJS += dma-isa.o
-  endif
+ifeq ($(CONFIG_LEDS),y)
+  OX_OBJS += leds-$(MACHINE).o
+endif
+
+ifeq ($(CONFIG_MODULES),y)
+  OX_OBJS += $(OX_OBJS_$(MACHINE))
 else
-  O_OBJS += dma-dummy.o
+  O_OBJS += $(OX_OBJS_$(MACHINE))
 endif
 
-O_OBJS += $(O_OBJS_$(MACHINE))
+ifeq ($(CONFIG_ARTHUR),y)
+  O_OBJS += arthur.o
+else
+  ifeq ($(CONFIG_ARTHUR),m)
+    M_OBJS += arthur.o
+  endif
+endif
 
 $(HEAD_OBJ): $(HEAD_OBJ:.o=.S)
-	$(CC) -D__ASSEMBLY__ -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@
-
-$(ENTRY_OBJ): $(ENTRY_OBJ:.o=.S)
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c $(ENTRY_OBJ:.o=.S) -o $@
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@
 
 include $(TOPDIR)/Rules.make
 
-$(ENTRY_OBJ): ../lib/constants.h
-
-.PHONY: lib
+.S.o:
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_$@) -c -o $*.o $<
 
-lib:
-	$(MAKE) -C ../lib constants.h
+# Spell out some dependencies that `make dep' doesn't spot
+entry-armv.o: calls.S ../lib/constants.h
+entry-armo.o: calls.S ../lib/constants.h
diff -urN linux-2.2.18-pre8/arch/arm/kernel/armksyms.c linux/arch/arm/kernel/armksyms.c
--- linux-2.2.18-pre8/arch/arm/kernel/armksyms.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/armksyms.c	Mon Dec  3 20:25:13 2001
@@ -2,24 +2,49 @@
 #include <linux/module.h>
 #include <linux/user.h>
 #include <linux/string.h>
+#include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
+#include <linux/in6.h>
+#include <linux/vt_kern.h>
 
-#include <asm/ecard.h>
+#include <asm/byteorder.h>
 #include <asm/elf.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 #include <asm/dma.h>
 #include <asm/pgtable.h>
+#include <asm/proc-fns.h>
+#include <asm/processor.h>
+#include <asm/semaphore.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 
+extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(struct pt_regs *, struct user_fp_struct *);
 extern void inswb(unsigned int port, void *to, int len);
 extern void outswb(unsigned int port, const void *to, int len);
 
+extern unsigned int local_bh_count[NR_CPUS];
+extern unsigned int local_irq_count[NR_CPUS];
+
+extern unsigned long system_serial_low;
+extern unsigned long system_serial_high;
+extern unsigned int system_rev;
+
+/*
+ * syscalls
+ */
+extern int sys_write(int, const char *, int);
+extern int sys_read(int, char *, int);
+extern int sys_lseek(int, off_t, int);
+extern int sys_exit(int);
+extern int sys_wait4(int, int *, int, struct rusage *);
+
 /*
  * libgcc functions - functions that are used internally by the
  * compiler...  (prototypes are not correct though, but that
@@ -43,6 +68,8 @@
 extern void __umoddi3(void);
 extern void __umodsi3(void);
 
+extern void ret_from_exception(void);
+extern void fpundefinstr(void);
 extern void fp_enter(void);
 #define EXPORT_SYMBOL_ALIAS(sym,orig) \
  const char __kstrtab_##sym##[] __attribute__((section(".kstrtab"))) = \
@@ -57,32 +84,47 @@
 EXPORT_SYMBOL_ALIAS(fp_printk,printk);
 EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig);
 
+#ifdef CONFIG_CPU_26
+EXPORT_SYMBOL(fpundefinstr);
+EXPORT_SYMBOL(ret_from_exception);
+#endif
+
+EXPORT_SYMBOL(kd_mksound);
+
 	/* platform dependent support */
 EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(dump_fpu);
 EXPORT_SYMBOL(udelay);
 EXPORT_SYMBOL(xchg_str);
-
-	/* expansion card support */
-#ifdef CONFIG_ARCH_ACORN
-EXPORT_SYMBOL(ecard_startfind);
-EXPORT_SYMBOL(ecard_find);
-EXPORT_SYMBOL(ecard_readchunk);
-EXPORT_SYMBOL(ecard_address);
-#endif
-
+EXPORT_SYMBOL(local_bh_count);
+EXPORT_SYMBOL(local_irq_count);
+#if defined(CONFIG_CPU_32) && !defined(CONFIG_ARCH_EBSA110)
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(__iounmap);
+#endif
+EXPORT_SYMBOL(init_mm);
+EXPORT_SYMBOL(kernel_thread);
+EXPORT_SYMBOL(system_rev);
+EXPORT_SYMBOL(system_serial_low);
+EXPORT_SYMBOL(system_serial_high);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
 
 	/* processor dependencies */
 EXPORT_SYMBOL(processor);
-EXPORT_SYMBOL(machine_type);
+EXPORT_SYMBOL(__machine_arch_type);
+
+	/* networking */
+EXPORT_SYMBOL(csum_partial_copy);
+EXPORT_SYMBOL(__csum_ipv6_magic);
 
 	/* io */
-EXPORT_SYMBOL(outswb);
+EXPORT_SYMBOL(outsb);
 EXPORT_SYMBOL(outsw);
-EXPORT_SYMBOL(inswb);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insb);
 EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
 
 	/* address translation */
 #ifndef __virt_to_phys__is_a_macro
@@ -98,7 +140,9 @@
 EXPORT_SYMBOL(__bus_to_virt);
 #endif
 
+#ifndef CONFIG_NO_PGT_CACHE
 EXPORT_SYMBOL(quicklists);
+#endif
 EXPORT_SYMBOL(__bad_pmd);
 EXPORT_SYMBOL(__bad_pmd_kernel);
 
@@ -116,19 +160,20 @@
 EXPORT_SYMBOL_NOVERS(strpbrk);
 EXPORT_SYMBOL_NOVERS(strtok);
 EXPORT_SYMBOL_NOVERS(strrchr);
+EXPORT_SYMBOL_NOVERS(strstr);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memmove);
 EXPORT_SYMBOL_NOVERS(memcmp);
 EXPORT_SYMBOL_NOVERS(memscan);
-EXPORT_SYMBOL_NOVERS(memzero);
+EXPORT_SYMBOL_NOVERS(__memzero);
 
 	/* user mem (segment) */
 #if defined(CONFIG_CPU_32)
 EXPORT_SYMBOL(__arch_copy_from_user);
 EXPORT_SYMBOL(__arch_copy_to_user);
 EXPORT_SYMBOL(__arch_clear_user);
-EXPORT_SYMBOL(__arch_strlen_user);
+EXPORT_SYMBOL(__arch_strnlen_user);
 #elif defined(CONFIG_CPU_26)
 EXPORT_SYMBOL(uaccess_kernel);
 EXPORT_SYMBOL(uaccess_user);
@@ -164,6 +209,19 @@
 EXPORT_SYMBOL(find_next_zero_bit);
 
 	/* elf */
-EXPORT_SYMBOL(armidlist);
-EXPORT_SYMBOL(armidindex);
 EXPORT_SYMBOL(elf_platform);
+EXPORT_SYMBOL(elf_hwcap);
+
+	/* syscalls */
+EXPORT_SYMBOL(sys_write);
+EXPORT_SYMBOL(sys_read);
+EXPORT_SYMBOL(sys_lseek);
+EXPORT_SYMBOL(sys_open);
+EXPORT_SYMBOL(sys_exit);
+EXPORT_SYMBOL(sys_wait4);
+
+	/* semaphores */
+EXPORT_SYMBOL_NOVERS(__down_failed);
+EXPORT_SYMBOL_NOVERS(__down_interruptible_failed);
+EXPORT_SYMBOL_NOVERS(__up_wakeup);
+
diff -urN linux-2.2.18-pre8/arch/arm/kernel/arthur.c linux/arch/arm/kernel/arthur.c
--- linux-2.2.18-pre8/arch/arm/kernel/arthur.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/arthur.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,88 @@
+/*
+ * Arthur personality
+ * Copyright (C) 1998 Philip Blundell
+ */
+
+#include <linux/personality.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+
+#include <asm/ptrace.h>
+
+/* RISC OS doesn't have many signals, and a lot of those that it does
+   have don't map easily to any Linux equivalent.  Never mind.  */
+
+#define RISCOS_SIGABRT		1
+#define RISCOS_SIGFPE		2
+#define RISCOS_SIGILL		3
+#define RISCOS_SIGINT		4
+#define RISCOS_SIGSEGV		5
+#define RISCOS_SIGTERM		6
+#define RISCOS_SIGSTAK		7
+#define RISCOS_SIGUSR1		8
+#define RISCOS_SIGUSR2		9
+#define RISCOS_SIGOSERROR	10
+
+static unsigned long riscos_to_linux_signals[32] = {
+	0,	1,	2,	3,	4,	5,	6,	7,
+	8,	9,	10,	11,	12,	13,	14,	15,
+	16,	17,	18,	19,	20,	21,	22,	23,
+	24,	25,	26,	27,	28,	29,	30,	31
+};
+
+static unsigned long linux_to_riscos_signals[32] = {
+	0,		-1,		RISCOS_SIGINT,	-1,
+       	RISCOS_SIGILL,	5,		RISCOS_SIGABRT,	7,
+	RISCOS_SIGFPE,	9,		RISCOS_SIGUSR1,	RISCOS_SIGSEGV,	
+	RISCOS_SIGUSR2,	13,		14,		RISCOS_SIGTERM,
+	16,		17,		18,		19,
+	20,		21,		22,		23,
+	24,		25,		26,		27,
+	28,		29,		30,		31
+};
+
+static void arthur_lcall7(int nr, struct pt_regs *regs)
+{
+	struct siginfo info;
+	info.si_signo = SIGSWI;
+	info.si_code = nr;
+	/* Bounce it to the emulator */
+	send_sig_info(SIGSWI, &info, current);
+}
+
+static struct exec_domain riscos_exec_domain = {
+	"Arthur",	/* name */
+	(lcall7_func)arthur_lcall7,
+	PER_RISCOS, PER_RISCOS,
+	riscos_to_linux_signals,
+	linux_to_riscos_signals,
+#ifdef MODULE
+	&__this_module,	/* No usage counter. */
+#else
+	NULL,
+#endif
+	NULL		/* Nothing after this in the list. */
+};
+
+/*
+ * We could do with some locking to stop Arthur being removed while
+ * processes are using it.
+ */
+
+#ifdef MODULE
+int init_module(void)
+#else
+int initialise_arthur(void)
+#endif
+{
+	return register_exec_domain(&riscos_exec_domain);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	unregister_exec_domain(&riscos_exec_domain);
+}
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/kernel/calls.S linux/arch/arm/kernel/calls.S
--- linux-2.2.18-pre8/arch/arm/kernel/calls.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/calls.S	Mon Dec  3 20:25:13 2001
@@ -31,7 +31,7 @@
 		.long	SYMBOL_NAME(sys_lseek)
 /* 20 */	.long	SYMBOL_NAME(sys_getpid)
 		.long	SYMBOL_NAME(sys_mount_wrapper)
-		.long	SYMBOL_NAME(sys_umount)
+		.long	SYMBOL_NAME(sys_oldumount)
 		.long	SYMBOL_NAME(sys_setuid)
 		.long	SYMBOL_NAME(sys_getuid)
 /* 25 */	.long	SYMBOL_NAME(sys_stime)
@@ -61,7 +61,7 @@
 		.long	SYMBOL_NAME(sys_geteuid)
 /* 50 */	.long	SYMBOL_NAME(sys_getegid)
 		.long	SYMBOL_NAME(sys_acct)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_phys */
+		.long	SYMBOL_NAME(sys_umount)
 		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_lock */
 		.long	SYMBOL_NAME(sys_ioctl)
 /* 55 */	.long	SYMBOL_NAME(sys_fcntl)
@@ -110,7 +110,7 @@
 		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_profil */
 		.long	SYMBOL_NAME(sys_statfs)
 /* 100 */	.long	SYMBOL_NAME(sys_fstatfs)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* .long	_sys_ioperm */
+		.long	SYMBOL_NAME(sys_ni_syscall)
 		.long	SYMBOL_NAME(sys_socketcall)
 		.long	SYMBOL_NAME(sys_syslog)
 		.long	SYMBOL_NAME(sys_setitimer)
@@ -129,7 +129,7 @@
 		.long	SYMBOL_NAME(sys_ipc)
 		.long	SYMBOL_NAME(sys_fsync)
 		.long	SYMBOL_NAME(sys_sigreturn_wrapper)
-		.long	SYMBOL_NAME(sys_clone_wapper)
+/* 120 */	.long	SYMBOL_NAME(sys_clone_wapper)
 		.long	SYMBOL_NAME(sys_setdomainname)
 		.long	SYMBOL_NAME(sys_newuname)
 		.long	SYMBOL_NAME(sys_ni_syscall)		/* .long	SYMBOL_NAME(sys_modify_ldt) */
@@ -157,8 +157,8 @@
 /* 145 */	.long	SYMBOL_NAME(sys_readv)
 		.long	SYMBOL_NAME(sys_writev)
 		.long	SYMBOL_NAME(sys_getsid)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_ni_syscall)
+		.long	SYMBOL_NAME(sys_fdatasync)
+		.long	SYMBOL_NAME(sys_sysctl)
 /* 150 */	.long	SYMBOL_NAME(sys_mlock)
 		.long	SYMBOL_NAME(sys_munlock)
 		.long	SYMBOL_NAME(sys_mlockall)
@@ -196,6 +196,10 @@
 		.long	SYMBOL_NAME(sys_capget)
 /* 185 */	.long	SYMBOL_NAME(sys_capset)
 		.long	SYMBOL_NAME(sys_sigaltstack_wrapper)
+		.long	SYMBOL_NAME(sys_sendfile)
+		.long	SYMBOL_NAME(sys_ni_syscall)
+		.long	SYMBOL_NAME(sys_ni_syscall)
+/* 190 */	.long	SYMBOL_NAME(sys_vfork_wrapper)
 
 		.rept	NR_syscalls-186
 			.long	SYMBOL_NAME(sys_ni_syscall)
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dec21285.c linux/arch/arm/kernel/dec21285.c
--- linux-2.2.18-pre8/arch/arm/kernel/dec21285.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dec21285.c	Mon Dec  3 20:25:13 2001
@@ -8,17 +8,17 @@
 #include <linux/pci.h>
 #include <linux/ptrace.h>
 #include <linux/interrupt.h>
+#include <linux/mm.h>
 #include <linux/init.h>
 
 #include <asm/irq.h>
 #include <asm/system.h>
+#include <asm/hardware.h>
 
-#define MAX_SLOTS		20
+#define MAX_SLOTS		21
 
 extern void pcibios_fixup_ebsa285(struct pci_dev *dev);
 extern void pcibios_init_ebsa285(void);
-extern void pcibios_fixup_vnc(struct pci_dev *dev);
-extern void pcibios_init_vnc(void);
 
 int
 pcibios_present(void)
@@ -33,11 +33,12 @@
 		int slot = PCI_SLOT(dev_fn);
 		
 		if (slot < MAX_SLOTS)
-			return 0xf8c00000 + (slot << 11) + (PCI_FUNC(dev_fn) << 8);
+			return PCICFG0_BASE + 0xc00000 +
+				(slot << 11) + (PCI_FUNC(dev_fn) << 8);
 		else
 			return 0;
 	} else
-		return 0xf9000000 | (bus << 16) | (dev_fn << 8);
+		return PCICFG1_BASE | (bus << 16) | (dev_fn << 8);
 }
 
 int
@@ -151,10 +152,7 @@
 	struct pci_dev *dev;
 
 	for (dev = pci_devices; dev; dev = dev->next) {
-		if (machine_is_ebsa285() || machine_is_cats())
-			pcibios_fixup_ebsa285(dev);
-		if (machine_is_netwinder())
-			pcibios_fixup_vnc(dev);
+		pcibios_fixup_ebsa285(dev);
 
 		pcibios_write_config_byte(dev->bus->number, dev->devfn,
 					  PCI_INTERRUPT_LINE, dev->irq);
@@ -164,18 +162,83 @@
 			dev->bus->number, dev->devfn,
 			dev->vendor, dev->device, dev->irq);
 	}
-	if (machine_is_netwinder())
-		hw_init();
+
+	hw_init();
 }
 
 __initfunc(void pcibios_init(void))
 {
-	if (machine_is_ebsa285() || machine_is_cats())
-		pcibios_init_ebsa285();
-	if (machine_is_netwinder())
-		pcibios_init_vnc();
+	unsigned int mem_size = (unsigned int)high_memory - PAGE_OFFSET;
+	unsigned long cntl;
+
+	*CSR_SDRAMBASEMASK    = (mem_size - 1) & 0x0ffc0000;
+	*CSR_SDRAMBASEOFFSET  = 0;
+	*CSR_ROMBASEMASK      = 0x80000000;
+	*CSR_CSRBASEMASK      = 0;
+	*CSR_CSRBASEOFFSET    = 0;
+	*CSR_PCIADDR_EXTN     = 0;
+
+#ifdef CONFIG_HOST_FOOTBRIDGE
+	/*
+	 * Against my better judgement, Philip Blundell still seems
+	 * to be saying that we should initialise the PCI stuff here
+	 * when the PCI_CFN bit is not set, dispite my comment below,
+	 * which he decided to remove.  If it is not set, then
+	 * the card is in add-in mode, and we're in a machine where
+	 * the bus is set up by 'others'.
+	 *
+	 * We should therefore not mess about with the mapping in
+	 * anyway, and we should not be using the virt_to_bus functions
+	 * that exist in the HOST architecture mode (since they assume
+	 * a fixed mapping).
+	 *
+	 * Instead, you should be using ADDIN mode, which allows for
+	 * this situation.  This does assume that you have correctly
+	 * initialised the PCI bus, which you must have done to get
+	 * your PC booted.
+	 *
+	 * Unfortunately, he seems to be blind to this.  I guess he'll
+	 * also remove all this.
+	 *
+	 * And THIS COMMENT STAYS, even if this gets patched, thank
+	 * you.
+	 */
+
+	/*
+	 * Map our SDRAM at a known address in PCI space, just in case
+	 * the firmware had other ideas.  Using a nonzero base is
+	 * necessary, since some VGA cards forcefully use PCI addresses
+	 * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards).
+	 *
+	 * NOTE! If you need to chec the PCI_CFN bit in the SA110
+	 * control register then you've configured the kernel wrong.
+	 * If you're not using host mode, then DO NOT set
+	 * CONFIG_HOST_FOOTBRIDGE, but use CONFIG_ADDIN_FOOTBRIDGE
+	 * instead.  In this case, you MUST supply some firmware
+	 * to allow your PC to boot, plus we should not modify the
+	 * mappings that the PC BIOS has set up for us.
+	 */
+	*CSR_PCICACHELINESIZE = 0x00002008;
+	*CSR_PCICSRBASE       = 0;
+	*CSR_PCICSRIOBASE     = 0;
+	*CSR_PCISDRAMBASE     = virt_to_bus((void *)PAGE_OFFSET);
+	*CSR_PCIROMBASE       = 0;
+	*CSR_PCICMD           = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+				PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK |
+				PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY |
+				(1 << 31) | (1 << 29) | (1 << 28) | (1 << 24);
+#endif
+
+	/*
+	 * Clear any existing errors - we aren't
+	 * interested in historical data...
+	 */
+	cntl = *CSR_SA110_CNTL & 0xffffde07;
+	*CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR;
+
+	pcibios_init_ebsa285();
 
-	printk("DEC21285 PCI revision %02X\n", *(unsigned char *)0xfe000008);
+	printk(KERN_DEBUG"PCI: DEC21285 revision %02lX\n", *CSR_CLASSREV & 0xff);
 }
 
 __initfunc(void pcibios_fixup_bus(struct pci_bus *bus))
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-a5k.c linux/arch/arm/kernel/dma-a5k.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-a5k.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-a5k.c	Mon Dec  3 20:25:13 2001
@@ -12,7 +12,6 @@
 #include <asm/fiq.h>
 #include <asm/io.h>
 #include <asm/hardware.h>
-#include <asm/pgtable.h>
 
 #include "dma.h"
 
@@ -37,8 +36,9 @@
 	if (channel != DMA_VIRTUAL_FLOPPY)
 		printk("arch_dma_count: invalid channel %d\n", channel);
 	else {
-		extern int floppy_fiqresidual(void);
-		return floppy_fiqresidual();
+		struct pt_regs regs;
+		get_fiq_regs(&regs);
+		return regs.ARM_r9;
 	}
 	return 0;
 }
@@ -48,6 +48,7 @@
 	if (channel != DMA_VIRTUAL_FLOPPY)
 		printk("arch_enable_dma: invalid channel %d\n", channel);
 	else {
+		struct pt_regs regs;
 		void *fiqhandler_start;
 		unsigned int fiqhandler_length;
 		extern void floppy_fiqsetup(unsigned long len, unsigned long addr,
@@ -67,8 +68,10 @@
 			return;
 		}
 		memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length);
-		flush_page_to_ram(0);
-		floppy_fiqsetup(dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE);
+		regs.ARM_r9 = dma->buf.length;
+		regs.ARM_r10 = __bus_to_virt(dma->buf.address);
+		regs.ARM_fp = (int)PCIO_FLOPPYDMABASE;
+		set_fiq_regs(&regs);
 		enable_irq(dma->dma_irq);
 	}
 }
@@ -83,7 +86,12 @@
 	}
 }
 
-__initfunc(void arch_dma_init(dma_t *dma))
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
+}
+
+void __init arch_dma_init(dma_t *dma)
 {
 	dma[DMA_VIRTUAL_FLOPPY].dma_irq = 64;
 }
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-arc.c linux/arch/arm/kernel/dma-arc.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-arc.c	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/kernel/dma-arc.c	Mon Dec  3 20:25:13 2001
@@ -1,10 +1,11 @@
 /*
  * arch/arm/kernel/dma-arc.c
  *
- * Copyright (C) 1998 Dave Gilbert / Russell King
+ * Copyright (C) 1998-1999 Dave Gilbert / Russell King
  *
  * DMA functions specific to Archimedes architecture
  */
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/init.h>
 
@@ -14,8 +15,11 @@
 
 #include "dma.h"
 
-int arch_request_dma(dmach_t channel, dma_t *dma)
+#define DEBUG
+
+int arch_request_dma(dmach_t channel, dma_t *dma, const char * dev_id)
 {
+  printk("arch_request_dma channel=%d F0=%d F1=%d\n",channel,DMA_VIRTUAL_FLOPPY0,DMA_VIRTUAL_FLOPPY1);
 	if (channel == DMA_VIRTUAL_FLOPPY0 ||
 	    channel == DMA_VIRTUAL_FLOPPY1)
 		return 0;
@@ -25,16 +29,13 @@
 
 void arch_free_dma(dmach_t channel, dma_t *dma)
 {
-	if (channel != DMA_VIRTUAL_FLOPPY0 &&
-	    channel != DMA_VIRTUAL_FLOPPY1)
-		return 0;
-	else
-		return -EINVAL;
 }
 
 void arch_enable_dma(dmach_t channel, dma_t *dma)
 {
+  printk("arch_enable_dma channel=%d F0=%d F1=%d\n",channel,DMA_VIRTUAL_FLOPPY0,DMA_VIRTUAL_FLOPPY1);
 	switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD1772
 	case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
 		switch (dma->dma_mode) {
 		case DMA_MODE_READ: /* read */
@@ -96,9 +97,38 @@
 		restore_flags(flags);
 	}
 	break;
+#endif
 	}
 }
 
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+  switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD1772
+    case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
+        extern unsigned int fdc1772_bytestogo;
+
+        /* 10/1/1999 DAG - I presume its the number of bytes left? */
+        return fdc1772_bytestogo;
+      };
+      break;
+
+    case DMA_VIRTUAL_FLOPPY1: { /* Command completed */
+        /* 10/1/1999 DAG - Presume whether there is an outstanding command? */
+        extern unsigned int fdc1772_fdc_int_done;
+
+        return (fdc1772_fdc_int_done==0)?1:0; /* Explicit! If the int done is 0 then 1 int to go */
+      };
+      break;
+
+#endif
+
+    default:
+      printk("dma-arc.c:arch_get_dma_residue called with unknown/unconfigured DMA channel\n");
+      return 0;
+  };
+}
+
 void arch_disable_dma(dmach_t channel, dma_t *dma)
 {
 	if (channel != DMA_VIRTUAL_FLOPPY0 &&
@@ -108,7 +138,12 @@
 		disable_irq(dma->dma_irq);
 }
 
-__initfunc(void arch_dma_init(dma_t *dma))
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
+}
+
+void __init arch_dma_init(dma_t *dma)
 {
 	dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64;
 	dma[DMA_VIRTUAL_FLOPPY1].dma_irq = 65;
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-dummy.c linux/arch/arm/kernel/dma-dummy.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-dummy.c	Fri May  8 00:42:38 1998
+++ linux/arch/arm/kernel/dma-dummy.c	Mon Dec  3 20:25:13 2001
@@ -9,6 +9,10 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 
+#include <asm/spinlock.h>
+
+spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED;
+
 int request_dma(int channel, const char *device_id)
 {
 	return -EINVAL;
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-ebsa285.c linux/arch/arm/kernel/dma-ebsa285.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-ebsa285.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-ebsa285.c	Wed Dec 31 16:00:00 1969
@@ -1,101 +0,0 @@
-/*
- * arch/arm/kernel/dma-ebsa285.c
- *
- * Copyright (C) 1998 Phil Blundell
- *
- * DMA functions specific to EBSA-285/CATS architectures
- *
- * Changelog:
- *  09/11/1998	RMK	Split out ISA DMA functions to dma-isa.c
- */
-
-#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mman.h>
-#include <linux/init.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
-
-#include "dma.h"
-#include "dma-isa.h"
-
-int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
-{
-	switch (channel) {
-	case 0:
-	case 1:	/* 21285 internal channels */
-		return 0;
-
-	case 2 ... 9:
-		if (machine_is_cats())
-			return isa_request_dma(channel - 2, dma, dev_name);
-	}
-
-	return -EINVAL;
-}
-
-void arch_free_dma(dmach_t channel, dma_t *dma)
-{
-	/* nothing to do */
-}
-
-int arch_get_dma_residue(dmach_t channel, dma_t *dma)
-{
-	int residue = 0;
-
-	switch (channel) {
-	case 0:
-	case 1:
-		break;
-#ifdef CONFIG_CATS
-	case 2 ... 9:
-		if (machine_is_cats())
-			residue = isa_get_dma_residue(channel - 2);
-#endif
-	}
-	return residue;
-}
-
-void arch_enable_dma(dmach_t channel, dma_t *dma)
-{
-	switch (channel) {
-	case 0:
-	case 1:
-		/*
-		 * Not yet implemented
-		 */
-		break;
-#ifdef CONFIG_CATS
-	case 2 ... 9:
-		if (machine_is_cats())
-			isa_enable_dma(channel - 2, dma);
-#endif
-	}
-}
-
-void arch_disable_dma(dmach_t channel, dma_t *dma)
-{
-	switch (channel) {
-	case 0:
-	case 1:
-		/*
-		 * Not yet implemented
-		 */
-		break;
-#ifdef CONFIG_CATS
-	case 2 ... 9:
-		if (machine_is_cats())
-			isa_disable_dma(channel - 2, dma);
-#endif
-	}
-}
-
-__initfunc(void arch_dma_init(dma_t *dma))
-{
-	/* Nothing to do */
-}
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-footbridge.c linux/arch/arm/kernel/dma-footbridge.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-footbridge.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/dma-footbridge.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,109 @@
+/*
+ * arch/arm/kernel/dma-ebsa285.c
+ *
+ * Copyright (C) 1998 Phil Blundell
+ *
+ * DMA functions specific to EBSA-285/CATS architectures
+ *
+ * Changelog:
+ *  09-Nov-1998	RMK	Split out ISA DMA functions to dma-isa.c
+ *  17-Mar-1999	RMK	Allow any EBSA285-like architecture to have
+ *			ISA DMA controllers.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+
+#include "dma.h"
+#include "dma-isa.h"
+
+#ifdef CONFIG_ISA_DMA
+static int has_isa_dma;
+#else
+#define has_isa_dma 0
+#endif
+
+int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
+{
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):	/* 21285 internal channels */
+		return 0;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			return isa_request_dma(channel - _ISA_DMA(0), dma, dev_name);
+	}
+
+	return -EINVAL;
+}
+
+void arch_free_dma(dmach_t channel, dma_t *dma)
+{
+	/* nothing to do */
+}
+
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+	int residue = 0;
+
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):
+		break;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			residue = isa_get_dma_residue(channel - _ISA_DMA(0), dma);
+	}
+	return residue;
+}
+
+void arch_enable_dma(dmach_t channel, dma_t *dma)
+{
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):
+		/*
+		 * Not yet implemented
+		 */
+		break;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			isa_enable_dma(channel - _ISA_DMA(0), dma);
+	}
+}
+
+void arch_disable_dma(dmach_t channel, dma_t *dma)
+{
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):
+		/*
+		 * Not yet implemented
+		 */
+		break;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			isa_disable_dma(channel - _ISA_DMA(0), dma);
+	}
+}
+
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
+}
+
+void __init arch_dma_init(dma_t *dma)
+{
+#ifdef CONFIG_ISA_DMA
+	has_isa_dma = isa_init_dma();
+#endif
+}
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-isa.c linux/arch/arm/kernel/dma-isa.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-isa.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-isa.c	Mon Dec  3 20:25:13 2001
@@ -11,6 +11,7 @@
  *  Copyright (C) 1998 Phil Blundell
  */
 #include <linux/sched.h>
+#include <linux/init.h>
 
 #include <asm/dma.h>
 #include <asm/io.h>
@@ -18,6 +19,11 @@
 #include "dma.h"
 #include "dma-isa.h"
 
+#define ISA_DMA_MODE_READ	0x44
+#define ISA_DMA_MODE_WRITE	0x48
+#define ISA_DMA_MODE_CASCADE	0xc0
+#define ISA_DMA_AUTOINIT	0x10
+
 #define ISA_DMA_MASK		0
 #define ISA_DMA_MODE		1
 #define ISA_DMA_CLRFF		2
@@ -40,10 +46,7 @@
 
 int isa_request_dma(int channel, dma_t *dma, const char *dev_name)
 {
-	if (channel != 4)
-		return 0;
-
-	return -EINVAL;
+	return 0;
 }
 
 void isa_free_dma(int channel, dma_t *dma)
@@ -56,25 +59,27 @@
 	unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT];
 	int count;
 
-	count = 1 + inb(io_port) + (inb(io_port) << 8);
+	count = 1 + inb(io_port);
+	count |= inb(io_port) << 8;
 
 	return channel < 4 ? count : (count << 1);
 }
 
 void isa_enable_dma(int channel, dma_t *dma)
 {
-	unsigned long address, length;
-
 	if (dma->invalid) {
+		unsigned long address, length;
+		unsigned int mode;
+
 		address = dma->buf.address;
 		length  = dma->buf.length - 1;
 
-		outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
 		outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]);
+		outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
 
 		if (channel >= 4) {
 			address >>= 1;
-			length = (length >> 1) & 0xfe; /* why &0xfe? */
+			length >>= 1;
 		}
 
 		outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]);
@@ -85,17 +90,31 @@
 		outb(length, isa_dma_port[channel][ISA_DMA_COUNT]);
 		outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]);
 
-		outb(dma->dma_mode | (channel & 3), isa_dma_port[channel][ISA_DMA_MODE]);
+		mode = channel & 3;
 
-		switch (dma->dma_mode) {
+		switch (dma->dma_mode & DMA_MODE_MASK) {
 		case DMA_MODE_READ:
+			mode |= ISA_DMA_MODE_READ;
 			dma_cache_inv(__bus_to_virt(dma->buf.address), dma->buf.length);
 			break;
 
 		case DMA_MODE_WRITE:
+			mode |= ISA_DMA_MODE_WRITE;
 			dma_cache_wback(__bus_to_virt(dma->buf.address), dma->buf.length);
 			break;
+
+		case DMA_MODE_CASCADE:
+			mode |= ISA_DMA_MODE_CASCADE;
+			break;
+
+		default:
+			break;
 		}
+
+		if (dma->dma_mode & DMA_AUTOINIT)
+			mode |= ISA_DMA_AUTOINIT;
+
+		outb(mode, isa_dma_port[channel][ISA_DMA_MODE]);
 		dma->invalid = 0;
 	}
 	outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]);
@@ -104,4 +123,57 @@
 void isa_disable_dma(int channel, dma_t *dma)
 {
 	outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]);
+}
+
+__initfunc(int isa_init_dma(void))
+{
+	int dmac_found;
+
+	outb(0xff, 0x0d);
+	outb(0xff, 0xda);
+
+	outb(0x55, 0x00);
+	outb(0xaa, 0x00);
+
+	dmac_found = inb(0x00) == 0x55 && inb(0x00) == 0xaa;
+
+	if (dmac_found) {
+		int channel;
+
+		for (channel = 0; channel < 8; channel++)
+			isa_disable_dma(channel, NULL);
+
+		outb(0x40, 0x0b);
+		outb(0x41, 0x0b);
+		outb(0x42, 0x0b);
+		outb(0x43, 0x0b);
+
+		outb(0xc0, 0xd6);
+		outb(0x41, 0xd6);
+		outb(0x42, 0xd6);
+		outb(0x43, 0xd6);
+
+		outb(0, 0xd4);
+
+		outb(0x10, 0x08);
+		outb(0x10, 0xd0);
+
+		/*
+		 * Is this correct?  According to
+		 * my documentation, it doesn't
+		 * appear to be.  It should be
+		 * outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
+		 */
+		outb(0x30, 0x40b);
+		outb(0x31, 0x40b);
+		outb(0x32, 0x40b);
+		outb(0x33, 0x40b);
+		outb(0x31, 0x4d6);
+		outb(0x32, 0x4d6);
+		outb(0x33, 0x4d6);
+
+		request_dma(DMA_ISA_CASCADE, "cascade");
+	}
+
+	return dmac_found;
 }
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-isa.h linux/arch/arm/kernel/dma-isa.h
--- linux-2.2.18-pre8/arch/arm/kernel/dma-isa.h	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-isa.h	Mon Dec  3 20:25:13 2001
@@ -23,3 +23,7 @@
  */
 void isa_disable_dma(int channel, dma_t *dma);
 
+/*
+ * Initialise DMA
+ */
+int isa_init_dma(void);
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-rpc.c linux/arch/arm/kernel/dma-rpc.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-rpc.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/dma-rpc.c	Mon Dec  3 20:25:13 2001
@@ -11,10 +11,10 @@
 #include <linux/init.h>
 
 #include <asm/page.h>
-#include <asm/pgtable.h>
 #include <asm/dma.h>
 #include <asm/fiq.h>
 #include <asm/io.h>
+#include <asm/iomd.h>
 #include <asm/hardware.h>
 #include <asm/uaccess.h>
 
@@ -223,8 +223,9 @@
 		break;
 
 	case DMA_VIRTUAL_FLOPPY: {
-		extern int floppy_fiqresidual(void);
-		residue = floppy_fiqresidual();
+		struct pt_regs regs;
+		get_fiq_regs(&regs);
+		return regs.ARM_r9;
 		}
 		break;
 	}
@@ -286,7 +287,6 @@
 		set_fiq_handler(fiqhandler_start, fiqhandler_length);
 		set_fiq_regs(&regs);
 		enable_irq(dma->dma_irq);
-
 		}
 		break;
 
@@ -319,14 +319,56 @@
 	}
 }
 
-__initfunc(void arch_dma_init(dma_t *dma))
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle)
+{
+	int tcr, speed;
+
+	if (cycle < 188)
+		speed = 3;
+	else if (cycle <= 250)
+		speed = 2;
+	else if (cycle < 438)
+		speed = 1;
+	else
+		speed = 0;
+
+	tcr = inb(IOMD_DMATCR);
+	speed &= 3;
+
+	switch (channel) {
+	case DMA_0:
+		tcr = (tcr & ~0x03) | speed;
+		break;
+
+	case DMA_1:
+		tcr = (tcr & ~0x0c) | (speed << 2);
+		break;
+
+	case DMA_2:
+		tcr = (tcr & ~0x30) | (speed << 4);
+		break;
+
+	case DMA_3:
+		tcr = (tcr & ~0xc0) | (speed << 6);
+		break;
+
+	default:
+		break;
+	}
+
+	outb(tcr, IOMD_DMATCR);
+
+	return speed;
+}
+
+void __init arch_dma_init(dma_t *dma)
 {
 	outb(0, IOMD_IO0CR);
 	outb(0, IOMD_IO1CR);
 	outb(0, IOMD_IO2CR);
 	outb(0, IOMD_IO3CR);
 
-//	outb(0xf0, IOMD_DMATCR);
+	outb(0xa0, IOMD_DMATCR);
 
 	dma[0].dma_base = ioaddr(IOMD_IO0CURA);
 	dma[0].dma_irq  = IRQ_DMA0;
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma-vnc.c linux/arch/arm/kernel/dma-vnc.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma-vnc.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma-vnc.c	Wed Dec 31 16:00:00 1969
@@ -1,51 +0,0 @@
-/*
- * arch/arm/kernel/dma-vnc.c
- *
- * Copyright (C) 1998 Russell King
- */
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mman.h>
-#include <linux/init.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
-
-#include "dma.h"
-#include "dma-isa.h"
-
-int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
-{
-	if (channel < 8)
-		return isa_request_dma(channel, dma, dev_name);
-	return -EINVAL;
-}
-
-void arch_free_dma(dmach_t channel, dma_t *dma)
-{
-	isa_free_dma(channel, dma);
-}
-
-int arch_get_dma_residue(dmach_t channel, dma_t *dma)
-{
-	return isa_get_dma_residue(channel, dma);
-}
-
-void arch_enable_dma(dmach_t channel, dma_t *dma)
-{
-	isa_enable_dma(channel, dma);
-}
-
-void arch_disable_dma(dmach_t channel, dma_t *dma)
-{
-	isa_disable_dma(channel, dma);
-}
-
-__initfunc(void arch_dma_init(dma_t *dma))
-{
-	/* Nothing to do */
-}
-
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma.c linux/arch/arm/kernel/dma.c
--- linux-2.2.18-pre8/arch/arm/kernel/dma.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/dma.c	Mon Dec  3 20:25:13 2001
@@ -21,7 +21,6 @@
 #include <linux/init.h>
 
 #include <asm/page.h>
-#include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
@@ -201,6 +200,12 @@
 		printk (KERN_ERR "Trying to disable free DMA%d\n", channel);
 }
 
+void set_dma_speed(dmach_t channel, int cycle_ns)
+{
+	dma_chan[channel].speed =
+	   arch_set_dma_speed(channel, &dma_chan[channel], cycle_ns);
+}
+
 int get_dma_residue(dmach_t channel)
 {
 	return arch_get_dma_residue(channel, &dma_chan[channel]);
@@ -214,8 +219,9 @@
 EXPORT_SYMBOL(set_dma_mode);
 EXPORT_SYMBOL(get_dma_residue);
 EXPORT_SYMBOL(set_dma_sg);
+EXPORT_SYMBOL(set_dma_speed);
 
-__initfunc(void init_dma(void))
+void __init init_dma(void)
 {
 	arch_dma_init(dma_chan);
 }
diff -urN linux-2.2.18-pre8/arch/arm/kernel/dma.h linux/arch/arm/kernel/dma.h
--- linux-2.2.18-pre8/arch/arm/kernel/dma.h	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/kernel/dma.h	Mon Dec  3 20:25:13 2001
@@ -15,6 +15,7 @@
 	unsigned int	active:1;	/* Transfer active		*/
 	unsigned int	invalid:1;	/* Address/Count changed	*/
 	dmamode_t	dma_mode;	/* DMA mode			*/
+	int		speed;		/* DMA speed			*/
 
 	unsigned int	lock;		/* Device is allocated		*/
 	const char	*device_id;	/* Device name			*/
@@ -62,6 +63,15 @@
  * Returns  : Number of bytes left to DMA
  */
 int arch_get_dma_residue(dmach_t channel, dma_t *dma);
+
+/* Prototype: int arch_set_dma_speed(channel, dma, cycle)
+ * Purpose  : Convert a cycle time to a register setting
+ * Params   : channel - DMA channel number
+ *          : dma     - DMA structure for channel
+ *          : cycle   - cycle time in NS
+ * Returns  : setting for 'dma->speed'
+ */
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle);
 
 /* Prototype: void arch_dma_init(dma)
  * Purpose  : Initialise architecture specific DMA
diff -urN linux-2.2.18-pre8/arch/arm/kernel/ecard.c linux/arch/arm/kernel/ecard.c
--- linux-2.2.18-pre8/arch/arm/kernel/ecard.c	Mon Dec 28 11:04:21 1998
+++ linux/arch/arm/kernel/ecard.c	Mon Dec  3 20:25:13 2001
@@ -7,32 +7,42 @@
  *
  * Created from information from Acorns RiscOS3 PRMs
  *
- * 08-Dec-1996	RMK	Added code for the 9'th expansion card - the ether podule slot.
+ * 08-Dec-1996	RMK	Added code for the 9'th expansion card - the ether
+ *			podule slot.
  * 06-May-1997  RMK	Added blacklist for cards whose loader doesn't work.
- * 12-Sep-1997	RMK	Created new handling of interrupt enables/disables - cards can
- *			now register their own routine to control interrupts (recommended).
- * 29-Sep-1997	RMK	Expansion card interrupt hardware not being re-enabled on reset from
- *			Linux. (Caused cards not to respond under RiscOS without hard reset).
+ * 12-Sep-1997	RMK	Created new handling of interrupt enables/disables
+ *			- cards can now register their own routine to control
+ *			interrupts (recommended).
+ * 29-Sep-1997	RMK	Expansion card interrupt hardware not being re-enabled
+ *			on reset from Linux. (Caused cards not to respond
+ *			under RiscOS without hard reset).
  * 15-Feb-1998	RMK	Added DMA support
  * 12-Sep-1998	RMK	Added EASI support
+ * 10-Jan-1999	RMK	Run loaders in a simulated RISC OS environment.
+ * 17-Apr-1999	RMK	Support for EASI Type C cycles.
  */
 
 #define ECARD_C
+#define __KERNEL_SYSCALLS__
 
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
 #include <linux/init.h>
 
-#include <asm/io.h>
-#include <asm/hardware.h>
+#include <asm/dma.h>
 #include <asm/ecard.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/dma.h>
+#include <asm/pgtable.h>
 
 #ifdef CONFIG_ARCH_ARC
 #include <asm/arch/oldlatches.h>
@@ -40,45 +50,420 @@
 #define oldlatch_init()
 #endif
 
-#define BLACKLIST_NAME(m,p,s)	{ m, p, NULL, s }
-#define BLACKLIST_LOADER(m,p,l)	{ m, p, l, NULL }
-#define BLACKLIST_NOLOADER(m,p)	{ m, p, noloader, blacklisted_str }
-#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+enum req {
+	req_readbytes,
+	req_reset
+};
 
-extern unsigned long atomwide_serial_loader[], oak_scsi_loader[], noloader[];
-static const char blacklisted_str[] = "*loader s/w is not 32-bit compliant*";
+struct ecard_request {
+	enum req	req;
+	ecard_t		*ec;
+	unsigned int	address;
+	unsigned int	length;
+	unsigned int	use_loader;
+	void		*buffer;
+};
 
-static const struct expcard_blacklist {
+struct expcard_blacklist {
 	unsigned short	 manufacturer;
 	unsigned short	 product;
-	const loader_t 	 loader;
 	const char	*type;
-} blacklist[] = {
-/* Cards without names */
-    BLACKLIST_NAME(MANU_ACORN,		PROD_ACORN_ETHER1,	"Acorn Ether1"),
-
-/* Cards with corrected loader */
-  BLACKLIST_LOADER(MANU_ATOMWIDE,	PROD_ATOMWIDE_3PSERIAL,	atomwide_serial_loader),
-  BLACKLIST_LOADER(MANU_OAK,		PROD_OAK_SCSI,		oak_scsi_loader),
+};
 
-/* Supported cards with broken loader */
-  { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI, noloader, "AlSystems PowerTec SCSI" },
+static ecard_t *cards;
+static ecard_t *slot_to_expcard[MAX_ECARDS];
+static unsigned int ectcr;
+#ifdef HAS_EXPMASK
+static unsigned int have_expmask;
+#endif
 
-/* Unsupported cards with no loader */
-  BLACKLIST_NOLOADER(MANU_MCS,		PROD_MCS_CONNECT32)
+/* List of descriptions of cards which don't have an extended
+ * identification, or chunk directories containing a description.
+ */
+static const struct expcard_blacklist __init blacklist[] = {
+	{ MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" }
 };
 
+asmlinkage extern int
+ecard_loader_reset(volatile unsigned char *pa, loader_t loader);
+asmlinkage extern int
+ecard_loader_read(int off, volatile unsigned char *pa, loader_t loader);
 extern int setup_arm_irq(int, struct irqaction *);
+extern void do_ecard_IRQ(int, struct pt_regs *);
+
+
+static void
+ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs);
+
+static struct irqaction irqexpansioncard = {
+	ecard_irq_noexpmask, SA_INTERRUPT, 0, "expansion cards", NULL, NULL
+};
+
+static inline unsigned short
+ecard_getu16(unsigned char *v)
+{
+	return v[0] | v[1] << 8;
+}
+
+static inline signed long
+ecard_gets24(unsigned char *v)
+{
+	return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
+}
+
+static inline ecard_t *
+slot_to_ecard(unsigned int slot)
+{
+	return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
+}
 
+/* ===================== Expansion card daemon ======================== */
 /*
- * from linux/arch/arm/kernel/irq.c
+ * Since the loader programs on the expansion cards need to be run
+ * in a specific environment, create a separate task with this
+ * environment up, and pass requests to this task as and when we
+ * need to.
+ *
+ * This should allow 99% of loaders to be called from Linux.
+ *
+ * From a security standpoint, we trust the card vendors.  This
+ * may be a misplaced trust.
  */
-extern void do_ecard_IRQ(int irq, struct pt_regs *);
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+#define POD_INT_ADDR(x)	((volatile unsigned char *)\
+			 ((BUS_ADDR((x)) - IO_BASE) + IO_START))
 
-static ecard_t expcard[MAX_ECARDS];
-static signed char irqno_to_expcard[16];
-static unsigned int ecard_numcards, ecard_numirqcards;
-static unsigned int have_expmask;
+static void
+ecard_task_reset(struct ecard_request *req)
+{
+	if (req->ec == NULL) {
+		ecard_t *ec;
+
+		for (ec = cards; ec; ec = ec->next) {
+			printk(KERN_DEBUG "Resetting card %d\n",
+			       ec->slot_no);
+
+			if (ec->loader)
+				ecard_loader_reset(POD_INT_ADDR(ec->podaddr),
+						   ec->loader);
+		}
+		printk(KERN_DEBUG "All cards reset\n");
+	} else if (req->ec->loader)
+		ecard_loader_reset(POD_INT_ADDR(req->ec->podaddr),
+				   req->ec->loader);
+}
+
+static void
+ecard_task_readbytes(struct ecard_request *req)
+{
+	unsigned char *buf = (unsigned char *)req->buffer;
+	volatile unsigned char *base_addr =
+		(volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr);
+	unsigned int len = req->length;
+
+	if (req->ec->slot_no == 8) {
+		/*
+		 * The card maintains an index which
+		 * increments the address into a 4096-byte
+		 * page on each access.  We need to keep
+		 * track of the counter.
+		 */
+		static unsigned int index;
+		unsigned int offset, page;
+		unsigned char byte = 0; /* keep gcc quiet */
+
+		offset = req->address & 4095;
+		page   = req->address >> 12;
+
+		if (page > 256)
+			return;
+
+		page *= 4;
+
+		if (offset == 0 || index > offset) {
+			/*
+			 * We need to reset the index counter.
+			 */
+			*base_addr = 0;
+			index = 0;
+		}
+
+		while (index <= offset) {
+			byte = base_addr[page];
+			index += 1;
+		}
+
+		while (len--) {
+			*buf++ = byte;
+			if (len) {
+				byte = base_addr[page];
+				index += 1;
+			}
+		}
+	} else {
+		unsigned int off = req->address;
+
+		if (!req->use_loader || !req->ec->loader) {
+			off *= 4;
+			while (len--) {
+				*buf++ = base_addr[off];
+				off += 4;
+			}
+		} else {
+			while(len--) {
+				/*
+				 * The following is required by some
+				 * expansion card loader programs.
+				 */
+				*(unsigned long *)0x108 = 0;
+				*buf++ = ecard_loader_read(off++, base_addr,
+							   req->ec->loader);
+			}
+		}
+	}
+
+}
+
+#ifdef CONFIG_CPU_32
+static pid_t ecard_pid;
+static struct wait_queue *ecard_wait;
+static struct wait_queue *ecard_done;
+static struct ecard_request *ecard_req;
+
+/*
+ * Set up the expansion card daemon's environment.
+ */
+static void
+ecard_init_task(void)
+{
+	/* We want to set up the page tables for the following mapping:
+	 *  Virtual	Physical
+	 *  0x03000000	0x03000000
+	 *  0x03010000	unmapped
+	 *  0x03210000	0x03210000
+	 *  0x03400000	unmapped
+	 *  0x08000000	0x08000000
+	 *  0x10000000	unmapped
+	 *
+	 * FIXME: we don't follow this 100% yet.
+	 */
+	pgd_t *src_pgd, *dst_pgd;
+	unsigned int dst_addr = IO_START;
+
+	src_pgd = pgd_offset(current->mm, IO_BASE);
+	dst_pgd = pgd_offset(current->mm, dst_addr);
+
+	while (dst_addr < IO_START + IO_SIZE) {
+		*dst_pgd++ = *src_pgd++;
+		dst_addr += PGDIR_SIZE;
+	}
+
+	flush_tlb_range(current->mm, IO_START, IO_START + IO_SIZE);
+
+	dst_addr = EASI_START;
+	src_pgd = pgd_offset(current->mm, EASI_BASE);
+	dst_pgd = pgd_offset(current->mm, dst_addr);
+
+	while (dst_addr < EASI_START + EASI_SIZE) {
+		*dst_pgd++ = *src_pgd++;
+		dst_addr += PGDIR_SIZE;
+	}
+
+	flush_tlb_range(current->mm, EASI_START, EASI_START + EASI_SIZE);
+}
+
+static int
+ecard_task(void * unused)
+{
+	current->session = 1;
+	current->pgrp = 1;
+
+	/*
+	 * We don't want /any/ signals, not even SIGKILL
+	 */
+	sigfillset(&current->blocked);
+	sigemptyset(&current->signal);
+
+	strcpy(current->comm, "kecardd");
+
+	/*
+	 * Set up the environment
+	 */
+	ecard_init_task();
+
+	while (1) {
+		struct ecard_request *req;
+
+		do {
+			req = xchg(&ecard_req, NULL);
+
+			if (req == NULL) {
+				sigemptyset(&current->signal);
+				interruptible_sleep_on(&ecard_wait);
+			}
+		} while (req == NULL);
+
+		switch (req->req) {
+		case req_readbytes:
+			ecard_task_readbytes(req);
+			break;
+
+		case req_reset:
+			ecard_task_reset(req);
+			break;
+		}
+		wake_up(&ecard_done);
+	}
+}
+
+/*
+ * Wake the expansion card daemon to action our request.
+ *
+ * FIXME: The test here is not sufficient to detect if the
+ * kcardd is running.
+ */
+static inline void
+ecard_call(struct ecard_request *req)
+{
+	/*
+	 * If we're called from task 0, or from an
+	 * interrupt (will be keyboard interrupt),
+	 * we forcefully set up the memory map, and
+	 * call the loader.  We can't schedule, or
+	 * sleep for this call.
+	 */
+	if ((current == task[0] || in_interrupt()) &&
+	    req->req == req_reset && req->ec == NULL) {
+		ecard_init_task();
+		ecard_task_reset(req);
+	} else {
+		if (ecard_pid <= 0)
+			ecard_pid = kernel_thread(ecard_task, NULL, 0);
+
+		ecard_req = req;
+
+		wake_up(&ecard_wait);
+
+		sleep_on(&ecard_done);
+	}
+}
+#else
+/*
+ * On 26-bit processors, we don't need the kcardd thread to access the
+ * expansion card loaders.  We do it directly.
+ */
+static inline void
+ecard_call(struct ecard_request *req)
+{
+	if (req->req == req_reset)
+		ecard_task_reset(req);
+	else
+		ecard_task_readbytes(req);
+}
+#endif
+
+/* ======================= Mid-level card control ===================== */
+/*
+ * This is called to reset the loaders for each expansion card on reboot.
+ *
+ * This is required to make sure that the card is in the correct state
+ * that RiscOS expects it to be.
+ */
+void
+ecard_reset(int slot)
+{
+	struct ecard_request req;
+
+	req.req = req_reset;
+
+	if (slot < 0)
+		req.ec = NULL;
+	else
+		req.ec = slot_to_ecard(slot);
+
+	ecard_call(&req);
+
+#ifdef HAS_EXPMASK
+	if (have_expmask && slot < 0) {
+		have_expmask |= ~0;
+		EXPMASK_ENABLE = have_expmask;
+	}
+#endif
+}
+
+static void
+ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
+{
+	struct ecard_request req;
+
+	req.req		= req_readbytes;
+	req.ec		= ec;
+	req.address	= off;
+	req.length	= len;
+	req.use_loader	= useld;
+	req.buffer	= addr;
+
+	ecard_call(&req);
+}
+
+int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+{
+	struct ex_chunk_dir excd;
+	int index = 16;
+	int useld = 0;
+
+	if (!ec->cid.cd)
+		return 0;
+
+	while(1) {
+		ecard_readbytes(&excd, ec, index, 8, useld);
+		index += 8;
+		if (c_id(&excd) == 0) {
+			if (!useld && ec->loader) {
+				useld = 1;
+				index = 0;
+				continue;
+			}
+			return 0;
+		}
+		if (c_id(&excd) == 0xf0) { /* link */
+			index = c_start(&excd);
+			continue;
+		}
+		if (c_id(&excd) == 0x80) { /* loader */
+			if (!ec->loader) {
+				ec->loader = (loader_t)kmalloc(c_len(&excd),
+							       GFP_KERNEL);
+				if (ec->loader)
+					ecard_readbytes(ec->loader, ec,
+							(int)c_start(&excd),
+							c_len(&excd), useld);
+				else
+					return 0;
+			}
+			continue;
+		}
+		if (c_id(&excd) == id && num-- == 0)
+			break;
+	}
+
+	if (c_id(&excd) & 0x80) {
+		switch (c_id(&excd) & 0x70) {
+		case 0x70:
+			ecard_readbytes((unsigned char *)excd.d.string, ec,
+					(int)c_start(&excd), c_len(&excd),
+					useld);
+			break;
+		case 0x00:
+			break;
+		}
+	}
+	cd->start_offset = c_start(&excd);
+	memcpy(cd->d.string, excd.d.string, 256);
+	return 1;
+}
+
+/* ======================= Interrupt control ============================ */
 
 static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
 {
@@ -100,6 +485,11 @@
 #endif
 }
 
+static int ecard_def_irq_pending(ecard_t *ec)
+{
+	return !ec->irqmask || ec->irqaddr[0] & ec->irqmask;
+}
+
 static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
 {
 	panic("ecard_def_fiq_enable called - impossible");
@@ -110,11 +500,18 @@
 	panic("ecard_def_fiq_disable called - impossible");
 }
 
+static int ecard_def_fiq_pending(ecard_t *ec)
+{
+	return !ec->fiqmask || ec->fiqaddr[0] & ec->fiqmask;
+}
+
 static expansioncard_ops_t ecard_default_ops = {
 	ecard_def_irq_enable,
 	ecard_def_irq_disable,
+	ecard_def_irq_pending,
 	ecard_def_fiq_enable,
-	ecard_def_fiq_disable
+	ecard_def_fiq_disable,
+	ecard_def_fiq_pending
 };
 
 /*
@@ -125,10 +522,9 @@
  */
 void ecard_enableirq(unsigned int irqnr)
 {
-	irqnr &= 7;
-	if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+	ecard_t *ec = slot_to_ecard(irqnr - 32);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -142,10 +538,9 @@
 
 void ecard_disableirq(unsigned int irqnr)
 {
-	irqnr &= 7;
-	if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+	ecard_t *ec = slot_to_ecard(irqnr - 32);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -156,10 +551,9 @@
 
 void ecard_enablefiq(unsigned int fiqnr)
 {
-	fiqnr &= 7;
-	if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+	ecard_t *ec = slot_to_ecard(fiqnr);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -173,10 +567,9 @@
 
 void ecard_disablefiq(unsigned int fiqnr)
 {
-	fiqnr &= 7;
-	if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+	ecard_t *ec = slot_to_ecard(fiqnr);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -185,41 +578,89 @@
 	}
 }
 
-static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
+static void
+ecard_dump_irq_state(ecard_t *ec)
 {
-	const int num_cards = ecard_numirqcards;
-	int i, called = 0;
+	printk("  %d: %sclaimed, ",
+	       ec->slot_no,
+	       ec->claimed ? "" : "not ");
+
+	if (ec->ops && ec->ops->irqpending &&
+	    ec->ops != &ecard_default_ops)
+		printk("irq %spending\n",
+		       ec->ops->irqpending(ec) ? "" : "not ");
+	else
+		printk("irqaddr %p, mask = %02X, status = %02X\n",
+		       ec->irqaddr, ec->irqmask, *ec->irqaddr);
+}
 
-	for (i = 0; i < num_cards; i++) {
-		if (expcard[i].claimed && expcard[i].irq &&
-		    (!expcard[i].irqmask ||
-		     expcard[i].irqaddr[0] & expcard[i].irqmask)) {
-			do_ecard_IRQ(expcard[i].irq, regs);
-			called ++;
+static void
+ecard_check_lockup(void)
+{
+	static int last, lockup;
+	ecard_t *ec;
+
+	/*
+	 * If the timer interrupt has not run since the last million
+	 * unrecognised expansion card interrupts, then there is
+	 * something seriously wrong.  Disable the expansion card
+	 * interrupts so at least we can continue.
+	 *
+	 * Maybe we ought to start a timer to re-enable them some time
+	 * later?
+	 */
+	if (last == jiffies) {
+		lockup += 1;
+		if (lockup > 1000000) {
+			printk(KERN_ERR "\nInterrupt lockup detected - "
+			       "disabling all expansion card interrupts\n");
+
+			disable_irq(IRQ_EXPANSIONCARD);
+
+			printk("Expansion card IRQ state:\n");
+
+			for (ec = cards; ec; ec = ec->next)
+				ecard_dump_irq_state(ec);
 		}
+	} else
+		lockup = 0;
+
+	/*
+	 * If we did not recognise the source of this interrupt,
+	 * warn the user, but don't flood the user with these messages.
+	 */
+	if (!last || time_after(jiffies, last + 5*HZ)) {
+		last = jiffies;
+		printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
 	}
-	cli();
-	if (called == 0) {
-		static int last, lockup;
+}
 
-		if (last == jiffies) {
-			lockup += 1;
-			if (lockup > 1000000) {
-				printk(KERN_ERR "\nInterrupt lockup detected - disabling expansion card IRQs\n");
-				disable_irq(intr_no);
-				printk("Expansion card IRQ state:\n");
-				for (i = 0; i < num_cards; i++)
-					printk("  %d: %sclaimed, irqaddr = %p, irqmask = %X, status=%X\n", expcard[i].irq - 32,
-						expcard[i].claimed ? "" : "not", expcard[i].irqaddr, expcard[i].irqmask, *expcard[i].irqaddr);
-			}
-		} else
-			lockup = 0;
+static void
+ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
+{
+	ecard_t *ec;
+	int called = 0;
+
+	for (ec = cards; ec; ec = ec->next) {
+		int pending;
+
+		if (!ec->claimed || ec->irq == NO_IRQ || ec->slot_no == 8)
+			continue;
+
+		if (ec->ops && ec->ops->irqpending)
+			pending = ec->ops->irqpending(ec);
+		else
+			pending = ecard_default_ops.irqpending(ec);
 
-		if (!last || time_after(jiffies, last + 5*HZ)) {
-			last = jiffies;
-			printk(KERN_ERR "\nUnrecognised interrupt from backplane\n");
+		if (pending) {
+			do_ecard_IRQ(ec->irq, regs);
+			called ++;
 		}
 	}
+	cli();
+
+	if (called == 0)
+		ecard_check_lockup();
 }
 
 #ifdef HAS_EXPMASK
@@ -234,31 +675,35 @@
 	0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
 };
 
-static void ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
+static void
+ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
 {
 	const unsigned int statusmask = 15;
 	unsigned int status;
 
 	status = EXPMASK_STATUS & statusmask;
 	if (status) {
-		unsigned int irqno;
+		unsigned int slot;
 		ecard_t *ec;
 again:
-		irqno = first_set[status];
-		ec = expcard + irqno_to_expcard[irqno];
+		slot = first_set[status];
+		ec = slot_to_ecard(slot);
 		if (ec->claimed) {
 			unsigned int oldexpmask;
 			/*
-			 * this ugly code is so that we can operate a prioritorising system.
+			 * this ugly code is so that we can operate a
+			 * prioritorising system:
+			 *
 			 * Card 0 	highest priority
 			 * Card 1
 			 * Card 2
 			 * Card 3	lowest priority
+			 *
 			 * Serial cards should go in 0/1, ethernet/scsi in 2/3
 			 * otherwise you will lose serial data at high speeds!
 			 */
 			oldexpmask = have_expmask;
-			EXPMASK_ENABLE = (have_expmask &= priority_masks[irqno]);
+			EXPMASK_ENABLE = (have_expmask &= priority_masks[slot]);
 			sti();
 			do_ecard_IRQ(ec->irq, regs);
 			cli();
@@ -267,15 +712,18 @@
 			if (status)
 				goto again;
 		} else {
-			printk(KERN_WARNING "card%d: interrupt from unclaimed card???\n", irqno);
-			EXPMASK_ENABLE = (have_expmask &= ~(1 << irqno));
+			printk(KERN_WARNING "card%d: interrupt from unclaimed "
+			       "card???\n", slot);
+			EXPMASK_ENABLE = (have_expmask &= ~(1 << slot));
 		}
 	} else
 		printk(KERN_WARNING "Wild interrupt from backplane (masks)\n");
 }
 
-static int ecard_checkirqhw(void)
+__initfunc(static void
+ecard_probeirqhw(void))
 {
+	ecard_t *ec;
 	int found;
 
 	EXPMASK_ENABLE = 0x00;
@@ -283,62 +731,78 @@
 	found = ((EXPMASK_STATUS & 15) == 0);
 	EXPMASK_ENABLE = 0xff;
 
-	return found;
+	if (!found)
+		return;
+
+	printk(KERN_DEBUG "Expansion card interrupt "
+	       "management hardware found\n");
+
+	irqexpansioncard.handler = ecard_irq_expmask;
+
+	/* for each card present, set a bit to '1' */
+	have_expmask = 0x80000000;
+
+	for (ec = cards; ec; ec = ec->next)
+		have_expmask |= 1 << ec->slot_no;
+
+	EXPMASK_ENABLE = have_expmask;
 }
+#else
+#define ecard_probeirqhw()
 #endif
 
-static void ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
+#ifndef IO_EC_MEMC8_BASE
+#define IO_EC_MEMC8_BASE 0
+#endif
+
+unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
 {
-	extern int ecard_loader_read(int off, volatile unsigned int pa, loader_t loader);
-	unsigned char *a = (unsigned char *)addr;
+	unsigned long address = 0;
+	int slot = ec->slot_no;
 
-	if (ec->slot_no == 8) {
-		static unsigned int lowaddress;
-		unsigned int laddr, haddr;
-		unsigned char byte = 0; /* keep gcc quiet */
+	if (ec->slot_no == 8)
+		return IO_EC_MEMC8_BASE;
 
-		laddr = off & 4095;	/* number of bytes to read from offset + base addr */
-		haddr = off >> 12;	/* offset into card from base addr */
+	ectcr &= ~(1 << slot);
 
-		if (haddr > 256)
-			return;
+	switch (type) {
+	case ECARD_MEMC:
+		if (slot < 4)
+			address = IO_EC_MEMC_BASE + (slot << 12);
+		break;
 
-		/*
-		 * If we require a low address or address 0, then reset, and start again...
-		 */
-		if (!off || lowaddress > laddr) {
-			outb(0, ec->podaddr);
-			lowaddress = 0;
-		}
-		while (lowaddress <= laddr) {
-			byte = inb(ec->podaddr + haddr);
-			lowaddress += 1;
-		}
-		while (len--) {
-			*a++ = byte;
-			if (len) {
-				byte = inb(ec->podaddr + haddr);
-				lowaddress += 1;
-			}
-		}
-	} else {
-		if (!useld || !ec->loader) {
-			while(len--)
-				*a++ = inb(ec->podaddr + (off++));
-		} else {
-			while(len--) {
-				*(unsigned long *)0x108 = 0; /* hack for some loaders!!! */
-				*a++ = ecard_loader_read(off++, BUS_ADDR(ec->podaddr), ec->loader);
-			}
-		}
+	case ECARD_IOC:
+		if (slot < 4)
+			address = IO_EC_IOC_BASE + (slot << 12);
+#ifdef IO_EC_IOC4_BASE
+		else
+			address = IO_EC_IOC4_BASE + ((slot - 4) << 12);
+#endif
+		if (address)
+			address +=  speed << 17;
+		break;
+
+#ifdef IO_EC_EASI_BASE
+	case ECARD_EASI:
+		address = IO_EC_EASI_BASE + (slot << 22);
+		if (speed == ECARD_FAST)
+			ectcr |= 1 << slot;
+		break;
+#endif
 	}
+
+#ifdef IOMD_ECTCR
+	outb(ectcr, IOMD_ECTCR);
+#endif
+	return address;
 }
 
 static int ecard_prints(char *buffer, ecard_t *ec)
 {
 	char *start = buffer;
 
-	buffer += sprintf(buffer, "\n  %d: ", ec->slot_no);
+	buffer += sprintf(buffer, "  %d: %s ", ec->slot_no,
+			  ec->type == ECARD_EASI ? "EASI" : "    ");
 
 	if (ec->cid.id == 0) {
 		struct in_chunk_dir incd;
@@ -346,63 +810,59 @@
 		buffer += sprintf(buffer, "[%04X:%04X] ",
 			ec->cid.manufacturer, ec->cid.product);
 
-		if (!ec->card_desc && ec->cid.is && ec->cid.cd &&
+		if (!ec->card_desc && ec->cid.cd &&
 		    ecard_readchunk(&incd, ec, 0xf5, 0))
 			ec->card_desc = incd.d.string;
 
 		if (!ec->card_desc)
 			ec->card_desc = "*unknown*";
 
-		buffer += sprintf(buffer, "%s", ec->card_desc);
+		buffer += sprintf(buffer, "%s\n", ec->card_desc);
 	} else
-		buffer += sprintf(buffer, "Simple card %d", ec->cid.id);
+		buffer += sprintf(buffer, "Simple card %d\n", ec->cid.id);
 
 	return buffer - start;
 }
 
-static inline unsigned short ecard_getu16(unsigned char *v)
-{
-	return v[0] | v[1] << 8;
-}
-
-static inline signed long ecard_gets24(unsigned char *v)
-{
-	return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
-}
-
 /*
  * Probe for an expansion card.
  *
  * If bit 1 of the first byte of the card is set, then the
  * card does not exist.
  */
-__initfunc(static int ecard_probe(int card, int freeslot, card_type_t type))
+__initfunc(static int
+ecard_probe(int slot, card_type_t type))
 {
-	ecard_t *ec = expcard + freeslot;
+	ecard_t **ecp;
+	ecard_t *ec;
 	struct ex_ecid cid;
 	char buffer[200];
-	int i;
+	int i, rc = -ENOMEM;
+
+	ec = kmalloc(sizeof(ecard_t), GFP_KERNEL);
+
+	if (!ec)
+		goto nodev;
 
-	irqno_to_expcard[card] = -1;
+	memset(ec, 0, sizeof(ecard_t));
 
-	ec->slot_no	= card;
+	ec->slot_no	= slot;
+	ec->type	= type;
 	ec->irq		= NO_IRQ;
 	ec->fiq		= NO_IRQ;
 	ec->dma		= NO_DMA;
 	ec->card_desc	= NULL;
 	ec->ops		= &ecard_default_ops;
 
+	rc = -ENODEV;
 	if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0)
-		return 0;
+		goto nodev;
 
 	cid.r_zero = 1;
 	ecard_readbytes(&cid, ec, 0, 16, 0);
 	if (cid.r_zero)
-		return 0;
+		goto nodev;
 
-	irqno_to_expcard[card] = freeslot;
-
-	ec->type	= type;
 	ec->cid.id	= cid.r_id;
 	ec->cid.cd	= cid.r_cd;
 	ec->cid.is	= cid.r_is;
@@ -415,9 +875,9 @@
 	ec->cid.fiqmask = cid.r_fiqmask;
 	ec->cid.fiqoff  = ecard_gets24(cid.r_fiqoff);
 	ec->fiqaddr	=
-	ec->irqaddr	= (unsigned char *)BUS_ADDR(ec->podaddr);
+	ec->irqaddr	= (unsigned char *)ioaddr(ec->podaddr);
 
-	if (ec->cid.cd && ec->cid.is) {
+	if (ec->cid.is) {
 		ec->irqmask = ec->cid.irqmask;
 		ec->irqaddr += ec->cid.irqoff;
 		ec->fiqmask = ec->cid.fiqmask;
@@ -430,88 +890,69 @@
 	for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++)
 		if (blacklist[i].manufacturer == ec->cid.manufacturer &&
 		    blacklist[i].product == ec->cid.product) {
-			ec->loader = blacklist[i].loader;
 			ec->card_desc = blacklist[i].type;
 			break;
 		}
 
-	ecard_prints(buffer, ec);
-	printk("%s", buffer);
-
-	ec->irq = 32 + card;
+	ec->irq = 32 + slot;
 #ifdef IO_EC_MEMC8_BASE
-	if (card == 8)
+	if (slot == 8)
 		ec->irq = 11;
 #endif
 #ifdef CONFIG_ARCH_RPC
 	/* On RiscPC, only first two slots have DMA capability */
-	if (card < 2)
-		ec->dma = 2 + card;
+	if (slot < 2)
+		ec->dma = 2 + slot;
 #endif
 #if 0	/* We don't support FIQs on expansion cards at the moment */
-	ec->fiq = 96 + card;
+	ec->fiq = 96 + slot;
 #endif
 
-	return 1;
-}
+	rc = 0;
 
-/*
- * This is called to reset the loaders for each expansion card on reboot.
- *
- * This is required to make sure that the card is in the correct state
- * that RiscOS expects it to be.
- */
-void ecard_reset(int card)
-{
-	extern int ecard_loader_reset(volatile unsigned int pa, loader_t loader);
+	for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
 
-	if (card >= ecard_numcards)
-		return;
-    
-	if (card < 0) {
-		for (card = 0; card < ecard_numcards; card++)
-			if (expcard[card].loader)
-				ecard_loader_reset(BUS_ADDR(expcard[card].podaddr),
-							expcard[card].loader);
-	} else
-		if (expcard[card].loader)
-			ecard_loader_reset(BUS_ADDR(expcard[card].podaddr),
-						expcard[card].loader);
+	*ecp = ec;
 
-#ifdef HAS_EXPMASK
-	if (have_expmask) {
-		have_expmask |= ~0;
-		EXPMASK_ENABLE = have_expmask;
+nodev:
+	if (rc && ec)
+		kfree(ec);
+	else {
+		slot_to_expcard[slot] = ec;
+
+		ecard_prints(buffer, ec);
+		printk("%s", buffer);
 	}
-#endif
+	return rc;
 }
 
-static unsigned int ecard_startcard;
+static ecard_t *finding_pos;
 
 void ecard_startfind(void)
 {
-	ecard_startcard = 0;
+	finding_pos = NULL;
 }
 
 ecard_t *ecard_find(int cid, const card_ids *cids)
 {
-	int card;
+	if (!finding_pos)
+		finding_pos = cards;
+	else
+		finding_pos = finding_pos->next;
+
+	for (; finding_pos; finding_pos = finding_pos->next) {
+		if (finding_pos->claimed)
+			continue;
 
-	if (!cids) {
-		for (card = ecard_startcard; card < ecard_numcards; card++)
-			if (!expcard[card].claimed &&
-			    (expcard[card].cid.id ^ cid) == 0)
+		if (!cids) {
+			if ((finding_pos->cid.id ^ cid) == 0)
 				break;
-	} else {
-		for (card = ecard_startcard; card < ecard_numcards; card++) {
+		} else {
 			unsigned int manufacturer, product;
 			int i;
 
-			if (expcard[card].claimed)
-				continue;
-
-			manufacturer = expcard[card].cid.manufacturer;
-			product = expcard[card].cid.product;
+			manufacturer = finding_pos->cid.manufacturer;
+			product = finding_pos->cid.product;
 
 			for (i = 0; cids[i].manufacturer != 65535; i++)
 				if (manufacturer == cids[i].manufacturer &&
@@ -523,111 +964,24 @@
 		}
 	}
 
-	ecard_startcard = card + 1;
-
-	return card < ecard_numcards ? &expcard[card] : NULL;
+	return finding_pos;
 }
 
-int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+__initfunc(static void ecard_free_all(void))
 {
-	struct ex_chunk_dir excd;
-	int index = 16;
-	int useld = 0;
+	ecard_t *ec, *ecn;
 
-	if (!ec->cid.is || !ec->cid.cd)
-		return 0;
-
-	while(1) {
-		ecard_readbytes(&excd, ec, index, 8, useld);
-		index += 8;
-		if (c_id(&excd) == 0) {
-			if (!useld && ec->loader) {
-				useld = 1;
-				index = 0;
-				continue;
-			}
-			return 0;
-		}
-		if (c_id(&excd) == 0xf0) { /* link */
-			index = c_start(&excd);
-			continue;
-		}
-		if (c_id(&excd) == 0x80) { /* loader */
-			if (!ec->loader) {
-				ec->loader = (loader_t)kmalloc(c_len(&excd), GFP_KERNEL);
-				ecard_readbytes(ec->loader, ec, (int)c_start(&excd), c_len(&excd), useld);
-			}
-			continue;
-		}
-		if (c_id(&excd) == id && num-- == 0)
-			break;
-	}
+	for (ec = cards; ec; ec = ecn) {
+		ecn = ec->next;
 
-	if (c_id(&excd) & 0x80) {
-		switch (c_id(&excd) & 0x70) {
-		case 0x70:
-			ecard_readbytes((unsigned char *)excd.d.string, ec,
-					(int)c_start(&excd), c_len(&excd), useld);
-			break;
-		case 0x00:
-			break;
-		}
+		kfree(ec);
 	}
-	cd->start_offset = c_start(&excd);
-	memcpy(cd->d.string, excd.d.string, 256);
-	return 1;
-}
-
-unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
-{
-	switch (ec->slot_no) {
-	case 0 ... 3:
-		switch (type) {
-		case ECARD_MEMC:
-			return IO_EC_MEMC_BASE + (ec->slot_no << 12);
-
-		case ECARD_IOC:
-			return IO_EC_IOC_BASE + (speed << 17) + (ec->slot_no << 12);
-
-#ifdef IO_EC_EASI_BASE
-		case ECARD_EASI:
-			return IO_EC_EASI_BASE + (ec->slot_no << 22);
-#endif
-		}
-		break;
 
-	case 4 ... 7:
-		switch (type) {
-#ifdef IO_EC_IOC4_BASE
-		case ECARD_IOC:
-			return IO_EC_IOC4_BASE + (speed << 17) + ((ec->slot_no - 4) << 12);
-#endif
-#ifdef IO_EC_EASI_BASE
-		case ECARD_EASI:
-			return IO_EC_EASI_BASE + (ec->slot_no << 22);
-#endif
-		default:
-			break;
-		}
-		break;
+	cards = NULL;
 
-#ifdef IO_EC_MEMC8_BASE
-	case 8:
-		return IO_EC_MEMC8_BASE;
-#endif
-	}
-	return 0;
+	memset(slot_to_expcard, 0, sizeof(slot_to_expcard));
 }
 
-static struct irqaction irqexpansioncard = {
-	ecard_irq_noexpmask,
-	SA_INTERRUPT,
-	0,
-	"expansion cards",
-	NULL,
-	NULL
-};
-
 /*
  * Initialise the expansion card system.
  * Locate all hardware - interrupt management and
@@ -635,51 +989,31 @@
  */
 __initfunc(void ecard_init(void))
 {
-	int i, nc = 0;
+	int slot;
 
-	memset(expcard, 0, sizeof(expcard));
-
-#ifdef HAS_EXPMASK
-	if (ecard_checkirqhw()) {
-		printk(KERN_DEBUG "Expansion card interrupt management hardware found\n");
-		irqexpansioncard.handler = ecard_irq_expmask;
-		irqexpansioncard.flags |= SA_IRQNOMASK;
-		have_expmask = -1;
-	}
-#endif
-
-	printk("Installed expansion cards:");
+	oldlatch_init();
 
-	/*
-	 * First of all, probe all cards on the expansion card interrupt line
-	 */
-	for (i = 0; i < 8; i++)
-		if (ecard_probe(i, nc, ECARD_IOC) || ecard_probe(i, nc, ECARD_EASI))
-			nc += 1;
-		else
-			have_expmask &= ~(1<<i);
+	printk("Probing expansion cards: (does not imply support)\n");
 
-	ecard_numirqcards = nc;
+	for (slot = 0; slot < 8; slot ++) {
+		if (ecard_probe(slot, ECARD_EASI) == -ENODEV)
+			ecard_probe(slot, ECARD_IOC);
+	}
 
-	/* Now probe other cards with different interrupt lines
-	 */
 #ifdef IO_EC_MEMC8_BASE
-	if (ecard_probe(8, nc, ECARD_IOC))
-		nc += 1;
+	ecard_probe(8, ECARD_IOC);
 #endif
 
-	printk("\n");
-	ecard_numcards = nc;
+	ecard_probeirqhw();
 
-	if (nc && setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
-		printk("Could not allocate interrupt for expansion cards\n");
-		return;
+	if (setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
+		printk(KERN_ERR "Unable to claim IRQ%d for expansion cards\n",
+		       IRQ_EXPANSIONCARD);
+		ecard_free_all();
 	}
-	
-#ifdef HAS_EXPMASK
-	if (nc && have_expmask)
-		EXPMASK_ENABLE = have_expmask;
-#endif
-
-	oldlatch_init();
 }
+
+EXPORT_SYMBOL(ecard_startfind);
+EXPORT_SYMBOL(ecard_find);
+EXPORT_SYMBOL(ecard_readchunk);
+EXPORT_SYMBOL(ecard_address);
diff -urN linux-2.2.18-pre8/arch/arm/kernel/entry-armo.S linux/arch/arm/kernel/entry-armo.S
--- linux-2.2.18-pre8/arch/arm/kernel/entry-armo.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/entry-armo.S	Mon Dec  3 20:25:13 2001
@@ -159,8 +159,8 @@
 		.macro	restore_user_regs
 		ldmia	sp, {r0 - lr}^
 		mov	r0, r0
-		add	sp, sp, #15*4
-		ldr	lr, [sp], #8
+		ldr	lr, [sp, #15*4]
+		add	sp, sp, #15*4+8
 		movs	pc, lr
 		.endm
 
@@ -226,13 +226,6 @@
 		str	r0, [sp, #S_OLD_R0]	;\
 		mov	fp, #0
 
-#define USER_RESTORE_ALL			\
-		ldmia	sp, {r0 - lr}^		;\
-		mov	r0, r0			;\
-		add	sp, sp, #15*4		;\
-		ldr	lr, [sp], #8		;\
-		movs	pc, lr
-
 #define SVC_RESTORE_ALL				\
 		ldmfd	sp, {r0 - pc}^
 		
@@ -253,7 +246,7 @@
 		mov	r0, r0
 		movs	pc, lr
 
-Lfiqmsg:	.ascii	"*** Unexpeced FIQ\n\0"
+Lfiqmsg:	.ascii	"*** Unexpected FIQ\n\0"
 		.align
 
 .LCfiq:		.word	__temp_fiq
@@ -315,14 +308,14 @@
 		and	r4, r10, #255			@ get offset
 		and	r6, r10, #0x000f0000
 		tst	r10, #0x00800000		@ +/-
-		rsbeq	r4, r4, #0
 		ldr	r5, [sp, r6, lsr #14]		@ Load reg
+		rsbeq	r4, r4, #0
 		add	r5, r5, r4, lsl #2
 		str	r5, [sp, r6, lsr #14]		@ Save reg
 		b	ret_from_exception
 
-wfs_mask_data:	.word	0x0e200110			@ WFS
-		.word	0x0fff0fff
+wfs_mask_data:	.word	0x0e200110			@ WFS/RFS
+		.word	0x0fef0fff
 		.word	0x0d0d0100			@ LDF [sp]/STF [sp]
 		.word	0x0d0b0100			@ LDF [fp]/STF [fp]
 		.word	0x0f0f0f00
@@ -341,8 +334,7 @@
 		save_user_regs
 		teqp	pc, #0x00000003		@ NOT a problem - doesnt change mode
 		mask_pc	r0, lr			@ Address of abort
-		mov	r1, #FAULT_CODE_PREFETCH|FAULT_CODE_USER @ Error code
-		mov	r2, sp			@ Tasks registers
+		mov	r1, sp			@ Tasks registers
 		bl	SYMBOL_NAME(do_PrefetchAbort)
 		teq	r0, #0			@ If non-zero, we believe this abort..
 		bne	ret_from_sys_call
@@ -451,6 +443,7 @@
 		adr	lr, 1b
 		orr	lr, lr, #0x08000003		@ Force SVC
 		bne	do_IRQ
+		mov	r4, #0
 		b	ret_with_reschedule
 
 		irq_prio_table
@@ -562,8 +555,8 @@
 		and	r0, r0, #15 << 2	@ Mask out reg.
 		teq	r0, #15 << 2
 		ldr	r0, [r3, r0]		@ Get register
-		biceq	r0, r0, #PCMASK
 		mov	r1, r4, lsl #20
+		biceq	r0, r0, #PCMASK
 		tst	r4, #1 << 23
 		addne	r0, r0, r1, lsr #20
 		subeq	r0, r0, r1, lsr #20
@@ -578,12 +571,12 @@
 		and	r0, r0, #15 << 2	@ Mask out reg.
 		teq	r0, #15 << 2
 		ldr	r0, [r3, r0]		@ Get register
-		biceq	r0, r0, #PCMASK
 		and	r7, r4, #15
+		biceq	r0, r0, #PCMASK
 		teq	r7, #15			@ Check for PC
 		ldr	r7, [r3, r7, lsl #2]	@ Get Rm
-		biceq	r7, r7, #PCMASK
 		and	r8, r4, #0x60		@ Get shift types
+		biceq	r7, r7, #PCMASK
 		mov	r9, r4, lsr #7		@ Get shift amount
 		and	r9, r9, #31
 		teq	r8, #0
@@ -645,8 +638,8 @@
 		and	r0, r0, #15 << 2	@ Mask out reg.
 		teq	r0, #15 << 2
 		ldr	r0, [r3, r0]		@ Get register
-		biceq	r0, r0, #PCMASK
 		mov	r1, r4, lsl #24		@ Get offset
+		biceq	r0, r0, #PCMASK
 		tst	r4, #1 << 23
 		addne	r0, r0, r1, lsr #24
 		subeq	r0, r0, r1, lsr #24
@@ -656,9 +649,54 @@
 #endif
 		b	SYMBOL_NAME(do_DataAbort)
 
-#include "entry-common.S"
+/*
+ *=============================================================================
+ *		Low-level interface code
+ *-----------------------------------------------------------------------------
+ *		Trap initialisation
+ *-----------------------------------------------------------------------------
+ *
+ * Note - FIQ code has changed.  The default is a couple of words in 0x1c, 0x20
+ * that call _unexp_fiq.  Nowever, we now copy the FIQ routine to 0x1c (removes
+ * some excess cycles).
+ *
+ * What we need to put into 0-0x1c are branches to branch to the kernel.
+ */
 
-		.data
+		.section ".text.init",#alloc,#execinstr
+
+.Ljump_addresses:
+		swi	SYS_ERROR0
+		.word	vector_undefinstr	- 12
+		.word	vector_swi		- 16
+		.word	vector_prefetch		- 20
+		.word	vector_data		- 24
+		.word	vector_addrexcptn	- 28
+		.word	vector_IRQ		- 32
+		.word	_unexp_fiq		- 36
+		b	. + 8
+/*
+ * initialise the trap system
+ */
+ENTRY(trap_init)
+		stmfd	sp!, {r4 - r7, lr}
+		adr	r1, .Ljump_addresses
+		ldmia	r1, {r1 - r7, ip, lr}
+		orr	r2, lr, r2, lsr #2
+		orr	r3, lr, r3, lsr #2
+		orr	r4, lr, r4, lsr #2
+		orr	r5, lr, r5, lsr #2
+		orr	r6, lr, r6, lsr #2
+		orr	r7, lr, r7, lsr #2
+		orr	ip, lr, ip, lsr #2
+		mov	r0, #0
+		stmia	r0, {r1 - r7, ip}
+		ldmfd	sp!, {r4 - r7, pc}^
+
+		.text
+
+#include "entry-common.S"
 
-__temp_irq:	.word	0				@ saved lr_irq
+		.bss
+__temp_irq:	.space	4				@ saved lr_irq
 __temp_fiq:	.space	128
diff -urN linux-2.2.18-pre8/arch/arm/kernel/entry-armv.S linux/arch/arm/kernel/entry-armv.S
--- linux-2.2.18-pre8/arch/arm/kernel/entry-armv.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/entry-armv.S	Mon Dec  3 20:25:13 2001
@@ -19,6 +19,10 @@
 
 #include "../lib/constants.h"
 
+#ifndef MODE_SVC
+#define MODE_SVC 0x13
+#endif
+
 		.text
 
 #define PF_TRACESYS	0x20
@@ -61,8 +65,12 @@
 #define S_R1		4
 #define S_R0		0
 
+#define OFF_CR_ALIGNMENT(x)	cr_alignment - x
+
 #ifdef IOC_BASE
 /* IOC / IOMD based hardware */
+#include <asm/iomd.h>
+
 		.equ	ioc_base_high, IOC_BASE & 0xff000000
 		.equ	ioc_base_low, IOC_BASE & 0x00ff0000
 		.macro	disable_fiq
@@ -186,113 +194,113 @@
 		.byte	 6, 6, 6, 6, 2, 2, 2, 2, 3, 3, 6, 6, 2, 2, 2, 2
 		.endm
 
-#elif defined(CONFIG_ARCH_EBSA285)
+#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
+#include <asm/dec21285.h>
 
 		.macro	disable_fiq
 		.endm
 
+		.equ	irq_mask_pci_err_high, IRQ_MASK_PCI_ERR & 0xff000000
+		.equ	irq_mask_pci_err_low,  IRQ_MASK_PCI_ERR & 0x00ffffff
+		.equ	dc21285_high, ARMCSR_BASE & 0xff000000
+		.equ	dc21285_low, ARMCSR_BASE & 0x00ffffff
+
 		.macro	get_irqnr_and_base, irqnr, irqstat, base
-		mov	r4, #0xfe000000
+		mov	r4, #dc21285_high
+		.if	dc21285_low
+		orr	r4, r4, #dc21285_low
+		.endif
 		ldr	\irqstat, [r4, #0x180]		@ get interrupts
-		mov	\irqnr, #0
-1001:		tst	\irqstat, #1
-		addeq	\irqnr, \irqnr, #1
-		moveq	\irqstat, \irqstat, lsr #1
-		tsteq	\irqnr, #32
-		beq	1001b
-		teq	\irqnr, #32
-		.endm
 
-		.macro	irq_prio_table
-		.endm
+		mov	\irqnr, #IRQ_SDRAMPARITY
+		tst	\irqstat, #IRQ_MASK_SDRAMPARITY
+		bne	1001f
 
-#elif defined(CONFIG_ARCH_NEXUSPCI)
+		tst	\irqstat, #IRQ_MASK_UART_RX
+		movne	\irqnr, #IRQ_CONRX
+		bne	1001f
 
-		.macro	disable_fiq
-		.endm
+		tst	\irqstat, #IRQ_MASK_DMA1
+		movne	\irqnr, #IRQ_DMA1
+		bne	1001f
 
-		.macro	get_irqnr_and_base, irqnr, irqstat, base
-		ldr	r4, =0xffe00000
-		ldr	\irqstat, [r4, #0x180]		@ get interrupts
-		mov	\irqnr, #0
-1001:		tst	\irqstat, #1
-		addeq	\irqnr, \irqnr, #1
-		moveq	\irqstat, \irqstat, lsr #1
-		tsteq	\irqnr, #32
-		beq	1001b
-		teq	\irqnr, #32
-		.endm
+		tst	\irqstat, #IRQ_MASK_DMA2
+		movne	\irqnr, #IRQ_DMA2
+		bne	1001f
 
-		.macro	irq_prio_table
-		.endm
+		tst	\irqstat, #IRQ_MASK_IN0
+		movne	\irqnr, #IRQ_IN0
+		bne	1001f
 
-#elif defined(CONFIG_ARCH_VNC)
+		tst	\irqstat, #IRQ_MASK_IN1
+		movne	\irqnr, #IRQ_IN1
+		bne	1001f
 
-		.macro	disable_fiq
-		.endm
+		tst	\irqstat, #IRQ_MASK_IN2
+		movne	\irqnr, #IRQ_IN2
+		bne	1001f
 
-		.equ	pci_iack_high, PCI_IACK & 0xff000000
-		.equ	pci_iack_low,  PCI_IACK & 0x00ff0000
+		tst	\irqstat, #IRQ_MASK_IN3
+		movne	\irqnr, #IRQ_IN3
+		bne	1001f
 
-		.macro	get_irqnr_and_base, irqnr, irqstat, base
-		mov	r4, #IO_BASE_ARM_CSR
-		ldr	\irqstat, [r4, #CSR_IRQ_STATUS]	@ just show us the unmasked ones
+		tst	\irqstat, #IRQ_MASK_PCI
+		movne	\irqnr, #IRQ_PCI
+		bne	1001f
 
-		@ run through hard priorities
-		@ timer
-		tst	\irqstat, #IRQ_MASK_TIMER0
-		movne	\irqnr, #IRQ_TIMER0
+		tst	\irqstat, #IRQ_MASK_DOORBELLHOST
+		movne	\irqnr, #IRQ_DOORBELLHOST
+		bne     1001f
+	
+		tst	\irqstat, #IRQ_MASK_I2OINPOST
+		movne	\irqnr, #IRQ_I2OINPOST
 		bne	1001f
 
-		@ ether10
-		tst	\irqstat, #IRQ_MASK_ETHER10
-		movne	\irqnr, #IRQ_ETHER10
+		tst	\irqstat, #IRQ_MASK_TIMER1
+		movne	\irqnr, #IRQ_TIMER1
 		bne	1001f
 
-		@ ether100
-		tst	\irqstat, #IRQ_MASK_ETHER100
-		movne	\irqnr, #IRQ_ETHER100
+		tst	\irqstat, #IRQ_MASK_TIMER2
+		movne	\irqnr, #IRQ_TIMER2
 		bne	1001f
 
-		@ video compressor
-		tst	\irqstat, #IRQ_MASK_VIDCOMP
-		movne	\irqnr, #IRQ_VIDCOMP
+		tst	\irqstat, #IRQ_MASK_TIMER3
+		movne	\irqnr, #IRQ_TIMER3
 		bne	1001f
 
-		@ now try all the PIC sources
-		@ determine whether we have an irq
-		tst	\irqstat, #IRQ_MASK_EXTERN_IRQ
-		beq	1002f
-		mov	r4, #pci_iack_high
-		orr	r4, r4, #pci_iack_low
-		ldrb	\irqnr, [r4]			@ get the IACK byte
-		b	1001f
+		tst	\irqstat, #IRQ_MASK_UART_TX
+		movne	\irqnr, #IRQ_CONTX
+		bne	1001f
 
-1002:		@ PCI errors
-		tst	\irqstat, #IRQ_MASK_PCI_ERR
+		tst	\irqstat, #irq_mask_pci_err_high
+		tsteq	\irqstat, #irq_mask_pci_err_low
 		movne	\irqnr, #IRQ_PCI_ERR
 		bne	1001f
+1001:
+		.endm
 
-		@ softint
-		tst	\irqstat, #IRQ_MASK_SOFTIRQ
-		movne	\irqnr, #IRQ_SOFTIRQ
-		bne	1001f
+		.macro	irq_prio_table
+		.endm
 
-		@ debug uart
-		tst	\irqstat, #IRQ_MASK_UART_DEBUG
-		movne	\irqnr, #IRQ_CONRX
-		bne	1001f
+#elif defined(CONFIG_ARCH_NEXUSPCI)
 
-		@ watchdog
-		tst	\irqstat, #IRQ_MASK_WATCHDOG
-		movne	\irqnr, #IRQ_WATCHDOG
+		.macro	disable_fiq
+		.endm
 
-1001:		@ If Z is set, then we will not enter an interrupt
+		.macro	get_irqnr_and_base, irqnr, irqstat, base
+		ldr	r4, =0xffe00000
+		ldr	\irqstat, [r4, #0x180]		@ get interrupts
+		mov	\irqnr, #0
+1001:		tst	\irqstat, #1
+		addeq	\irqnr, \irqnr, #1
+		moveq	\irqstat, \irqstat, lsr #1
+		tsteq	\irqnr, #32
+		beq	1001b
+		teq	\irqnr, #32
 		.endm
 
 		.macro	irq_prio_table
 		.endm
-
 #else
 #error Unknown architecture
 #endif
@@ -306,22 +314,20 @@
 		stmia	sp, {r0 - r12}			@ Calling r0 - r12
 		add	r8, sp, #S_PC
 		stmdb	r8, {sp, lr}^			@ Calling sp, lr
-		mov	r7, r0
+		str	lr, [r8, #0]			@ Save calling PC
 		mrs	r6, spsr
-		mov	r5, lr
-		stmia	r8, {r5, r6, r7}		@ Save calling PC, CPSR, OLD_R0
+		str	r6, [r8, #4]			@ Save CPSR
+		str	r0, [r8, #8]			@ Save OLD_R0
 		.endm
 
 		.macro	restore_user_regs
-		mrs	r0, cpsr			@ disable IRQs
-		orr	r0, r0, #I_BIT
-		msr	cpsr, r0
 		ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
+		msr	cpsr_c, #I_BIT | MODE_SVC	@ disable IRQs
 		msr	spsr, r0			@ save in spsr_svc
 		ldmia	sp, {r0 - lr}^			@ Get calling r0 - lr
 		mov	r0, r0
-		add	sp, sp, #S_PC
-		ldr	lr, [sp], #S_FRAME_SIZE - S_PC	@ Get PC and jump over PC, PSR, OLD_R0
+		ldr	lr, [sp, #S_PC]			@ Get PC
+		add	sp, sp, #S_FRAME_SIZE
 		movs	pc, lr				@ return & move spsr_svc into cpsr
 		.endm
 
@@ -331,16 +337,15 @@
 		/* If we're optimising for StrongARM the resulting code won't 
 		   run on an ARM7 and we can save a couple of instructions.  
 									--pb */
-#ifdef __ARM_ARCH_4__
 		.macro	arm700_bug_check, instr, temp
 		.endm
-#else
 		.macro	arm700_bug_check, instr, temp
+#ifndef __ARM_ARCH_4__
 		and	\temp, \instr, #0x0f000000	@ check for SWI
 		teq	\temp, #0x0f000000
 		bne	.Larm700bug
-		.endm
 #endif
+		.endm
 
 		.macro	enable_irqs, temp
 		mrs	\temp, cpsr
@@ -348,25 +353,6 @@
 		msr	cpsr, \temp
 		.endm
 
-		.macro	initialise_traps_extra
-		mrs	r0, cpsr
-		bic	r0, r0, #31
-		orr	r0, r0, #0xd3
-		msr	cpsr, r0
-		.endm
-
-
-#ifndef __ARM_ARCH_4__
-.Larm700bug:	str	lr, [r8]
-		ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
-		msr	spsr, r0
-		ldmia	sp, {r0 - lr}^			@ Get calling r0 - lr
-		mov	r0, r0
-		add	sp, sp, #S_PC
-		ldr	lr, [sp], #S_FRAME_SIZE - S_PC	@ Get PC and jump over PC, PSR, OLD_R0
-		movs	pc, lr
-#endif
-
 		.macro	get_current_task, rd
 		mov	\rd, sp, lsr #13
 		mov	\rd, \rd, lsl #13
@@ -379,269 +365,124 @@
 		adr\cond	\reg, \label
 		.endm
 
-/*=============================================================================
- * Address exception handler
- *-----------------------------------------------------------------------------
- * These aren't too critical.
- * (they're not supposed to happen, and won't happen in 32-bit mode).
- */
-
-vector_addrexcptn:
-		b	vector_addrexcptn
-
-/*=============================================================================
- * Undefined FIQs
- *-----------------------------------------------------------------------------
- * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
- * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
- * Basically to switch modes, we *HAVE* to clobber one register...  brain
- * damage alert!  I don't think that we can execute any code in here in any
- * other mode than FIQ...  Ok you can switch to another mode, but you can't
- * get out of that mode without clobbering one register.
- */
-_unexp_fiq:	disable_fiq
-		subs	pc, lr, #4
-
-/*=============================================================================
- * Interrupt entry dispatcher
- *-----------------------------------------------------------------------------
- * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
- */
-vector_IRQ:	@
-		@ save mode specific registers
-		@
-		ldr	r13, .LCirq
-		sub	lr, lr, #4
-		str	lr, [r13]			@ save lr_IRQ
-		mrs	lr, spsr
-		str	lr, [r13, #4]			@ save spsr_IRQ
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr			@ switch to SVC mode
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		cmp	lr, #4
-		addlts	pc, pc, lr, lsl #2		@ Changes mode and branches
-		b	__irq_invalid			@  4 - 15
-		b	__irq_usr			@  0  (USR_26 / USR_32)
-		b	__irq_invalid			@  1  (FIQ_26 / FIQ_32)
-		b	__irq_invalid			@  2  (IRQ_26 / IRQ_32)
-		b	__irq_svc			@  3  (SVC_26 / SVC_32)
-/*
- *------------------------------------------------------------------------------------------------
- * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
- */
-.LCirq:		.word	__temp_irq
-.LCund:		.word	__temp_und
-.LCabt:		.word	__temp_abt
+		.macro	alignment_trap, rbase, rtemp, sym
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	\rtemp, [\rbase, #OFF_CR_ALIGNMENT(\sym)]
+		mcr	p15, 0, \rtemp, c1, c0
+#endif
+		.endm
 
-vector_undefinstr:
-		@
-		@ save mode specific registers
-		@
-		ldr	r13, [pc, #.LCund - . - 8]
-		str	lr, [r13]
-		mrs	lr, spsr
-		str	lr, [r13, #4]
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		cmp	lr, #4
-		addlts	pc, pc, lr, lsl #2		@ Changes mode and branches
-		b	__und_invalid			@  4 - 15
-		b	__und_usr			@  0 (USR_26 / USR_32)
-		b	__und_invalid			@  1 (FIQ_26 / FIQ_32)
-		b	__und_invalid			@  2 (IRQ_26 / IRQ_32)
-		b	__und_svc			@  3 (SVC_26 / SVC_32)
-/*
- *------------------------------------------------------------------------------------------------
- * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
- */
-vector_prefetch:
-		@
-		@ save mode specific registers
-		@
-		sub	lr, lr, #4
-		ldr	r13, .LCabt
-		str	lr, [r13]
-		mrs	lr, spsr
-		str	lr, [r13, #4]
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		adds	pc, pc, lr, lsl #2		@ Changes mode and branches
-		b	__pabt_invalid			@  4 - 15
-		b	__pabt_usr			@  0  (USR_26 / USR_32)
-		b	__pabt_invalid			@  1  (FIQ_26 / FIQ_32)
-		b	__pabt_invalid			@  2  (IRQ_26 / IRQ_32)
-		b	__pabt_invalid			@  3  (SVC_26 / SVC_32)
 /*
- *------------------------------------------------------------------------------------------------
- * Data abort dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ * Invalid mode handlers
  */
-vector_data:	@
-		@ save mode specific registers
-		@
-		sub	lr, lr, #8
-		ldr	r13, .LCabt
-		str	lr, [r13]
-		mrs	lr, spsr
-		str	lr, [r13, #4]
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		cmp	lr, #4
-		addlts	pc, pc, lr, lsl #2		@ Changes mode & branches
-		b	__dabt_invalid			@  4 - 15
-		b	__dabt_usr			@  0  (USR_26 / USR_32)
-		b	__dabt_invalid			@  1  (FIQ_26 / FIQ_32)
-		b	__dabt_invalid			@  2  (IRQ_26 / IRQ_32)
-		b	__dabt_svc			@  3  (SVC_26 / SVC_32)
+__pabt_invalid:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
+		stmia	sp, {r0 - lr}			@ Save XXX r0 - lr
+		ldr	r4, .LCabt
+		mov	r1, #BAD_PREFETCH
+		b	1f
 
-/*=============================================================================
- * Prefetch abort handler
- *-----------------------------------------------------------------------------
- */
-pabtmsg:	.ascii	"Pabt: %08lX\n\0"
-		.align
-__pabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
-		stmia	sp, {r0 - r12}			@ Save r0 - r12
-		add	r8, sp, #S_PC
-		stmdb	r8, {sp, lr}^			@ Save sp_usr lr_usr
+__dabt_invalid:	sub	sp, sp, #S_FRAME_SIZE
+		stmia	sp, {r0 - lr}			@ Save SVC r0 - lr [lr *should* be intact]
 		ldr	r4, .LCabt
-		ldmia	r4, {r5 - r7}			@ Get USR pc, cpsr
-		stmia	r8, {r5 - r7}			@ Save USR pc, cpsr, old_r0
+		mov	r1, #BAD_DATA
+		b	1f
 
-		mrs	r7, cpsr			@ Enable interrupts if they were
-		bic	r7, r7, #I_BIT			@ previously
-		msr	cpsr, r7
-		mov	r0, r5				@ address (pc)
-		mov	r1, sp				@ regs
-		bl	SYMBOL_NAME(do_PrefetchAbort)	@ call abort handler
-		teq	r0, #0				@ Does this still apply???
-		bne	ret_from_exception		@ Return from exception
-#ifdef DEBUG_UNDEF
-		adr	r0, t
-		bl	SYMBOL_NAME(printk)
-#endif
-		mov	r0, r5
-		mov	r1, sp
-		and	r2, r6, #31
-		bl	SYMBOL_NAME(do_undefinstr)
-		ldr	lr, [sp, #S_PSR]		@ Get USR cpsr
-		msr	spsr, lr
-		ldmia	sp, {r0 - pc}^			@ Restore USR registers
+__irq_invalid:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate space on stack for frame
+		stmfd	sp, {r0 - lr}			@ Save r0 - lr
+		ldr	r4, .LCirq
+		mov	r1, #BAD_IRQ
+		b	1f
 
-__pabt_invalid:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
-		stmia	sp, {r0 - lr}			@ Save XXX r0 - lr
-		mov	r7, r0				@ OLD R0
-		ldr	r4, .LCabt
-		ldmia	r4, {r5 - r7}			@ Get XXX pc, cpsr
+__und_invalid:	sub	sp, sp, #S_FRAME_SIZE
+		stmia	sp, {r0 - lr}
+		ldr	r4, .LCund
+		mov	r1, #BAD_UNDEFINSTR		@ int reason
+
+1:		mov	fp, #0
+		ldmia	r4, {r5 - r7}			@ Get XXX pc, cpsr, old_r0
 		add	r4, sp, #S_PC
 		stmia	r4, {r5 - r7}			@ Save XXX pc, cpsr, old_r0
-		mov	r0, sp				@ Prefetch aborts are definitely *not*
-		mov	r1, #BAD_PREFETCH		@ allowed in non-user modes.  We cant
-		and	r2, r6, #31			@ recover from this problem.
+		mov	r0, sp
+		and	r2, r6, #31			@ int mode
 		b	SYMBOL_NAME(bad_mode)
 
-#ifdef DEBUG_UNDEF
-t:		.ascii "*** undef ***\r\n\0"
-		.align
-#endif
 
-/*=============================================================================
- * Data abort handler code
- *-----------------------------------------------------------------------------
+wfs_mask_data:	.word	0x0e200110			@ WFS/RFS
+		.word	0x0fef0fff
+		.word	0x0d0d0100			@ LDF [sp]/STF [sp]
+		.word	0x0d0b0100			@ LDF [fp]/STF [fp]
+		.word	0x0f0f0f00
+
+/* We get here if an undefined instruction happens and the floating
+ * point emulator is not present.  If the offending instruction was
+ * a WFS, we just perform a normal return as if we had emulated the
+ * operation.  This is a hack to allow some basic userland binaries
+ * to run so that the emulator module proper can be loaded. --philb
  */
-.LCprocfns:	.word	SYMBOL_NAME(processor)
+fpe_not_present:
+		adr	r10, wfs_mask_data
+		ldmia	r10, {r4, r5, r6, r7, r8}
+		ldr	r10, [sp, #S_PC]		@ Load PC
+		sub	r10, r10, #-4
+		mask_pc	r10, r10
+		ldrt	r10, [r10]			@ get instruction
+		and	r5, r10, r5
+		teq	r5, r4				@ Is it WFS?
+		moveq	pc, r9
+		and	r5, r10, r8
+		teq	r5, r6				@ Is it LDF/STF on sp or fp?
+		teqne	r5, r7
+		movne	pc, lr
+		tst	r10, #0x00200000		@ Does it have WB
+		moveq	pc, r9
+		and	r4, r10, #255			@ get offset
+		and	r6, r10, #0x000f0000
+		tst	r10, #0x00800000		@ +/-
+		ldr	r5, [sp, r6, lsr #14]		@ Load reg
+		rsbeq	r4, r4, #0
+		add	r5, r5, r4, lsl #2
+		str	r5, [sp, r6, lsr #14]		@ Save reg
+		mov	pc, r9
 
-__dabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
-		stmia	sp, {r0 - r12}			@ save r0 - r12
-		add	r3, sp, #S_PC
-		stmdb	r3, {sp, lr}^
-		ldr	r0, .LCabt
-		ldmia	r0, {r0 - r2}			@ Get USR pc, cpsr
-		stmia	r3, {r0 - r2}			@ Save USR pc, cpsr, old_r0
-		mov	fp, #0
-		mrs	r2, cpsr			@ Enable interrupts if they were
-		bic	r2, r2, #I_BIT			@ previously
-		msr	cpsr, r2
-		ldr	r2, .LCprocfns
-		mov	lr, pc
-		ldr	pc, [r2, #8]			@ call processor specific code
-		mov	r3, sp
-		bl	SYMBOL_NAME(do_DataAbort)
-		b	ret_from_sys_call
-
-__dabt_svc:	sub	sp, sp, #S_FRAME_SIZE
+/*
+ * SVC mode handlers
+ */
+		.align	5
+__dabt_svc:	sub	sp, sp, #S_FRAME_SIZE
 		stmia	sp, {r0 - r12}			@ save r0 - r12
 		ldr	r2, .LCabt
 		add	r0, sp, #S_FRAME_SIZE
+		ldmia	r2, {r2 - r4}			@ get pc, cpsr
 		add	r5, sp, #S_SP
 		mov	r1, lr
-		ldmia	r2, {r2 - r4}			@ get pc, cpsr
 		stmia	r5, {r0 - r4}			@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+		mrs	r9, cpsr			@ Enable interrupts if they were
 		tst	r3, #I_BIT
-		mrseq	r0, cpsr			@ Enable interrupts if they were
-		biceq	r0, r0, #I_BIT			@ previously
-		msreq	cpsr, r0
+		biceq	r9, r9, #I_BIT			@ previously
 		mov	r0, r2
+/*
+ * This routine must not corrupt r9
+ */
 		ldr	r2, .LCprocfns
 		mov	lr, pc
 		ldr	pc, [r2, #8]			@ call processor specific code
+		msr	cpsr_c, r9
 		mov	r3, sp
 		bl	SYMBOL_NAME(do_DataAbort)
+		msr	cpsr_c, #I_BIT | MODE_SVC
 		ldr	r0, [sp, #S_PSR]
 		msr	spsr, r0
 		ldmia	sp, {r0 - pc}^			@ load r0 - pc, cpsr
 
-__dabt_invalid:	sub	sp, sp, #S_FRAME_SIZE
-		stmia	sp, {r0 - lr}			@ Save SVC r0 - lr [lr *should* be intact]
-		mov	r7, r0
-		ldr	r4, .LCabt
-		ldmia	r4, {r5, r6}			@ Get SVC pc, cpsr
-		add	r4, sp, #S_PC
-		stmia	r4, {r5, r6, r7}		@ Save SVC pc, cpsr, old_r0
-		mov	r0, sp
-		mov	r1, #BAD_DATA
-		and	r2, r6, #31
-		b	SYMBOL_NAME(bad_mode)
-
-/*=============================================================================
- * Interrupt (IRQ) handler
- *-----------------------------------------------------------------------------
- */
-__irq_usr:	sub	sp, sp, #S_FRAME_SIZE
+		.align	5
+__irq_svc:	sub	sp, sp, #S_FRAME_SIZE
 		stmia	sp, {r0 - r12}			@ save r0 - r12
-		add	r8, sp, #S_PC
-		stmdb	r8, {sp, lr}^
-		ldr	r4, .LCirq
-		ldmia	r4, {r5 - r7}			@ get saved PC, SPSR
-		stmia	r8, {r5 - r7}			@ save pc, psr, old_r0
+		ldr	r7, .LCirq
+		add	r5, sp, #S_FRAME_SIZE
+		ldmia	r7, {r7 - r9}
+		add	r4, sp, #S_SP
+		mov	r6, lr
+		stmia	r4, {r5, r6, r7, r8, r9}	@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
 1:		get_irqnr_and_base r0, r6, r5
 		movne	r1, sp
 		@
@@ -649,148 +490,388 @@
 		@
 		adrsvc	ne, lr, 1b
 		bne	do_IRQ
-		b	ret_with_reschedule
-
-		irq_prio_table
+		ldr	r0, [sp, #S_PSR]		@ irqs are already disabled
+		msr	spsr, r0
+		ldmia	sp, {r0 - pc}^			@ load r0 - pc, cpsr
 
-__irq_svc:	sub	sp, sp, #S_FRAME_SIZE
+		.align	5
+__und_svc:	sub	sp, sp, #S_FRAME_SIZE
 		stmia	sp, {r0 - r12}			@ save r0 - r12
+		ldr	r7, .LCund
 		mov	r6, lr
-		ldr	r7, .LCirq
 		ldmia	r7, {r7 - r9}
 		add	r5, sp, #S_FRAME_SIZE
 		add	r4, sp, #S_SP
-		stmia	r4, {r5, r6, r7, r8, r9}	@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+		stmia	r4, {r5 - r9}			@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+
+		adrsvc	al, r9, 1f			@ r9  = normal FP return
+		bl	call_fpe			@ lr  = undefined instr return
+
+		mov	r0, r5				@ unsigned long pc
+		mov	r1, sp				@ struct pt_regs *regs
+		bl	SYMBOL_NAME(do_undefinstr)
+
+1:		msr	cpsr_c, #I_BIT | MODE_SVC
+		ldr	lr, [sp, #S_PSR]		@ Get SVC cpsr
+		msr	spsr, lr
+		ldmia	sp, {r0 - pc}^			@ Restore SVC registers
+
+		.align	5
+.LCirq:		.word	__temp_irq
+.LCund:		.word	__temp_und
+.LCabt:		.word	__temp_abt
+.LCprocfns:	.word	SYMBOL_NAME(processor)
+.LCfp:		.word	SYMBOL_NAME(fp_enter)
+#ifdef CONFIG_ALIGNMENT_TRAP
+.LCswi:		.word	SYMBOL_NAME(cr_alignment)
+#endif
+
+		irq_prio_table
+
+/*
+ * User mode handlers
+ */
+		.align	5
+__dabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
+		stmia	sp, {r0 - r12}			@ save r0 - r12
+		ldr	r4, .LCabt
+		add	r3, sp, #S_PC
+		ldmia	r4, {r0 - r2}			@ Get USR pc, cpsr
+		stmia	r3, {r0 - r2}			@ Save USR pc, cpsr, old_r0
+		stmdb	r3, {sp, lr}^
+		alignment_trap r4, r7, __temp_abt
+		mov	fp, #0
+		ldr	r2, .LCprocfns
+		mov	lr, pc
+		ldr	pc, [r2, #8]			@ call processor specific code
+		msr	cpsr_c, #MODE_SVC		@ Enable interrupts
+		mov	r3, sp
+		adrsvc	al, lr, ret_from_sys_call
+		b	SYMBOL_NAME(do_DataAbort)
+
+		.align	5
+__irq_usr:	sub	sp, sp, #S_FRAME_SIZE
+		stmia	sp, {r0 - r12}			@ save r0 - r12
+		ldr	r4, .LCirq
+		add	r8, sp, #S_PC
+		ldmia	r4, {r5 - r7}			@ get saved PC, SPSR
+		stmia	r8, {r5 - r7}			@ save pc, psr, old_r0
+		stmdb	r8, {sp, lr}^
+		alignment_trap r4, r7, __temp_irq
+		mov	fp, #0
 1:		get_irqnr_and_base r0, r6, r5
 		movne	r1, sp
+		adrsvc	ne, lr, 1b
 		@
 		@ routine called with r0 = irq number, r1 = struct pt_regs *
 		@
-		adrsvc	ne, lr, 1b
 		bne	do_IRQ
-		ldr	r0, [sp, #S_PSR]
-		msr	spsr, r0
-		ldmia	sp, {r0 - pc}^			@ load r0 - pc, cpsr
-
-__irq_invalid:	sub	sp, sp, #S_FRAME_SIZE	@ Allocate space on stack for frame
-		stmfd	sp, {r0 - lr}		@ Save r0 - lr
-		mov	r7, #-1
-		ldr	r4, .LCirq
-		ldmia	r4, {r5, r6}		@ get saved pc, psr
-		add	r4, sp, #S_PC
-		stmia	r4, {r5, r6, r7}
-		mov	fp, #0
-		mov	r0, sp
-		mov	r1, #BAD_IRQ
-		b	SYMBOL_NAME(bad_mode)
-
-/*=============================================================================
- * Undefined instruction handler
- *-----------------------------------------------------------------------------
- * Handles floating point instructions
- */
-.LC2:		.word	SYMBOL_NAME(fp_enter)
+		mov	r4, #0
+		b	ret_with_reschedule
 
+		.align	5
 __und_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
 		stmia	sp, {r0 - r12}			@ Save r0 - r12
-		add	r8, sp, #S_PC
-		stmdb	r8, {sp, lr}^			@ Save user r0 - r12
 		ldr	r4, .LCund
+		add	r8, sp, #S_PC
 		ldmia	r4, {r5 - r7}
 		stmia	r8, {r5 - r7}			@ Save USR pc, cpsr, old_r0
+		stmdb	r8, {sp, lr}^			@ Save user r0 - r12
+		alignment_trap r4, r7, __temp_und
 		mov	fp, #0
-
-		adrsvc	al, r9, ret_from_exception	@ r9  = normal FP return
+		adrsvc	al, r9, ret_from_sys_call	@ r9  = normal FP return
 		adrsvc	al, lr, fpundefinstr		@ lr  = undefined instr return
 
-1:		get_current_task r10
+call_fpe:	get_current_task r10
 		mov	r8, #1
 		strb	r8, [r10, #TSK_USED_MATH]	@ set current->used_math
+		ldr	r4, .LCfp
 		add	r10, r10, #TSS_FPESAVE		@ r10 = workspace
-		ldr	r4, .LC2
 		ldr	pc, [r4]			@ Call FP module USR entry point
 
-__und_svc:	sub	sp, sp, #S_FRAME_SIZE
-		stmia	sp, {r0 - r12}			@ save r0 - r12
-		mov	r6, lr
-		ldr	r7, .LCund
-		ldmia	r7, {r7 - r9}
-		add	r5, sp, #S_FRAME_SIZE
-		add	r4, sp, #S_SP
-		stmia	r4, {r5 - r9}			@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
-
-		adrsvc	al, r9, 3f			@ r9  = normal FP return
-		bl	1b				@ lr  = undefined instr return
+fpundefinstr:	mov	r0, lr
+		mov	r1, sp
+		msr	cpsr_c, #MODE_SVC
+		adrsvc	al, lr, ret_from_sys_call
+		b	SYMBOL_NAME(do_undefinstr)
 
-		mov	r0, r5				@ unsigned long pc
-		mov	r1, sp				@ struct pt_regs *regs
+		.align	5
+__pabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
+		stmia	sp, {r0 - r12}			@ Save r0 - r12
+		ldr	r4, .LCabt
+		add	r8, sp, #S_PC
+		ldmia	r4, {r5 - r7}			@ Get USR pc, cpsr
+		stmia	r8, {r5 - r7}			@ Save USR pc, cpsr, old_r0
+		stmdb	r8, {sp, lr}^			@ Save sp_usr lr_usr
+		alignment_trap r4, r7, __temp_abt
+		mov	fp, #0
+		msr	cpsr_c, #MODE_SVC		@ Enable interrupts
+		mov	r0, r5				@ address (pc)
+		mov	r1, sp				@ regs
+		bl	SYMBOL_NAME(do_PrefetchAbort)	@ call abort handler
+		teq	r0, #0				@ Does this still apply???
+		bne	ret_from_sys_call		@ Return from exception
+#ifdef DEBUG_UNDEF
+		adr	r0, t
+		bl	SYMBOL_NAME(printk)
+#endif
+		mov	r0, r5
+		mov	r1, sp
+		and	r2, r6, #31
 		bl	SYMBOL_NAME(do_undefinstr)
-
-3:		ldr	lr, [sp, #S_PSR]		@ Get SVC cpsr
+		ldr	lr, [sp, #S_PSR]		@ Get USR cpsr
 		msr	spsr, lr
-		ldmia	sp, {r0 - pc}^			@ Restore SVC registers
+		ldmia	sp, {r0 - pc}^			@ Restore USR registers
 
-fpundefinstr:	mov	r0, lr
-		mov	r1, sp
-		mrs	r4, cpsr			@ Enable interrupts
-		bic	r4, r4, #I_BIT
-		msr	cpsr, r4
-		adrsvc	al, lr, ret_from_exception
-		b	SYMBOL_NAME(do_undefinstr)
+#ifdef DEBUG_UNDEF
+t:		.ascii "Prefetch -> undefined instruction\n\0"
+		.align
+#endif
 
-__und_invalid:	sub	sp, sp, #S_FRAME_SIZE
-		stmia	sp, {r0 - lr}
-		mov	r7, r0
-		ldr	r4, .LCund
-		ldmia	r4, {r5, r6}			@ Get UND/IRQ/FIQ/ABT pc, cpsr
-		add	r4, sp, #S_PC
-		stmia	r4, {r5, r6, r7}		@ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0
-		mov	r0, sp				@ struct pt_regs *regs
-		mov	r1, #BAD_UNDEFINSTR		@ int reason
-		and	r2, r6, #31			@ int mode
-		b	SYMBOL_NAME(bad_mode)		@ Does not ever return...
+#include "entry-common.S"
 
-/* We get here if an undefined instruction happens and the floating
- * point emulator is not present.  If the offending instruction was
- * a WFS, we just perform a normal return as if we had emulated the
- * operation.  This is a hack to allow some basic userland binaries
- * to run so that the emulator module proper can be loaded. --philb
+		.text
+
+#ifndef __ARM_ARCH_4__
+.Larm700bug:	ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
+		str	lr, [r8]
+		msr	spsr, r0
+		ldmia	sp, {r0 - lr}^			@ Get calling r0 - lr
+		mov	r0, r0
+		ldr	lr, [sp, #S_PC]			@ Get PC
+		add	sp, sp, #S_FRAME_SIZE
+		movs	pc, lr
+#endif
+
+		.section ".text.init",#alloc,#execinstr
+/*
+ * Vector stubs.  NOTE that we only align 'vector_IRQ' to a cache line boundary,
+ * and we rely on each stub being exactly 48 (1.5 cache lines) in size.  This
+ * means that we only ever load two cache lines for this code, or one if we're
+ * lucky.  We also copy this code to 0x200 so that we can use branches in the
+ * vectors, rather than ldr's.
  */
-fpe_not_present:
-		adr	r10, wfs_mask_data
-		ldmia	r10, {r4, r5, r6, r7, r8}
-		ldr	r10, [sp, #S_PC]		@ Load PC
-		sub	r10, r10, #4
-		mask_pc	r10, r10
-		ldrt	r10, [r10]			@ get instruction
-		and	r5, r10, r5
-		teq	r5, r4				@ Is it WFS?
-		moveq	pc, r9
-		and	r5, r10, r8
-		teq	r5, r6				@ Is it LDF/STF on sp or fp?
-		teqne	r5, r7
-		movne	pc, lr
-		tst	r10, #0x00200000		@ Does it have WB
-		moveq	pc, r9
-		and	r4, r10, #255			@ get offset
-		and	r6, r10, #0x000f0000
-		tst	r10, #0x00800000		@ +/-
-		rsbeq	r4, r4, #0
-		ldr	r5, [sp, r6, lsr #14]		@ Load reg
-		add	r5, r5, r4, lsl #2
-		str	r5, [sp, r6, lsr #14]		@ Save reg
-		mov	pc, r9
+		.align	5
+__stubs_start:
+/*
+ * Interrupt dispatcher
+ * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ */
+vector_IRQ:	@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsirq
+		sub	lr, lr, #4
+		str	lr, [r13]			@ save lr_IRQ
+		mrs	lr, spsr
+		str	lr, [r13, #4]			@ save spsr_IRQ
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
 
-wfs_mask_data:	.word	0x0e200110			@ WFS
-		.word	0x0fff0fff
-		.word	0x0d0d0100			@ LDF [sp]/STF [sp]
-		.word	0x0d0b0100			@ LDF [fp]/STF [fp]
-		.word	0x0f0f0f00
+		and	lr, lr, #15
+		ldr	lr, [pc, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
 
-#include "entry-common.S"
+.LCtab_irq:	.word	__irq_usr			@  0  (USR_26 / USR_32)
+		.word	__irq_invalid			@  1  (FIQ_26 / FIQ_32)
+		.word	__irq_invalid			@  2  (IRQ_26 / IRQ_32)
+		.word	__irq_svc			@  3  (SVC_26 / SVC_32)
+		.word	__irq_invalid			@  4
+		.word	__irq_invalid			@  5
+		.word	__irq_invalid			@  6
+		.word	__irq_invalid			@  7
+		.word	__irq_invalid			@  8
+		.word	__irq_invalid			@  9
+		.word	__irq_invalid			@  a
+		.word	__irq_invalid			@  b
+		.word	__irq_invalid			@  c
+		.word	__irq_invalid			@  d
+		.word	__irq_invalid			@  e
+		.word	__irq_invalid			@  f
+
+		.align	5
+
+/*
+ * Data abort dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+vector_data:	@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsabt
+		sub	lr, lr, #8
+		str	lr, [r13]
+		mrs	lr, spsr
+		str	lr, [r13, #4]
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
+
+		and	lr, lr, #15
+		ldr	lr, [pc, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
+
+.LCtab_dabt:	.word	__dabt_usr			@  0  (USR_26 / USR_32)
+		.word	__dabt_invalid			@  1  (FIQ_26 / FIQ_32)
+		.word	__dabt_invalid			@  2  (IRQ_26 / IRQ_32)
+		.word	__dabt_svc			@  3  (SVC_26 / SVC_32)
+		.word	__dabt_invalid			@  4
+		.word	__dabt_invalid			@  5
+		.word	__dabt_invalid			@  6
+		.word	__dabt_invalid			@  7
+		.word	__dabt_invalid			@  8
+		.word	__dabt_invalid			@  9
+		.word	__dabt_invalid			@  a
+		.word	__dabt_invalid			@  b
+		.word	__dabt_invalid			@  c
+		.word	__dabt_invalid			@  d
+		.word	__dabt_invalid			@  e
+		.word	__dabt_invalid			@  f
+
+		.align	5
+
+/*
+ * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+vector_prefetch:
+		@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsabt
+		sub	lr, lr, #4
+		str	lr, [r13]			@ save lr_ABT
+		mrs	lr, spsr
+		str	lr, [r13, #4]			@ save spsr_ABT
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
+
+		ands	lr, lr, #15
+		ldreq	lr, .LCtab_pabt
+		ldrne	lr, .LCtab_pabt + 4
+		movs	pc, lr
+
+.LCtab_pabt:	.word	__pabt_usr
+		.word	__pabt_invalid
+
+		.align	5
+
+/*
+ * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ */
+vector_undefinstr:
+		@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsund
+		str	lr, [r13]			@ save lr_UND
+		mrs	lr, spsr
+		str	lr, [r13, #4]			@ save spsr_UND
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
+
+		and	lr, lr, #15
+		ldr	lr, [pc, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
+
+.LCtab_und:	.word	__und_usr			@  0 (USR_26 / USR_32)
+		.word	__und_invalid			@  1 (FIQ_26 / FIQ_32)
+		.word	__und_invalid			@  2 (IRQ_26 / IRQ_32)
+		.word	__und_svc			@  3 (SVC_26 / SVC_32)
+		.word	__und_invalid			@  4
+		.word	__und_invalid			@  5
+		.word	__und_invalid			@  6
+		.word	__und_invalid			@  7
+		.word	__und_invalid			@  8
+		.word	__und_invalid			@  9
+		.word	__und_invalid			@  a
+		.word	__und_invalid			@  b
+		.word	__und_invalid			@  c
+		.word	__und_invalid			@  d
+		.word	__und_invalid			@  e
+		.word	__und_invalid			@  f
+
+		.align	5
+
+/*=============================================================================
+ * Undefined FIQs
+ *-----------------------------------------------------------------------------
+ * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
+ * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
+ * Basically to switch modes, we *HAVE* to clobber one register...  brain
+ * damage alert!  I don't think that we can execute any code in here in any
+ * other mode than FIQ...  Ok you can switch to another mode, but you can't
+ * get out of that mode without clobbering one register.
+ */
+vector_FIQ:	disable_fiq
+		subs	pc, lr, #4
+
+/*=============================================================================
+ * Address exception handler
+ *-----------------------------------------------------------------------------
+ * These aren't too critical.
+ * (they're not supposed to happen, and won't happen in 32-bit data mode).
+ */
+
+vector_addrexcptn:
+		b	vector_addrexcptn
+
+/*
+ * We group all the following data together to optimise
+ * for CPUs with separate I & D caches.
+ */
+		.align	5
+
+.LCvswi:	.word	vector_swi
+
+.LCsirq:	.word	__temp_irq
+.LCsund:	.word	__temp_und
+.LCsabt:	.word	__temp_abt
+
+__stubs_end:
+
+		.equ	__real_stubs_start, .LCvectors + 0x200
+
+.LCvectors:	swi	SYS_ERROR0
+		b	__real_stubs_start + (vector_undefinstr - __stubs_start)
+		ldr	pc, __real_stubs_start + (.LCvswi - __stubs_start)
+		b	__real_stubs_start + (vector_prefetch - __stubs_start)
+		b	__real_stubs_start + (vector_data - __stubs_start)
+		b	__real_stubs_start + (vector_addrexcptn - __stubs_start)
+		b	__real_stubs_start + (vector_IRQ - __stubs_start)
+		b	__real_stubs_start + (vector_FIQ - __stubs_start)
+
+ENTRY(trap_init)
+		stmfd	sp!, {r4 - r6, lr}
+
+		adr	r1, .LCvectors			@ set up the vectors
+		mov	r0, #0
+		ldmia	r1, {r1, r2, r3, r4, r5, r6, ip, lr}
+		stmia	r0, {r1, r2, r3, r4, r5, r6, ip, lr}
+
+		add	r2, r0, #0x200
+		adr	r0, __stubs_start		@ copy stubs to 0x200
+		adr	r1, __stubs_end
+1:		ldr	r3, [r0], #4
+		str	r3, [r2], #4
+		cmp	r0, r1
+		blt	1b
+		LOADREGS(fd, sp!, {r4 - r6, pc})
 
 		.data
 
+/*
+ * Do not reorder these, and do not insert extra data between...
+ */
+
 __temp_irq:	.word	0				@ saved lr_irq
 		.word	0				@ saved spsr_irq
 		.word	-1				@ old_r0
@@ -800,3 +881,10 @@
 __temp_abt:	.word	0				@ Saved lr_abt
 		.word	0				@ Saved spsr_abt
 		.word	-1				@ old_r0
+
+		.globl	SYMBOL_NAME(cr_alignment)
+		.globl	SYMBOL_NAME(cr_no_alignment)
+SYMBOL_NAME(cr_alignment):
+		.space	4
+SYMBOL_NAME(cr_no_alignment):
+		.space	4
diff -urN linux-2.2.18-pre8/arch/arm/kernel/entry-common.S linux/arch/arm/kernel/entry-common.S
--- linux-2.2.18-pre8/arch/arm/kernel/entry-common.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/entry-common.S	Mon Dec  3 20:25:13 2001
@@ -1,51 +1,54 @@
 /*============================================================================
  * All exits to user mode from the kernel go through this code.
  */
-
-#include <linux/config.h>
-
 		.globl	ret_from_sys_call
 
-ret_from_exception:
-		adr	r0, 1f
-		ldmia	r0, {r0, r1}
+		.align	5
+fast_syscall_return:
+		str	r0, [sp, #S_R0 + 4]		@ returned r0
+slow_syscall_return:
+		add	sp, sp, #4
+ret_from_sys_call:
+		adr	r0, bh_data
+		ldmia	r0, {r0, r4}
 		ldr	r0, [r0]
-		ldr	r1, [r1]
+		ldr	r1, [r4]
 		tst	r0, r1
 		blne	SYMBOL_NAME(do_bottom_half)
-ret_from_intr:	ldr	r0, [sp, #S_PSR]
-		tst	r0, #3
-		beq	ret_with_reschedule
-		b	ret_from_all
+ret_with_reschedule:
+		get_current_task r1		@ check for scheduling
+		ldr	r0, [r1, #TSK_NEED_RESCHED]
+		teq	r0, #0
+		bne	ret_reschedule
+		ldr	r1, [r1, #TSK_SIGPENDING]
+		teq	r1, #0			@ check for signals
+		bne	ret_signal
+
+ret_from_all:	restore_user_regs
 
 ret_signal:	mov	r1, sp
 		adrsvc	al, lr, ret_from_all
+		mov	r2, r4
 		b	SYMBOL_NAME(do_signal)
 
-2:		bl	SYMBOL_NAME(schedule)
+ret_reschedule:	adrsvc	al, lr, ret_with_reschedule
+		b	SYMBOL_NAME(schedule)
 
-ret_from_sys_call:
-		adr	r0, 1f
+		.globl	ret_from_exception
+ret_from_exception:
+		adr	r0, bh_data
 		ldmia	r0, {r0, r1}
 		ldr	r0, [r0]
 		ldr	r1, [r1]
+		mov	r4, #0
 		tst	r0, r1
-		adrsvc	ne, lr, ret_from_intr
-		bne	SYMBOL_NAME(do_bottom_half)
-
-ret_with_reschedule:
-		get_current_task r1
-		ldr	r0, [r1, #TSK_NEED_RESCHED]
-		teq	r0, #0
-		bne	2b
-		ldr	r1, [r1, #TSK_SIGPENDING]
-		teq	r1, #0
-		bne	ret_signal
-
-ret_from_all:	restore_user_regs
+		blne	SYMBOL_NAME(do_bottom_half)
+		ldr	r0, [sp, #S_PSR]
+		tst	r0, #3			@ returning to user mode?
+		beq	ret_with_reschedule
+		b	ret_from_all
 
-1:		.word	SYMBOL_NAME(bh_mask)
-		.word	SYMBOL_NAME(bh_active)
+#include "calls.S"
 
 /*=============================================================================
  * SWI handler
@@ -57,84 +60,65 @@
  * too worried.
  */
 
-#include "calls.S"
-
+		.align	5
 vector_swi:	save_user_regs
-		mov	fp, #0
 		mask_pc	lr, lr
-		ldr	r6, [lr, #-4]!		@ get SWI instruction
+		mov	fp, #0
+		ldr	r6, [lr, #-4]		@ get SWI instruction
 		arm700_bug_check r6, r7
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	r7, .LCswi
+		ldr	r7, [r7]
+		mcr	p15, 0, r7, c1, c0
+#endif
 		enable_irqs r7
-		
+
+		str	r4, [sp, #-4]!		@ new style: (r0 = arg1, r4 = arg5)
+		adrsvc	al, lr, fast_syscall_return
+
 		bic	r6, r6, #0xff000000	@ mask off SWI op-code
 		eor	r6, r6, #OS_NUMBER<<20	@ check OS number
 		cmp	r6, #NR_syscalls	@ check upper syscall limit
 		bcs	2f
 
-		get_current_task r5
-		ldr	ip, [r5, #TSK_FLAGS]	@ check for syscall tracing
+		get_current_task r7
+		ldr	ip, [r7, #TSK_FLAGS]	@ check for syscall tracing
+		adr	r5, SYMBOL_NAME(sys_call_table)
 		tst	ip, #PF_TRACESYS
-		bne	1f
+		ldreq	pc, [r5, r6, lsl #2]	@ call sys routine
 
-		adr	ip, SYMBOL_NAME(sys_call_table)
-		str	r4, [sp, #-4]!		@ new style: (r0 = arg1, r5 = arg5)
-		mov	lr, pc
-		ldr	pc, [ip, r6, lsl #2]	@ call sys routine
-		add	sp, sp, #4
-		str	r0, [sp, #S_R0]		@ returned r0
-		b	ret_from_sys_call
-
-1:		ldr	r7, [sp, #S_IP]		@ save old IP
+		ldr	r7, [sp, #S_IP + 4]	@ save old IP
 		mov	r0, #0
-		str	r0, [sp, #S_IP]		@ trace entry [IP = 0]
+		str	r0, [sp, #S_IP + 4]	@ trace entry [IP = 0]
 		bl	SYMBOL_NAME(syscall_trace)
-		str	r7, [sp, #S_IP]
-		ldmia	sp, {r0 - r3}		@ have to reload r0 - r3
-		adr	ip, SYMBOL_NAME(sys_call_table)
-		str	r4, [sp, #-4]!		@ new style: (r0 = arg1, r5 = arg5)
+		str	r7, [sp, #S_IP + 4]
+
+		ldmib	sp, {r0 - r3}		@ have to reload r0 - r3
 		mov	lr, pc
-		ldr	pc, [ip, r6, lsl #2]	@ call sys routine
-		add	sp, sp, #4
-		str	r0, [sp, #S_R0]		@ returned r0
+		ldr	pc, [r5, r6, lsl #2]	@ call sys routine
+		str	r0, [sp, #S_R0 + 4]	@ returned r0
+
 		mov	r0, #1
-		str	r0, [sp, #S_IP]		@ trace exit [IP = 1]
+		str	r0, [sp, #S_IP + 4]	@ trace exit [IP = 1]
 		bl	SYMBOL_NAME(syscall_trace)
-		str	r7, [sp, #S_IP]
-		b	ret_from_sys_call
+		str	r7, [sp, #S_IP + 4]
+		b	slow_syscall_return
 
-2:		tst	r6, #0x00f00000		@ is it a Unix SWI?
+2:		add	r1, sp, #4
+		tst	r6, #0x00f00000		@ is it a Unix SWI?
 		bne	3f
-		cmp	r6, #(KSWI_SYS_BASE - KSWI_BASE)
-		bcc	4f			@ not private func
-		bic	r0, r6, #0x000f0000
-		mov	r1, sp
-		bl	SYMBOL_NAME(arm_syscall)
-		b	ret_from_sys_call
-
-3:		eor	r0, r6, #OS_NUMBER<<20	@ Put OS number back
-		mov	r1, sp
-		bl	SYMBOL_NAME(deferred)
-		ldmfd	sp, {r0 - r3}
-		b	ret_from_sys_call
-
-4:		bl	SYMBOL_NAME(sys_ni_syscall)
-		str	r0, [sp, #0]		@ returned r0
-		b	ret_from_sys_call
+		subs	r0, r6, #(KSWI_SYS_BASE - KSWI_BASE)
+		bcs	SYMBOL_NAME(arm_syscall)
+		b	SYMBOL_NAME(sys_ni_syscall) @ not private func
 
-@ r0 = syscall number
-@ r1 = syscall r0
-@ r5 = syscall r4
-@ ip = syscall table
-SYMBOL_NAME(sys_syscall):
-		mov	r6, r0
-		eor	r6, r6, #OS_NUMBER << 20
-		cmp	r6, #NR_syscalls		@ check range
-		movgt	r0, #-ENOSYS
-		movgt	pc, lr
-		add	sp, sp, #4			@ take of the save of our r4
-		ldmib	sp, {r0 - r4}			@ get our args
-		str	r4, [sp, #-4]!			@ Put our arg on the stack
-		ldr	pc, [ip, r6, lsl #2]
+3:		eor	r0, r6, #OS_NUMBER <<20	@ Put OS number back
+		adrsvc	al, lr, slow_syscall_return
+		b	SYMBOL_NAME(deferred)
+
+		.align	5
+
+bh_data:	.word	SYMBOL_NAME(bh_mask)
+		.word	SYMBOL_NAME(bh_active)
 
 ENTRY(sys_call_table)
 #include "calls.S"
@@ -142,10 +126,26 @@
 /*============================================================================
  * Special system call wrappers
  */
+@ r0 = syscall number
+@ r5 = syscall table
+SYMBOL_NAME(sys_syscall):
+		eor	r6, r0, #OS_NUMBER << 20
+		cmp	r6, #NR_syscalls	@ check range
+		addle	ip, sp, #4
+		ldmleib	ip, {r0 - r4}		@ get our args
+		strle	r4, [sp]		@ Put our arg on the stack
+		ldrle	pc, [r5, r6, lsl #2]
+		mov	r0, #-ENOSYS
+		mov	pc, lr
+
 sys_fork_wrapper:
 		add	r0, sp, #4
 		b	SYMBOL_NAME(sys_fork)
 
+sys_vfork_wrapper:
+		add	r0, sp, #4
+		b	SYMBOL_NAME(sys_vfork)
+
 sys_execve_wrapper:
 		add	r3, sp, #4
 		b	SYMBOL_NAME(sys_execve)
@@ -191,99 +191,6 @@
 sys_sigaltstack_wrapper:
 		ldr	r2, [sp, #4 + S_SP]
 		b	do_sigaltstack
-
-/*
- *=============================================================================
- *		Low-level interface code
- *-----------------------------------------------------------------------------
- *		Trap initialisation
- *-----------------------------------------------------------------------------
- *
- * Note - FIQ code has changed.  The default is a couple of words in 0x1c, 0x20
- * that call _unexp_fiq.  Nowever, we now copy the FIQ routine to 0x1c (removes
- * some excess cycles).
- *
- * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000
- * (the kernel).
- * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for
- * the actual address to jump to.
- */
-
-		.section ".text.init",#alloc,#execinstr
-
-#if defined(CONFIG_CPU_32)
-/*
- * these go into 0x00
- */
-.Lbranches:	swi	SYS_ERROR0
-		ldr	pc, .Lbranches + 0xe4
-		ldr	pc, .Lbranches + 0xe8
-		ldr	pc, .Lbranches + 0xec
-		ldr	pc, .Lbranches + 0xf0
-		ldr	pc, .Lbranches + 0xf4
-		ldr	pc, .Lbranches + 0xf8
-		ldr	pc, .Lbranches + 0xfc
-/*
- * this is put into 0xe4 and above
- */
-.Ljump_addresses:
-		.word	vector_undefinstr	@ 0xe4
-		.word	vector_swi		@ 0xe8
-		.word	vector_prefetch		@ 0xec
-		.word	vector_data		@ 0xf0
-		.word	vector_addrexcptn	@ 0xf4
-		.word	vector_IRQ		@ 0xf8
-		.word	_unexp_fiq		@ 0xfc
-/*
- * initialise the trap system
- */
-ENTRY(trap_init)
-		stmfd	sp!, {r4 - r7, lr}
-		initialise_traps_extra
-		mov	r0, #0xe4
-		adr	r1, .Ljump_addresses
-		ldmia	r1, {r1 - r7}
-		stmia	r0, {r1 - r7}
-		mov	r0, #0
-		adr	r1, .Lbranches
-		ldmia	r1, {r1 - r7}
-		stmia	r0, {r1 - r7}
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-#elif defined(CONFIG_CPU_26)
-.Ljump_addresses:
-		swi	SYS_ERROR0
-		.word	vector_undefinstr	- 12
-		.word	vector_swi		- 16
-		.word	vector_prefetch		- 20
-		.word	vector_data		- 24
-		.word	vector_addrexcptn	- 28
-		.word	vector_IRQ		- 32
-		.word	_unexp_fiq		- 36
-		b	. + 8
-/*
- * initialise the trap system
- */
-ENTRY(trap_init)
-		stmfd	sp!, {r4 - r7, lr}
-		adr	r1, .Ljump_addresses
-		ldmia	r1, {r1 - r7, ip, lr}
-		orr	r2, lr, r2, lsr #2
-		orr	r3, lr, r3, lsr #2
-		orr	r4, lr, r4, lsr #2
-		orr	r5, lr, r5, lsr #2
-		orr	r6, lr, r6, lsr #2
-		orr	r7, lr, r7, lsr #2
-		orr	ip, lr, ip, lsr #2
-		mov	r0, #0
-		stmia	r0, {r1 - r7, ip}
-		ldmfd	sp!, {r4 - r7, pc}^
-#endif
-
-		.previous
-
-/*============================================================================
- * FP support
- */
 
 		.data
 
diff -urN linux-2.2.18-pre8/arch/arm/kernel/fiq.c linux/arch/arm/kernel/fiq.c
--- linux-2.2.18-pre8/arch/arm/kernel/fiq.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/fiq.c	Mon Dec  3 20:25:13 2001
@@ -2,6 +2,8 @@
  *  linux/arch/arm/kernel/fiq.c
  *
  *  Copyright (C) 1998 Russell King
+ *  Copyright (C) 1998, 1999 Phil Blundell
+ *
  *  FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
  *
  *  FIQ support re-written by Russell King to be more generic
@@ -51,15 +53,12 @@
 #ifdef CONFIG_CPU_32
 static inline void unprotect_page_0(void)
 {
-	__asm__ __volatile__("mcr	p15, 0, %0, c3, c0" :
-			: "r" (DOMAIN_USER_MANAGER |
-			       DOMAIN_KERNEL_CLIENT |
-			       DOMAIN_IO_CLIENT));
+	modify_domain(DOMAIN_USER, DOMAIN_MANAGER);
 }
 
 static inline void protect_page_0(void)
 {
-	set_fs(get_fs());
+	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
 }
 #else
 
@@ -78,7 +77,7 @@
 		unprotect_page_0();
 		*(unsigned long *)FIQ_VECTOR = no_fiq_insn;
 		protect_page_0();
-		__flush_entry_to_ram(FIQ_VECTOR);
+		flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + 4);
 	}
 
 	return 0;
@@ -106,28 +105,77 @@
 	memcpy((void *)FIQ_VECTOR, start, length);
 
 	protect_page_0();
-#ifdef CONFIG_CPU_32
-	processor.u.armv3v4._flush_cache_area(FIQ_VECTOR, FIQ_VECTOR + length, 1);
-#endif
+	flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + length);
 }
 
+/*
+ * Taking an interrupt in FIQ mode is death, so both these functions
+ * disable irqs for the duration. 
+ */
 void set_fiq_regs(struct pt_regs *regs)
 {
-	/* not yet -
-	 * this is temporary to get the floppy working
-	 * again on RiscPC.  It *will* become more
-	 * generic.
-	 */
-#ifdef CONFIG_ARCH_ACORN
-	extern void floppy_fiqsetup(unsigned long len, unsigned long addr,
-					     unsigned long port);
-	floppy_fiqsetup(regs->ARM_r9, regs->ARM_r10, regs->ARM_fp);
+	register unsigned long tmp, tmp2;
+	__asm__ volatile (
+#ifdef CONFIG_CPU_26
+	"mov	%0, pc
+	bic	%1, %0, #0x3
+	orr	%1, %1, #0x0c000001
+	teqp	%1, #0		@ select FIQ mode
+	mov	r0, r0
+	ldmia	%2, {r8 - r14}
+	teqp	%0, #0		@ return to SVC mode
+	mov	r0, r0"
 #endif
+#ifdef CONFIG_CPU_32
+	"mrs	%0, cpsr
+	bic	%1, %0, #0xf
+	orr	%1, %1, #0xc1
+	msr	cpsr, %1	@ select FIQ mode
+	mov	r0, r0
+	ldmia	%2, {r8 - r14}
+	msr	cpsr, %0	@ return to SVC mode
+	mov	r0, r0"
+#endif
+	: "=&r" (tmp), "=&r" (tmp2)
+	: "r" (&regs->ARM_r8)
+	/* These registers aren't modified by the above code in a way
+	   visible to the compiler, but we mark them as clobbers anyway
+	   so that GCC won't put any of the input or output operands in
+	   them.  */
+	: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
 }
 
 void get_fiq_regs(struct pt_regs *regs)
 {
-	/* not yet */
+	register unsigned long tmp, tmp2;
+	__asm__ volatile (
+#ifdef CONFIG_CPU_26
+	"mov	%0, pc
+	bic	%1, %0, #0x3
+	orr	%1, %1, #0x0c000001
+	teqp	%1, #0		@ select FIQ mode
+	mov	r0, r0
+	stmia	%2, {r8 - r14}
+	teqp	%0, #0		@ return to SVC mode
+	mov	r0, r0"
+#endif
+#ifdef CONFIG_CPU_32
+	"mrs	%0, cpsr
+	bic	%1, %0, #0xf
+	orr	%1, %1, #0xc1
+	msr	cpsr, %1	@ select FIQ mode
+	mov	r0, r0
+	stmia	%2, {r8 - r14}
+	msr	cpsr, %0	@ return to SVC mode
+	mov	r0, r0"
+#endif
+	: "=&r" (tmp), "=&r" (tmp2)
+	: "r" (&regs->ARM_r8)
+	/* These registers aren't modified by the above code in a way
+	   visible to the compiler, but we mark them as clobbers anyway
+	   so that GCC won't put any of the input or output operands in
+	   them.  */
+	: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
 }
 
 int claim_fiq(struct fiq_handler *f)
@@ -165,7 +213,7 @@
 	while (current_fiq->fiq_op(current_fiq->dev_id, 0));
 }
 
-__initfunc(void init_FIQ(void))
+void __init init_FIQ(void)
 {
 	no_fiq_insn = *(unsigned long *)FIQ_VECTOR;
 	set_fs(get_fs());
diff -urN linux-2.2.18-pre8/arch/arm/kernel/head-armv.S linux/arch/arm/kernel/head-armv.S
--- linux-2.2.18-pre8/arch/arm/kernel/head-armv.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/head-armv.S	Mon Dec  3 20:25:13 2001
@@ -1,157 +1,238 @@
 /*
- * linux/arch/arm/kernel/head32.S
+ * linux/arch/arm/kernel/head-armv.S
  *
- * Copyright (C) 1994-1998 Russell King
+ * Copyright (C) 1994-1999 Russell King
  *
- * Kernel 32 bit startup code for ARM6 / ARM7 / StrongARM
+ * 32-bit kernel startup code for all architectures
  */
 #include <linux/config.h>
 #include <linux/linkage.h>
 
-#ifndef CONFIG_ARCH_VNC
+#include <asm/assembler.h>
+
 #if (TEXTADDR & 0xffff) != 0x8000
 #error TEXTADDR must start at 0xXXXX8000
 #endif
-#else
-		.text
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
+
+#define SWAPPER_PGDIR_OFFSET	0x4000
+
+		.globl	SYMBOL_NAME(swapper_pg_dir)
+		.equ	SYMBOL_NAME(swapper_pg_dir),	TEXTADDR - 0x8000 + SWAPPER_PGDIR_OFFSET
+
+		.section ".text.init",#alloc,#execinstr
+ENTRY(stext)
+ENTRY(_stext)
+
+#ifdef CONFIG_ARCH_NETWINDER
+/*
+ * Compatability cruft for old NetWinder NeTTroms.  This
+ * code is currently scheduled for destruction in 2.5.xx
+ */
+		.rept	8
 		mov	r0, r0
+		.endr
+
+		adr	r2, 1f
+		ldmdb	r2, {r7, r8}
+		and	r3, r2, #0xc000
+		teq	r3, #0x8000
+		beq	__entry
+		bic	r3, r2, #0xc000
+		orr	r3, r3, #0x8000
+		mov	r0, r3
+		mov	r4, #64
+		sub	r5, r8, r7
+		b	1f
+
+		.word	_stext
+		.word	__bss_start
+
+1:
+		.rept	4
+		ldmia	r2!, {r6, r7, r8, r9}
+		stmia	r3!, {r6, r7, r8, r9}
+		.endr
+		subs	r4, r4, #64
+		bcs	1b
+		movs	r4, r5
+		mov	r5, #0
+		movne	pc, r0
+
 		mov	r0, #0
 		mov	r1, #5
 #endif
 
-#define DEBUG
+/*
+ * Entry point.  Entry *must* be called with r0 == 0, with the MMU off.
+ *  r1 contains the unique architecture number.  See
+ * linux/arch/arm/kernel/setup.c machine_desc[] array for the complete
+ * list.  If you require a new number, please follow the instructions
+ * given in Documentation/arm/README.
+ */
+__entry:	teq	r0, #0
+		movne	r0, #'i'
+		bne	__error
+		bl	__lookup_processor_type
+		teq	r10, #0				@ invalid processor?
+		moveq	r0, #'p'
+		beq	__error
+		bl	__lookup_architecture_type
+		teq	r7, #0				@ invalid architecture?
+		moveq	r0, #'a'
+		beq	__error
+		bl	__create_page_tables
+		adr	lr, __ret
+		add	pc, r10, #12			@ flush caches (returns ctrl reg)
 
-		.globl	SYMBOL_NAME(swapper_pg_dir)
-		.equ	SYMBOL_NAME(swapper_pg_dir),	TEXTADDR - 0x4000
+__switch_data:	.long	__mmap_switched
+		.long	SYMBOL_NAME(__bss_start)
+		.long	SYMBOL_NAME(_end)
+		.long	SYMBOL_NAME(processor_id)
+		.long	SYMBOL_NAME(__machine_arch_type)
+		.long	SYMBOL_NAME(cr_alignment)
+		.long	SYMBOL_NAME(init_task_union)+8192
+
+__ret:		ldr	lr, __switch_data
+		mcr	p15, 0, r0, c1, c0
+		mov	r0, r0
+		mov	r0, r0
+		mov	r0, r0
+		mov	pc, lr
+
+		/*
+		 * This code follows on after the page
+		 * table switch and jump above.
+		 *
+		 * r0  = processor control register
+		 * r1  = machine ID
+		 * r9  = processor ID
+		 */
+		.align	5
+__mmap_switched:
+		adr	r3, __switch_data + 4
+		ldmia	r3, {r4, r5, r6, r7, r8, sp}	@ Setup stack
+
+		mov	fp, #0				@ Clear BSS
+1:		cmp	r4, r5
+		strcc	fp, [r4],#4
+		bcc	1b
+
+		str	r9, [r6]			@ Save processor ID
+		str	r1, [r7]			@ Save machine type
+#ifdef CONFIG_ALIGNMENT_TRAP
+		orr	r0, r0, #2			@ ...........A.
+#endif
+		bic	r2, r0, #2			@ Clear 'A' bit
+		stmia	r8, {r0, r2}			@ Save control register values
+		b	SYMBOL_NAME(start_kernel)
 
-		.text
-/*
- * Entry point and restart point.  Entry *must* be called with r0 == 0,
- * MMU off.  Note! These should be unique!!! Please read Documentation/ARM-README
- * for more information.
- *
- *  r1 = 0 -> DEC EBSA-110
- *  r1 = 1 -> Acorn RiscPC
- *  r1 = 2 -> ebsit
- *  r1 = 3 -> nexuspci
- *  r1 = 4 -> DEC EBSA-285
- *  r1 = 5 -> Corel Netwinder
- *  r1 = 6 -> CATS
- *  r1 = 7 -> tbox
- */
 
-ENTRY(stext)
-ENTRY(_stext)
-__entry:	teq	r0, #0					@ check for illegal entry...
-		bne	.Lerror					@ loop indefinitely
-		cmp	r1, #8					@ Unknown machine architecture
-		bge	.Lerror
-/* First thing to do is to get the page tables set up so that we can call the kernel
- * in the correct place.  This is relocatable code...
- * - Read processor ID register (CP#15, CR0).
- */
-		mrc	p15, 0, r9, c0, c0			@ get Processor ID
-/* Values are:
- *		XX01XXXX = ARMv4 architecture (StrongARM)
- *		XX00XXXX = ARMv3 architecture
- *		4156061X = ARM 610
- */
-		adr	r10, .LCProcTypes
-1:		ldmia	r10!, {r5, r6, r8}			@ Get Set, Mask, MMU Flags
-		teq	r5, #0					@ End of list?
-		beq	.Lerror
-		eor	r5, r5, r9
-		tst	r5, r6
-		addne	r10, r10, #8
-		bne	1b
 
-		adr	r4, .LCMachTypes
-		add	r4, r4, r1, lsl #4
-		ldmia	r4, {r4, r5, r6}
 /*
- * r4 = page dir in physical ram
+ * Setup the initial page tables.  We only setup the barest
+ * amount which are required to get the kernel running, which
+ * generally means mapping in the kernel code.
+ *
+ * We only map in 4MB of RAM, which should be sufficient in
+ * all cases.
+ *
  * r5 = physical address of start of RAM
- * r6 = I/O address
+ * r6 = physical IO address
+ * r7 = byte offset into page tables for IO
+ * r8 = page table flags
  */
+__create_page_tables:
+		add	r4, r5, #SWAPPER_PGDIR_OFFSET
 		mov	r0, r4
 		mov	r3, #0
-		add	r2, r0, #0x4000
-1:		str	r3, [r0], #4				@ Clear page table
+		add	r2, r0, #0x4000			@ Clear page table
+1:		str	r3, [r0], #4
+		str	r3, [r0], #4
+		str	r3, [r0], #4
+		str	r3, [r0], #4
 		teq	r0, r2
 		bne	1b
-/*
- * Add enough entries to allow the kernel to be called.
- * It will sort out the real mapping in paging_init.
- * We map in 2MB of memory into (TEXTADDR-0x8000) + 2MB
- */
-		add	r0, r4, #(TEXTADDR - 0x8000) >> 18
-		mov	r3, #0x0000000c				@ SECT_CACHEABLE | SECT_BUFFERABLE
+		/*
+		 * Create identity mapping for first MB of kernel.
+		 * map in four sections (4MB) for kernel.
+		 * these are marked cacheable and bufferable.
+		 *
+		 * The identity mapping will be removed by  paging_init()
+		 */
+		mov	r3, #0x0c
 		orr	r3, r3, r8
 		add	r3, r3, r5
+		add	r0, r4, r5, lsr #18
+		str	r3, [r0]
+		add	r0, r4, #(TEXTADDR - 0x8000) >> 18
 		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
 		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
-#ifdef DEBUG
-/* Map in IO space
- * This allows debug messages to be output via a serial
- * before/while paging_init.
- */
-		add	r0, r4, #0x3800
+		str	r3, [r0], #4
+		add	r3, r3, #1 << 20
+		str	r3, [r0], #4
+#ifdef CONFIG_DEBUG_LL
+		/*
+		 * Map in IO space for serial debugging.
+		 * This allows debug messages to be output
+		 * via a serial before paging_init.
+		 */
+		add	r0, r4, r7
+		rsb	r3, r7, #0x4000 @ PTRS_PER_PGD*sizeof(long)
+		cmp	r3, #0x0800
+		addge	r2, r0, #0x0800
+		addlt	r2, r0, r3
 		orr	r3, r6, r8
-		add	r2, r0, #0x0800
 1:		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
 		teq	r0, r2
 		bne	1b
-#ifdef CONFIG_ARCH_VNC
-		add	r0, r4, #0x3f00
-		add	r0, r0, #0x00f8
+#ifdef CONFIG_ARCH_NETWINDER
+		teq	r1, #5
+		bne	1f
+		add	r0, r4, #0x3fc0
 		mov	r3, #0x7c000000
 		orr	r3, r3, r8
 		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
 		str	r3, [r0], #4
+1:
 #endif
 #endif
 #ifdef CONFIG_ARCH_RPC
-/* Map in screen at 0x02000000 & SCREEN2_BASE
- * Similar reasons here - for debug, and when things go
- * wrong to a certain extent.  This is of limited use to
- * non-Acorn RiscPC architectures though.
- */
+		/*
+		 * Map in screen at 0x02000000 & SCREEN2_BASE
+		 * Similar reasons here - for debug.  This is
+		 * only for Acorn RiscPC architectures.
+		 */
 		teq	r5, #0
-		addne	r0, r4, #0x80				@ 02000000
+		addne	r0, r4, #0x80			@ 02000000
 		movne	r3, #0x02000000
 		orrne	r3, r3, r8
 		strne	r3, [r0]
-		addne	r0, r4, #0x3600				@ d8000000
+		addne	r0, r4, #0x3600			@ d8000000
 		strne	r3, [r0]
 #endif
-@
-@ The following should work on both v3 and v4 implementations
-@
-		mov	lr, pc
-		mov	pc, r10					@ Call processor flush (returns ctrl reg)
-		adr	r5, __entry
-		sub	r10, r10, r5				@ Make r10 PIC
-		ldr	lr, .Lbranch
-		mcr	p15, 0, r0, c1, c0			@ Enable MMU & caches.  In 3 instructions
-								@ we lose this page!
 		mov	pc, lr
 
-.Lerror:
+
+
+/*
+ * Exception handling.  Something went wrong and we can't
+ * proceed.  We ought to tell the user, but since we
+ * don't have any guarantee that we're even running on
+ * the right architecture, we do virtually nothing.
+ * r0 = ascii error character
+ *
+ * Generally, only serious errors cause this.
+ */
+__error:
 #ifdef CONFIG_ARCH_RPC
-/* Turn the screen red on a error - RiscPC only.
+/*
+ * Turn the screen red on a error - RiscPC only.
  */
-1:		mov	r0, #0x02000000
+		mov	r0, #0x02000000
 		mov	r3, #0x11
 		orr	r3, r3, r3, lsl #8
 		orr	r3, r3, r3, lsl #16
@@ -160,160 +241,157 @@
 		str	r3, [r0], #4
 		str	r3, [r0], #4
 #endif
+1:		mov	r0, r0
 		b	1b
 
-.Lbranch:	.long	.Lalready_done_mmap			@ Real address of routine
 
-		@ DEC EBSA110 (pg dir phys, phys ram start, phys i/o)
-.LCMachTypes:	.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000	@ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0xe0000000				@ I/O address
+
+/*
+ * Read processor ID register (CP#15, CR0), and determine
+ * processor type.
+ *
+ * Returns:
+ *	r5, r6, r7 corrupted
+ *	r8  = page table flags
+ *	r9  = processor ID
+ *	r10 = pointer to processor structure
+ */
+__lookup_processor_type:
+		adr	r5, 2f
+		ldmia	r5, {r7, r9, r10}
+		sub	r5, r5, r9
+		add	r7, r7, r5
+		add	r10, r10, r5
+		mrc	p15, 0, r9, c0, c0		@ get processor id
+1:		ldmia	r10, {r5, r6, r8}		@ value, mask, mmuflags
+		eor	r5, r5, r9
+		tst	r5, r6
+		moveq	pc, lr
+		add	r10, r10, #36			@ sizeof(proc_info_list)
+		cmp	r10, r7
+		blt	1b
+		mov	r10, #0
+		mov	pc, lr
+
+2:		.long	__proc_info_end
+		.long	2b
+		.long	__proc_info_begin
+
+/*
+ * Lookup machine architecture
+ * r1 = machine architecture number
+ * Returns:
+ *  r4 = unused word
+ *  r5 = physical start address of RAM
+ *  r6 = physical address of IO
+ *  r7 = byte offset into page tables for IO
+ */
+__lookup_architecture_type:
+		cmp	r1, #(__arch_types_end - __arch_types_start) / 16
+		bge	1f
+		adr	r4, __arch_types_start
+		add	r4, r4, r1, lsl #4
+		ldmia	r4, {r4, r5, r6, r7}
+		mov	r7, r7, lsr #18
+		mov	pc, lr
+1:		mov	r7, #0
+		mov	pc, lr
+
+/*
+ * Machine parameters.  Each machine requires 4 words, which are:
+ *
+ * word0: unused
+ * word1: physical start address of RAM
+ * word2: physical start address of IO
+ * word3: virtual start address of IO
+ *
+ * The IO mappings entered here are used to set up mappings
+ * required for debugging information to be shown to the user.
+ * paging_init() does the real page table initialisation.
+ */
+		@ 0x00 - DEC EBSA110
+__arch_types_start:
+		.long	0
 		.long	0
+		.long	0xe0000000
+		.long	0xe0000000
 
-		@ Acorn RiscPC
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x10000000
+		@ 0x01 - Acorn RiscPC
+		.long	0
 		.long	0x10000000
 		.long	0x03000000
-		.long	0
+		.long	0xe0000000
 
-		@ EBSIT ???
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000
+		@ 0x02 - Unused
 		.long	0
-		.long	0xe0000000
 		.long	0
+		.long	0xe0000000
+		.long	0xe0000000
 
-		@ NexusPCI
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x40000000
+		@ 0x03 - NexusPCI
+		.long	0
 		.long	0x40000000
 		.long	0x10000000
-		.long	0
+		.long	0xe0000000
 
-		@ DEC EBSA285
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0x24000000				@ I/O base address (0x42000000 -> 0xFE000000)
+		@ 0x04 - DEC EBSA285
 		.long	0
-
-		@ Corel VNC
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0x24000000				@ I/O base address (0x42000000 -> 0xfe000000)
 		.long	0
+		.long	0x42000000
+		.long	0xfe000000
 
-		@ CATS
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0x24000000				@ I/O base address (0x42000000 -> 0xfe000000)
+		@ 0x05 - Rebel.com NetWinder
 		.long	0
-
-		@ tbox
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x80000000
-		.long	0x80000000				@ Address of RAM
-		.long	0x00400000				@ Uart
 		.long	0
+		.long	0x42000000
+		.long	0xfe000000
 
-.LCProcTypes:	@ ARM6 / 610
-		.long	0x41560600
-		.long	0xffffff00
-		.long	0x00000c12
-		b	.Larmv3_flush_early			@ arm v3 flush & ctrl early setup
-		mov	pc, lr
-
-		@ ARM7
-		.long	0x41007000
-		.long	0xfffff000
-		.long	0x00000c12
-		b	.Larmv3_flush_late			@ arm v3 flush & ctrl late setup
-		mov	pc, lr
-
-		@ ARM710
-		.long	0x41007000
-		.long	0xfff8f000				@ arm710 processors are weird
-		.long	0x00000c12
-		b	.Larmv3_flush_late			@ arm v3 flush & ctrl late setup
-		mov	pc, lr
+		@ 0x06 - CATS
+		.long	0
+		.long	0
+		.long	0x42000000
+		.long	0xfe000000
 
-		@ StrongARM
-		.long	0x4401a100
-		.long	0xfffffff0
-		.long	0x00000c02
-		b	.Larmv4_flush_early
-		b	.Lsa_fastclock
+		@ 0x07 - tbox
+		.long	0
+		.long	0x80000000
+		.long	0x00400000			@ Uart
+		.long	0xe0000000
 
+		@ 0x08 - DEC EBSA285 as co-processor
 		.long	0
-		.align
+		.long	0
+		.long	0x42000000			@ Physical I/O base address
+		.long	0x7cf00000			@ Virtual I/O base address
 
-.Larmv3_flush_early:
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c0			@ flush caches on v3
-		mcr	p15, 0, r0, c5, c0			@ flush TLBs on v3
-		mcr	p15, 0, r4, c2, c0			@ load page table pointer
-		mov	r0, #0x1f				@ Domains 0, 1 = client
-		mcr	p15, 0, r0, c3, c0			@ load domain access register
-		mov	r0, #0x3d				@ ....S..DPWC.M
-		orr	r0, r0, #0x100
-		mov	pc, lr
+		@ 0x09 - CL-PS7110
 
-.Larmv3_flush_late:
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c0			@ flush caches on v3
-		mcr	p15, 0, r0, c5, c0			@ flush TLBs on v3
-		mcr	p15, 0, r4, c2, c0			@ load page table pointer
-		mov	r0, #0x1f				@ Domains 0, 1 = client
-		mcr	p15, 0, r0, c3, c0			@ load domain access register
-		mov	r0, #0x7d				@ ....S.LDPWC.M
-		orr	r0, r0, #0x100
-		mov	pc, lr
+		@ 0x0a - Acorn Archimedes
 
-.Larmv4_flush_early:
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c7			@ flush I,D caches on v4
-		mcr	p15, 0, r0, c7, c10, 4			@ drain write buffer on v4
-		mcr	p15, 0, r0, c8, c7			@ flush I,D TLBs on v4
-		mcr	p15, 0, r4, c2, c0			@ load page table pointer
-		mov	r0, #0x1f				@ Domains 0, 1 = client
-		mcr	p15, 0, r0, c3, c0			@ load domain access register
-		mrc	p15, 0, r0, c1, c0			@ get control register v4
-		bic	r0, r0, #0x0e00
-		bic	r0, r0, #0x0002
-		orr	r0, r0, #0x003d				@ I...S..DPWC.M
-		orr	r0, r0, #0x1100				@ v4 supports separate I cache
-		mov	pc, lr
+		@ 0x0b - Acorn A5000
 
-		.section ".text.init",#alloc,#execinstr
+		@ 0x0c - Etoile
 
-.Lsa_fastclock:	mcr	p15, 0, r4, c15, c1, 2			@ Enable clock switching
-		mov	pc, lr
+		@ 0x0d - LaCie_NAS
 
-.LC0:		.long	SYMBOL_NAME(__entry)
-		.long	SYMBOL_NAME(machine_type)
-		.long	SYMBOL_NAME(__bss_start)
-		.long	SYMBOL_NAME(processor_id)
-		.long	SYMBOL_NAME(_end)
-		.long	SYMBOL_NAME(init_task_union)+8192
-		.align
+		@ 0x0e - CL-PS7500
 
-.Lalready_done_mmap:
-		adr	r4, .LC0
-		ldmia	r4, {r3, r4, r5, r6, r8, sp}		@ Setup stack
-		add	r10, r10, r3				@ Add base back in
-		mov	fp, #0
-1:		cmp	r5, r8					@ Clear BSS
-		strcc	fp, [r5],#4
-		bcc	1b
+		@ 0x0f - Shark
 
-		str	r1, [r4]				@ Save machine type
-		str	r9, [r6]				@ Save processor ID
-		mov	lr, pc
-		add	pc, r10, #4				@ Call post-processor init
-		mov	fp, #0
-		b	SYMBOL_NAME(start_kernel)
+		@ 0x10 - SA1100
+
+		/*
+		 * Don't add anything here unless you have an
+		 * architecture number allocated - see
+		 * Documentation/arm/README
+		 */
+__arch_types_end:
 
-		.text
-#ifdef DEBUG
+#ifdef CONFIG_DEBUG_LL
 /*
  * Some debugging routines (useful if you've got MM problems and
- * printk isn't working).  For DEBUGGING ONLY!!!
+ * printk isn't working).  For DEBUGGING ONLY!!!  Do not leave
+ * references to these in a production kernel!
  */
 #if defined(CONFIG_ARCH_RPC)
 		.macro	addruart,rx
@@ -362,64 +440,71 @@
 		beq	1001b
 		.endm
 
-#elif defined(CONFIG_ARCH_EBSA285)
+#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
+#ifndef CONFIG_DEBUG_DC21285_PORT
+	/* For NetWinder debugging */
 		.macro	addruart,rx
-		mov	\rx, #0xfe000000
+		mov	\rx, #0xff000000
+		orr	\rx, \rx, #0x000003f8
 		.endm
 
 		.macro	senduart,rd,rx
-		str	\rd, [\rx, #0x160]	@ UARTDR
+		strb	\rd, [\rx]
 		.endm
 
 		.macro	busyuart,rd,rx
-1001:		ldr	\rd, [\rx, #0x178]	@ UARTFLG
-		tst	\rd, #1 << 3
-		bne	1001b
+1002:		ldrb	\rd, [\rx, #0x5]
+		and	\rd, \rd, #0x60
+		teq	\rd, #0x60
+		bne	1002b
 		.endm
 
 		.macro	waituart,rd,rx
+1001:		ldrb	\rd, [\rx, #0x6]
+		tst	\rd, #0x10
+		beq	1001b
 		.endm
+#else
+	/* For EBSA285 debugging */
+		.equ	dc21285_high, ARMCSR_BASE & 0xff000000
+		.equ	dc21285_low,  ARMCSR_BASE & 0x00ffffff
 
-#elif defined(CONFIG_ARCH_NEXUSPCI)
 		.macro	addruart,rx
-		ldr	\rx, =0xfff00000
+		mov	\rx, #dc21285_high
+		.if	dc21285_low
+		orr	\rx, \rx, #dc21285_low
+		.endif
 		.endm
 
 		.macro	senduart,rd,rx
-		str	\rd, [\rx, #0xc]
+		str	\rd, [\rx, #0x160]	@ UARTDR
 		.endm
 
 		.macro	busyuart,rd,rx
-1001:		ldr	\rd, [\rx, #0x4]
-		tst	\rd, #1 << 0
+1001:		ldr	\rd, [\rx, #0x178]	@ UARTFLG
+		tst	\rd, #1 << 3
 		bne	1001b
 		.endm
 
 		.macro	waituart,rd,rx
 		.endm
-
-#elif defined(CONFIG_ARCH_VNC)
+#endif
+#elif defined(CONFIG_ARCH_NEXUSPCI)
 		.macro	addruart,rx
-		mov	\rx, #0xff000000
-		orr	\rx, \rx, #0x00e00000
-		orr	\rx, \rx, #0x000003f8
+		ldr	\rx, =0xfff00000
 		.endm
 
 		.macro	senduart,rd,rx
-		strb	\rd, [\rx]
+		str	\rd, [\rx, #0xc]
 		.endm
 
 		.macro	busyuart,rd,rx
-1002:		ldrb	\rd, [\rx, #0x5]
-		and	\rd, \rd, #0x60
-		teq	\rd, #0x60
-		bne	1002b
+1001:		ldr	\rd, [\rx, #0x4]
+		tst	\rd, #1 << 0
+		bne	1001b
 		.endm
 
 		.macro	waituart,rd,rx
-1001:		ldrb	\rd, [\rx, #0x6]
-		tst	\rd, #0x10
-		beq	1001b
 		.endm
 #else
 #error Unknown architecture
@@ -475,8 +560,6 @@
 		mov	r1, r0
 		mov	r0, #0
 		b	1b
-
-		.ltorg
 
 		.bss
 hexbuf:		.space 16
diff -urN linux-2.2.18-pre8/arch/arm/kernel/hw-ebsa285.c linux/arch/arm/kernel/hw-ebsa285.c
--- linux-2.2.18-pre8/arch/arm/kernel/hw-ebsa285.c	Wed Dec 23 09:44:40 1998
+++ linux/arch/arm/kernel/hw-ebsa285.c	Wed Dec 31 16:00:00 1969
@@ -1,161 +0,0 @@
-/*
- * arch/arm/kernel/hw-ebsa286.c
- *
- * EBSA285 hardware specific functions
- *
- * Copyright (C) 1998 Russell King, Phil Blundel
- */
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ptrace.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-
-#include <asm/irq.h>
-#include <asm/system.h>
-
-extern int setup_arm_irq(int, struct irqaction *);
-
-extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set);
-extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr);
-extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq);
-
-static int irqmap_ebsa[] __initdata = { 9, 8, 18, 11 };
-static int irqmap_cats[] __initdata = { 18, 8, 9, 11 };
-
-__initfunc(static int ebsa_irqval(struct pci_dev *dev))
-{
-	unsigned char pin;
-	
-	pcibios_read_config_byte(dev->bus->number,
-				 dev->devfn,
-				 PCI_INTERRUPT_PIN,
-				 &pin);
-	
-	return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3];
-}
-
-__initfunc(static int cats_irqval(struct pci_dev *dev))
-{
-	if (dev->irq >= 128)
-		return 32 + (dev->irq & 0x1f);
-
-	switch (dev->irq) {
-	case 1:
-	case 2:
-	case 3:
-	case 4:
-		return irqmap_cats[dev->irq - 1];
-	case 0:
-		return 0;
-	}
-
-	printk("PCI: device %02x:%02x has unknown irq line %x\n",
-	       dev->bus->number, dev->devfn, dev->irq);
-	return 0;
-}
-
-__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev))
-{
-	char cmd;
-
-	/* sort out the irq mapping for this device */
-	switch (machine_type) {
-	case MACH_TYPE_EBSA285:
-		dev->irq = ebsa_irqval(dev);
-		break;
-	case MACH_TYPE_CATS:
-		dev->irq = cats_irqval(dev);
-		break;
-	}
-
-	/* Turn on bus mastering - boot loader doesn't
-	 * - perhaps it should! - dag
-	 */
-	pci_read_config_byte(dev, PCI_COMMAND, &cmd);
-	pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
-}
-
-static void irq_pci_err(int irq, void *dev_id, struct pt_regs *regs)
-{
-	const char *err = "unknown";
-	unsigned long cmd = *(unsigned long *)0xfe000004 & 0xffff;
-	unsigned long ctrl = *(unsigned long *)0xfe00013c & 0xffffde07;
-	static unsigned long next_warn[7];
-	int idx = 6;
-
-	switch(irq) {
-	case IRQ_PCIPARITY:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 31;
-		idx = 0;
-		err = "parity";
-		break;
-
-	case IRQ_PCITARGETABORT:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 28;
-		idx = 1;
-		err = "target abort";
-		break;
-
-	case IRQ_PCIMASTERABORT:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 29;
-		idx = 2;
-		err = "master abort";
-		break;
-
-	case IRQ_PCIDATAPARITY:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 24;
-		idx = 3;
-		err = "data parity";
-		break;
-
-	case IRQ_DISCARDTIMER:
-		*(unsigned long *)0xfe00013c = ctrl | 1 << 8;
-		idx = 4;
-		err = "discard timer";
-		break;
-
-	case IRQ_SERR:
-		*(unsigned long *)0xfe00013c = ctrl | 1 << 3;
-		idx = 5;
-		err = "system";
-		break;
-	}
-	if (time_after_eq(jiffies, next_warn[idx])) {
-		next_warn[idx] = jiffies + 3 * HZ / 100;
-		printk(KERN_ERR "PCI %s error detected\n", err);
-	}
-}
-
-static struct irqaction irq_pci_error = {
-	irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL
-};
-
-__initfunc(void pcibios_init_ebsa285(void))
-{
-	setup_arm_irq(IRQ_PCIPARITY, &irq_pci_error);
-	setup_arm_irq(IRQ_PCITARGETABORT, &irq_pci_error);
-	setup_arm_irq(IRQ_PCIMASTERABORT, &irq_pci_error);
-	setup_arm_irq(IRQ_PCIDATAPARITY, &irq_pci_error);
-	setup_arm_irq(IRQ_DISCARDTIMER, &irq_pci_error);
-	setup_arm_irq(IRQ_SERR, &irq_pci_error);
-
-	/*
-	 * Map our SDRAM at a known address in PCI space, just in case
-	 * the firmware had other ideas.  Using a nonzero base is slightly
-	 * bizarre but apparently necessary to avoid problems with some
-	 * video cards.
-	 *
-	 * We should really only do this if the central function is enabled.
-	 */
-	*(unsigned long *)0xfe000010 = 0;
-	*(unsigned long *)0xfe000018 = 0xe0000000;
-	*(unsigned long *)0xfe0000f8 = 0;
-	*(unsigned long *)0xfe0000fc = 0;
-	*(unsigned long *)0xfe000100 = 0x01fc0000;
-	*(unsigned long *)0xfe000104 = 0;
-	*(unsigned long *)0xfe000108 = 0x80000000;
-	*(unsigned long *)0xfe000004 = 0x17;
-}
diff -urN linux-2.2.18-pre8/arch/arm/kernel/hw-footbridge.c linux/arch/arm/kernel/hw-footbridge.c
--- linux-2.2.18-pre8/arch/arm/kernel/hw-footbridge.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/hw-footbridge.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,951 @@
+/*
+ * arch/arm/kernel/hw-footbridge.c
+ *
+ * Footbridge-dependent machine fixup
+ *
+ * Copyright (C) 1998, 1999 Russell King, Phil Blundell
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/dec21285.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#define IRDA_IO_BASE		0x180
+#define ETHER10_IO_BASE		0x301
+#define GP1_IO_BASE		0x338
+#define GP2_IO_BASE		0x33a
+#define DEC21143_IO_BASE	0x401
+#define DEC21143_MEM_BASE	0x00800000
+#define CYBER2000_MEM_BASE	0x01000000
+
+int	have_isa_bridge;
+
+extern int setup_arm_irq(int, struct irqaction *);
+extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set);
+extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr);
+extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq);
+extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
+
+#ifdef CONFIG_PCI
+
+static int irqmap_ebsa[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 };
+
+__initfunc(static int ebsa_irqval(struct pci_dev *dev))
+{
+	unsigned char pin;
+	
+	pcibios_read_config_byte(dev->bus->number,
+				 dev->devfn,
+				 PCI_INTERRUPT_PIN,
+				 &pin);
+	
+	return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3];
+}
+
+#ifdef CONFIG_CATS
+static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
+
+__initfunc(static int cats_irqval(struct pci_dev *dev))
+{
+	if (dev->irq >= 128)
+		return 16 + (dev->irq & 0x1f);
+
+	switch (dev->irq) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+		return irqmap_cats[dev->irq - 1];
+	case 0:
+		return 0;
+	}
+
+	printk("PCI: device %02x:%02x has unknown irq line %x\n",
+	       dev->bus->number, dev->devfn, dev->irq);
+	return 0;
+}
+#endif
+
+__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev))
+{
+	/* Latency timer of 32 */
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32);
+
+	/* 32-byte cache line size */
+	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8);
+
+	/* Set SysErr enable, Parity enable */
+	pci_set_cmd(dev, 0, PCI_COMMAND_FAST_BACK | PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+
+	/* If this device is an ISA bridge, set the
+	 * have_isa_bridge flag.  We will then go looking
+	 * for things like keyboard, etc
+	 */
+	if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA ||
+	    (dev->class >> 8) == PCI_CLASS_BRIDGE_EISA)
+		have_isa_bridge = !0;
+
+	/* sort out the irq mapping for this device */
+	switch (machine_arch_type) {
+	case MACH_TYPE_EBSA285:
+		dev->irq = ebsa_irqval(dev);
+		/* Turn on bus mastering - boot loader doesn't
+		 * - perhaps it should! - dag
+		 */
+		pci_set_cmd(dev, 0, PCI_COMMAND_MASTER);
+		break;
+
+#ifdef CONFIG_CATS
+	case MACH_TYPE_CATS:
+		dev->irq = cats_irqval(dev);
+		/* Turn on bus mastering - boot loader doesn't
+		 * - perhaps it should! - dag
+		 */
+		pci_set_cmd(dev, 0, PCI_COMMAND_MASTER);
+		break;
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+	case MACH_TYPE_NETWINDER:
+		/* disable ROM */
+		pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0);
+
+#define DEV(v,d) ((v)<<16|(d))
+		switch (DEV(dev->vendor, dev->device)) {
+		/* Ether 100 */
+		case DEV(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142):
+			pci_set_base_addr(dev, 0, DEC21143_IO_BASE);
+			pci_set_base_addr(dev, 1, DEC21143_MEM_BASE);
+			pci_set_cmd(dev, 0, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+			/* Put the chip to sleep in case the driver isn't loaded */
+			pci_write_config_dword(dev, 0x40, 0x80000000);
+			dev->irq = IRQ_NETWINDER_ETHER100;
+			break;
+
+		/* Ether 10 */
+		case DEV(PCI_VENDOR_ID_WINBOND2,0x5a5a):
+			pci_set_base_addr(dev, 0, ETHER10_IO_BASE);
+			pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO);
+			dev->irq = IRQ_NETWINDER_ETHER10;
+			break;
+
+		/* ISA bridge */
+		case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_83C553):
+			pci_set_base_addr(dev, 0, 0);
+			pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO);
+			/*
+			 * Enable all memory requests from ISA to be channeled to PCI
+			 */
+			pci_write_config_byte(dev, 0x48, 255);
+			/*
+			 * Disable ping-pong (as per errata)
+			 */
+			pci_write_config_byte(dev, 0x42, 0);
+			/*
+			 * Enable PCI packet retry
+			 */
+			pci_write_config_byte(dev, 0x40, 0x22);
+			/*
+			 * Do not use PCI CPU park enable, park on
+			 * last master, disable GAT bit
+			 */
+			pci_write_config_byte(dev, 0x83, 0x02);
+			/*
+			 * Default rotating priorities
+			 */
+			pci_write_config_byte(dev, 0x80, 0xe0);
+			/*
+			 * Rotate bank 4
+			 */
+			pci_write_config_byte(dev, 0x81, 0x01);
+			break;
+
+		/* IDE */
+		case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_82C105):
+			pci_set_base_addr(dev, 0, 0x1f1);
+			pci_set_base_addr(dev, 1, 0x3f5);
+			pci_set_base_addr(dev, 2, 0x171);
+			pci_set_base_addr(dev, 3, 0x375);
+			pci_set_base_addr(dev, 4, 0xe801);
+			pci_set_cmd(dev, PCI_COMMAND_MEMORY, PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+			dev->irq = IRQ_ISA_HARDDISK1;
+			break;
+
+		/* VGA */
+		case DEV(PCI_VENDOR_ID_INTERG,0x2000):
+			pci_set_base_addr(dev, 0, CYBER2000_MEM_BASE);
+			pci_set_cmd(dev, PCI_COMMAND_MASTER, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+			dev->irq = IRQ_NETWINDER_VGA;
+			break;
+		}
+#endif
+	}
+}
+
+static inline void
+report_pci_dev_error(void)
+{
+	struct pci_dev *dev;
+
+	for (dev = pci_devices; dev; dev = dev->next) {
+		unsigned short status;
+
+		pci_read_config_word(dev, PCI_STATUS, &status);
+		if (status & 0xf900) {
+			printk(KERN_DEBUG "PCI: [%04X:%04X] status = %X\n",
+				dev->vendor, dev->device, status);
+
+			pci_write_config_word(dev, PCI_STATUS, status & 0xf900);
+		}
+	}
+}
+#else
+#define report_pci_dev_error()
+#endif
+
+/*
+ * Warn on PCI errors.  Please report any occurances!
+ */
+static void
+irq_pci_err(int irq, void *dev_id, struct pt_regs *regs)
+{
+	static unsigned long next_warn;
+	unsigned long cmd       = *CSR_PCICMD & 0x0000ffff;
+	unsigned long ctrl      = (*CSR_SA110_CNTL) & 0xffffde07;
+	unsigned long irqstatus = *CSR_IRQ_RAWSTATUS;
+	int warn = time_after_eq(jiffies, next_warn);
+
+	ctrl |= SA110_CNTL_DISCARDTIMER;
+
+	if (warn) {
+		next_warn = jiffies + 3 * HZ / 100;
+		printk(KERN_DEBUG "PCI: ");
+	}
+
+	if (irqstatus & (1 << 31)) {
+		if (warn)
+			printk("parity error ");
+		cmd |= 1 << 31;
+	}
+
+	if (irqstatus & (1 << 30)) {
+		if (warn)
+			printk("target abort ");
+		cmd |= 1 << 28;
+	}
+
+	if (irqstatus & (1 << 29)) {
+		if (warn)
+			printk("master abort ");
+		cmd |= 1 << 29;
+	}
+
+	if (irqstatus & (1 << 28)) {
+		if (warn)
+			printk("data parity error ");
+		cmd |= 1 << 24;
+	}
+
+	if (irqstatus & (1 << 27)) {
+		if (warn)
+			printk("discard timer expired ");
+		ctrl &= ~SA110_CNTL_DISCARDTIMER;
+	}
+
+	if (irqstatus & (1 << 23)) {
+		if (warn)
+			printk("system error ");
+		ctrl |= SA110_CNTL_RXSERR;
+	}
+
+	if (warn)
+		printk("pc=[<%08lX>]\n", instruction_pointer(regs));
+
+	report_pci_dev_error();
+
+	*CSR_PCICMD = cmd;
+	*CSR_SA110_CNTL = ctrl;
+}
+
+static struct irqaction irq_pci_error = {
+	irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL
+};
+
+__initfunc(void pcibios_init_ebsa285(void))
+{
+	setup_arm_irq(IRQ_PCI_ERR, &irq_pci_error);
+}
+
+/*
+ * Netwinder stuff
+ */
+#ifdef CONFIG_ARCH_NETWINDER
+
+/*
+ * Winbond WB83977F accessibility stuff
+ */
+static inline void wb977_open(void)
+{
+	outb(0x87, 0x370);
+	outb(0x87, 0x370);
+}
+
+static inline void wb977_close(void)
+{
+	outb(0xaa, 0x370);
+}
+
+static inline void wb977_wb(int reg, int val)
+{
+	outb(reg, 0x370);
+	outb(val, 0x371);
+}
+
+static inline void wb977_ww(int reg, int val)
+{
+	outb(reg, 0x370);
+	outb(val >> 8, 0x371);
+	outb(reg + 1, 0x370);
+	outb(val, 0x371);
+}
+
+#define wb977_device_select(dev)	wb977_wb(0x07, dev)
+#define wb977_device_disable()		wb977_wb(0x30, 0x00)
+#define wb977_device_enable()		wb977_wb(0x30, 0x01)
+
+/*
+ * This is a lock for accessing ports GP1_IO_BASE and GP2_IO_BASE
+ */
+spinlock_t __netwinder_data gpio_lock = SPIN_LOCK_UNLOCKED;
+
+static unsigned int __netwinder_data current_gpio_op = 0;
+static unsigned int __netwinder_data current_gpio_io = 0;
+static unsigned int __netwinder_data current_cpld = 0;
+
+void __netwinder_text gpio_modify_op(int mask, int set)
+{
+	unsigned int new_gpio, changed;
+
+	new_gpio = (current_gpio_op & ~mask) | set;
+	changed = new_gpio ^ current_gpio_op;
+	current_gpio_op = new_gpio;
+
+	if (changed & 0xff)
+		outb(new_gpio, GP1_IO_BASE);
+	if (changed & 0xff00)
+		outb(new_gpio >> 8, GP2_IO_BASE);
+}
+
+static inline void __gpio_modify_io(int mask, int in)
+{
+	unsigned int new_gpio, changed;
+	int port;
+
+	new_gpio = (current_gpio_io & ~mask) | in;
+	changed = new_gpio ^ current_gpio_io;
+	current_gpio_io = new_gpio;
+
+	changed >>= 1;
+	new_gpio >>= 1;
+
+	wb977_device_select(7);
+
+	for (port = 0xe1; changed && port < 0xe8; changed >>= 1) {
+		wb977_wb(port, new_gpio & 1);
+
+		port += 1;
+		new_gpio >>= 1;
+	}
+
+	wb977_device_select(8);
+
+	for (port = 0xe8; changed && port < 0xec; changed >>= 1) {
+		wb977_wb(port, new_gpio & 1);
+
+		port += 1;
+		new_gpio >>= 1;
+	}
+}
+
+void __netwinder_text gpio_modify_io(int mask, int in)
+{
+	/* Open up the SuperIO chip */
+	wb977_open();
+
+	__gpio_modify_io(mask, in);
+
+	/* Close up the EFER gate */
+	wb977_close();
+}
+
+int __netwinder_text gpio_read(void)
+{
+	return inb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;
+}
+
+/*
+ * Initialise the Winbond W83977F global registers
+ */
+static inline void wb977_init_global(void)
+{
+	/*
+	 * Enable R/W config registers
+	 */
+	wb977_wb(0x26, 0x40);
+
+	/*
+	 * Power down FDC (not used)
+	 */
+	wb977_wb(0x22, 0xfe);
+
+	/*
+	 * GP12, GP11, CIRRX, IRRXH, GP10
+	 */
+	wb977_wb(0x2a, 0xc1);
+
+	/*
+	 * GP23, GP22, GP21, GP20, GP13
+	 */
+	wb977_wb(0x2b, 0x6b);
+
+	/*
+	 * GP17, GP16, GP15, GP14
+	 */
+	wb977_wb(0x2c, 0x55);
+}
+
+/*
+ * Initialise the Winbond W83977F printer port
+ */
+static inline void wb977_init_printer(void)
+{
+	wb977_device_select(1);
+
+	/*
+	 * mode 1 == EPP
+	 */
+	wb977_wb(0xf0, 0x01);
+}
+
+/*
+ * Initialise the Winbond W83977F keyboard controller
+ */
+static inline void wb977_init_keyboard(void)
+{
+	wb977_device_select(5);
+
+	/*
+	 * Keyboard controller address
+	 */
+	wb977_ww(0x60, 0x0060);
+	wb977_ww(0x62, 0x0064);
+
+	/*
+	 * Keyboard IRQ 1, active high, edge trigger
+	 */
+	wb977_wb(0x70, 1);
+	wb977_wb(0x71, 0x02);
+
+	/*
+	 * Mouse IRQ 5, active high, edge trigger
+	 */
+	wb977_wb(0x72, 5);
+	wb977_wb(0x73, 0x02);
+
+	/*
+	 * KBC 8MHz
+	 */
+	wb977_wb(0xf0, 0x40);
+
+	/*
+	 * Enable device
+	 */
+	wb977_device_enable();
+}
+
+/*
+ * Initialise the Winbond W83977F Infra-Red device
+ */
+static inline void wb977_init_irda(void)
+{
+	wb977_device_select(6);
+
+	/*
+	 * IR base address
+	 */
+	wb977_ww(0x60, IRDA_IO_BASE);
+
+	/*
+	 * IRDA IRQ 6, active high, edge trigger
+	 */
+	wb977_wb(0x70, 6);
+	wb977_wb(0x71, 0x02);
+
+	/*
+	 * RX DMA - ISA DMA 0
+	 */
+	wb977_wb(0x74, 0x00);
+
+	/*
+	 * TX DMA - Disable Tx DMA
+	 */
+	wb977_wb(0x75, 0x04);
+
+	/*
+	 * Append CRC, Enable bank selection
+	 */
+	wb977_wb(0xf0, 0x03);
+
+	/*
+	 * Enable device
+	 */
+	wb977_device_enable();
+}
+
+/*
+ * Initialise Winbond W83977F general purpose IO
+ */
+static inline void wb977_init_gpio(void)
+{
+	unsigned long flags;
+
+	/*
+	 * Set up initial I/O definitions
+	 */
+	current_gpio_io = -1;
+	__gpio_modify_io(-1, GPIO_DONE | GPIO_WDTIMER);
+
+	wb977_device_select(7);
+
+	/*
+	 * Group1 base address
+	 */
+	wb977_ww(0x60, GP1_IO_BASE);
+	wb977_ww(0x62, 0);
+	wb977_ww(0x64, 0);
+
+	/*
+	 * GP10 (Orage button) IRQ 10, active high, edge trigger
+	 */
+	wb977_wb(0x70, 10);
+	wb977_wb(0x71, 0x02);
+
+	/*
+	 * GP10: Debounce filter enabled, IRQ, input
+	 */
+	wb977_wb(0xe0, 0x19);
+
+	/*
+	 * Enable Group1
+	 */
+	wb977_device_enable();
+
+	wb977_device_select(8);
+
+	/*
+	 * Group2 base address
+	 */
+	wb977_ww(0x60, GP2_IO_BASE);
+
+	/*
+	 * Clear watchdog timer regs
+	 *  - timer disable
+	 */
+	wb977_wb(0xf2, 0x00);
+
+	/*
+	 *  - disable LED, no mouse nor keyboard IRQ
+	 */
+	wb977_wb(0xf3, 0x00);
+
+	/*
+	 *  - timer counting, disable power LED, disable timeouot
+	 */
+	wb977_wb(0xf4, 0x00);
+
+	/*
+	 * Enable group2
+	 */
+	wb977_device_enable();
+
+	/*
+	 * Set Group1/Group2 outputs
+	 */
+	spin_lock_irqsave(&gpio_lock, flags);
+	gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN);
+	spin_unlock_irqrestore(&gpio_loc, flags);
+}
+
+/*
+ * Initialise the Winbond W83977F chip.
+ */
+__initfunc(static void wb977_init(void))
+{
+	request_region(0x370, 2, "W83977AF configuration");
+
+	/*
+	 * Open up the SuperIO chip
+	 */
+	wb977_open();
+
+	/*
+	 * Initialise the global registers
+	 */
+	wb977_init_global();
+
+	/*
+	 * Initialise the various devices in
+	 * the multi-IO chip.
+	 */
+	wb977_init_printer();
+	wb977_init_keyboard();
+	wb977_init_irda();
+	wb977_init_gpio();
+
+	/*
+	 * Close up the EFER gate
+	 */
+	wb977_close();
+}
+
+void __netwinder_text cpld_modify(int mask, int set)
+{
+	int msk;
+
+	current_cpld = (current_cpld & ~mask) | set;
+
+	gpio_modify_io(GPIO_DATA, 0);
+	gpio_modify_op(GPIO_IOLOAD, 0);
+
+	for (msk = 8; msk; msk >>= 1) {
+		int bit = current_cpld & msk;
+
+		gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0);
+		gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK);
+	}
+
+	gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0);
+	gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK);
+	gpio_modify_op(GPIO_IOLOAD, 0);
+}
+
+__initfunc(static void cpld_init(void))
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	cpld_modify(-1, CPLD_UNMUTE | 4);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static unsigned char rwa_unlock[] __initdata =
+{ 0x00, 0x00, 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b,
+  0x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74,
+  0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 };
+
+#ifndef DEBUG
+#define dprintk if (0) printk
+#else
+#define dprintk printk
+#endif
+
+#define WRITE_RWA(r,v) do { outb((r), 0x279); outb((v), 0xa79); } while (0)
+
+static inline void rwa010_unlock(void)
+{
+	int i;
+
+	WRITE_RWA(2, 2);
+	mdelay(10);
+
+	for (i = 0; i < sizeof(rwa_unlock); i++)
+		outb(rwa_unlock[i], 0x279);
+}
+
+static inline void rwa010_read_ident(void)
+{
+	unsigned char si[9];
+	int i, j;
+
+	WRITE_RWA(3, 0);
+	WRITE_RWA(0, 128);
+
+	outb(1, 0x279);
+
+	mdelay(10);
+
+	dprintk("Identifier: ");
+	for (i = 0; i < 9; i++) {
+		si[i] = 0;
+		for (j = 0; j < 8; j++) {
+			int bit;
+			mdelay(1);
+			inb(0x203);
+			mdelay(1);
+			bit = inb(0x203);
+			dprintk("%02X ", bit);
+			si[i] |= bit << j;
+		}
+		mdelay(10);
+		dprintk("%02X ", si[i]);
+	}
+	dprintk("\n");
+}
+
+static inline void rwa010_global_init(void)
+{
+	WRITE_RWA(6, 2);	// Assign a card no = 2
+
+	dprintk("Card no = %d\n", inb(0x203));
+
+	WRITE_RWA(7, 3);
+	WRITE_RWA(0x30, 0);
+
+	WRITE_RWA(7, 4);
+	WRITE_RWA(0x30, 0);
+
+	WRITE_RWA(7, 2);
+	WRITE_RWA(0x30, 0);
+}
+
+static inline void rwa010_game_port_init(void)
+{
+	int i;
+
+	WRITE_RWA(7, 5);
+
+	dprintk("Slider base: ");
+	WRITE_RWA(0x61, 1);
+	i = inb(0x203);
+
+	WRITE_RWA(0x60, 2);
+	dprintk("%02X%02X (201)\n", inb(0x203), i);
+
+	WRITE_RWA(0x30, 1);
+}
+
+static inline void rwa010_waveartist_init(int base, int irq, int dma)
+{
+	int i;
+
+	WRITE_RWA(7, 0);
+
+	dprintk("WaveArtist base: ");
+	WRITE_RWA(0x61, base);
+	i = inb(0x203);
+
+	WRITE_RWA(0x60, base >> 8);
+	dprintk("%02X%02X (%X),", inb(0x203), i, base);
+
+	WRITE_RWA(0x70, irq);
+	dprintk(" irq: %d (%d),", inb(0x203), irq);
+
+	WRITE_RWA(0x74, dma);
+	dprintk(" dma: %d (%d)\n", inb(0x203), dma);
+
+	WRITE_RWA(0x30, 1);
+}
+
+static inline void rwa010_soundblaster_init(int sb_base, int al_base, int irq, int dma)
+{
+	int i;
+
+	WRITE_RWA(7, 1);
+
+	dprintk("SoundBlaster base: ");
+	WRITE_RWA(0x61, sb_base);
+	i = inb(0x203);
+
+	WRITE_RWA(0x60, sb_base >> 8);
+	dprintk("%02X%02X (%X),", inb(0x203), i, sb_base);
+
+	dprintk(" irq: ");
+	WRITE_RWA(0x70, irq);
+	dprintk("%d (%d),", inb(0x203), irq);
+
+	dprintk(" 8-bit DMA: ");
+	WRITE_RWA(0x74, dma);
+	dprintk("%d (%d)\n", inb(0x203), dma);
+
+	dprintk("AdLib base: ");
+	WRITE_RWA(0x63, al_base);
+	i = inb(0x203);
+
+	WRITE_RWA(0x62, al_base >> 8);
+	dprintk("%02X%02X (%X)\n", inb(0x203), i, al_base);
+
+	WRITE_RWA(0x30, 1);
+}
+
+static void rwa010_soundblaster_reset(void)
+{
+	int i;
+
+	outb(1, 0x226);
+	udelay(3);
+	outb(0, 0x226);
+
+	for (i = 0; i < 5; i++) {
+		if (inb(0x22e) & 0x80)
+			break;
+		mdelay(1);
+	}
+	if (i == 5)
+		printk("SoundBlaster: DSP reset failed\n");
+
+	dprintk("SoundBlaster DSP reset: %02X (AA)\n", inb(0x22a));
+
+	for (i = 0; i < 5; i++) {
+		if ((inb(0x22c) & 0x80) == 0)
+			break;
+		mdelay(1);
+	}
+
+	if (i == 5)
+		printk("SoundBlaster: DSP not ready\n");
+	else {
+		outb(0xe1, 0x22c);
+
+		dprintk("SoundBlaster DSP id: ");
+		i = inb(0x22a);
+		udelay(1);
+		i |= inb(0x22a) << 8;
+		dprintk("%04X\n", i);
+
+		for (i = 0; i < 5; i++) {
+			if ((inb(0x22c) & 0x80) == 0)
+				break;
+			mdelay(1);
+		}
+
+		if (i == 5)
+			printk("SoundBlaster: could not turn speaker off\n");
+
+		outb(0xd3, 0x22c);
+	}
+
+	/* turn on OPL3 */
+	outb(5, 0x38a);
+	outb(1, 0x38b);
+}
+
+__initfunc(static void rwa010_init(void))
+{
+	rwa010_unlock();
+	rwa010_read_ident();
+	rwa010_global_init();
+	rwa010_game_port_init();
+	rwa010_waveartist_init(0x250, 3, 7);
+	rwa010_soundblaster_init(0x220, 0x388, 3, 1);
+	rwa010_soundblaster_reset();
+}
+
+EXPORT_SYMBOL(gpio_lock);
+EXPORT_SYMBOL(gpio_modify_op);
+EXPORT_SYMBOL(gpio_modify_io);
+EXPORT_SYMBOL(cpld_modify);
+
+#endif
+
+#ifdef CONFIG_LEDS
+#define DEFAULT_LEDS	0
+#else
+#define DEFAULT_LEDS	GPIO_GREEN_LED
+#endif
+
+/*
+ * CATS stuff
+ */
+#ifdef CONFIG_CATS
+
+#define CONFIG_PORT	0x370
+#define INDEX_PORT	(CONFIG_PORT)
+#define DATA_PORT	(CONFIG_PORT + 1)
+
+static void __init cats_hw_init(void)
+{
+	/* Set Aladdin to CONFIGURE mode */
+	outb(0x51, CONFIG_PORT);
+	outb(0x23, CONFIG_PORT);
+
+	/* Select logical device 3 */
+	outb(0x07, INDEX_PORT);
+	outb(0x03, DATA_PORT);
+
+	/* Set parallel port to DMA channel 3, ECP+EPP1.9, 
+	   enable EPP timeout */
+	outb(0x74, INDEX_PORT);
+	outb(0x03, DATA_PORT);
+	
+	outb(0xf0, INDEX_PORT);
+	outb(0x0f, DATA_PORT);
+
+	outb(0xf1, INDEX_PORT);
+	outb(0x07, DATA_PORT);
+
+	/* Select logical device 4 */
+	outb(0x07, INDEX_PORT);
+	outb(0x04, DATA_PORT);
+
+	/* UART1 high speed mode */
+	outb(0xf0, INDEX_PORT);
+	outb(0x02, DATA_PORT);
+
+	/* Select logical device 5 */
+	outb(0x07, INDEX_PORT);
+	outb(0x05, DATA_PORT);
+
+	/* UART2 high speed mode */
+	outb(0xf0, INDEX_PORT);
+	outb(0x02, DATA_PORT);
+
+	/* Set Aladdin to RUN mode */
+	outb(0xbb, CONFIG_PORT);
+}
+
+#endif
+
+__initfunc(void hw_init(void))
+{
+	extern void register_isa_ports(unsigned int, unsigned int, 
+				       unsigned int);
+	register_isa_ports(DC21285_PCI_MEM, DC21285_PCI_IO, 0);
+#ifdef CONFIG_ARCH_NETWINDER
+	/*
+	 * this ought to have a better home...
+	 * Since this calls the above routines, which are
+	 * compiled only if CONFIG_ARCH_NETWINDER is set,
+	 * these should only be parsed by the compiler
+	 * in the same circumstance.
+	 */
+	if (machine_is_netwinder()) {
+		unsigned long flags;
+
+		wb977_init();
+		cpld_init();
+		rwa010_init();
+
+		spin_lock_irqsave(&gpio_lock, flags);
+		gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS);
+		spin_unlock_irqrestore(&gpio_lock, flags);
+	}
+#endif
+
+	if (machine_is_cats())
+		cats_hw_init();
+
+	leds_event(led_start);
+}
diff -urN linux-2.2.18-pre8/arch/arm/kernel/iic.c linux/arch/arm/kernel/iic.c
--- linux-2.2.18-pre8/arch/arm/kernel/iic.c	Sat Jul 18 11:55:23 1998
+++ linux/arch/arm/kernel/iic.c	Mon Dec  3 20:25:13 2001
@@ -7,20 +7,24 @@
  */
 
 #include <linux/delay.h>
+#include <linux/errno.h>
 
 #include <asm/system.h>
-#include <asm/io.h>
 #include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/ioc.h>
+
+#define FORCE_ONES	0xdc
 
 /*
  * if delay loop has been calibrated then us that,
  * else use IOC timer 1.
  */
-static void iic_delay (void)
+static void iic_delay(void)
 {
 	extern unsigned long loops_per_sec;
 	if (loops_per_sec != (1 << 12)) {
-		udelay(10);
+		udelay(100); /* was 10 */
 		return;
 	} else {
 		unsigned long flags;
@@ -30,7 +34,7 @@
 		outb(255,  IOC_T1LTCHH);
 		outb(0,    IOC_T1GO);
 		outb(1<<6, IOC_IRQCLRA);			/* clear T1 irq */
-		outb(4,    IOC_T1LTCHL);
+		outb(10,   IOC_T1LTCHL); /* was 4 */
 		outb(0,    IOC_T1LTCHH);
 		outb(0,    IOC_T1GO);
 		while ((inb(IOC_IRQSTATA) & (1<<6)) == 0);
@@ -38,124 +42,207 @@
 	}
 }
 
-static inline void iic_start (void)
+#define IIC_INIT()		dat = (inb(IOC_CONTROL) | FORCE_ONES) & ~3
+#define IIC_SET_DAT		outb(dat|=1, IOC_CONTROL);
+#define IIC_CLR_DAT		outb(dat&=~1, IOC_CONTROL);
+#define IIC_SET_CLK		outb(dat|=2, IOC_CONTROL);
+#define IIC_CLR_CLK		outb(dat&=~2, IOC_CONTROL);
+#define IIC_DELAY		iic_delay();
+#define IIC_READ_DATA()		(inb(IOC_CONTROL) & 1)
+
+static inline void iic_set_lines(int clk, int dat)
 {
-	unsigned char out;
+	int old;
 
-	out = inb(IOC_CONTROL) | 0xc2;
+	old = inb(IOC_CONTROL) | FORCE_ONES;
 
-	outb(out, IOC_CONTROL);
-	iic_delay();
+	old &= ~3;
+
+	if (clk)
+		old |= 2;
+	if (dat)
+		old |= 1;
+
+	outb(old, IOC_CONTROL);
 
-	outb(out ^ 1, IOC_CONTROL);
 	iic_delay();
 }
 
-static inline void iic_stop (void)
+static inline unsigned int iic_read_data(void)
 {
-	unsigned char out;
+	return inb(IOC_CONTROL) & 1;
+}
 
-	out = inb(IOC_CONTROL) | 0xc3;
+/*
+ * C: ==~~_
+ * D: =~~__
+ */
+static inline void iic_start(void)
+{
+	unsigned int dat;
 
-	iic_delay();
-	outb(out ^ 1, IOC_CONTROL);
+	IIC_INIT();
 
-	iic_delay();
-	outb(out, IOC_CONTROL);
+	IIC_SET_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
+
+	IIC_CLR_DAT
+	IIC_DELAY
+	IIC_CLR_CLK
+	IIC_DELAY
 }
 
-static int iic_sendbyte (unsigned char b)
+/*
+ * C: __~~
+ * D: =__~
+ */
+static inline void iic_stop(void)
 {
-	unsigned char out, in;
-	int i;
+	unsigned int dat;
 
-	out = (inb(IOC_CONTROL) & 0xfc) | 0xc0;
+	IIC_INIT();
 
-	outb(out, IOC_CONTROL);
-	for (i = 7; i >= 0; i--) {
-		unsigned char c;
-		c = out | ((b & (1 << i)) ? 1 : 0);
+	IIC_CLR_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
+	IIC_SET_DAT
+	IIC_DELAY
+}
 
-		outb(c, IOC_CONTROL);
-		iic_delay();
+/*
+ * C: __~_
+ * D: =___
+ */
+static inline void iic_acknowledge(void)
+{
+	unsigned int dat;
 
-		outb(c | 2, IOC_CONTROL);
-		iic_delay();
+	IIC_INIT();
 
-		outb(c, IOC_CONTROL);
-	}
-	outb(out | 1, IOC_CONTROL);
-	iic_delay();
+	IIC_CLR_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
+	IIC_CLR_CLK
+	IIC_DELAY
+}
 
-	outb(out | 3, IOC_CONTROL);
-	iic_delay();
+/*
+ * C: __~_
+ * D: =~H~
+ */
+static inline int iic_is_acknowledged(void)
+{
+	unsigned int dat, ack_bit;
 
-	in = inb(IOC_CONTROL) & 1;
+	IIC_INIT();
 
-	outb(out | 1, IOC_CONTROL);
-	iic_delay();
+	IIC_SET_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
 
-	outb(out, IOC_CONTROL);
-	iic_delay();
+	ack_bit = IIC_READ_DATA();
 
-	if(in) {
-		printk("No acknowledge from RTC\n");
-		return 1;
-	} else
-		return 0;
+	IIC_CLR_CLK
+	IIC_DELAY
+
+	return ack_bit == 0;
+}
+
+/*
+ * C: _~__~__~__~__~__~__~__~_
+ * D: =DDXDDXDDXDDXDDXDDXDDXDD
+ */
+static void iic_sendbyte(unsigned int b)
+{
+	unsigned int dat, i;
+
+	IIC_INIT();
+
+	for (i = 0; i < 8; i++) {
+		if (b & 128)
+			IIC_SET_DAT
+		else
+			IIC_CLR_DAT
+		IIC_DELAY
+
+		IIC_SET_CLK
+		IIC_DELAY
+		IIC_CLR_CLK
+		IIC_DELAY
+
+		b <<= 1;
+	}
 }
 
-static unsigned char iic_recvbyte (void)
+/*
+ * C: __~_~_~_~_~_~_~_~_
+ * D: =~HHHHHHHHHHHHHHHH
+ */
+static unsigned char iic_recvbyte(void)
 {
-	unsigned char out, in;
-	int i;
+	unsigned int dat, i, in;
+
+	IIC_INIT();
 
-	out = (inb(IOC_CONTROL) & 0xfc) | 0xc0;
+	IIC_SET_DAT
+	IIC_DELAY
 
-	outb(out, IOC_CONTROL);
 	in = 0;
-	for (i = 7; i >= 0; i--) {
-		outb(out | 1, IOC_CONTROL);
-		iic_delay();
-		outb(out | 3, IOC_CONTROL);
-		iic_delay();
-		in = (in << 1) | (inb(IOC_CONTROL) & 1);
-		outb(out | 1, IOC_CONTROL);
-		iic_delay();
+	for (i = 0; i < 8; i++) {
+		IIC_SET_CLK
+		IIC_DELAY
+
+		in = (in << 1) | IIC_READ_DATA();
+
+		IIC_CLR_CLK
+		IIC_DELAY
 	}
-	outb(out, IOC_CONTROL);
-	iic_delay();
-	outb(out | 2, IOC_CONTROL);
-	iic_delay();
 
 	return in;
 }
 
-void iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
+int iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
 {
-	iic_start();
+	int i, err = -EIO;
 
-	if (iic_sendbyte(addr & 0xfe))
+	iic_start();
+	iic_sendbyte(addr & 0xfe);
+	if (!iic_is_acknowledged())
 		goto error;
 
-	if (iic_sendbyte(loc))
+	iic_sendbyte(loc);
+	if (!iic_is_acknowledged())
 		goto error;
 
 	if (addr & 1) {
-		int i;
-
-		for (i = 0; i < len; i++)
-			if (iic_sendbyte (buf[i]))
-				break;
-	} else {
-		int i;
-
 		iic_stop();
 		iic_start();
 		iic_sendbyte(addr|1);
-		for (i = 0; i < len; i++)
-			buf[i] = iic_recvbyte ();
+		if (!iic_is_acknowledged())
+			goto error;
+
+		for (i = 0; i < len - 1; i++) {
+			buf[i] = iic_recvbyte();
+			iic_acknowledge();
+		}
+		buf[i] = iic_recvbyte();
+	} else {
+		for (i = 0; i < len; i++) {
+			iic_sendbyte(buf[i]);
+
+			if (!iic_is_acknowledged())
+				goto error;
+		}
 	}
+
+	err = 0;
 error:
 	iic_stop();
+
+	return err;
 }
diff -urN linux-2.2.18-pre8/arch/arm/kernel/ioport.c linux/arch/arm/kernel/ioport.c
--- linux-2.2.18-pre8/arch/arm/kernel/ioport.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/ioport.c	Mon Dec  3 20:25:13 2001
@@ -1,29 +1,36 @@
 /*
  * linux/arch/arm/kernel/ioport.c
  *
- * Io-port support is not used for ARM
+ * IO permission support for ARM.
  */
 
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/ioport.h>
+#include <linux/mm.h>
 
-/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
-/*asmlinkage void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
-{
-}*/
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
 
-asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
+#ifdef CONFIG_CPU_32
+asmlinkage int sys_iopl(unsigned long turn_on)
 {
-	return -ENOSYS;
-}
+	if (turn_on && !capable(CAP_SYS_RAWIO))
+		return -EPERM;
 
-asmlinkage int sys_iopl(long ebx,long ecx,long edx,
-	     long esi, long edi, long ebp, long eax, long ds,
-	     long es, long fs, long gs, long orig_eax,
-	     long eip,long cs,long eflags,long esp,long ss)
+	/*
+	 * We only support an on_off approach
+	 */
+	modify_domain(DOMAIN_IO, turn_on ? DOMAIN_MANAGER : DOMAIN_CLIENT);
+
+	return 0;
+}
+#else
+asmlinkage int sys_iopl(unsigned long turn_on)
 {
 	return -ENOSYS;
 }
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c
--- linux-2.2.18-pre8/arch/arm/kernel/irq.c	Wed May  3 17:16:30 2000
+++ linux/arch/arm/kernel/irq.c	Mon Dec  3 20:25:13 2001
@@ -23,7 +23,6 @@
 #include <linux/sched.h>
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
-#include <linux/timex.h>
 #include <linux/malloc.h>
 #include <linux/random.h>
 #include <linux/smp.h>
@@ -32,7 +31,6 @@
 
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/pgtable.h>
 #include <asm/system.h>
 
 #ifndef SMP
@@ -46,10 +44,22 @@
 #define cliIF()
 #endif
 
+/*
+ * Maximum IRQ count.  Currently, this is arbitary.
+ * However, it should not be set too low to prevent
+ * false triggering.  Conversely, if it is set too
+ * high, then you could miss a stuck IRQ.
+ *
+ * Maybe we ought to set a timer and re-enable the
+ * IRQ at a later time?
+ */
+#define MAX_IRQ_CNT	100000
+
 unsigned int local_bh_count[NR_CPUS];
 unsigned int local_irq_count[NR_CPUS];
 spinlock_t irq_controller_lock;
 
+int setup_arm_irq(int, struct irqaction *);
 extern int get_fiq_list(char *);
 extern void init_FIQ(void);
 
@@ -60,17 +70,29 @@
 	unsigned int	 probing  : 1;		/* IRQ in use for a probe     */
 	unsigned int	 probe_ok : 1;		/* IRQ can be used for probe  */
 	unsigned int	 valid    : 1;		/* IRQ claimable	      */
-	unsigned int	 unused   :26;
+	unsigned int	 noautoenable : 1;	/* don't automatically enable IRQ */
+	unsigned int	 unused   :25;
 	void (*mask_ack)(unsigned int irq);	/* Mask and acknowledge IRQ   */
 	void (*mask)(unsigned int irq);		/* Mask IRQ		      */
 	void (*unmask)(unsigned int irq);	/* Unmask IRQ		      */
 	struct irqaction *action;
-	unsigned int	 unused2[3];
+	/*
+	 * IRQ lock detection
+	 */
+	unsigned int	 lck_cnt;
+	unsigned int	 lck_pc;
+	unsigned int	 lck_jif;
 };
 
 static struct irqdesc irq_desc[NR_IRQS];
 
 /*
+ * Get architecture specific interrupt handlers
+ * and interrupt initialisation.
+ */
+#include <asm/arch/irq.h>
+
+/*
  * Dummy mask/unmask handler
  */
 static void dummy_mask_unmask_irq(unsigned int irq)
@@ -94,10 +116,12 @@
 
 	spin_lock_irqsave(&irq_controller_lock, flags);
 	cliIF();
-	irq_desc[irq].enabled = 1;
 	irq_desc[irq].probing = 0;
 	irq_desc[irq].triggered = 0;
-	irq_desc[irq].unmask(irq);
+	if (!irq_desc[irq].noautoenable) {
+		irq_desc[irq].enabled = 1;
+		irq_desc[irq].unmask(irq);
+	}
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
 
@@ -119,21 +143,52 @@
 		*p++ = '\n';
 	}
 
-#ifdef CONFIG_ACORN
+#ifdef CONFIG_ARCH_ACORN
 	p += get_fiq_list(p);
 #endif
 	return p - buf;
 }
 
 /*
+ * IRQ lock detection.
+ *
+ * Hopefully, this should get us out of a few locked situations.
+ * However, it may take a while for this to happen, since we need
+ * a large number if IRQs to appear in the same jiffie with the
+ * same instruction pointer (or within 2 instructions).
+ */
+static void check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs)
+{
+	unsigned long instr_ptr = instruction_pointer(regs);
+
+	if (desc->lck_jif == jiffies &&
+	    desc->lck_pc >= instr_ptr && desc->lck_pc < instr_ptr + 8) {
+		desc->lck_cnt += 1;
+
+		if (desc->lck_cnt > MAX_IRQ_CNT) {
+			printk(KERN_ERR "IRQ LOCK: IRQ%d is locking the system, disabled\n", irq);
+			disable_irq(irq);
+		}
+	} else {
+		desc->lck_cnt = 0;
+		desc->lck_pc  = instruction_pointer(regs);
+		desc->lck_jif = jiffies;
+	}
+}
+
+/*
  * do_IRQ handles all normal device IRQ's
  */
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
-	struct irqdesc * desc = irq_desc + irq;
+	struct irqdesc * desc;
 	struct irqaction * action;
 	int status, cpu;
 
+	irq = fixup_irq(irq);
+
+	desc = irq_desc + irq;
+
 	spin_lock(&irq_controller_lock);
 	desc->mask_ack(irq);
 	spin_unlock(&irq_controller_lock);
@@ -174,6 +229,12 @@
 		}
 	}
 
+	/*
+	 * Debug measure - hopefully we can continue if an
+	 * IRQ lockup problem occurs...
+	 */
+	check_irq_lock(desc, irq, regs);
+
 	irq_exit(cpu, irq);
 
 	/*
@@ -181,15 +242,10 @@
 	 * a return code from the irq handler to tell us
 	 * whether the handler wants us to do software bottom
 	 * half handling or not..
-	 *
-	 * ** IMPORTANT NOTE: do_bottom_half() ENABLES IRQS!!! **
-	 * **  WE MUST DISABLE THEM AGAIN, ELSE IDE DISKS GO   **
-	 * **                       AWOL                       **
 	 */
 	if (1) {
 		if (bh_active & bh_mask)
 			do_bottom_half();
-		__cli();
 	}
 }
 
@@ -227,11 +283,27 @@
 	struct irqaction *old, **p;
 	unsigned long flags;
 
-	if (new->flags & SA_SAMPLE_RANDOM)
+	/*
+	 * Some drivers like serial.c use request_irq() heavily,
+	 * so we have to be careful not to interfere with a
+	 * running system.
+	 */
+	if (new->flags & SA_SAMPLE_RANDOM) {
+		/*
+		 * This function might sleep, we want to call it first,
+		 * outside of the atomic block.
+		 * Yes, this might clear the entropy pool if the wrong
+		 * driver is attempted to be loaded, without actually
+		 * installing a new handler, but is this really a problem,
+		 * only the sysadmin is able to do this.
+		 */
 	        rand_initialize_irq(irq);
+	}
 
+	/*
+	 * The following block of code has to be executed atomically
+	 */
 	spin_lock_irqsave(&irq_controller_lock, flags);
-
 	p = &irq_desc[irq].action;
 	if ((old = *p) != NULL) {
 		/* Can't share interrupts unless both agree to */
@@ -252,28 +324,24 @@
 
 	if (!shared) {
 		irq_desc[irq].nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
-		irq_desc[irq].enabled = 1;
 		irq_desc[irq].probing = 0;
-		irq_desc[irq].unmask(irq);
+		if (!irq_desc[irq].noautoenable) {
+			irq_desc[irq].enabled = 1;
+			irq_desc[irq].unmask(irq);
+		}
 	}
 
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 	return 0;
 }
 
-/*
- * Using "struct sigaction" is slightly silly, but there
- * are historical reasons and it works well, so..
- */
 int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 		 unsigned long irq_flags, const char * devname, void *dev_id)
 {
 	unsigned long retval;
 	struct irqaction *action;
 
-	if (!irq_desc[irq].valid)
-		return -EINVAL;
-	if (!handler)
+	if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler)
 		return -EINVAL;
 
 	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
@@ -299,28 +367,30 @@
 	struct irqaction * action, **p;
 	unsigned long flags;
 
-	if (!irq_desc[irq].valid) {
+	if (irq >= NR_IRQS || !irq_desc[irq].valid) {
 		printk(KERN_ERR "Trying to free IRQ%d\n",irq);
 #ifdef CONFIG_DEBUG_ERRORS
 		__backtrace();
 #endif
 		return;
 	}
+
+	spin_lock_irqsave(&irq_controller_lock, flags);
 	for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
 		if (action->dev_id != dev_id)
 			continue;
 
 	    	/* Found it - now free it */
-		save_flags_cli (flags);
 		*p = action->next;
-		restore_flags (flags);
 		kfree(action);
-		return;
+		goto out;
 	}
 	printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
 #ifdef CONFIG_DEBUG_ERRORS
 	__backtrace();
 #endif
+out:
+	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
 
 /* Start the interrupt probing.  Unlike other architectures,
@@ -346,7 +416,6 @@
 			continue;
 
 		irq_desc[i].probing = 1;
-		irq_desc[i].enabled = 1;
 		irq_desc[i].triggered = 0;
 		irq_desc[i].unmask(i);
 		irqs += 1;
@@ -364,7 +433,8 @@
 	 */
 	spin_lock_irq(&irq_controller_lock);
 	for (i = 0; i < NR_IRQS; i++) {
-		if (irq_desc[i].probing && irq_desc[i].triggered) {
+		if (irq_desc[i].probing &&
+		    irq_desc[i].triggered) {
 			irq_desc[i].probing = 0;
 			irqs -= 1;
 		}
@@ -383,7 +453,7 @@
 int probe_irq_off(unsigned long irqs)
 {
 	unsigned int i;
-	int irq_found = -1;
+	int irq_found = NO_IRQ;
 
 	/*
 	 * look at the interrupts, and find exactly one
@@ -393,7 +463,7 @@
 	for (i = 0; i < NR_IRQS; i++) {
 		if (irq_desc[i].probing &&
 		    irq_desc[i].triggered) {
-			if (irq_found != -1) {
+			if (irq_found != NO_IRQ) {
 				irq_found = NO_IRQ;
 				goto out;
 			}
@@ -405,21 +475,19 @@
 		irq_found = NO_IRQ;
 out:
 	spin_unlock_irq(&irq_controller_lock);
+
 	return irq_found;
 }
 
-/*
- * Get architecture specific interrupt handlers
- * and interrupt initialisation.
- */
-#include <asm/arch/irq.h>
-
-unsigned long __init init_IRQ(unsigned long memory)
+unsigned long  __init init_IRQ(unsigned long memory)
 {
 	extern void init_dma(void);
 	int irq;
 
 	for (irq = 0; irq < NR_IRQS; irq++) {
+		irq_desc[irq].probe_ok = 0;
+		irq_desc[irq].valid    = 0;
+		irq_desc[irq].noautoenable = 0;
 		irq_desc[irq].mask_ack = dummy_mask_unmask_irq;
 		irq_desc[irq].mask     = dummy_mask_unmask_irq;
 		irq_desc[irq].unmask   = dummy_mask_unmask_irq;
diff -urN linux-2.2.18-pre8/arch/arm/kernel/isa.c linux/arch/arm/kernel/isa.c
--- linux-2.2.18-pre8/arch/arm/kernel/isa.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/isa.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,47 @@
+/*
+ * arch/arm/kernel/isa.c
+ *
+ * ISA shared memory and I/O port support
+ *
+ * Copyright (C) 1999 Phil Blundell
+ */
+
+/* 
+ * Nothing about this is actually ARM specific.  One day we could move
+ * it into kernel/resource.c or some place like that.  
+ */
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/linkage.h>
+#include <linux/fs.h>
+#include <linux/sysctl.h>
+#include <linux/init.h>
+
+static unsigned int isa_membase, isa_portbase, isa_portshift;
+
+static ctl_table ctl_isa_vars[4] = {
+	{BUS_ISA_MEM_BASE, "membase", &isa_membase, 
+	 sizeof(isa_membase), 0444, NULL, &proc_dointvec},
+	{BUS_ISA_PORT_BASE, "portbase", &isa_portbase, 
+	 sizeof(isa_portbase), 0444, NULL, &proc_dointvec},
+	{BUS_ISA_PORT_SHIFT, "portshift", &isa_portshift, 
+	 sizeof(isa_portshift), 0444, NULL, &proc_dointvec},
+	{0}
+};
+
+static struct ctl_table_header *isa_sysctl_header;
+
+static ctl_table ctl_isa[2] = {{BUS_ISA, "isa", NULL, 0, 0555, ctl_isa_vars},
+			       {0}};
+static ctl_table ctl_bus[2] = {{CTL_BUS, "bus", NULL, 0, 0555, ctl_isa},
+			       {0}};
+
+void __init
+register_isa_ports(unsigned int membase, unsigned int portbase, unsigned int portshift)
+{
+	isa_membase = membase;
+	isa_portbase = portbase;
+	isa_portshift = portshift;
+	isa_sysctl_header = register_sysctl_table(ctl_bus, 0);
+}
diff -urN linux-2.2.18-pre8/arch/arm/kernel/leds-ebsa110.c linux/arch/arm/kernel/leds-ebsa110.c
--- linux-2.2.18-pre8/arch/arm/kernel/leds-ebsa110.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/leds-ebsa110.c	Mon Dec  3 20:25:13 2001
@@ -7,11 +7,13 @@
  *
  *  - Red - toggles state every 50 timer interrupts
  */
+#include <linux/module.h>
+
 #include <asm/hardware.h>
 #include <asm/leds.h>
 #include <asm/system.h>
 
-void leds_event(led_event_t ledevt)
+void ebsa110_leds_event(led_event_t ledevt)
 {
 	unsigned long flags;
 
@@ -28,3 +30,7 @@
 
 	restore_flags(flags);
 }
+
+void (*leds_event)(led_event_t) = ebsa110_leds_event;
+
+EXPORT_SYMBOL(leds_event);
diff -urN linux-2.2.18-pre8/arch/arm/kernel/leds-ebsa285.c linux/arch/arm/kernel/leds-ebsa285.c
--- linux-2.2.18-pre8/arch/arm/kernel/leds-ebsa285.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/kernel/leds-ebsa285.c	Wed Dec 31 16:00:00 1969
@@ -1,44 +0,0 @@
-/*
- * arch/arm/kernel/leds-ebsa285.c
- *
- * Copyright (C) 1998 Russell King
- *
- * EBSA-285 LED control routines.  We use the leds as follows:
- *
- *  - Green - toggles state every 50 timer interrupts
- *  - Amber - On if system is not idle
- *  - Red   - currently unused
- */
-#include <asm/hardware.h>
-#include <asm/leds.h>
-#include <asm/system.h>
-
-static char led_state = XBUS_LED_RED | XBUS_LED_GREEN;
-
-void leds_event(led_event_t ledevt)
-{
-	unsigned long flags;
-
-	save_flags_cli(flags);
-
-	switch(ledevt) {
-	case led_idle_start:
-		led_state |= XBUS_LED_AMBER;
-		break;
-
-	case led_idle_end:
-		led_state &= ~XBUS_LED_AMBER;
-		break;
-
-	case led_timer:
-		led_state ^= XBUS_LED_GREEN;
-		break;
-
-	default:
-		break;
-	}
-
-	restore_flags(flags);
-
-	*XBUS_LEDS = led_state;
-}
diff -urN linux-2.2.18-pre8/arch/arm/kernel/leds-footbridge.c linux/arch/arm/kernel/leds-footbridge.c
--- linux-2.2.18-pre8/arch/arm/kernel/leds-footbridge.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/leds-footbridge.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,245 @@
+/*
+ * arch/arm/kernel/leds-footbridge.c
+ *
+ * Copyright (C) 1998-1999 Russell King
+ *
+ * EBSA-285 and NetWinder LED control routines.
+ *
+ * The EBSA-285 uses the leds as follows:
+ *  - Green - toggles state every 50 timer interrupts
+ *  - Amber - On if system is not idle
+ *  - Red   - currently unused
+ *
+ * The Netwinder uses the leds as follows:
+ *  - Green - toggles state every 50 timer interrupts
+ *  - Red   - On if the system is not idle
+ *
+ * Changelog:
+ *   02-05-1999	RMK	Various cleanups
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/spinlock.h>
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+static char led_state;
+static char hw_led_state;
+
+static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED;
+extern spinlock_t gpio_lock;
+
+#ifdef CONFIG_FOOTBRIDGE
+
+static void __ebsa285_text ebsa285_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+
+	switch (evt) {
+	case led_start:
+		hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN;
+#ifndef CONFIG_LEDS_IDLE
+		hw_led_state |= XBUS_LED_AMBER;
+#endif
+		led_state |= LED_STATE_ENABLED;
+		break;
+
+	case led_stop:
+		led_state &= ~LED_STATE_ENABLED;
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= XBUS_LED_GREEN;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state |= XBUS_LED_RED;
+		break;
+
+	case led_idle_end:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state &= ~XBUS_LED_RED;
+		break;
+#endif
+
+	case led_green_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~XBUS_LED_GREEN;
+		break;
+
+	case led_green_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= XBUS_LED_GREEN;
+		break;
+
+	case led_amber_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~XBUS_LED_AMBER;
+		break;
+
+	case led_amber_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= XBUS_LED_AMBER;
+		break;
+
+	case led_red_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~XBUS_LED_RED;
+		break;
+
+	case led_red_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= XBUS_LED_RED;
+		break;
+
+	default:
+		break;
+	}
+
+	if  (led_state & LED_STATE_ENABLED)
+		*XBUS_LEDS = hw_led_state;
+
+	spin_unlock_irqrestore(&leds_lock, flags);
+}
+
+#endif
+
+#ifdef CONFIG_ARCH_NETWINDER
+
+static void __netwinder_text netwinder_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+
+	switch (evt) {
+	case led_start:
+		led_state |= LED_STATE_ENABLED;
+		hw_led_state = GPIO_GREEN_LED;
+		break;
+
+	case led_stop:
+		led_state &= ~LED_STATE_ENABLED;
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		hw_led_state = 0;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = 0;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= GPIO_GREEN_LED;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state &= ~GPIO_RED_LED;
+		break;
+
+	case led_idle_end:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state |= GPIO_RED_LED;
+		break;
+#endif
+
+	case led_green_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= GPIO_GREEN_LED;
+		break;
+
+	case led_green_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~GPIO_GREEN_LED;
+		break;
+
+	case led_amber_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= GPIO_GREEN_LED | GPIO_RED_LED;
+		break;
+
+	case led_amber_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~(GPIO_GREEN_LED | GPIO_RED_LED);
+		break;
+
+	case led_red_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= GPIO_RED_LED;
+		break;
+
+	case led_red_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~GPIO_RED_LED;
+		break;
+
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&leds_lock, flags);
+
+	if  (led_state & LED_STATE_ENABLED) {
+		spin_lock_irqsave(&gpio_lock, flags);
+		gpio_modify_op(GPIO_RED_LED | GPIO_GREEN_LED, hw_led_state);
+		spin_unlock_irqrestore(&gpio_lock, flags);
+	}
+}
+
+#endif
+
+static void dummy_leds_event(led_event_t evt)
+{
+}
+
+static void __init
+init_leds_event(led_event_t evt)
+{
+	leds_event = dummy_leds_event;
+
+#ifdef CONFIG_FOOTBRIDGE
+	if (machine_is_ebsa285() || machine_is_co285())
+		leds_event = ebsa285_leds_event;
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+	if (machine_is_netwinder())
+		leds_event = netwinder_leds_event;
+#endif
+
+	leds_event(evt);
+}
+
+void (*leds_event)(led_event_t) = init_leds_event;
+
+EXPORT_SYMBOL(leds_event);
diff -urN linux-2.2.18-pre8/arch/arm/kernel/oldlatches.c linux/arch/arm/kernel/oldlatches.c
--- linux-2.2.18-pre8/arch/arm/kernel/oldlatches.c	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/kernel/oldlatches.c	Mon Dec  3 20:25:13 2001
@@ -4,6 +4,7 @@
  * (c) David Alan Gilbert 1995/1996
  */
 #include <linux/kernel.h>
+#include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/hardware.h>
@@ -40,7 +41,7 @@
 }
 #endif
 
-void oldlatch_init(void)
+void __init oldlatch_init(void)
 {
     printk("oldlatch: init\n");
 #ifdef LATCHAADDR
diff -urN linux-2.2.18-pre8/arch/arm/kernel/process.c linux/arch/arm/kernel/process.c
--- linux-2.2.18-pre8/arch/arm/kernel/process.c	Mon Aug  9 12:04:38 1999
+++ linux/arch/arm/kernel/process.c	Mon Dec  3 20:25:13 2001
@@ -32,11 +32,11 @@
 #include <linux/init.h>
 
 #include <asm/uaccess.h>
-#include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/io.h>
 
 extern char *processor_modes[];
+extern void setup_mm_for_reboot(char mode);
 
 asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
 
@@ -53,46 +53,57 @@
 }
 
 /*
- * The idle loop on an arm..
+ * The idle loop on an ARM...
  */
 asmlinkage int sys_idle(void)
 {
-	int ret = -EPERM;
+	current->priority = 0;
+	current->counter = -100;
 
-	lock_kernel();
-	if (current->pid != 0)
-		goto out;
 	/* endless idle loop with no priority at all */
-	current->priority = -100;
-	for (;;)
-	{
+	while (1) {
+		if (!current->need_resched && !hlt_counter)
+			arch_do_idle();
+		schedule();
+#ifndef CONFIG_NO_PGT_CACHE
 		check_pgt_cache();
-#if 0 //def ARCH_IDLE_OK
-		if (!hlt_counter && !current->need_resched)
-			proc_idle ();
 #endif
-		run_task_queue(&tq_scheduler);
-		schedule();
 	}
-	ret = 0;
-out:
-	unlock_kernel();
-	return ret;
 }
 
+static char reboot_mode = 'h';
+
 __initfunc(void reboot_setup(char *str, int *ints))
 {
+	reboot_mode = str[0];
 }
 
-/*
- * This routine reboots the machine by resetting the expansion cards via
- * their loaders, turning off the processor cache (if ARM3), copying the
- * first instruction of the ROM to 0, and executing it there.
- */
 void machine_restart(char * __unused)
 {
-	proc_hard_reset ();
-	arch_hard_reset ();
+	/*
+	 * Clean and disable cache, turn off interrupts
+	 */
+	processor._proc_fin();
+
+	/*
+	 * Tell the mm system that we are going to reboot -
+	 * we may need to insert some 1:1 mappings so that
+	 * soft boot works.
+	 */
+	setup_mm_for_reboot(reboot_mode);
+
+	/*
+	 * Now call the architecture specific reboot code.
+	 */
+	arch_reset(reboot_mode);
+
+	/*
+	 * Whoops - the architecture was unable to reboot.
+	 * Tell the user!
+	 */
+	mdelay(1000);
+	panic("Reboot failed -- System halted\n");
+	while (1);
 }
 
 void machine_halt(void)
@@ -148,6 +159,67 @@
 }
 
 /*
+ * Task structure and kernel stack allocation.
+ *
+ * Taken from the i386 version.
+ */
+#ifdef CONFIG_CPU_32
+#define EXTRA_TASK_STRUCT	8
+static struct task_struct *task_struct_stack[EXTRA_TASK_STRUCT];
+static int task_struct_stack_ptr = -1;
+#endif
+
+struct task_struct *alloc_task_struct(void)
+{
+	struct task_struct *tsk;
+
+#ifndef EXTRA_TASK_STRUCT
+	tsk = ll_alloc_task_struct();
+#else
+	int index;
+
+	index = task_struct_stack_ptr;
+	if (index >= EXTRA_TASK_STRUCT/2)
+		goto use_cache;
+
+	tsk = ll_alloc_task_struct();
+
+	if (!tsk) {
+		index = task_struct_stack_ptr;
+
+		if (index >= 0) {
+use_cache:		tsk = task_struct_stack[index];
+			task_struct_stack_ptr = index - 1;
+		}
+	}
+#endif
+#ifdef CONFIG_SYSRQ
+	/* You need this if you want SYSRQ-T to give sensible stack
+	 * usage information
+	 */
+	if (tsk) {
+		char *p = (char *)tsk;
+		memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE);
+	}
+#endif
+
+	return tsk;
+}
+
+void free_task_struct(struct task_struct *p)
+{
+#ifdef EXTRA_TASK_STRUCT
+	int index = task_struct_stack_ptr + 1;
+
+	if (index < EXTRA_TASK_STRUCT) {
+		task_struct_stack[index] = p;
+		task_struct_stack_ptr = index;
+	} else
+#endif
+		ll_free_task_struct(p);
+}
+
+/*
  * Free current thread data structures etc..
  */
 void exit_thread(void)
@@ -177,9 +249,10 @@
 	childregs = ((struct pt_regs *)((unsigned long)p + 8192)) - 1;
 	*childregs = *regs;
 	childregs->ARM_r0 = 0;
+	childregs->ARM_sp = esp;
 
 	save = ((struct context_save_struct *)(childregs)) - 1;
-	copy_thread_css(save);
+	init_thread_css(save);
 	p->tss.save = save;
 
 	return 0;
@@ -222,3 +295,29 @@
 	dump->regs = *regs;
 	dump->u_fpvalid = dump_fpu (regs, &dump->u_fp);
 }
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+{
+	extern int sys_exit(int) __attribute__((noreturn));
+	pid_t __ret;
+
+	__asm__ __volatile__(
+	"mov	r0, %1		@ kernel_thread sys_clone\n"
+"	mov	r1, #0\n"
+	__syscall(clone)"\n"
+"	mov	%0, r0"
+        : "=r" (__ret)
+        : "Ir" (flags | CLONE_VM) : "r0", "r1");
+	if (__ret == 0)
+		sys_exit((fn)(arg));
+	return __ret;
+}
+
diff -urN linux-2.2.18-pre8/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c
--- linux-2.2.18-pre8/arch/arm/kernel/ptrace.c	Wed Sep  9 08:56:58 1998
+++ linux/arch/arm/kernel/ptrace.c	Mon Dec  3 20:25:13 2001
@@ -25,6 +25,17 @@
  * Breakpoint SWI instruction: SWI &9F0001
  */
 #define BREAKINST	0xef9f0001
+#define PTRACE_GETREGS		12
+#define PTRACE_SETREGS		13
+#define PTRACE_GETFPREGS	14
+#define PTRACE_SETFPREGS	15
+
+static inline struct pt_regs *
+get_user_regs(struct task_struct *task)
+{
+	return (struct pt_regs *)
+		((unsigned long)task + 8192 - sizeof(struct pt_regs));
+}
 
 /*
  * this routine will get a word off of the processes privileged stack.
@@ -34,11 +45,7 @@
  */
 static inline long get_stack_long(struct task_struct *task, int offset)
 {
-	unsigned char *stack;
-
-	stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
-	stack += offset << 2;
-	return *(unsigned long *)stack;
+	return get_user_regs(task)->uregs[offset];
 }
 
 /*
@@ -47,15 +54,21 @@
  * this routine assumes that all the privileged stacks are in our
  * data space.
  */
-static inline long put_stack_long(struct task_struct *task, int offset,
-	unsigned long data)
+static inline int
+put_stack_long(struct task_struct *task, int offset, long data)
 {
-	unsigned char *stack;
+	struct pt_regs newregs, *regs = get_user_regs(task);
+	int ret = -EINVAL;
 
-	stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
-	stack += offset << 2;
-	*(unsigned long *) stack = data;
-	return 0;
+	newregs = *regs;
+	newregs.uregs[offset] = data;
+
+	if (valid_user_regs(&newregs)) {
+		regs->uregs[offset] = data;
+		ret = 0;
+	}
+
+	return ret;
 }
 
 /*
@@ -71,39 +84,42 @@
 	pmd_t *pgmiddle;
 	pte_t *pgtable;
 	unsigned long page;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
-	if (pgd_none(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
-	}
+	if (pgd_none(*pgdir))
+		goto none;
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
 		pgd_clear(pgdir);
 		return 0;
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
-	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
-	}
+	if (pmd_none(*pgmiddle))
+		goto none;
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
 		pmd_clear(pgmiddle);
 		return 0;
 	}
 	pgtable = pte_offset(pgmiddle, addr);
-	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
-	}
+	if (!pte_present(*pgtable))
+		goto none;
 	page = pte_page(*pgtable);
  
 	if(MAP_NR(page) >= max_mapnr)
 		return 0;
 	page += addr & ~PAGE_MASK;
 	return *(unsigned long *)page;
+
+none:
+	fault = handle_mm_fault(tsk, vma, addr, 0);
+	if (fault > 0)
+		goto repeat;
+	if (fault < 0)
+		force_sig(SIGKILL, tsk);
+	return 0;
 }
 
 /*
@@ -122,46 +138,52 @@
 	pmd_t *pgmiddle;
 	pte_t *pgtable;
 	unsigned long page;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
-	if (!pgd_present(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (!pgd_present(*pgdir))
+		goto none;
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
 		pgd_clear(pgdir);
 		return;
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
-	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (pmd_none(*pgmiddle))
+		goto none;
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
 		pmd_clear(pgmiddle);
 		return;
 	}
 	pgtable = pte_offset(pgmiddle, addr);
-	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (!pte_present(*pgtable))
+		goto none;
 	page = pte_page(*pgtable);
-	if (!pte_write(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (!pte_write(*pgtable))
+		goto none;
 	
 	if (MAP_NR(page) < max_mapnr) {
 		page += addr & ~PAGE_MASK;
+
+		flush_cache_range(vma->vm_mm, addr, addr + sizeof(unsigned long));
+
 		*(unsigned long *)page = data;
-		__flush_entry_to_ram(page);
+
+		clean_cache_area(page, sizeof(unsigned long));
+
+		set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+		flush_tlb_page(vma, addr & PAGE_MASK);
 	}
-	set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
-	flush_tlb();
+	return;
+
+none:
+	fault = handle_mm_fault(tsk, vma, addr, 1);
+	if (fault > 0)
+		goto repeat;
+	if (fault < 0)
+		force_sig(SIGKILL, tsk);
 }
 
 static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
@@ -669,7 +691,6 @@
 				return 0;
 			wake_up_process (child);
 			child->exit_code = SIGKILL;
-			ptrace_cancel_bpt (child);
 			/* make sure single-step breakpoint is gone. */
 			ptrace_cancel_bpt (child);
 			ret = 0;
@@ -685,6 +706,54 @@
 			child->exit_code = data;
 			/* give it a chance to run. */
 			ret = 0;
+			goto out;
+			
+		case PTRACE_GETREGS:
+		{	/* Get all gp regs from the child. */
+			unsigned char *stack;
+
+			ret = 0;
+			stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs));
+			if (copy_to_user((void *)data, stack,
+					 sizeof(struct pt_regs)))
+				ret = -EFAULT;
+
+			goto out;
+		};
+
+		/* Set all gp regs in the child. */
+		case PTRACE_SETREGS:
+		{
+			struct pt_regs newregs;
+
+			ret = -EFAULT;
+			if (copy_from_user(&newregs, (void *)data,
+					   sizeof(struct pt_regs)) == 0) {
+				struct pt_regs *regs = get_user_regs(child);
+
+				ret = -EINVAL;
+				if (valid_user_regs(&newregs)) {
+					*regs = newregs;
+					ret = 0;
+				}
+			}
+			goto out;
+		}
+
+		case PTRACE_GETFPREGS: 
+			/* Get the child FPU state. */
+			ret = 0;
+			if (copy_to_user((void *)data, &child->tss.fpstate,
+					 sizeof(struct user_fp)))
+				ret = -EFAULT;
+			goto out;
+		
+		case PTRACE_SETFPREGS:
+			/* Set the child FPU state. */
+			ret = 0;
+			if (copy_from_user(&child->tss.fpstate, (void *)data,
+					   sizeof(struct user_fp)))
+				ret = -EFAULT;
 			goto out;
 
 		case PTRACE_DETACH:				/* detach a process that was attached. */
diff -urN linux-2.2.18-pre8/arch/arm/kernel/setup.c linux/arch/arm/kernel/setup.c
--- linux-2.2.18-pre8/arch/arm/kernel/setup.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/setup.c	Mon Dec  3 20:25:13 2001
@@ -1,68 +1,73 @@
 /*
  *  linux/arch/arm/kernel/setup.c
  *
- *  Copyright (C) 1995-1998 Russell King
+ *  Copyright (C) 1995-1999 Russell King
  */
-
-/*
- * This file obtains various parameters about the system that the kernel
- * is running on.
- */
-
 #include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/stddef.h>
-#include <linux/unistd.h>
-#include <linux/ptrace.h>
-#include <linux/malloc.h>
-#include <linux/user.h>
-#include <linux/a.out.h>
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
-#include <linux/major.h>
 #include <linux/utsname.h>
 #include <linux/blk.h>
-#include <linux/init.h>
 #include <linux/console.h>
+#include <linux/init.h>
 
 #include <asm/elf.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/pgtable.h>
 #include <asm/procinfo.h>
-#include <asm/segment.h>
 #include <asm/setup.h>
 #include <asm/system.h>
 
-/* Work out which CPUs to support */
-#ifdef CONFIG_ARCH_ACORN
-#define SUPPORT_CPU_ARM6
-#define SUPPORT_CPU_ARM7
-#define SUPPORT_CPU_SA110
-#else
-#define SUPPORT_CPU_SA110
-#endif
-#ifdef CONFIG_CPU_ARM6
-#define SUPPORT_CPU_ARM6
-#endif
-#ifdef CONFIG_CPU_ARM7
-#define SUPPORT_CPU_ARM7
-#endif
-#ifdef CONFIG_CPU_SA110
-#define SUPPORT_CPU_SA110
+#ifndef MEM_SIZE
+#define MEM_SIZE	(16*1024*1024)
 #endif
 
 #ifndef CONFIG_CMDLINE
-#define CONFIG_CMDLINE	"root=/dev/nfs rw"
+#define CONFIG_CMDLINE ""
 #endif
-#define MEM_SIZE	(16*1024*1024)
-#define COMMAND_LINE_SIZE 256
+
+extern void reboot_setup(char *str, int *ints);
+extern void disable_hlt(void);
+extern int root_mountflags;
+extern int _stext, _text, _etext, _edata, _end;
+
+unsigned int processor_id;
+unsigned int __machine_arch_type;
+unsigned int vram_size;
+unsigned int system_rev;
+unsigned int system_serial_low;
+unsigned int system_serial_high;
+unsigned int elf_hwcap;
+
+#ifdef CONFIG_ARCH_ACORN
+unsigned int memc_ctrl_reg;
+unsigned int number_mfm_drives;
+#endif
+
+static unsigned long mem_end;
+
+struct machine_desc {
+	const char	*name;		/* architecture name	*/
+	unsigned int	param_offset;	/* parameter page	*/
+	unsigned int	video_start;	/* start of video RAM	*/
+	unsigned int	video_end;	/* end of video RAM	*/
+	unsigned int	reserve_lp0 :1;	/* never has lp0	*/
+	unsigned int	reserve_lp1 :1;	/* never has lp1	*/
+	unsigned int	reserve_lp2 :1;	/* never has lp2	*/
+	unsigned int	broken_hlt  :1;	/* hlt is broken	*/
+	unsigned int	soft_reboot :1;	/* soft reboot		*/
+	void		(*fixup)(struct machine_desc *,
+				 struct param_struct *, char **);
+};
+
+struct processor processor;
 
 struct drive_info_struct { char dummy[32]; } drive_info;
+
 struct screen_info screen_info = {
  orig_video_lines:	30,
  orig_video_cols:	80,
@@ -71,312 +76,429 @@
  orig_video_isVGA:	1,
  orig_video_points:	8
 };
-struct processor processor;
+
 unsigned char aux_device_present;
+char elf_platform[ELF_PLATFORM_SIZE];
+char saved_command_line[COMMAND_LINE_SIZE];
 
-extern const struct processor arm2_processor_functions;
-extern const struct processor arm250_processor_functions;
-extern const struct processor arm3_processor_functions;
-extern const struct processor arm6_processor_functions;
-extern const struct processor arm7_processor_functions;
-extern const struct processor sa110_processor_functions;
+static struct proc_info_item proc_info;
+static const char *machine_name;
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
 
-char elf_platform[ELF_PLATFORM_SIZE];
+static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
+static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
+#define ENDIANNESS ((char)endian_test.l)
 
-const struct armversions armidlist[] = {
-  /*-- Match -- --- Mask -- -- Manu --  Processor  uname -m   --- ELF STUFF ---
-	--- processor asm funcs --- */
-#if defined(CONFIG_CPU_26)
-  { 0x41560200, 0xfffffff0, "ARM/VLSI",	"arm2"	 , "armv1"  , "v1", 0,
-	&arm2_processor_functions   },
-  { 0x41560250, 0xfffffff0, "ARM/VLSI",	"arm250" , "armv2"  , "v2", HWCAP_SWP,
-	&arm250_processor_functions },
-  { 0x41560300, 0xfffffff0, "ARM/VLSI",	"arm3"	 , "armv2"  , "v2", HWCAP_SWP,
-	&arm3_processor_functions   },
-#elif defined(CONFIG_CPU_32)
-#ifdef SUPPORT_CPU_ARM6
-  { 0x41560600, 0xfffffff0, "ARM/VLSI",	"arm6"	 , "armv3"  , "v3", HWCAP_SWP,
-	&arm6_processor_functions   },
-  { 0x41560610, 0xfffffff0, "ARM/VLSI",	"arm610" , "armv3"  , "v3", HWCAP_SWP,
-	&arm6_processor_functions   },
-#endif
-#ifdef SUPPORT_CPU_ARM7
-  { 0x41007000, 0xffffff00, "ARM/VLSI",	"arm7"	 , "armv3"  , "v3", HWCAP_SWP,
-	&arm7_processor_functions   },
-  /* ARM710 IDs are non-standard */
-  { 0x41007100, 0xfff8ff00, "ARM/VLSI",	"arm710" , "armv3"  , "v3", HWCAP_SWP,
-	&arm7_processor_functions   },
-#endif
-#ifdef SUPPORT_CPU_SA110
-  { 0x4401a100, 0xfffffff0, "DEC",	"sa110"	 , "armv4"  , "v3", HWCAP_SWP|HWCAP_HALF,
-	&sa110_processor_functions  },
-#endif
-#endif
-  { 0x00000000, 0x00000000, "***", "unknown", "unknown", "**", 0, NULL }
-};
+static void __init setup_processor(void)
+{
+	extern struct proc_info_list __proc_info_begin, __proc_info_end;
+	struct proc_info_list *list;
 
-/*
- * From head-armv.S
- */
-unsigned int processor_id;
-unsigned int machine_type;
-int armidindex;
+	/*
+	 * locate processor in the list of supported processor
+	 * types.  The linker builds this table for us from the
+	 * entries in arch/arm/mm/proc-*.S
+	 */
+	for (list = &__proc_info_begin; list < &__proc_info_end ; list++)
+		if ((processor_id & list->cpu_mask) == list->cpu_val)
+			break;
 
-extern int root_mountflags;
-extern int _etext, _edata, _end;
+	/*
+	 * If processor type is unrecognised, then we
+	 * can do nothing...
+	 */
+	if (list >= &__proc_info_end) {
+		printk("CPU configuration botched (ID %08x), unable "
+		       "to continue.\n", processor_id);
+		while (1);
+	}
 
-/*-------------------------------------------------------------------------
- * Early initialisation routines for various configurable items in the
- * kernel.  Each one either supplies a setup_ function, or defines this
- * symbol to be empty if not configured.
- */
+	proc_info = *list->info;
 
-/*
- * Risc-PC specific initialisation
- */
-#ifdef CONFIG_ARCH_RPC
+	processor = *list->proc;
 
-#include <asm/arch/mmu.h>
+	printk("Processor: %s %s revision %d\n",
+	       proc_info.manufacturer, proc_info.cpu_name,
+	       (int)processor_id & 15);
 
-unsigned int vram_half_sam;
+	sprintf(system_utsname.machine, "%s%c", list->arch_name, ENDIANNESS);
+	sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);
+	elf_hwcap = list->elf_hwcap;
 
-static void
-setup_rpc(struct param_struct *params)
-{
-	extern void init_dram_banks(const struct param_struct *params);
+	processor._proc_init();
+}
 
-	init_dram_banks(params);
+static unsigned long __init memparse(char *ptr, char **retptr)
+{
+	unsigned long ret = simple_strtoul(ptr, retptr, 0);
 
-	switch (params->u1.s.pages_in_vram) {
-	case 256:
-		vram_half_sam = 1024;
-		break;
-	case 512:
+	switch (**retptr) {
+	case 'M':
+	case 'm':
+		ret <<= 10;
+	case 'K':
+	case 'k':
+		ret <<= 10;
+		(*retptr)++;
 	default:
-		vram_half_sam = 2048;
+		break;
 	}
+	return ret;
 }
-#else
-#define setup_rpc(x)
-#endif
-
-#ifdef PARAMS_BASE
-
-#ifdef CONFIG_ARCH_ACORN
-int memc_ctrl_reg;
-int number_ide_drives;
-int number_mfm_drives;
-#endif
 
-static struct param_struct *params = (struct param_struct *)PARAMS_BASE;
-
-__initfunc(static char *
-setup_params(unsigned long *mem_end_p))
+/*
+ * Initial parsing of the command line.  We need to pick out the
+ * memory size.  We look for mem=size, where size is "size[KkMm]"
+ */
+static void __init
+parse_cmdline(char **cmdline_p, char *from)
 {
-	ROOT_DEV	  = to_kdev_t(params->u1.s.rootdev);
-	ORIG_X		  = params->u1.s.video_x;
-	ORIG_Y		  = params->u1.s.video_y;
-	ORIG_VIDEO_COLS	  = params->u1.s.video_num_cols;
-	ORIG_VIDEO_LINES  = params->u1.s.video_num_rows;
+	char c = ' ', *to = command_line;
+	int usermem = 0, len = 0;
 
-#ifdef CONFIG_ARCH_ACORN
-#ifndef CONFIG_FB
-	{
-		extern int bytes_per_char_h;
-		extern int bytes_per_char_v;
-
-		bytes_per_char_h  = params->u1.s.bytes_per_char_h;
-		bytes_per_char_v  = params->u1.s.bytes_per_char_v;
-	}
-#endif
-	memc_ctrl_reg	  = params->u1.s.memc_control_reg;
-	number_ide_drives = (params->u1.s.adfsdrives >> 6) & 3;
-	number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
+	for (;;) {
+		if (c == ' ' && !memcmp(from, "mem=", 4)) {
+			unsigned long size, start;
 
-	setup_rpc(params);
+			if (to != command_line)
+				to -= 1;
 
-	if (!(params->u1.s.flags & FLAG_READONLY))
-		root_mountflags &= ~MS_RDONLY;
-#endif
-#ifdef CONFIG_BLK_DEV_RAM
-	{
-		extern int rd_doload;
-		extern int rd_prompt;
-		extern int rd_image_start;
-
-		rd_image_start = params->u1.s.rd_start;
-		rd_prompt = (params->u1.s.flags & FLAG_RDPROMPT) == 0;
-		rd_doload = (params->u1.s.flags & FLAG_RDLOAD) == 0;
+			start = PAGE_OFFSET;
+			size  = memparse(from + 4, &from);
+			if (*from == '@')
+				memparse(from + 1, &from);
+
+			if (usermem == 0) {
+				usermem = 1;
+				mem_end = start + size;
+			}
+		}
+		c = *from++;
+		if (!c)
+			break;
+		if (COMMAND_LINE_SIZE <= ++len)
+			break;
+		*to++ = c;
 	}
-#endif
-
-#ifdef CONFIG_ARCH_ACORN
-	*mem_end_p = GET_MEMORY_END(params);
-#elif defined(CONFIG_ARCH_EBSA285)
-	*mem_end_p = PAGE_OFFSET + params->u1.s.page_size * params->u1.s.nr_pages;
-#else
-	*mem_end_p = PAGE_OFFSET + MEM_SIZE;
-#endif
+	*to = '\0';
+	*cmdline_p = command_line;
 
-	return params->commandline;
+	/* remove trailing spaces */
+	while (*--to == ' ' && to != command_line)
+		*to = '\0';
 }
 
-#else
-
-static char default_command_line[] __initdata = CONFIG_CMDLINE;
-
-__initfunc(static char *
-setup_params(unsigned long *mem_end_p))
+static void __init
+setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)
 {
-	ROOT_DEV	  = 0x00ff;
-
 #ifdef CONFIG_BLK_DEV_RAM
-	{
-		extern int rd_doload;
-		extern int rd_prompt;
-		extern int rd_image_start;
-
-		rd_image_start = 0;
-		rd_prompt = 1;
-		rd_doload = 1;
-	}
-#endif
+	extern int rd_doload, rd_prompt, rd_image_start, rd_size;
 
-	*mem_end_p = PAGE_OFFSET + MEM_SIZE;
+	rd_image_start = image_start;
+	rd_prompt = prompt;
+	rd_doload = doload;
 
-	return default_command_line;
-}
+	if (rd_sz)
+		rd_size = rd_sz;
 #endif
+}
 
 /*
  * initial ram disk
  */
-#ifdef CONFIG_BLK_DEV_INITRD
-__initfunc(static void
-setup_initrd(const struct param_struct *params))
+static void __init setup_initrd(unsigned int start, unsigned int size)
 {
-	if (params->u1.s.initrd_start) {
-		initrd_start = params->u1.s.initrd_start;
-		initrd_end   = initrd_start + params->u1.s.initrd_size;
-	} else {
-		initrd_start = 0;
-		initrd_end   = 0;
-	}
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (start == 0)
+		size = 0;
+	initrd_start = start;
+	initrd_end   = start + size;
+#endif
 }
 
-__initfunc(static void
-check_initrd(unsigned long mem_start, unsigned long mem_end))
+/*
+ * Initialise memory.
+ */
+static void __init setup_bootmem(void)
 {
+	unsigned long mem_start;
+
+	mem_start = (unsigned long)&_end;
+
+#ifdef CONFIG_BLK_DEV_INITRD
 	if (initrd_end > mem_end) {
 		printk ("initrd extends beyond end of memory "
 			"(0x%08lx > 0x%08lx) - disabling initrd\n",
 			initrd_end, mem_end);
 		initrd_start = 0;
 	}
+#endif
 }
 
-#else
-#define setup_initrd(p)
-#define check_initrd(ms,me)
+/*
+ * Architecture specific fixups.  This is where any
+ * parameters in the params struct are fixed up, or
+ * any additional architecture specific information
+ * is pulled from the params struct.
+ */
+static void __init
+fixup_acorn(struct machine_desc *desc, struct param_struct *params,
+	    char **cmdline)
+{
+#ifdef CONFIG_ARCH_ACORN
+	if (machine_is_riscpc()) {
+		/*
+		 * RiscPC can't handle half-word loads and stores
+		 */
+		elf_hwcap &= ~HWCAP_HALF;
+
+		switch (params->u1.s.pages_in_vram) {
+		case 512:
+			vram_size += PAGE_SIZE * 256;
+		case 256:
+			vram_size += PAGE_SIZE * 256;
+		default:
+			break;
+		}
+	}
+	memc_ctrl_reg	  = params->u1.s.memc_control_reg;
+	number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
 #endif
+}
 
-__initfunc(void
-setup_processor(void))
+static void __init
+fixup_ebsa285(struct machine_desc *desc, struct param_struct *params,
+	      char **cmdline)
 {
-	armidindex = 0;
-
-	while ((armidlist[armidindex].id ^ processor_id) &
-	       armidlist[armidindex].mask)
-		armidindex += 1;
-
-	if (armidlist[armidindex].id == 0) {
-#ifdef CONFIG_ARCH_ACORN
-		int i;
+	ORIG_X		 = params->u1.s.video_x;
+	ORIG_Y		 = params->u1.s.video_y;
+	ORIG_VIDEO_COLS  = params->u1.s.video_num_cols;
+	ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
+}
 
-		for (i = 0; i < 3200; i++)
-			((unsigned long *)SCREEN2_BASE)[i] = 0x77113322;
-#endif
-		while (1);
+/*
+ * Older NeTTroms either do not provide a parameters
+ * page, or they don't supply correct information in
+ * the parameter page.
+ */
+static void __init
+fixup_netwinder(struct machine_desc *desc, struct param_struct *params,
+		char **cmdline)
+{
+	if (params->u1.s.nr_pages != 0x02000 &&
+	    params->u1.s.nr_pages != 0x04000 &&
+	    params->u1.s.nr_pages != 0x08000 &&
+	    params->u1.s.nr_pages != 0x10000) {
+		printk(KERN_WARNING "Warning: bad NeTTrom parameters "
+		       "detected, using defaults\n");
+
+		params->u1.s.nr_pages = 0x2000;	/* 32MB */
+		params->u1.s.ramdisk_size = 0;
+		params->u1.s.flags = FLAG_READONLY;
+		params->u1.s.initrd_start = 0;
+		params->u1.s.initrd_size = 0;
+		params->u1.s.rd_start = 0;
 	}
+}
 
-	processor = *armidlist[armidindex].proc;
-	processor._proc_init();
+/*
+ * CATS uses soft-reboot by default, since
+ * hard reboots fail on early boards.
+ */
+static void __init
+fixup_cats(struct machine_desc *desc, struct param_struct *params,
+	   char **cmdline)
+{
+	ORIG_VIDEO_LINES  = 25;
+	ORIG_VIDEO_POINTS = 16;
+	ORIG_Y = 24;
 }
 
-static char command_line[COMMAND_LINE_SIZE] = { 0, };
-       char saved_command_line[COMMAND_LINE_SIZE];
+#define NO_PARAMS	0
+#define NO_VIDEO	0, 0
 
-__initfunc(static void
-setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_end))
+/*
+ * This is the list of all architectures supported by
+ * this kernel.  This should be integrated with the list
+ * in head-armv.S.
+ */
+static struct machine_desc machine_desc[] __initdata = {
+	{ "EBSA110",		/* RMK			*/
+		0x00000400,
+		NO_VIDEO,
+		1, 0, 1, 1, 1,
+		NULL
+	}, { "Acorn-RiscPC",	/* RMK			*/
+		0x10000100,
+		NO_VIDEO,
+		1, 1, 0, 0, 0,
+		fixup_acorn
+	}, { "unknown",
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "FTV/PCI",		/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "EBSA285",		/* RMK			*/
+		0x00000100,
+		0x000a0000, 0x000bffff,
+		0, 0, 0, 0, 0,
+		fixup_ebsa285
+	}, { "Rebel-NetWinder",	/* RMK			*/
+		0x00000100,
+		0x000a0000, 0x000bffff,
+		1, 0, 1, 0, 0,
+		fixup_netwinder
+	}, { "Chalice-CATS",	/* Philip Blundell	*/
+		NO_PARAMS,
+		0x000a0000, 0x000bffff,
+		0, 0, 0, 0, 1,
+		fixup_cats
+	}, { "unknown-TBOX",	/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "co-EBSA285",	/* Mark van Doesburg	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "CL-PS7110",	/* Werner Almesberger	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "Acorn-Archimedes",/* RMK/DAG		*/
+		0x0207c000,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_acorn
+	}, { "Acorn-A5000",	/* RMK/PB		*/
+		0x0207c000,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_acorn
+	}, { "Etoile",		/* Alex de Vries	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "LaCie_NAS",	/* Benjamin Herrenschmidt */
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "CL-PS7500",	/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "Shark",		/* Alexander Schulz	*/
+		NO_PARAMS,
+		/* do you really mean 0x200000? */
+		0x06000000, 0x06000000+0x00200000,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "SA1100-based",	/* Nicolas Pitre	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}
+};
+
+void __init
+setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)
 {
-	char c, *to = command_line;
-	int len = 0;
+	struct param_struct *params = NULL;
+	struct machine_desc *mdesc;
+	char *from = default_command_line;
 
-	*mem_start = (unsigned long)&_end;
+#if defined(CONFIG_ARCH_ARC)
+	__machine_arch_type = MACH_TYPE_ARCHIMEDES;
+#elif defined(CONFIG_ARCH_A5K)
+	__machine_arch_type = MACH_TYPE_A5K;
+#endif
 
-	for (;;) {
-		if (cmd_line[0] == ' ' &&
-		    cmd_line[1] == 'm' &&
-		    cmd_line[2] == 'e' &&
-		    cmd_line[3] == 'm' &&
-		    cmd_line[4] == '=') {
-			*mem_end = simple_strtoul(cmd_line+5, &cmd_line, 0);
-			switch(*cmd_line) {
-			case 'M':
-			case 'm':
-				*mem_end <<= 10;
-			case 'K':
-			case 'k':
-				*mem_end <<= 10;
-				cmd_line++;
-			}
-			*mem_end = *mem_end + PAGE_OFFSET;
-		}
-		c = *cmd_line++;
-		if (!c)
-			break;
-		if (COMMAND_LINE_SIZE <= ++len)
-			break;
-		*to++ = c;
+	setup_processor();
+
+	ROOT_DEV = MKDEV(0, 255);
+
+	mdesc = machine_desc + machine_arch_type;
+	machine_name = mdesc->name;
+
+	if (mdesc->broken_hlt)
+		disable_hlt();
+
+	if (mdesc->soft_reboot)
+		reboot_setup("s", NULL);
+
+	if (mdesc->param_offset)
+		params = phys_to_virt(mdesc->param_offset);
+
+	if (mdesc->fixup)
+		mdesc->fixup(mdesc, params, &from);
+
+	if (params && params->u1.s.page_size != PAGE_SIZE) {
+		printk(KERN_WARNING "Warning: bad configuration page, "
+		       "trying to continue\n");
+		params = NULL;
 	}
 
-	*to = '\0';
-}
+	if (params) {
+		mem_end	  = params->u1.s.page_size *
+			    params->u1.s.nr_pages;
+
+		ROOT_DEV	   = to_kdev_t(params->u1.s.rootdev);
+		system_rev	   = params->u1.s.system_rev;
+		system_serial_low  = params->u1.s.system_serial_low;
+		system_serial_high = params->u1.s.system_serial_high;
+
+		setup_ramdisk((params->u1.s.flags & FLAG_RDLOAD) == 0,
+			      (params->u1.s.flags & FLAG_RDPROMPT) == 0,
+			      params->u1.s.rd_start,
+			      params->u1.s.ramdisk_size);
 
-__initfunc(void
-setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p))
-{
-	static unsigned char smptrap;
-	unsigned long memory_end;
-	char endian = 'l';
-	char *from;
-
-	if (smptrap == 1)
-		return;
-	smptrap = 1;
+		setup_initrd(params->u1.s.initrd_start,
+			     params->u1.s.initrd_size);
 
-	setup_processor();
+		if (!(params->u1.s.flags & FLAG_READONLY))
+			root_mountflags &= ~MS_RDONLY;
+
+#ifdef CONFIG_ARCH_RPC
+		{
+			extern void init_dram_banks(struct param_struct *);
+			init_dram_banks(params);
+		}
+#endif
+		mem_end -= vram_size;
 
-	from = setup_params(&memory_end);
-	setup_initrd(params);
+		from = params->commandline;
+	}
 
-	/* Save unparsed command line copy for /proc/cmdline */
-	memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
-	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+	if (!mem_end)
+		mem_end = MEM_SIZE;
 
-	setup_mem(from, memory_start_p, &memory_end);
-	check_initrd(*memory_start_p, memory_end);
+	mem_end += PAGE_OFFSET;
 
-	init_task.mm->start_code = TASK_SIZE;
-	init_task.mm->end_code	 = TASK_SIZE + (unsigned long) &_etext;
-	init_task.mm->end_data	 = TASK_SIZE + (unsigned long) &_edata;
-	init_task.mm->brk	 = TASK_SIZE + (unsigned long) &_end;
+	init_task.mm->start_code = (unsigned long) &_text;
+	init_task.mm->end_code	 = (unsigned long) &_etext;
+	init_task.mm->end_data	 = (unsigned long) &_edata;
+	init_task.mm->brk	 = (unsigned long) &_end;
 
-	*cmdline_p = command_line;
-	*memory_end_p = memory_end;
+	memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
+	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+	parse_cmdline(cmdline_p, from);
+	setup_bootmem();
 
-	sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, endian);
-	sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, endian);
+#ifdef CONFIG_NWFPE
+	fpe_init();
+#endif
 
 #ifdef CONFIG_VT
 #if defined(CONFIG_VGA_CONSOLE)
@@ -385,69 +507,32 @@
 	conswitchp = &dummy_con;
 #endif
 #endif
+
+	*cmdline_p = command_line;
+	*memory_start_p = (unsigned long)&_end;
+	*memory_end_p = mem_end;
 }
 
-static const struct {
-	char *machine_name;
-	char *bus_name;
-} machine_desc[] = {
-	{ "DEC-EBSA110",	"DEC"		},
-	{ "Acorn-RiscPC",	"Acorn"		},
-	{ "Nexus-NexusPCI",	"PCI"		},
-	{ "DEC-EBSA285",	"PCI"		},
-	{ "Corel-Netwinder",	"PCI/ISA"	},
-	{ "Chalice-CATS",	"PCI"		},
-	{ "unknown-TBOX",	"PCI"		}
-};
+int get_cpuinfo(char * buffer)
+{
+	char *p = buffer;
 
-#if defined(CONFIG_ARCH_ARC)
-#define HARDWARE "Acorn-Archimedes"
-#define IO_BUS	 "Acorn"
-#elif defined(CONFIG_ARCH_A5K)
-#define HARDWARE "Acorn-A5000"
-#define IO_BUS	 "Acorn"
-#endif
+	p += sprintf(p, "Processor\t: %s %s rev %d (%s)\n",
+		     proc_info.manufacturer, proc_info.cpu_name,
+		     (int)processor_id & 15, elf_platform);
 
-#if defined(CONFIG_CPU_ARM2)
-#define OPTIMISATION "ARM2"
-#elif defined(CONFIG_CPU_ARM3)
-#define OPTIMISATION "ARM3"
-#elif defined(CONFIG_CPU_ARM6)
-#define OPTIMISATION "ARM6"
-#elif defined(CONFIG_CPU_ARM7)
-#define OPTIMISATION "ARM7"
-#elif defined(CONFIG_CPU_SA110)
-#define OPTIMISATION "StrongARM"
-#else
-#define OPTIMISATION "unknown"
-#endif
+	p += sprintf(p, "BogoMIPS\t: %lu.%02lu\n",
+		     (loops_per_sec+2500) / 500000,
+		     ((loops_per_sec+2500) / 5000) % 100);
 
-int get_cpuinfo(char * buffer)
-{
-	int len;
+	p += sprintf(p, "Hardware\t: %s\n", machine_name);
 
-	len = sprintf(buffer,
-		"Processor\t: %s %s rev %d\n"
-		"BogoMips\t: %lu.%02lu\n"
-		"Hardware\t: %s\n"
-		"Optimisation\t: %s\n"
-		"IO Bus\t\t: %s\n",
-		armidlist[armidindex].manu,
-		armidlist[armidindex].name,
-		(int)processor_id & 15,
-		(loops_per_sec+2500) / 500000,
-		((loops_per_sec+2500) / 5000) % 100,
-#ifdef HARDWARE
-		HARDWARE,
-#else
-		machine_desc[machine_type].machine_name,
-#endif
-		OPTIMISATION,
-#ifdef IO_BUS
-		IO_BUS
-#else
-		machine_desc[machine_type].bus_name
-#endif
-		);
-	return len;
+	p += sprintf(p, "Revision\t: %04x\n",
+		     system_rev);
+
+	p += sprintf(p, "Serial\t\t: %08x%08x\n",
+		     system_serial_high,
+		     system_serial_low);
+
+	return p - buffer;
 }
diff -urN linux-2.2.18-pre8/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c
--- linux-2.2.18-pre8/arch/arm/kernel/signal.c	Thu Oct  8 09:32:22 1998
+++ linux/arch/arm/kernel/signal.c	Mon Dec  3 20:25:13 2001
@@ -28,7 +28,7 @@
 
 asmlinkage int sys_wait4(pid_t pid, unsigned long * stat_addr,
 			 int options, unsigned long *ru);
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs);
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
 extern int ptrace_cancel_bpt (struct task_struct *);
 extern int ptrace_set_bpt (struct task_struct *);
 
@@ -50,7 +50,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs, 0))
 			return regs->ARM_r0;
 	}
 }
@@ -78,7 +78,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs, 0))
 			return regs->ARM_r0;
 	}
 }
@@ -158,12 +158,8 @@
 #ifdef CONFIG_CPU_32
 	err |= __get_user(regs->ARM_cpsr, &sc->arm_cpsr);
 #endif
-	if (!valid_user_regs(regs))
-		return 1;
 
-	/* send SIGTRAP if we're single-stepping */
-	if (ptrace_cancel_bpt (current))
-		send_sig (SIGTRAP, current, 1);
+	err |= !valid_user_regs(regs);
 
 	return err;
 }
@@ -173,6 +169,14 @@
 	struct sigframe *frame;
 	sigset_t set;
 
+	/*
+	 * Since we stacked the signal on a word boundary,
+	 * then 'sp' should be word aligned here.  If it's
+	 * not, then the user is trying to mess with us.
+	 */
+	if (regs->ARM_sp & 3)
+		goto badframe;
+
 	frame = (struct sigframe *)regs->ARM_sp;
 
 	if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
@@ -192,6 +196,10 @@
 	if (restore_sigcontext(regs, &frame->sc))
 		goto badframe;
 
+	/* Send SIGTRAP if we're single-stepping */
+	if (ptrace_cancel_bpt (current))
+		send_sig(SIGTRAP, current, 1);
+
 	return regs->ARM_r0;
 
 badframe:
@@ -204,6 +212,14 @@
 	struct rt_sigframe *frame;
 	sigset_t set;
 
+	/*
+	 * Since we stacked the signal on a word boundary,
+	 * then 'sp' should be word aligned here.  If it's
+	 * not, then the user is trying to mess with us.
+	 */
+	if (regs->ARM_sp & 3)
+		goto badframe;
+
 	frame = (struct rt_sigframe *)regs->ARM_sp;
 
 	if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
@@ -220,6 +236,10 @@
 	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
 		goto badframe;
 
+	/* Send SIGTRAP if we're single-stepping */
+	if (ptrace_cancel_bpt (current))
+		send_sig(SIGTRAP, current, 1);
+
 	return regs->ARM_r0;
 
 badframe:
@@ -260,6 +280,26 @@
 	return err;
 }
 
+static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+				 unsigned long framesize)
+{
+	unsigned long sp = regs->ARM_sp;
+
+	/*
+	 * This is the X/Open sanctioned signal stack switching.
+	 */
+	if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp))
+		sp = current->sas_ss_sp + current->sas_ss_size;
+
+	/*
+	 * No matter what happens, 'sp' must be word
+	 * aligned otherwise nasty things could happen
+	 */
+	sp &= ~3;
+
+	return (void *)(sp - framesize);
+}
+
 static void setup_frame(int sig, struct k_sigaction *ka,
 			sigset_t *set, struct pt_regs *regs)
 {
@@ -267,9 +307,9 @@
 	unsigned long retcode;
 	int err = 0;
 
-	frame = (struct sigframe *)regs->ARM_sp - 1;
+	frame = get_sigframe(ka, regs, sizeof(*frame));
 
-	if (!access_ok(VERIFT_WRITE, frame, sizeof (*frame)))
+	if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
 		goto segv_and_exit;
 
 	err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
@@ -286,7 +326,7 @@
 	} else {
 		retcode = (unsigned long)&frame->retcode;
 		err |= __put_user(SWI_SYS_SIGRETURN, &frame->retcode);
-		__flush_entry_to_ram (&frame->retcode);
+		flush_icache_range(retcode, retcode + 4);
 	}
 
 	if (err)
@@ -299,6 +339,11 @@
 	regs->ARM_sp = (unsigned long)frame;
 	regs->ARM_lr = retcode;
 	regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+	/* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+	if (ka->sa.sa_flags & SA_THIRTYTWO)
+		regs->ARM_cpsr = USR_MODE;
+#endif
 	if (valid_user_regs(regs))
 		return;
 
@@ -315,7 +360,8 @@
 	unsigned long retcode;
 	int err = 0;
 
-	frame = (struct rt_sigframe *)regs->ARM_sp - 1;
+	frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe));
+
 	if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
 		goto segv_and_exit;
 
@@ -337,7 +383,7 @@
 	} else {
 		retcode = (unsigned long)&frame->retcode;
 		err |= __put_user(SWI_SYS_RT_SIGRETURN, &frame->retcode);
-		__flush_entry_to_ram (&frame->retcode);
+		flush_icache_range(retcode, retcode + 4);
 	}
 
 	if (err)
@@ -350,6 +396,11 @@
 	regs->ARM_sp = (unsigned long)frame;
 	regs->ARM_lr = retcode;
 	regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+	/* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+	if (ka->sa.sa_flags & SA_THIRTYTWO)
+		regs->ARM_cpsr = USR_MODE;
+#endif
 	if (valid_user_regs(regs))
 		return;
 
@@ -393,18 +444,25 @@
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
 {
-	unsigned long instr, *pc = (unsigned long *)(instruction_pointer(regs)-4);
 	struct k_sigaction *ka;
 	siginfo_t info;
-	int single_stepping, swi_instr;
+	int single_stepping;
+
+	/*
+	 * We want the common case to go fast, which
+	 * is why we may in certain cases get here from
+	 * kernel mode. Just return without doing anything
+	 * if so.
+	 */
+	if (!user_mode(regs))
+		return 0;
 
 	if (!oldset)
 		oldset = &current->blocked;
 
 	single_stepping = ptrace_cancel_bpt (current);
-	swi_instr = (!get_user (instr, pc) && (instr & 0x0f000000) == 0x0f000000);
 
 	for (;;) {
 		unsigned long signr;
@@ -503,7 +561,7 @@
 		}
 
 		/* Are we from a system call? */
-		if (swi_instr) {
+		if (syscall) {
 			switch (regs->ARM_r0) {
 			case -ERESTARTNOHAND:
 				regs->ARM_r0 = -EINTR;
@@ -527,7 +585,7 @@
 		return 1;
 	}
 
-	if (swi_instr &&
+	if (syscall &&
 	    (regs->ARM_r0 == -ERESTARTNOHAND ||
 	     regs->ARM_r0 == -ERESTARTSYS ||
 	     regs->ARM_r0 == -ERESTARTNOINTR)) {
diff -urN linux-2.2.18-pre8/arch/arm/kernel/sys_arm.c linux/arch/arm/kernel/sys_arm.c
--- linux-2.2.18-pre8/arch/arm/kernel/sys_arm.c	Sat May  8 11:14:01 1999
+++ linux/arch/arm/kernel/sys_arm.c	Mon Dec  3 20:25:13 2001
@@ -223,13 +223,7 @@
  */
 asmlinkage int sys_fork(struct pt_regs *regs)
 {
-	int ret;
-
-	lock_kernel();
-	ret = do_fork(SIGCHLD, regs->ARM_sp, regs);
-	unlock_kernel();
-
-	return ret;
+	return do_fork(SIGCHLD, regs->ARM_sp, regs);
 }
 
 /* Clone a task - this clones the calling program thread.
@@ -237,14 +231,14 @@
  */
 asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)
 {
-	int ret;
-
-	lock_kernel();
 	if (!newsp)
 		newsp = regs->ARM_sp;
-	ret = do_fork(clone_flags, newsp, regs);
-	unlock_kernel();
-	return ret;
+	return do_fork(clone_flags, newsp, regs);
+}
+
+asmlinkage int sys_vfork(struct pt_regs *regs)
+{
+	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs);
 }
 
 /* sys_execve() executes a new program.
diff -urN linux-2.2.18-pre8/arch/arm/kernel/time.c linux/arch/arm/kernel/time.c
--- linux-2.2.18-pre8/arch/arm/kernel/time.c	Mon Aug  9 12:04:38 1999
+++ linux/arch/arm/kernel/time.c	Mon Dec  3 20:25:13 2001
@@ -76,6 +76,25 @@
 		  )*60 + sec; /* finally seconds */
 }
 
+/*
+ * Handle profile stuff...
+ */
+static void do_profile(unsigned long pc)
+{
+	if (prof_buffer && current->pid) {
+		extern int _stext;
+
+		pc -= (unsigned long)&_stext;
+
+		pc >>= prof_shift;
+
+		if (pc >= prof_len)
+			pc = prof_len - 1;
+
+		prof_buffer[pc] += 1;
+	}
+}
+
 #include <asm/arch/time.h>
 
 static unsigned long do_gettimeoffset(void)
@@ -93,7 +112,7 @@
 
 	/*
 	 * xtime is atomically updated in timer_bh. lost_ticks is
-	 * nonzero if the tiemr bottom half hasnt executed yet.
+	 * nonzero if the timer bottom half hasnt executed yet.
 	 */
 	if (lost_ticks)
 		tv->tv_usec += USECS_PER_JIFFY;
@@ -127,27 +146,12 @@
 	time_status |= STA_UNSYNC;
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
-	sti ();
-}
-
-/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "do_timer()" routine every clocktick.
- */
-static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	if (reset_timer ())
-		do_timer(regs);
-
-	update_rtc ();
+	sti();
 }
 
-static struct irqaction irqtimer = { timer_interrupt, 0, 0, "timer", NULL, NULL};
-
-__initfunc(void time_init(void))
+void __init time_init(void)
 {
-	xtime.tv_sec = setup_timer();
 	xtime.tv_usec = 0;
 
-	setup_arm_irq(IRQ_TIMER, &irqtimer);
+	setup_timer();
 }
diff -urN linux-2.2.18-pre8/arch/arm/kernel/traps.c linux/arch/arm/kernel/traps.c
--- linux-2.2.18-pre8/arch/arm/kernel/traps.c	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/kernel/traps.c	Mon Dec  3 20:25:13 2001
@@ -24,7 +24,6 @@
 #include <asm/atomic.h>
 #include <asm/pgtable.h>
 
-extern void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret);
 extern void c_backtrace (unsigned long fp, int pmode);
 extern int ptrace_cancel_bpt (struct task_struct *);
 
@@ -45,16 +44,17 @@
 
 int kstack_depth_to_print = 200;
 
-static int verify_stack_pointer (unsigned long stackptr, int size)
+/*
+ * Stack pointers should always be within the kernels view of
+ * physical memory.  If it is not there, then we can't dump
+ * out any information relating to the stack.
+ */
+static int verify_stack(unsigned long sp)
 {
-#ifdef CONFIG_CPU_26
-	if (stackptr < 0x02048000 || stackptr + size > 0x03000000)
-        	return -EFAULT;
-#else
-	if (stackptr < PAGE_OFFSET || stackptr + size > (unsigned long)high_memory)
+	if (sp < PAGE_OFFSET || sp > (unsigned long)high_memory)
 		return -EFAULT;
-#endif
-		return 0;
+
+	return 0;
 }
 
 /*
@@ -90,22 +90,26 @@
 
 static void dump_instr(unsigned long pc, int user)
 {
-	unsigned long module_start, module_end;
 	int pmin = -2, pmax = 3, ok = 0;
 	extern char start_kernel, _etext;
 
 	if (!user) {
+		unsigned long module_start, module_end;
+		unsigned long kernel_start, kernel_end;
+
 		module_start = VMALLOC_START;
 		module_end   = module_start + MODULE_RANGE;
 
-		if ((pc >= (unsigned long) &start_kernel) &&
-		    (pc <= (unsigned long) &_etext)) {
-			if (pc + pmin < (unsigned long) &start_kernel)
-				pmin = ((unsigned long) &start_kernel) - pc;
-			if (pc + pmax > (unsigned long) &_etext)
-				pmax = ((unsigned long) &_etext) - pc;
+		kernel_start = (unsigned long)&start_kernel;
+		kernel_end   = (unsigned long)&_etext;
+
+		if (pc >= kernel_start && pc < kernel_end) {
+			if (pc + pmin < kernel_start)
+				pmin = kernel_start - pc;
+			if (pc + pmax > kernel_end)
+				pmax = kernel_end - pc;
 			ok = 1;
-		} else if (pc >= module_start && pc <= module_end) {
+		} else if (pc >= module_start && pc < module_end) {
 			if (pc + pmin < module_start)
 				pmin = module_start - pc;
 			if (pc + pmax > module_end)
@@ -125,119 +129,139 @@
 		printk ("pc not in code space\n");
 }
 
-static void dump_state(char *str, struct pt_regs *regs, int err)
+spinlock_t die_lock;
+
+/*
+ * This function is protected against re-entrancy.
+ */
+void die(const char *str, struct pt_regs *regs, int err)
 {
+	struct task_struct *tsk = current;
+
+	spin_lock_irq(&die_lock);
+
 	console_verbose();
 	printk("Internal error: %s: %x\n", str, err);
 	printk("CPU: %d\n", smp_processor_id());
 	show_regs(regs);
 	printk("Process %s (pid: %d, stackpage=%08lx)\n",
-		current->comm, current->pid, 4096+(unsigned long)current);
-}
+		current->comm, current->pid, 4096+(unsigned long)tsk);
 
-/*
- * This function is protected against kernel-mode re-entrancy.  If it
- * is re-entered it will hang the system since we can't guarantee in
- * this case that any of the functions that it calls are safe any more.
- * Even the panic function could be a problem, but we'll give it a go.
- */
-void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
-{
-	static int died = 0;
-	unsigned long cstack, sstack, frameptr;
-	
-	if (user_mode(regs))
-    		return;
+	if (!user_mode(regs)) {
+		unsigned long sp = (unsigned long)(regs + 1);
+		unsigned long fp;
+		int dump_info = 1;
+
+		printk("Stack: ");
+		if (verify_stack(sp)) {
+			printk("invalid kernel stack pointer %08lx", sp);
+			dump_info = 0;
+		} else if (sp < 4096+(unsigned long)tsk)
+			printk("kernel stack pointer underflow");
+		printk("\n");
+
+		if (dump_info)
+			dump_mem(sp - 16, 8192+(unsigned long)tsk);
+
+		dump_info = 1;
+
+		printk("Backtrace: ");
+		fp = regs->ARM_fp;
+		if (!fp) {
+			printk("no frame pointer");
+			dump_info = 0;
+		} else if (verify_stack(fp)) {
+			printk("invalid frame pointer %08lx", fp);
+			dump_info = 0;
+		} else if (fp < 4096+(unsigned long)tsk)
+			printk("frame pointer underflow");
+		printk("\n");
 
-	switch (died) {
-	case 2:
-		while (1);
-	case 1:
-		died ++;
-		panic ("die_if_kernel re-entered.  Major kernel corruption.  Please reboot me!");
-		break;
-	case 0:
-		died ++;
-		break;
-	}
+		if (dump_info)
+			c_backtrace(fp, processor_mode(regs));
 
-	dump_state(str, regs, err);
+		dump_instr(instruction_pointer(regs), 0);
+	}
 
-	cstack = (unsigned long)(regs + 1);
-	sstack = 4096+(unsigned long)current;
+	spin_unlock_irq(&die_lock);
+	do_exit(SIGSEGV);
+}
 
-	printk("Stack: ");
-	if (verify_stack_pointer(cstack, 4))
-		printk("invalid kernel stack pointer %08lx", cstack);
-	else if(cstack > sstack + 4096)
-		printk("(sp overflow)");
-	else if(cstack < sstack)
-		printk("(sp underflow)");
-	printk("\n");
-
-	dump_mem(cstack - 16, sstack + 4096);
-
-	frameptr = regs->ARM_fp;
-	if (frameptr) {
-		if (verify_stack_pointer (frameptr, 4))
-			printk ("Backtrace: invalid frame pointer\n");
-		else {
-			printk("Backtrace: \n");
-			c_backtrace (frameptr, processor_mode(regs));
-		}
-	}
+static void die_if_kernel(const char *str, struct pt_regs *regs, int err)
+{
+	if (user_mode(regs))
+    		return;
 
-	dump_instr(instruction_pointer(regs), 0);
-	died = 0;
-	if (ret != -1)
-		do_exit (ret);
-	else {
-		cli ();
-		while (1);
-	}
+    	die(str, regs, err);
 }
 
-void bad_user_access_alignment (const void *ptr)
+void bad_user_access_alignment(const void *ptr)
 {
-	void *pc;
-	__asm__("mov %0, lr\n": "=r" (pc));
-	printk (KERN_ERR "bad_user_access_alignment called: ptr = %p, pc = %p\n", ptr, pc);
+	printk(KERN_ERR "bad user access alignment: ptr = %p, pc = %p\n", ptr, 
+		__builtin_return_address(0));
 	current->tss.error_code = 0;
 	current->tss.trap_no = 11;
-	force_sig (SIGBUS, current);
-/*	die_if_kernel("Oops - bad user access alignment", regs, mode, SIGBUS);*/
+	force_sig(SIGBUS, current);
+/*	die_if_kernel("Oops - bad user access alignment", regs, mode);*/
 }
 
-asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode)
+asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode)
 {
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n",
+		current->comm, current->pid, instruction_pointer(regs));
+#endif
 	current->tss.error_code = 0;
 	current->tss.trap_no = 6;
-	force_sig (SIGILL, current);
-	die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL);
+	force_sig(SIGILL, current);
+	die_if_kernel("Oops - undefined instruction", regs, mode);
 }
 
-asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode)
+asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode)
 {
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n",
+		current->comm, current->pid, instruction_pointer(regs));
+#endif
 	current->tss.error_code = 0;
 	current->tss.trap_no = 11;
-	force_sig (SIGBUS, current);
-	die_if_kernel("Oops - address exception", regs, mode, SIGBUS);
+	force_sig(SIGBUS, current);
+	die_if_kernel("Oops - address exception", regs, mode);
 }
 
 asmlinkage void do_unexp_fiq (struct pt_regs *regs)
 {
 #ifndef CONFIG_IGNORE_FIQ
-	printk ("Hmm.  Unexpected FIQ received, but trying to continue\n");
-	printk ("You may have a hardware problem...\n");
+	printk("Hmm.  Unexpected FIQ received, but trying to continue\n");
+	printk("You may have a hardware problem...\n");
 #endif
 }
 
+/*
+ * bad_mode handles the impossible case in the vectors.
+ * If you see one of these, then it's extremely serious,
+ * and could mean you have buggy hardware.  It never
+ * returns, and never tries to sync.  We hope that we
+ * can dump out some state information...
+ */
 asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
 {
-	printk (KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
-		handler[reason],
-		processor_modes[proc_mode]);
-	die_if_kernel ("Oops", regs, 0, -1);
+	console_verbose();
+
+	printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
+		handler[reason], processor_modes[proc_mode]);
+
+	/*
+	 * Dump out the vectors and stub routines
+	 */
+	printk(KERN_CRIT "Vectors:\n");
+	dump_mem(0, 0x40);
+	printk(KERN_CRIT "Stubs:\n");
+	dump_mem(0x200, 0x4b8);
+
+	die("Oops", regs, 0);
+	cli();
+	while(1);
 }
 
 /*
@@ -249,54 +273,85 @@
  */
 asmlinkage void math_state_restore (void)
 {
-    	current->used_math = 1;
+	current->used_math = 1;
 }
 
-asmlinkage void arm_syscall (int no, struct pt_regs *regs)
+asmlinkage int arm_syscall (int no, struct pt_regs *regs)
 {
 	switch (no) {
 	case 0: /* branch through 0 */
 		force_sig(SIGSEGV, current);
-//		if (user_mode(regs)) {
-//			dump_state("branch through zero", regs, 0);
-//			if (regs->ARM_fp)
-//				c_backtrace (regs->ARM_fp, processor_mode(regs));
-//		}
-		die_if_kernel ("branch through zero", regs, 0, SIGSEGV);
+		die_if_kernel("branch through zero", regs, 0);
 		break;
 
 	case 1: /* SWI_BREAK_POINT */
 		regs->ARM_pc -= 4; /* Decrement PC by one instruction */
-		ptrace_cancel_bpt (current);
-		force_sig (SIGTRAP, current);
+		ptrace_cancel_bpt(current);
+		force_sig(SIGTRAP, current);
+		return regs->ARM_r0;
+
+	case 2:	/* sys_cacheflush */
+#ifdef CONFIG_CPU_32
+		/* r0 = start, r1 = length, r2 = flags */
+		processor.u.armv3v4._flush_cache_area(regs->ARM_r0,
+						      regs->ARM_r1,
+						      1);
+#endif
 		break;
 
 	default:
-		printk ("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
-		force_sig (SIGILL, current);
+		/* Calls 9f00xx..9f07ff are defined to return -ENOSYS
+		   if not implemented, rather than raising SIGILL.  This
+		   way the calling program can gracefully determine whether
+		   a feature is supported.  */
+		if (no <= 0x7ff)
+			return -ENOSYS;
+#ifdef CONFIG_DEBUG_USER
+		/* experiance shows that these seem to indicate that
+		 * something catastrophic has happened
+		 */
+		printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
 		if (user_mode(regs)) {
-			show_regs (regs);
-			c_backtrace (regs->ARM_fp, processor_mode(regs));
+			show_regs(regs);
+			c_backtrace(regs->ARM_fp, processor_mode(regs));
 		}
-		die_if_kernel ("Oops", regs, no, SIGILL);
+#endif
+		force_sig(SIGILL, current);
+		die_if_kernel("Oops", regs, no);
 		break;
 	}
+	return 0;
 }
 
 asmlinkage void deferred(int n, struct pt_regs *regs)
 {
-	dump_state("old system call", regs, n);
-	force_sig (SIGILL, current);
+	/* You might think just testing `handler' would be enough, but PER_LINUX
+	 * points it to no_lcall7 to catch undercover SVr4 binaries.  Gutted.
+	 */
+	if (current->personality != PER_LINUX && current->exec_domain->handler) {
+		/* Hand it off to iBCS.  The extra parameter and consequent type 
+		 * forcing is necessary because of the weird ARM calling convention.
+		 */
+		void (*handler)(int nr, struct pt_regs *regs) = (void *)current->exec_domain->handler;
+		(*handler)(n, regs);
+		return;
+	}
+
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_ERR "[%d] %s: old system call.\n", current->pid, 
+	       current->comm);
+#endif
+	force_sig(SIGILL, current);
 }
 
 asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr)
 {
-	printk ("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
+	printk("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
 }
 
-asmlinkage void arm_invalidptr (const char *function, int size)
+asmlinkage void arm_invalidptr(const char *function, int size)
 {
-	printk ("Invalid pointer size in %s (PC=%p) size %d\n",
+	printk("Invalid pointer size in %s (pc=%p) size %d\n",
 		function, __builtin_return_address(0), size);
 }
 
diff -urN linux-2.2.18-pre8/arch/arm/lib/Makefile linux/arch/arm/lib/Makefile
--- linux-2.2.18-pre8/arch/arm/lib/Makefile	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/lib/Makefile	Mon Dec  3 20:25:13 2001
@@ -1,19 +1,25 @@
 #
 # linux/arch/arm/lib/Makefile
 #
-# Copyright (C) 1995-1998 Russell King
+# Copyright (C) 1995-1999 Russell King
 #
 
 L_TARGET := lib.a
-L_OBJS   := backtrace.o bitops.o checksum.o delay.o io.o memcpy.o \
-	    system.o string.o uaccess.o
+L_OBJS   := changebit.o checksum.o \
+	    clearbit.o copy_page.o findbit.o \
+	    memchr.o memcpy.o memset.o memzero.o setbit.o strchr.o \
+	    strrchr.o testchangebit.o testclearbit.o testsetbit.o  \
+	    semaphore.o system.o uaccess.o
+
+O_TARGET := lib.o
+O_OBJS   := backtrace.o delay.o
 
 ifeq ($(PROCESSOR),armo)
   L_OBJS += uaccess-armo.o
 endif
 
 ifdef CONFIG_ARCH_ACORN
-  L_OBJS += loaders.o ll_char_wr.o io-acorn.o
+  L_OBJS += io-acorn.o
   ifdef CONFIG_ARCH_A5K
     L_OBJS += floppydma.o
   endif
@@ -24,31 +30,23 @@
 
 ifeq ($(MACHINE),ebsa110)
   L_OBJS += io-ebsa110.o
+else
+  OX_OBJS += io.o
 endif
 
-ifeq ($(MACHINE),vnc)
-  L_OBJS += io-ebsa285.o
-endif
-
-ifeq ($(MACHINE),ebsa285)
-  L_OBJS += io-ebsa285.o
+ifeq ($(MACHINE),footbridge)
+  L_OBJS += io-footbridge.o
 endif
 
 include $(TOPDIR)/Rules.make
 
+%.o: %.S
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
+
 constants.h: getconsdata.o extractconstants.pl
 	$(PERL) extractconstants.pl $(OBJDUMP) > $@
 
 getconsdata.o: getconsdata.c
 	$(CC) $(CFLAGS) -c getconsdata.c
 
-checksum.o: constants.h
-
-%.o: %.S
-ifneq ($(CONFIG_BINUTILS_NEW),y)
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s
-	$(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s
-	$(RM) ..tmp.$<.s
-else
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
-endif
+checksum.o string.o: constants.h
diff -urN linux-2.2.18-pre8/arch/arm/lib/backtrace.S linux/arch/arm/lib/backtrace.S
--- linux-2.2.18-pre8/arch/arm/lib/backtrace.S	Fri May  8 00:42:38 1998
+++ linux/arch/arm/lib/backtrace.S	Mon Dec  3 20:25:13 2001
@@ -21,6 +21,11 @@
 		mov	r0, fp
 
 ENTRY(c_backtrace)
+
+#ifndef CONFIG_FRAME_POINTER
+		mov	pc, lr
+#else
+
 		stmfd	sp!, {r4 - r8, lr}	@ Save an extra register so we have a location...
 #ifdef CONFIG_CPU_32
 		tst	r1, #0x10		@ 26 or 32-bit?
@@ -55,14 +60,14 @@
 
 		sub	r0, frame, #16
 		ldr	r1, [save, #4]
-		mov	r3, r1, lsr #10
+		mov	r3, r1, lsr #11
 		ldr	r2, .Ldsi+4
 		teq	r3, r2			@ Check for stmia sp!, {args}
 		addeq	save, save, #4		@ next instruction
 		bleq	.Ldumpstm
 
 		ldr	r1, [save, #4]		@ Get 'stmia sp!, {rlist, fp, ip, lr, pc}' instruction
-		mov	r3, r1, lsr #10
+		mov	r3, r1, lsr #11
 		ldr	r2, .Ldsi
 		teq	r3, r2
 		bleq	.Ldumpstm
@@ -78,28 +83,36 @@
 #define reg   r5
 #define stack r6
 
-.Ldumpstm:	stmfd	sp!, {instr, reg, stack, lr}
+.Ldumpstm:	stmfd	sp!, {instr, reg, stack, r7, lr}
 		mov	stack, r0
 		mov	instr, r1
-		mov	reg, #9
-
+		mov	reg, #10
+		mov	r7, #0
 1:		mov	r3, #1
 		tst	instr, r3, lsl reg
 		beq	2f
+		add	r7, r7, #1
+		teq	r7, #4
+		moveq	r7, #0
+		moveq	r3, #'\n'
+		movne	r3, #' '
 		ldr	r2, [stack], #-4
 		mov	r1, reg
 		adr	r0, .Lfp
 		bl	SYMBOL_NAME(printk)
 2:		subs	reg, reg, #1
 		bpl	1b
-
+		teq	r7, #0
+		adrne	r0, .Lcr
+		blne	SYMBOL_NAME(printk)
 		mov	r0, stack
-		LOADREGS(fd, sp!, {instr, reg, stack, pc})
+		LOADREGS(fd, sp!, {instr, reg, stack, r7, pc})
 
-.Lfe:		.ascii	"Function entered at [<%p>] from [<%p>]\n"
-		.byte 0
-.Lfp:		.ascii	"  r%d = %p\n"
-		.byte 0
+.Lfe:		.asciz	"Function entered at [<%p>] from [<%p>]\n"
+.Lfp:		.asciz	" r%d = %08X%c"
+.Lcr:		.asciz	"\n"
 		.align
-.Ldsi:		.word	0x00e92dd8 >> 2
-		.word	0x00e92d00 >> 2
+.Ldsi:		.word	0x00e92dd8 >> 3
+		.word	0x00e92d00 >> 3
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/lib/bitops.S linux/arch/arm/lib/bitops.S
--- linux-2.2.18-pre8/arch/arm/lib/bitops.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/lib/bitops.S	Wed Dec 31 16:00:00 1969
@@ -1,152 +0,0 @@
-/*
- * linux/arch/arm/lib/bitops.S
- *
- * Copyright (C) 1995, 1996 Russell King
- */
-
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-                .text
-
-@ Purpose  : Function to set a bit
-@ Prototype: int set_bit(int bit,int *addr)
-
-ENTRY(set_bit)
-                and     r2, r0, #7
-                mov     r3, #1
-                mov     r3, r3, lsl r2
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1, r0, lsr #3]
-		orr	r2, r2, r3
-		strb	r2, [r1, r0, lsr #3]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-ENTRY(test_and_set_bit)
-		add	r1, r1, r0, lsr #3	@ Get byte offset
-		and	r3, r0, #7		@ Get bit offset
-		mov	r0, #1
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1]
-		tst	r2, r0, lsl r3
-		orr	r2, r2, r0, lsl r3
-		moveq	r0, #0
-		strb	r2, [r1]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-@ Purpose  : Function to clear a bit
-@ Prototype: int clear_bit(int bit,int *addr)
-
-ENTRY(clear_bit)
-		and	r2, r0, #7
-		mov	r3, #1
-		mov	r3, r3, lsl r2
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1, r0, lsr #3]
-		bic	r2, r2, r3
-		strb	r2, [r1, r0, lsr #3]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-ENTRY(test_and_clear_bit)
-		add	r1, r1, r0, lsr #3	@ Get byte offset
-		and	r3, r0, #7	@ Get bit offset
-		mov	r0, #1
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1]
-		tst	r2, r0, lsl r3
-		bic	r2, r2, r0, lsl r3
-		moveq	r0, #0
-		strb	r2, [r1]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-/* Purpose  : Function to change a bit
- * Prototype: int change_bit(int bit,int *addr)
- */
-ENTRY(change_bit)
-                and     r2, r0, #7
-                mov     r3, #1
-                mov     r3, r3, lsl r2
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1, r0, lsr #3]
-		eor	r2, r2, r3
-		strb	r2, [r1, r0, lsr #3]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-ENTRY(test_and_change_bit)
-		add	r1, r1, r0, lsr #3
-		and	r3, r0, #7
-		mov	r0, #1
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1]
-		tst	r2, r0, lsl r3
-		eor	r2, r2, r0, lsl r3
-		moveq	r0, #0
-		strb	r2, [r1]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-@ Purpose  : Find a 'zero' bit
-@ Prototype: int find_first_zero_bit(char *addr,int maxbit);
-
-ENTRY(find_first_zero_bit)
-		mov	r2, #0			@ Initialise bit position
-Lfindzbit1lp:	ldrb	r3, [r0, r2, lsr #3]	@ Check byte, if 0xFF, then all bits set
-		teq	r3, #0xFF
-		bne	Lfoundzbit
-		add	r2, r2, #8
-		cmp	r2, r1			@ Check to see if we have come to the end
-		bcc	Lfindzbit1lp
-		add	r0, r1, #1		@ Make sure that we flag an error
-		RETINSTR(mov,pc,lr)
-Lfoundzbit:	tst	r3, #1			@ Check individual bits
-		moveq	r0, r2
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #2
-		addeq	r0, r2, #1
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #4
-		addeq	r0, r2, #2
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #8
-		addeq	r0, r2, #3
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #16
-		addeq	r0, r2, #4
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #32
-		addeq	r0, r2, #5
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #64
-		addeq	r0, r2, #6
-		RETINSTR(moveq,pc,lr)
-		add	r0, r2, #7
-		RETINSTR(mov,pc,lr)
-
-@ Purpose  : Find next 'zero' bit
-@ Prototype: int find_next_zero_bit(char *addr,int maxbit,int offset)
-
-ENTRY(find_next_zero_bit)
-		tst	r2, #7
-		beq	Lfindzbit1lp		@ If new byte, goto old routine
-		ldrb	r3, [r0, r2, lsr#3]
-		orr	r3, r3, #0xFF00		@ Set top bits so we wont get confused
-		stmfd	sp!, {r4}
-		and	r4, r2, #7
-		mov	r3, r3, lsr r4		@ Shift right by no. of bits
-		ldmfd	sp!, {r4}
-		and	r3, r3, #0xFF
-		teq	r3, #0xFF
-		orreq	r2, r2, #7
-		addeq	r2, r2, #1
-		beq	Lfindzbit1lp		@ If all bits are set, goto old routine
-		b	Lfoundzbit
diff -urN linux-2.2.18-pre8/arch/arm/lib/changebit.S linux/arch/arm/lib/changebit.S
--- linux-2.2.18-pre8/arch/arm/lib/changebit.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/changebit.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/arm/lib/changebit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+/* Purpose  : Function to change a bit
+ * Prototype: int change_bit(int bit,int *addr)
+ */
+ENTRY(change_bit)
+                and     r2, r0, #7
+                mov     r3, #1
+                mov     r3, r3, lsl r2
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1, r0, lsr #3]
+		eor	r2, r2, r3
+		strb	r2, [r1, r0, lsr #3]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/checksum.S linux/arch/arm/lib/checksum.S
--- linux-2.2.18-pre8/arch/arm/lib/checksum.S	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/checksum.S	Mon Dec  3 20:25:13 2001
@@ -74,8 +74,9 @@
 		stmfd	sp!, {r1 - r2, r4 - r8, fp, ip, lr, pc}
 		.endm
 
-#define LOAD_REGS(cond)					\
-		LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r8, fp, sp, pc})
+		.macro	load_regs,flags
+		ldm\flags	fp, {r1, r2, r4-r8, fp, sp, pc}
+		.endm
 
 		.macro	load1b,	reg1
 9999:		ldrbt	\reg1, [r0], $1
@@ -130,12 +131,13 @@
 		stmfd	sp!, {r1 - r2, r4 - r9, fp, ip, lr, pc}
 		mov	r9, sp, lsr #13
 		mov	r9, r9, lsl #13
-		ldr	r9, [r9, #TSK_ADDR_LIMIT]
+		ldr	r9, [r9, #ADDR_LIMIT]
 		mov	r9, r9, lsr #24
 		.endm
 
-#define LOAD_REGS(cond)					\
-		LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r9, fp, sp, pc})
+		.macro	load_regs,flags
+		ldm\flags	fp, {r1, r2, r4-r9, fp, sp, pc}^
+		.endm
 
 		.macro	load1b,	reg1
 		tst	r9, #0x01
@@ -245,7 +247,7 @@
 		adcs	r3, r3, r4
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		load1l	r4
 		tst	r2, #2
 		beq	.exit
@@ -259,11 +261,11 @@
 		andne	r4, r4, #255
 		adcnes	r3, r3, r4
 		adcs	r0, r3, #0
-		LOAD_REGS(al)
+		load_regs	ea
 
 .too_small_user:
 		teq	r2, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		cmp	r2, #2
 		blt	.too_small_user1
 		load2b	ip, r8
@@ -278,7 +280,7 @@
 		strb	ip, [r1], #1
 		adcs	r3, r3, ip
 .csum_exit:	adc	r0, r3, #0
-		LOAD_REGS(al)
+		load_regs	ea
 
 .src_not_aligned_user:
 		cmp	r2, #4
@@ -331,7 +333,7 @@
 		mov	r4, r5, lsr #8
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		tst	r2, #2
 		beq	.exit
 		adcs	r3, r3, r4, lsl #16
@@ -384,13 +386,16 @@
 		mov	r4, r5, lsr #16
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		tst	r2, #2
 		beq	.exit
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
+		tst	r2, #1
+		adceq	r0, r3, #0
+		load_regs	eqea
 		load1b	r4
 		b	.exit
 
@@ -437,7 +442,7 @@
 		mov	r4, r5, lsr #24
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		tst	r2, #2
 		beq	.exit
 		adcs	r3, r3, r4, lsl #16
@@ -461,7 +466,7 @@
 6002:		teq	r2, r1
 		strneb	r3, [r1], #1
 		bne	6002b
-		LOAD_REGS(al)
+		load_regs	ea
 #if defined(CONFIG_CPU_32)
 		.previous
 #endif
@@ -520,13 +525,13 @@
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		ldr	r4, [r0], #4
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
-		b	Lexit
+		b	Lexit_r4
 
 Ltoo_small:	teq	r2, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
@@ -538,10 +543,12 @@
 		adds	r3, r3, ip
 		strb	ip, [r1], #1
 		strb	r8, [r1], #1
-Lexit:		tst	r2, #1
-Ltoo_small1:	ldrneb	ip, [r0], #1
-		strneb	ip, [r1], #1
-		adcnes	r3, r3, ip
+		tst	r2, #1
+Ltoo_small1:	ldrneb	r4, [r0], #1
+Lexit_r4:	tst	r2, #1
+		strneb	r4, [r1], #1
+		andne	r4, r4, #255
+		adcnes	r3, r3, r4
 		adcs	r0, r3, #0
 		LOADREGS(ea,fp,{r4 - r8, fp, sp, pc})
 
@@ -598,13 +605,13 @@
 		adceq	r0, r3, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
-		b	Lexit
+		b	Lexit_r4
 
 Lsrc2_aligned:	mov	r4, r4, lsr #16
 		adds	r3, r3, #0
@@ -650,13 +657,13 @@
 		adceq	r0, r3, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
 		ldrb	r4, [r0], #1
-		b	Lexit
+		b	Lexit_r4
 
 Lsrc3_aligned:	mov	r4, r4, lsr #24
 		adds	r3, r3, #0
@@ -702,14 +709,14 @@
 		adceq	r0, r3, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		ldr	r4, [r0], #4
 		strb	r4, [r1], #1
 		adcs	r3, r3, r4, lsl #24
 		mov	r4, r4, lsr #8
-		b	Lexit
+		b	Lexit_r4
 
 ENTRY(__csum_ipv6_magic)
 		stmfd	sp!, {lr}
diff -urN linux-2.2.18-pre8/arch/arm/lib/clearbit.S linux/arch/arm/lib/clearbit.S
--- linux-2.2.18-pre8/arch/arm/lib/clearbit.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/clearbit.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/arm/lib/clearbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+@ Purpose  : Function to clear a bit
+@ Prototype: int clear_bit(int bit,int *addr)
+
+ENTRY(clear_bit)
+		and	r2, r0, #7
+		mov	r3, #1
+		mov	r3, r3, lsl r2
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1, r0, lsr #3]
+		bic	r2, r2, r3
+		strb	r2, [r1, r0, lsr #3]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/copy_page.S linux/arch/arm/lib/copy_page.S
--- linux-2.2.18-pre8/arch/arm/lib/copy_page.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/copy_page.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,35 @@
+/*
+ * linux/arch/arm/lib/copypage.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text 
+/*
+ * StrongARM optimised copy_page routine
+ * now 1.72bytes/cycle, was 1.60 bytes/cycle
+ * (50MHz bus -> 86MB/s)
+ */
+
+ENTRY(copy_page)
+		stmfd	sp!, {r4, lr}			@	2
+		mov	r2, #PAGE_SZ/64			@	1
+1:		ldmia	r1!, {r3, r4, ip, lr}		@	4
+		subs	r2, r2, #1			@	1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		ldmia	r1!, {r3, r4, ip, lr}		@	4+1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		ldmia	r1!, {r3, r4, ip, lr}		@	4+1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		ldmia	r1!, {r3, r4, ip, lr}		@	4+1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		bne	1b				@	1
+		LOADREGS(fd, sp!, {r4, pc})		@	3
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/findbit.S linux/arch/arm/lib/findbit.S
--- linux-2.2.18-pre8/arch/arm/lib/findbit.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/findbit.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,65 @@
+/*
+ * linux/arch/arm/lib/bitops.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+@ Purpose  : Find a 'zero' bit
+@ Prototype: int find_first_zero_bit(char *addr,int maxbit);
+
+ENTRY(find_first_zero_bit)
+		mov	r2, #0			@ Initialise bit position
+Lfindzbit1lp:	ldrb	r3, [r0, r2, lsr #3]	@ Check byte, if 0xFF, then all bits set
+		teq	r3, #0xFF
+		bne	Lfoundzbit
+		add	r2, r2, #8
+		cmp	r2, r1			@ Check to see if we have come to the end
+		bcc	Lfindzbit1lp
+		add	r0, r1, #1		@ Make sure that we flag an error
+		RETINSTR(mov,pc,lr)
+Lfoundzbit:	tst	r3, #1			@ Check individual bits
+		moveq	r0, r2
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #2
+		addeq	r0, r2, #1
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #4
+		addeq	r0, r2, #2
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #8
+		addeq	r0, r2, #3
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #16
+		addeq	r0, r2, #4
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #32
+		addeq	r0, r2, #5
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #64
+		addeq	r0, r2, #6
+		RETINSTR(moveq,pc,lr)
+		add	r0, r2, #7
+		RETINSTR(mov,pc,lr)
+
+@ Purpose  : Find next 'zero' bit
+@ Prototype: int find_next_zero_bit(char *addr,int maxbit,int offset)
+
+ENTRY(find_next_zero_bit)
+		tst	r2, #7
+		beq	Lfindzbit1lp		@ If new byte, goto old routine
+		ldrb	r3, [r0, r2, lsr#3]
+		orr	r3, r3, #0xFF00		@ Set top bits so we wont get confused
+		stmfd	sp!, {r4}
+		and	r4, r2, #7
+		mov	r3, r3, lsr r4		@ Shift right by no. of bits
+		ldmfd	sp!, {r4}
+		and	r3, r3, #0xFF
+		teq	r3, #0xFF
+		orreq	r2, r2, #7
+		addeq	r2, r2, #1
+		beq	Lfindzbit1lp		@ If all bits are set, goto old routine
+		b	Lfoundzbit
diff -urN linux-2.2.18-pre8/arch/arm/lib/floppydma.S linux/arch/arm/lib/floppydma.S
--- linux-2.2.18-pre8/arch/arm/lib/floppydma.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/lib/floppydma.S	Mon Dec  3 20:25:13 2001
@@ -26,32 +26,3 @@
 		strb	r12, [r11, #-4]
 		subs	pc, lr, #4
 SYMBOL_NAME(floppy_fiqout_end):
-
-@ Params:
-@ r0 = length
-@ r1 = address
-@ r2 = floppy port
-@ Puts these into R9_fiq, R10_fiq, R11_fiq
-ENTRY(floppy_fiqsetup)
-		mov	ip, sp
-		stmfd	sp!, {fp, ip, lr, pc}
-		sub	fp, ip, #4
-		MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ)	@ disable FIQs, IRQs, FIQ mode
-		mov	r0, r0
-		mov	r9, r0
-		mov	r10, r1
-		mov	r11, r2
-		RESTOREMODE(r3)				@ back to normal
-		mov	r0, r0
-		LOADREGS(ea,fp,{fp, sp, pc})
-
-ENTRY(floppy_fiqresidual)
-		mov	ip, sp
-		stmfd	sp!, {fp, ip, lr, pc}
-		sub	fp, ip, #4
-		MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ)	@ disable FIQs, IRQs, FIQ mode
-		mov	r0, r0
-		mov	r0, r9
-		RESTOREMODE(r3)
-		mov	r0, r0
-		LOADREGS(ea,fp,{fp, sp, pc})
diff -urN linux-2.2.18-pre8/arch/arm/lib/getconsdata.c linux/arch/arm/lib/getconsdata.c
--- linux-2.2.18-pre8/arch/arm/lib/getconsdata.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/getconsdata.c	Mon Dec  3 20:25:13 2001
@@ -10,23 +10,25 @@
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
 
+/*
+ * Make sure that the compiler and target are compatible
+ */
+#if (defined(__APCS_32__) && defined(CONFIG_CPU_26))
+#error Your compiler targets APCS-32 but this kernel requires APCS-26.
+#endif
+#if (defined(__APCS_26__) && defined(CONFIG_CPU_32))
+#error Your compiler targets APCS-26 but this kernel requires APCS-32.
+#endif
+
 #undef PAGE_READONLY
 
 #define OFF_TSK(n) (unsigned long)&(((struct task_struct *)0)->n)
 #define OFF_MM(n) (unsigned long)&(((struct mm_struct *)0)->n)
 
-#ifdef KERNEL_DOMAIN
-unsigned long DOM_KERNELDOMAIN = KERNEL_DOMAIN;
-#endif
-#ifdef USER_DOMAIN
-unsigned long DOM_USERDOMAIN = USER_DOMAIN;
-#endif
-
 unsigned long TSK_STATE = OFF_TSK(state);
 unsigned long TSK_FLAGS = OFF_TSK(flags);
 unsigned long TSK_NEED_RESCHED = OFF_TSK(need_resched);
 unsigned long TSK_SIGPENDING = OFF_TSK(sigpending);
-unsigned long TSK_ADDR_LIMIT = OFF_TSK(addr_limit);
 unsigned long TSK_USED_MATH = OFF_TSK(used_math);
 
 unsigned long MM = OFF_TSK(mm);
@@ -36,8 +38,12 @@
 unsigned long TSS_SAVE = OFF_TSK(tss.save);
 unsigned long TSS_FPESAVE = OFF_TSK(tss.fpstate.soft.save);
 #ifdef CONFIG_CPU_26
+unsigned long ADDR_LIMIT  = OFF_TSK(addr_limit);
 unsigned long TSS_MEMCMAP = OFF_TSK(tss.memcmap);
 #endif
+#ifdef CONFIG_CPU_32
+unsigned long TSS_DOMAIN = OFF_TSK(tss.domain);
+#endif
 
 #ifdef _PAGE_PRESENT
 unsigned long PAGE_PRESENT = _PAGE_PRESENT;
@@ -66,6 +72,25 @@
 #ifdef _PAGE_CLEAN
 unsigned long PAGE_CLEAN = _PAGE_CLEAN;
 #endif
+
+#ifdef PTE_TYPE_SMALL
+unsigned long HPTE_TYPE_SMALL = PTE_TYPE_SMALL;
+unsigned long HPTE_AP_READ    = PTE_AP_READ;
+unsigned long HPTE_AP_WRITE   = PTE_AP_WRITE;
+#endif
+
+#ifdef L_PTE_PRESENT
+unsigned long LPTE_PRESENT    = L_PTE_PRESENT;
+unsigned long LPTE_YOUNG      = L_PTE_YOUNG;
+unsigned long LPTE_BUFFERABLE = L_PTE_BUFFERABLE;
+unsigned long LPTE_CACHEABLE  = L_PTE_CACHEABLE;
+unsigned long LPTE_USER       = L_PTE_USER;
+unsigned long LPTE_WRITE      = L_PTE_WRITE;
+unsigned long LPTE_EXEC       = L_PTE_EXEC;
+unsigned long LPTE_DIRTY      = L_PTE_DIRTY;
+#endif
+
+unsigned long PAGE_SZ = PAGE_SIZE;
 
 unsigned long KSWI_BASE = 0x900000;
 unsigned long KSWI_SYS_BASE = 0x9f0000;
diff -urN linux-2.2.18-pre8/arch/arm/lib/io-acorn.S linux/arch/arm/lib/io-acorn.S
--- linux-2.2.18-pre8/arch/arm/lib/io-acorn.S	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/io-acorn.S	Mon Dec  3 20:25:13 2001
@@ -11,50 +11,514 @@
 		.text
 		.align
 
-#define OUT(reg)						\
-		mov	r8, reg, lsl $16			;\
-		orr	r8, r8, r8, lsr $16			;\
-		str	r8, [r3, r0, lsl $2]			;\
-		mov	r8, reg, lsr $16			;\
-		orr	r8, r8, r8, lsl $16			;\
-		str	r8, [r3, r0, lsl $2]
-
-#define IN(reg)							\
-		ldr	reg, [r0]				;\
-		and	reg, reg, ip				;\
-		ldr	lr, [r0]				;\
-		orr	reg, reg, lr, lsl $16
-
-		.equ	pcio_base_high, PCIO_BASE & 0xff000000
-		.equ	pcio_base_low,	PCIO_BASE & 0x00ff0000
-		.equ	io_base_high, IO_BASE & 0xff000000
-		.equ	io_base_low, IO_BASE & 0x00ff0000
-
-		.equ	addr_io_diff_hi, pcio_base_high - io_base_high
-		.equ	addr_io_diff_lo, pcio_base_low - io_base_low
-
-		.macro	addr	reg, off
-		tst	\off, #0x80000000
-		.if	addr_io_diff_hi
-		movne	\reg, #IO_BASE
-		moveq	\reg, #pcio_base_high
-		.if	pcio_base_low
-		addeq	\reg, \reg, #pcio_base_low
-		.endif
-		.else
-		mov	\reg, #IO_BASE
-		addeq	\reg, \reg, #addr_io_diff_lo
-		.endif
+		.equ	diff_pcio_base, PCIO_BASE - IO_BASE
+
+		.macro	outw2	rd
+		mov	r8, \rd, lsl #16
+		orr	r8, r8, r8, lsr #16
+		str	r8, [r3, r0, lsl #2]
+		mov	r8, \rd, lsr #16
+		orr	r8, r8, r8, lsl #16
+		str	r8, [r3, r0, lsl #2]
+		.endm
+
+		.macro	inw2	rd, mask, temp
+		ldr	\rd, [r0]
+		and	\rd, \rd, \mask
+		ldr	\temp, [r0]
+		orr	\rd, \rd, \temp, lsl #16
+		.endm
+
+		.macro	addr	rd
+		tst	\rd, #0x80000000
+		mov	\rd, \rd, lsl #2
+		add	\rd, \rd, #IO_BASE
+		addeq	\rd, \rd, #diff_pcio_base
 		.endm
 
-@ Purpose: read a block of data from a hardware register to memory.
-@ Proto  : insw(int from_port, void *to, int len_in_words);
-@ Proto  : inswb(int from_port, void *to, int len_in_bytes);
-@ Notes  : increment to
+.iosw_bad_align_msg:
+		.ascii	"insw: bad buffer alignment (%p), called from %08lX\n\0"
+.iosl_warning:
+		.ascii	"<4>insl/outsl not implemented, called from %08lX\0"
+		.align
+
+/*
+ * These make no sense on Acorn machines.
+ * Print a warning message.
+ */
+ENTRY(insl)
+ENTRY(outsl)
+		adr	r0, .iosl_warning
+		mov	r1, lr
+		b	SYMBOL_NAME(printk)
+
+.iosw_bad_alignment:
+		adr	r0, .iosw_bad_align_msg
+		mov	r2, lr
+		b	SYMBOL_NAME(panic)
+
+
+/* Purpose: read a block of data from a hardware register to memory.
+ * Proto  : void insw(int from_port, void *to, int len_in_words);
+ * Notes  : increment to, 'to' must be 16-bit aligned
+ */
+
+.insw_align:	tst	r1, #1
+		bne	.iosw_bad_alignment
+
+		ldr	r3, [r0]
+		strb	r3, [r1], #1
+		mov	r3, r3, lsr #8
+		strb	r3, [r1], #1
+
+		subs	r2, r2, #1
+		bne	.insw_aligned
 
 ENTRY(insw)
+		teq	r2, #0
+		RETINSTR(moveq,pc,lr)
+		addr	r0
+		tst	r1, #3
+		bne	.insw_align
+
+.insw_aligned:	mov	ip, #0xff
+		orr	ip, ip, ip, lsl #8
+		stmfd	sp!, {r4, r5, r6, lr}
+
+		subs	r2, r2, #8
+		bmi	.no_insw_8
+
+.insw_8_lp:	ldr	r3, [r0]
+		and	r3, r3, ip
+		ldr	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+
+		ldr	r4, [r0]
+		and	r4, r4, ip
+		ldr	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+
+		ldr	r5, [r0]
+		and	r5, r5, ip
+		ldr	r6, [r0]
+		orr	r5, r5, r6, lsl #16
+
+		ldr	r6, [r0]
+		and	r6, r6, ip
+		ldr	lr, [r0]
+		orr	r6, r6, lr, lsl #16
+
+		stmia	r1!, {r3 - r6}
+		subs	r2, r2, #8
+		bpl	.insw_8_lp
+		tst	r2, #7
+		LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_insw_8:	tst	r2, #4
+		beq	.no_insw_4
+
+		ldr	r3, [r0]
+		and	r3, r3, ip
+		ldr	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+
+		ldr	r4, [r0]
+		and	r4, r4, ip
+		ldr	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+
+		stmia	r1!, {r3, r4}
+
+.no_insw_4:	tst	r2, #2
+		beq	.no_insw_2
+
+		ldr	r3, [r0]
+		and	r3, r3, ip
+		ldr	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+
+		str	r3, [r1], #4
+
+.no_insw_2:	tst	r2, #1
+		ldrne	r3, [r0]
+		strneb	r3, [r1], #1
+		movne	r3, r3, lsr #8
+		strneb	r3, [r1]
+		LOADREGS(fd, sp!, {r4, r5, r6, pc})
+
+@ Purpose: write a block of data from memory to a hardware register.
+@ Proto  : outsw(int to_reg, void *from, int len_in_words);
+@ Notes  : increments from
+
+.outsw_align:	tst	r1, #1
+		bne	.iosw_bad_alignment
+
+		add	r1, r1, #2
+
+		ldr	r3, [r1, #-4]
+		mov	r3, r3, lsr #16
+		orr	r3, r3, r3, lsl #16
+		str	r3, [r0]
+		subs	r2, r2, #1
+		bne	.outsw_aligned
+
+ENTRY(outsw)
+		teq	r2, #0
+		RETINSTR(moveq,pc,lr)
+		addr	r0
+		tst	r1, #3
+		bne	.outsw_align
+
+.outsw_aligned:	stmfd	sp!, {r4, r5, r6, lr}
+
+		subs	r2, r2, #8
+		bmi	.no_outsw_8
+.outsw_8_lp:	ldmia	r1!, {r3, r4, r5, r6}
+
+		mov	ip, r3, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r3, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r5, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r5, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r6, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r6, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		subs	r2, r2, #8
+		bpl	.outsw_8_lp
+		tst	r2, #7
+		LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_outsw_8:	tst	r2, #4
+		beq	.no_outsw_4
+
+		ldmia	r1!, {r3, r4}
+
+		mov	ip, r3, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r3, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+.no_outsw_4:	tst	r2, #2
+		beq	.no_outsw_2
+
+		ldr	r3, [r1], #4
+
+		mov	ip, r3, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r3, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+.no_outsw_2:	tst	r2, #1
+
+		ldrne	r3, [r1]
+
+		movne	ip, r3, lsl #16
+		orrne	ip, ip, ip, lsr #16
+		strne	ip, [r0]
+
+		LOADREGS(fd, sp!, {r4, r5, r6, pc})
+
+.insb_align:	rsb	ip, ip, #4
+		cmp	ip, r2
+		movgt	ip, r2
+		cmp	ip, #2
+		ldrb	r3, [r0]
+		strb	r3, [r1], #1
+		ldrgeb	r3, [r0]
+		strgeb	r3, [r1], #1
+		ldrgtb	r3, [r0]
+		strgtb	r3, [r1], #1
+		subs	r2, r2, ip
+		bne	.insb_aligned
+
+ENTRY(insb)
+		teq	r2, #0
+		moveq	pc, lr
+		addr	r0
+		ands	ip, r1, #3
+		bne	.insb_align
+
+.insb_aligned:	stmfd	sp!, {r4 - r6, lr}
+
+		subs	r2, r2, #16
+		bmi	.insb_no_16
+
+.insb_16_lp:	ldrb	r3, [r0]
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #8
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #24
+		ldrb	r4, [r0]
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #8
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #24
+		ldrb	r5, [r0]
+		ldrb	r6, [r0]
+		orr	r5, r5, r6, lsl #8
+		ldrb	r6, [r0]
+		orr	r5, r5, r6, lsl #16
+		ldrb	r6, [r0]
+		orr	r5, r5, r6, lsl #24
+		ldrb	r6, [r0]
+		ldrb	ip, [r0]
+		orr	r6, r6, ip, lsl #8
+		ldrb	ip, [r0]
+		orr	r6, r6, ip, lsl #16
+		ldrb	ip, [r0]
+		orr	r6, r6, ip, lsl #24
+		stmia	r1!, {r3 - r6}
+		subs	r2, r2, #16
+		bpl	.insb_16_lp
+
+		tst	r2, #15
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+.insb_no_16:	tst	r2, #8
+		beq	.insb_no_8
+
+		ldrb	r3, [r0]
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #8
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #24
+		ldrb	r4, [r0]
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #8
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #24
+		stmia	r1!, {r3, r4}
+
+.insb_no_8:	tst	r2, #4
+		bne	.insb_no_4
+
+		ldrb	r3, [r0]
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #8
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #24
+		str	r3, [r1], #4
+
+.insb_no_4:	ands	r2, r2, #3
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+		cmp	r2, #2
+		ldrb	r3, [r0]
+		strb	r3, [r1], #1
+		ldrgeb	r3, [r0]
+		strgeb	r3, [r1], #1
+		ldrgtb	r3, [r0]
+		strgtb	r3, [r1]
+		LOADREGS(fd, sp!, {r4 - r6, pc})
+
+
+
+.outsb_align:	rsb	ip, ip, #4
+		cmp	ip, r2
+		mov	ip, r2
+		cmp	ip, #2
+		ldrb	r3, [r1], #1
+		strb	r3, [r0]
+		ldrgeb	r3, [r1], #1
+		strgeb	r3, [r0]
+		ldrgtb	r3, [r1], #1
+		strgtb	r3, [r0]
+		subs	r2, r2, ip
+		bne	.outsb_aligned
+
+ENTRY(outsb)
+		teq	r2, #0
+		moveq	pc, lr
+		addr	r0
+		ands	ip, r1, #3
+		bne	.outsb_align
+
+.outsb_aligned:	stmfd	sp!, {r4 - r6, lr}
+
+		subs	r2, r2, #16
+		bmi	.outsb_no_16
+
+.outsb_16_lp:	ldmia	r1!, {r3 - r6}
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+
+		strb	r5, [r0]
+		mov	r5, r5, lsr #8
+		strb	r5, [r0]
+		mov	r5, r5, lsr #8
+		strb	r5, [r0]
+		mov	r5, r5, lsr #8
+		strb	r5, [r0]
+
+		strb	r6, [r0]
+		mov	r6, r6, lsr #8
+		strb	r6, [r0]
+		mov	r6, r6, lsr #8
+		strb	r6, [r0]
+		mov	r6, r6, lsr #8
+		strb	r6, [r0]
+		subs	r2, r2, #16
+		bpl	.outsb_16_lp
+
+		tst	r2, #15
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+.outsb_no_16:	tst	r2, #8
+		beq	.outsb_no_8
+
+		ldmia	r1, {r3, r4}
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+
+.outsb_no_8:	tst	r2, #4
+		bne	.outsb_no_4
+
+		ldr	r3, [r1], #4
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+
+.outsb_no_4:	ands	r2, r2, #3
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+		cmp	r2, #2
+		ldrb	r3, [r1], #1
+		strb	r3, [r0]
+		ldrgeb	r3, [r1], #1
+		strgeb	r3, [r0]
+		ldrgtb	r3, [r1]
+		strgtb	r3, [r0]
+		LOADREGS(fd, sp!, {r4 - r6, pc})
+
+
+
+
+@ Purpose: write a memc register
+@ Proto  : void memc_write(int register, int value);
+@ Returns: nothing
+
+#if defined(CONFIG_CPU_26)
+ENTRY(memc_write)
+		cmp	r0, #7
+		RETINSTR(movgt,pc,lr)
+		mov	r0, r0, lsl #17
+		mov	r1, r1, lsl #15
+		mov	r1, r1, lsr #17
+		orr	r0, r0, r1, lsl #2
+		add	r0, r0, #0x03600000
+		strb	r0, [r0]
+		RETINSTR(mov,pc,lr)
+#define CPSR2SPSR(rt)
+#else
+#define CPSR2SPSR(rt) \
+		mrs	rt, cpsr; \
+		msr	spsr, rt
+#endif
+
+@ Purpose: call an expansion card loader to read bytes.
+@ Proto  : char read_loader(int offset, char *card_base, char *loader);
+@ Returns: byte read
+
+ENTRY(ecard_loader_read)
+		stmfd	sp!, {r4 - r12, lr}
+		mov	r11, r1
+		mov	r1, r0
+		CPSR2SPSR(r0)
+		mov	lr, pc
+		mov	pc, r2
+		LOADREGS(fd, sp!, {r4 - r12, pc})
+
+@ Purpose: call an expansion card loader to reset the card
+@ Proto  : void read_loader(int card_base, char *loader);
+@ Returns: byte read
+
+ENTRY(ecard_loader_reset)
+		stmfd	sp!, {r4 - r12, lr}
+		mov	r11, r0
+		CPSR2SPSR(r0)
+		mov	lr, pc
+		add	pc, r1, #8
+		LOADREGS(fd, sp!, {r4 - r12, pc})
+
+
+#if 0
 		mov	r2, r2, lsl#1
-ENTRY(inswb)
 		mov	ip, sp
 		stmfd	sp!, {r4 - r10, fp, ip, lr, pc}
 		sub	fp, ip, #4
@@ -122,14 +586,9 @@
 		bgt	Linsw_notaligned
 		LOADREGS(ea, fp, {r4 - r10, fp, sp, pc})
 
-@ Purpose: write a block of data from memory to a hardware register.
-@ Proto  : outsw(int to_reg, void *from, int len_in_words);
-@ Proto  : outswb(int to_reg, void *from, int len_in_bytes);
-@ Notes  : increments from
 
 ENTRY(outsw)
-		mov	r2, r2, LSL#1
-ENTRY(outswb)
+		mov	r2, r2, lsl#1
 		mov	ip, sp
 		stmfd	sp!, {r4 - r8, fp, ip, lr, pc}
 		sub	fp, ip, #4
@@ -166,56 +625,5 @@
 		bgt	3b
 		LOADREGS(ea, fp, {r4 - r8, fp, sp, pc})
 
-/*
- * These make no sense on Acorn machines atm.
- */
-ENTRY(insl)
-ENTRY(outsl)
-		RETINSTR(mov,pc,lr)
-
-@ Purpose: write a memc register
-@ Proto  : void memc_write(int register, int value);
-@ Returns: nothing
-
-#if defined(CONFIG_CPU_26)
-ENTRY(memc_write)
-		cmp	r0, #7
-		RETINSTR(movgt,pc,lr)
-		mov	r0, r0, lsl #17
-		mov	r1, r1, lsl #15
-		mov	r1, r1, lsr #17
-		orr	r0, r0, r1, lsl #2
-		add	r0, r0, #0x03600000
-		strb	r0, [r0]
-		RETINSTR(mov,pc,lr)
-#define CPSR2SPSR(rt)
-#else
-#define CPSR2SPSR(rt) \
-		mrs	rt, cpsr; \
-		msr	spsr, rt
 #endif
 
-@ Purpose: call an expansion card loader to read bytes.
-@ Proto  : char read_loader(int offset, char *card_base, char *loader);
-@ Returns: byte read
-
-ENTRY(ecard_loader_read)
-		stmfd	sp!, {r4 - r12, lr}
-		mov	r11, r1
-		mov	r1, r0
-		CPSR2SPSR(r0)
-		mov	lr, pc
-		mov	pc, r2
-		LOADREGS(fd, sp!, {r4 - r12, pc})
-
-@ Purpose: call an expansion card loader to reset the card
-@ Proto  : void read_loader(int card_base, char *loader);
-@ Returns: byte read
-
-ENTRY(ecard_loader_reset)
-		stmfd	sp!, {r4 - r12, lr}
-		mov	r11, r0
-		CPSR2SPSR(r0)
-		mov	lr, pc
-		add	pc, r1, #8
-		LOADREGS(fd, sp!, {r4 - r12, pc})
diff -urN linux-2.2.18-pre8/arch/arm/lib/io-ebsa110.S linux/arch/arm/lib/io-ebsa110.S
--- linux-2.2.18-pre8/arch/arm/lib/io-ebsa110.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/lib/io-ebsa110.S	Mon Dec  3 20:25:13 2001
@@ -22,6 +22,22 @@
 		ldr	lr, [r0]			;\
 		orr	reg, reg, lr, lsl $16
 
+/*
+ * These make no sense on these machines.
+ * Print a warning message.
+ */
+ENTRY(insl)
+ENTRY(outsl)
+ENTRY(insb)
+ENTRY(outsb)
+		adr	r0, io_long_warning
+		mov	r1, lr
+		b	SYMBOL_NAME(printk)
+
+io_long_warning:
+		.ascii	"<4>ins?/outs? not implemented on this architecture\0"
+		.align
+
 @ Purpose: read a block of data from a hardware register to memory.
 @ Proto  : insw(int from_port, void *to, int len_in_words);
 @ Proto  : inswb(int from_port, void *to, int len_in_bytes);
diff -urN linux-2.2.18-pre8/arch/arm/lib/io-ebsa285.S linux/arch/arm/lib/io-ebsa285.S
--- linux-2.2.18-pre8/arch/arm/lib/io-ebsa285.S	Thu Dec 17 09:05:42 1998
+++ linux/arch/arm/lib/io-ebsa285.S	Wed Dec 31 16:00:00 1969
@@ -1,197 +0,0 @@
-#include <linux/linkage.h>
-
-ENTRY(insl)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-		ands	ip, r1, #3
-		bne	2f
-
-1:		ldr	r3, [r0]
-		str	r3, [r1], #4
-		subs	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-2:		cmp	ip, #2
-		ldr	ip, [r0]
-		blt	3f
-		bgt	4f
-
-		strh	ip, [r1], #2
-		mov	ip, ip, lsr #16
-1:		subs	r2, r2, #1
-		ldrne	r3, [r0]
-		orrne	ip, ip, r3, lsl #16
-		strne	ip, [r1], #4
-		movne	ip, r3, lsr #16
-		bne	1b
-		strh	ip, [r1], #2
-		mov	pc, lr
-
-3:		strb	ip, [r1], #1
-		mov	ip, ip, lsr #8
-		strh	ip, [r1], #2
-		mov	ip, ip, lsr #16
-1:		subs	r2, r2, #1
-		ldrne	r3, [r0]
-		orrne	ip, ip, r3, lsl #8
-		strne	ip, [r1], #4
-		movne	ip, r3, lsr #24
-		bne	1b
-		strb	ip, [r1], #1
-		mov	pc, lr
-
-4:		strb	ip, [r1], #1
-		mov	ip, ip, lsr #8
-1:		subs	r2, r2, #1
-		ldrne	r3, [r0]
-		orrne	ip, ip, r3, lsl #24
-		strne	ip, [r1], #4
-		movne	ip, r3, lsr #8
-		bne	1b
-		strb	ip, [r1], #1
-		mov	ip, ip, lsr #8
-		strh	ip, [r1], #2
-		mov	pc, lr
-
-ENTRY(outsl)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-		ands	ip, r1, #3
-		bne	2f
-
-1:		ldr	r3, [r1], #4
-		str	r3, [r0]
-		subs	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-2:		bic	r1, r1, #3
-		cmp	ip, #2
-		ldr	ip, [r1], #4
-		mov	ip, ip, lsr #16
-		blt	3f
-		bgt	4f
-
-1:		ldr	r3, [r1], #4
-		orr	ip, ip, r3, lsl #16
-		str	ip, [r0]
-		mov	ip, r3, lsr #16
-		subs	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-3:		ldr	r3, [r1], #4
-		orr	ip, ip, r3, lsl #8
-		str	ip, [r0]
-		mov	ip, r3, lsr #24
-		subs	r2, r2, #1
-		bne	3b
-		mov	pc, lr
-
-4:		ldr	r3, [r1], #4
-		orr	ip, ip, r3, lsl #24
-		str	ip, [r0]
-		mov	ip, r3, lsr #8
-		subs	r2, r2, #1
-		bne	4b
-		mov	pc, lr
-
-		/* Nobody could say these are optimal, but not to worry. */
-
-ENTRY(outswb)
-		mov	r2, r2, lsr #1
-ENTRY(outsw)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-1:		subs	r2, r2, #1
-		ldrgeh	r3, [r1], #2
-		strgeh	r3, [r0]
-		bgt	1b
-		mov	pc, lr
-
-ENTRY(inswb)
-		mov	r2, r2, lsr #1
-ENTRY(insw)
-		stmfd	sp!, {r4, r5, lr}
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-						@ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17
-		subs	ip, r2, #8
-		blo	too_little
-						@ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
-		ands	lr, r1, #3		@ check alignment
-		beq	1f
-
-		ldrh	r3, [r0]
-		strh	r3, [r1], #2
-		sub	ip, ip, #1
-		cmn	ip, #8
-		blo	too_little
-
-1:		ldrh	r2, [r0]
-		ldrh	r3, [r0]
-		orr	r2, r2, r3, lsl #16
-		ldrh	r3, [r0]
-		ldrh	r4, [r0]
-		orr	r3, r3, r4, lsl #16
-		ldrh	r4, [r0]
-		ldrh	r5, [r0]
-		orr	r4, r4, r5, lsl #16
-		ldrh	r5, [r0]
-		ldrh	lr, [r0]
-		orr	r5, r5, lr, lsl #16
-		stmia	r1!, {r2, r3, r4, r5}
-		subs	ip, ip, #8
-						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1
-		bhs	1b
-						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7
-		cmn	ip, #4
-		ldrhsh	r2, [r0]		@ ... ... ... ... - 4 - 3 - 2 - 1 ... ...
-		ldrhsh	r3, [r0]
-		orrhs	r2, r2, r3, lsl #16
-		ldrhsh	r3, [r0]
-		ldrhsh	r4, [r0]
-		orrhs	r3, r3, r4, lsl #16
-		stmhsia	r1!, {r2, r3}
-
-		tst	ip, #2
-		ldrneh	r2, [r0]		@ ... ... - 6 - 5 ... ... - 2 - 1 ... ...
-		ldrneh	r3, [r0]
-		orrne	r2, r2, r3, lsl #16
-		strne	r2, [r1], #4
-
-		tst	ip, #1
-		ldrneh	r2, [r0]
-		strneh	r2, [r1], #2
-
-		ldmfd	sp!, {r4, r5, pc}
-
-too_little:	subs	r2, r2, #1
-		ldrgeh	r3, [r0]
-		strgeh	r3, [r1], #2
-		bgt	too_little
-
-		ldmfd	sp!, {r4, r5, pc}
-
-
-ENTRY(insb)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-1:		teq	r2, #0
-		ldrneb	r3, [r0]
-		strneb	r3, [r1], #1
-		subne	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-
-ENTRY(outsb)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-1:		teq	r2, #0
-		ldrneb	r3, [r1], #1
-		strneb	r3, [r0]
-		subne	r2, r2, #1
-		bne	1b
-		mov	pc, lr
diff -urN linux-2.2.18-pre8/arch/arm/lib/io-footbridge.S linux/arch/arm/lib/io-footbridge.S
--- linux-2.2.18-pre8/arch/arm/lib/io-footbridge.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/io-footbridge.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,202 @@
+#include <linux/linkage.h>
+#include <asm/hardware.h>
+
+		.equ	pcio_high, PCIO_BASE & 0xff000000
+		.equ	pcio_low,  PCIO_BASE & 0x00ffffff
+
+		.macro	ioaddr, rd,rn
+		add	\rd, \rn, #pcio_high
+		.if	pcio_low
+		add	\rd, \rd, #pcio_low
+		.endif
+		.endm
+
+ENTRY(insl)
+		ioaddr	r0, r0
+		ands	ip, r1, #3
+		bne	2f
+
+1:		ldr	r3, [r0]
+		str	r3, [r1], #4
+		subs	r2, r2, #1
+		bne	1b
+		mov	pc, lr
+
+2:		cmp	ip, #2
+		ldr	ip, [r0]
+		blt	4f
+		bgt	6f
+
+		strh	ip, [r1], #2
+		mov	ip, ip, lsr #16
+3:		subs	r2, r2, #1
+		ldrne	r3, [r0]
+		orrne	ip, ip, r3, lsl #16
+		strne	ip, [r1], #4
+		movne	ip, r3, lsr #16
+		bne	3b
+		strh	ip, [r1], #2
+		mov	pc, lr
+
+4:		strb	ip, [r1], #1
+		mov	ip, ip, lsr #8
+		strh	ip, [r1], #2
+		mov	ip, ip, lsr #16
+5:		subs	r2, r2, #1
+		ldrne	r3, [r0]
+		orrne	ip, ip, r3, lsl #8
+		strne	ip, [r1], #4
+		movne	ip, r3, lsr #24
+		bne	5b
+		strb	ip, [r1], #1
+		mov	pc, lr
+
+6:		strb	ip, [r1], #1
+		mov	ip, ip, lsr #8
+7:		subs	r2, r2, #1
+		ldrne	r3, [r0]
+		orrne	ip, ip, r3, lsl #24
+		strne	ip, [r1], #4
+		movne	ip, r3, lsr #8
+		bne	7b
+		strb	ip, [r1], #1
+		mov	ip, ip, lsr #8
+		strh	ip, [r1], #2
+		mov	pc, lr
+
+ENTRY(outsl)
+		ioaddr	r0, r0
+		ands	ip, r1, #3
+		bne	2f
+
+1:		ldr	r3, [r1], #4
+		str	r3, [r0]
+		subs	r2, r2, #1
+		bne	1b
+		mov	pc, lr
+
+2:		bic	r1, r1, #3
+		cmp	ip, #2
+		ldr	ip, [r1], #4
+		mov	ip, ip, lsr #16
+		blt	4f
+		bgt	5f
+
+3:		ldr	r3, [r1], #4
+		orr	ip, ip, r3, lsl #16
+		str	ip, [r0]
+		mov	ip, r3, lsr #16
+		subs	r2, r2, #1
+		bne	3b
+		mov	pc, lr
+
+4:		ldr	r3, [r1], #4
+		orr	ip, ip, r3, lsl #8
+		str	ip, [r0]
+		mov	ip, r3, lsr #24
+		subs	r2, r2, #1
+		bne	4b
+		mov	pc, lr
+
+5:		ldr	r3, [r1], #4
+		orr	ip, ip, r3, lsl #24
+		str	ip, [r0]
+		mov	ip, r3, lsr #8
+		subs	r2, r2, #1
+		bne	5b
+		mov	pc, lr
+
+		/* Nobody could say these are optimal, but not to worry. */
+
+ENTRY(outswb)
+		mov	r2, r2, lsr #1
+ENTRY(outsw)
+		ioaddr	r0, r0
+1:		subs	r2, r2, #1
+		ldrgeh	r3, [r1], #2
+		strgeh	r3, [r0]
+		bgt	1b
+		mov	pc, lr
+
+ENTRY(inswb)
+		mov	r2, r2, lsr #1
+ENTRY(insw)
+		stmfd	sp!, {r4, r5, lr}
+		ioaddr	r0, r0
+						@ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17
+		subs	ip, r2, #8
+		blo	too_little
+						@ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
+		ands	lr, r1, #3		@ check alignment
+		beq	1f
+
+		ldrh	r3, [r0]
+		strh	r3, [r1], #2
+		sub	ip, ip, #1
+		cmn	ip, #8
+		blo	too_little
+
+1:		ldrh	r2, [r0]
+		ldrh	r3, [r0]
+		orr	r2, r2, r3, lsl #16
+		ldrh	r3, [r0]
+		ldrh	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrh	r4, [r0]
+		ldrh	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+		ldrh	r5, [r0]
+		ldrh	lr, [r0]
+		orr	r5, r5, lr, lsl #16
+		stmia	r1!, {r2, r3, r4, r5}
+		subs	ip, ip, #8
+						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1
+		bhs	1b
+						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7
+		cmn	ip, #4
+		ldrhsh	r2, [r0]		@ ... ... ... ... - 4 - 3 - 2 - 1 ... ...
+		ldrhsh	r3, [r0]
+		orrhs	r2, r2, r3, lsl #16
+		ldrhsh	r3, [r0]
+		ldrhsh	r4, [r0]
+		orrhs	r3, r3, r4, lsl #16
+		stmhsia	r1!, {r2, r3}
+
+		tst	ip, #2
+		ldrneh	r2, [r0]		@ ... ... - 6 - 5 ... ... - 2 - 1 ... ...
+		ldrneh	r3, [r0]
+		orrne	r2, r2, r3, lsl #16
+		strne	r2, [r1], #4
+
+		tst	ip, #1
+		ldrneh	r2, [r0]
+		strneh	r2, [r1], #2
+
+		ldmfd	sp!, {r4, r5, pc}
+
+too_little:	subs	r2, r2, #1
+		ldrgeh	r3, [r0]
+		strgeh	r3, [r1], #2
+		bgt	too_little
+
+		ldmfd	sp!, {r4, r5, pc}
+
+
+ENTRY(insb)
+		ioaddr	r0, r0
+1:		teq	r2, #0
+		ldrneb	r3, [r0]
+		strneb	r3, [r1], #1
+		subne	r2, r2, #1
+		bne	1b
+		mov	pc, lr
+
+
+ENTRY(outsb)
+		ioaddr	r0, r0
+1:		teq	r2, #0
+		ldrneb	r3, [r1], #1
+		strneb	r3, [r0]
+		subne	r2, r2, #1
+		bne	1b
+		mov	pc, lr
diff -urN linux-2.2.18-pre8/arch/arm/lib/io.c linux/arch/arm/lib/io.c
--- linux-2.2.18-pre8/arch/arm/lib/io.c	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/io.c	Mon Dec  3 20:25:13 2001
@@ -1,3 +1,5 @@
+#include <linux/module.h>
+
 #include <asm/io.h>
 
 /*
@@ -18,7 +20,7 @@
  * Copy data from "real" memory space to IO memory space.
  * This needs to be optimized.
  */
-void _memcpy_toio(unsigned long to, void * from, unsigned long count)
+void _memcpy_toio(unsigned long to, const void * from, unsigned long count)
 {
 	while (count) {
 		count--;
@@ -40,3 +42,7 @@
 		dst++;
 	}
 }
+
+EXPORT_SYMBOL(_memcpy_fromio);
+EXPORT_SYMBOL(_memcpy_toio);
+EXPORT_SYMBOL(_memset_io);
diff -urN linux-2.2.18-pre8/arch/arm/lib/ll_char_wr.S linux/arch/arm/lib/ll_char_wr.S
--- linux-2.2.18-pre8/arch/arm/lib/ll_char_wr.S	Wed May 20 18:54:35 1998
+++ linux/arch/arm/lib/ll_char_wr.S	Wed Dec 31 16:00:00 1969
@@ -1,158 +0,0 @@
-/*
- * linux/arch/arm/lib/ll_char_wr.S
- *
- * Copyright (C) 1995, 1996 Russell King.
- *
- * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King.
- *
- * 10-04-96	RMK	Various cleanups & reduced register usage.
- * 08-04-98	RMK	Shifts re-ordered
- */
-
-@ Regs: [] = corruptible
-@       {} = used
-@       () = do not use
-
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-		.text
-
-#define BOLD            0x01
-#define ITALIC          0x02
-#define UNDERLINE       0x04
-#define FLASH           0x08
-#define INVERSE         0x10
-
-LC0:		.word	SYMBOL_NAME(bytes_per_char_h)
-		.word	SYMBOL_NAME(video_size_row)
-		.word	SYMBOL_NAME(cmap_80)
-		.word	SYMBOL_NAME(con_charconvtable)
-
-ENTRY(ll_write_char)
-		stmfd	sp!, {r4 - r7, lr}
-@
-@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc)
-@
-		eor	ip, r1, #UNDERLINE << 9
-/*
- * calculate colours
- */
-		tst	r1, #INVERSE << 9
-		moveq	r2, r1, lsr #16
-		moveq	r3, r1, lsr #24
-		movne	r2, r1, lsr #24
-		movne	r3, r1, lsr #16
-		and	r3, r3, #255
-		and	r2, r2, #255
-/*
- * calculate offset into character table
- */
-		mov	r1, r1, lsl #23
-		mov	r1, r1, lsr #20
-/*
- * calculate offset required for each row [maybe I should make this an argument to this fn.
- * Have to see what the register usage is like in the calling routines.
- */
-		adr	r4, LC0
-		ldmia	r4, {r4, r5, r6, lr}
-		ldr	r4, [r4]
-		ldr	r5, [r5]
-/*
- * Go to resolution-dependent routine...
- */
-		cmp	r4, #4
-		blt	Lrow1bpp
-		eor	r2, r3, r2			@ Create eor mask to change colour from bg
-		orr	r3, r3, r3, lsl #8		@ to fg.
-		orr	r3, r3, r3, lsl #16
-		add	r0, r0, r5, lsl #3		@ Move to bottom of character
-		add	r1, r1, #7
-		ldrb	r7, [r6, r1]
-		tst	ip, #UNDERLINE << 9
-		eoreq	r7, r7, #255
-		teq	r4, #8
-		beq	Lrow8bpplp
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
-@
-		orr	r3, r3, r3, lsl #4
-Lrow4bpplp:	ldr	r7, [lr, r7, lsl #2]
-		mul	r7, r2, r7
-		tst	r1, #7				@ avoid using r7 directly after
-		eor	ip, r3, r7
-		str	ip, [r0, -r5]!
-		LOADREGS(eqfd, sp!, {r4 - r7, pc})
-		sub	r1, r1, #1
-		ldrb	r7, [r6, r1]
-		ldr	r7, [lr, r7, lsl #2]
-		mul	r7, r2, r7
-		tst	r1, #7				@ avoid using r7 directly after
-		eor	ip, r3, r7
-		str	ip, [r0, -r5]!
-		subne	r1, r1, #1
-		ldrneb	r7, [r6, r1]
-		bne	Lrow4bpplp
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
-@
-Lrow8bpplp:	mov	ip, r7, lsr #4
-		ldr	ip, [lr, ip, lsl #2]
-		mul	r4, r2, ip
-		and	ip, r7, #15			@ avoid r4
-		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
-		mul	ip, r2, ip			@ avoid r4
-		eor	r4, r3, r4			@ avoid ip
-		tst	r1, #7				@ avoid ip
-		sub	r0, r0, r5			@ avoid ip
-		eor	ip, r3, ip
-		stmia	r0, {r4, ip}
-		LOADREGS(eqfd, sp!, {r4 - r7, pc})
-		sub	r1, r1, #1
-		ldrb	r7, [r6, r1]
-		mov	ip, r7, lsr #4
-		ldr	ip, [lr, ip, lsl #2]
-		mul	r4, r2, ip
-		and	ip, r7, #15			@ avoid r4
-		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
-		mul	ip, r2, ip			@ avoid r4
-		eor	r4, r3, r4			@ avoid ip
-		tst	r1, #7				@ avoid ip
-		sub	r0, r0, r5			@ avoid ip
-		eor	ip, r3, ip
-		stmia	r0, {r4, ip}
-		subne	r1, r1, #1
-		ldrneb	r7, [r6, r1]
-		bne	Lrow8bpplp
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc)
-@
-Lrow1bpp:	add	r6, r6, r1
-		ldmia	r6, {r4, r7}
-		tst	ip, #INVERSE << 9
-		mvnne	r4, r4
-		mvnne	r7, r7
-		strb	r4, [r0], r5
-		mov	r4, r4, lsr #8
-		strb	r4, [r0], r5
-		mov	r4, r4, lsr #8
-		strb	r4, [r0], r5
-		mov	r4, r4, lsr #8
-		strb	r4, [r0], r5
-		strb	r7, [r0], r5
-		mov	r7, r7, lsr #8
-		strb	r7, [r0], r5
-		mov	r7, r7, lsr #8
-		strb	r7, [r0], r5
-		mov	r7, r7, lsr #8
-		tst	ip, #UNDERLINE << 9
-		mvneq	r7, r7
-		strb	r7, [r0], r5
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-
-		.bss
-ENTRY(con_charconvtable)
-		.space	1024
diff -urN linux-2.2.18-pre8/arch/arm/lib/loaders.S linux/arch/arm/lib/loaders.S
--- linux-2.2.18-pre8/arch/arm/lib/loaders.S	Tue Jan 20 16:39:42 1998
+++ linux/arch/arm/lib/loaders.S	Wed Dec 31 16:00:00 1969
@@ -1,53 +0,0 @@
-/*
- * linux/arch/arm/lib/loaders.S
- *
- * This file contains the ROM loaders for buggy cards
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-
-/*
- * Oak SCSI
- */
-
-ENTRY(oak_scsi_loader)
-			b	Loak_scsi_read
-			.word	0
-Loak_scsi_reset:	bic	r10, r11, #0x00ff0000
-			ldr	r2, [r10]
-			RETINSTR(mov,pc,lr)
-
-Loak_scsi_read:		mov	r2, r1, lsr #3
-			and	r2, r2, #15 << 9
-			bic	r10, r11, #0x00ff0000
-			ldr	r2, [r10, r2]
-			mov	r2, r1, lsl #20
-			ldrb	r0, [r11, r2, lsr #18]
-			ldr	r2, [r10]
-			RETINSTR(mov,pc,lr)
-
-ENTRY(atomwide_serial_loader)
-			b	Latomwide_serial_read
-			.word	0
-Latomwide_serial_reset:	mov	r2, #0x3c00
-			strb	r2, [r11, r2]
-			RETINSTR(mov,pc,lr)
-
-Latomwide_serial_read:	cmp	r1, #0x8000
-			RETINSTR(movhi,pc,lr)
-			add	r0, r1, #0x800
-			mov	r0, r0, lsr #11
-			mov	r3, #0x3c00
-			strb	r0, [r11, r3]
-			mov	r2, r1, lsl #21
-			ldrb	r0, [r11, r2, lsr #19]
-			strb	r2, [r11, r3]
-			RETINSTR(mov,pc,lr)
-
-/*
- * Cards we don't know about yet
- */
-ENTRY(noloader)
-			mov	r0, r0
-			mov	r0, #0
-			RETINSTR(mov,pc,lr)
diff -urN linux-2.2.18-pre8/arch/arm/lib/memchr.S linux/arch/arm/lib/memchr.S
--- linux-2.2.18-pre8/arch/arm/lib/memchr.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/memchr.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,33 @@
+/*
+ * linux/arch/arm/lib/memchr.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+@ I have this hunch that this one can be optimised further.
+@ Beware false optimisation of ldrb the byte BEYOND the end of buffer (could
+@ be beyond end of memory) before testing to see if length had been exhausted
+@ on the previous byte.
+ENTRY(memchr)				@ 2nd argument char
+		stmfd	sp!, {lr}
+		and	r1, r1, #0xff	@ cast 2nd argument to char
+		add	r2, r2, r0	@ Point to end (exclusive)
+
+1:		teq	r0, r2
+		beq	2f		@ No more to do
+		ldrb	r3, [r0], #1
+		teq	r3, r1
+		bne	1b
+		sub	r0, r0, #1
+		LOADREGS(fd, sp!, {pc})
+
+2:		mov	r0, #0		@ Got to end and not found.
+		LOADREGS(fd, sp!, {pc})
diff -urN linux-2.2.18-pre8/arch/arm/lib/memcpy.S linux/arch/arm/lib/memcpy.S
--- linux-2.2.18-pre8/arch/arm/lib/memcpy.S	Sun Apr 12 11:42:15 1998
+++ linux/arch/arm/lib/memcpy.S	Mon Dec  3 20:25:13 2001
@@ -1,20 +1,17 @@
 /*
- * linux/arch/arm/lib/segment.S
+ * linux/arch/arm/lib/memcpy.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
  *
- * Copyright (C) 1995, 1996 Russell King
- * Except memcpy/memmove routine.
  */
-
-#include <asm/assembler.h>
 #include <linux/linkage.h>
-
-#ifndef ENTRY
-#define ENTRY(x...)				\
-		.globl	_##x;			\
-_##x:
-#endif
+#include <asm/assembler.h>
+#include "constants.h"
 
 		.text
+
 #define ENTER	\
 		mov	ip,sp	;\
 		stmfd	sp!,{r4-r9,fp,ip,lr,pc}	;\
@@ -26,9 +23,10 @@
 #define EXITEQ	\
 		LOADREGS(eqea, fp, {r4 - r9, fp, sp, pc})
 
-# Prototype: void memcpy(void *to,const void *from,unsigned long n);
-# ARM3: cant use memcopy here!!!
-
+/*
+ * Prototype: void memcpy(void *to,const void *from,unsigned long n);
+ * ARM3: cant use memcopy here!!!
+ */
 ENTRY(memcpy)
 ENTRY(memmove)
 		ENTER
@@ -316,4 +314,5 @@
 		b	24b
 
 		.align
+
 
diff -urN linux-2.2.18-pre8/arch/arm/lib/memset.S linux/arch/arm/lib/memset.S
--- linux-2.2.18-pre8/arch/arm/lib/memset.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/memset.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,88 @@
+/*
+ * linux/arch/arm/lib/memset.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+		.align	5
+ENTRY(memset)
+		mov	r3, r0
+		cmp	r2, #16
+		blt	6f
+		ands	ip, r3, #3
+		beq	1f
+		cmp	ip, #2
+		strltb	r1, [r3], #1			@ Align destination
+		strleb	r1, [r3], #1
+		strb	r1, [r3], #1
+		rsb	ip, ip, #4
+		sub	r2, r2, ip
+1:		orr	r1, r1, r1, lsl #8
+		orr	r1, r1, r1, lsl #16
+		cmp	r2, #256
+		blt	4f
+		stmfd	sp!, {r4, r5, lr}
+		mov	r4, r1
+		mov	r5, r1
+		mov	lr, r1
+		mov	ip, r2, lsr #6
+		sub	r2, r2, ip, lsl #6
+2:		stmia	r3!, {r1, r4, r5, lr}		@ 64 bytes at a time.
+		stmia	r3!, {r1, r4, r5, lr}
+		stmia	r3!, {r1, r4, r5, lr}
+		stmia	r3!, {r1, r4, r5, lr}
+		subs	ip, ip, #1
+		bne	2b
+		teq	r2, #0
+		LOADREGS(eqfd, sp!, {r4, r5, pc})	@ Now <64 bytes to go.
+		tst	r2, #32
+		stmneia	r3!, {r1, r4, r5, lr}
+		stmneia	r3!, {r1, r4, r5, lr}
+		tst	r2, #16
+		stmneia	r3!, {r1, r4, r5, lr}
+		ldmia	sp!, {r4, r5}
+3:		tst	r2, #8
+		stmneia	r3!, {r1, lr}
+		tst	r2, #4
+		strne	r1, [r3], #4
+		tst	r2, #2
+		strneb	r1, [r3], #1
+		strneb	r1, [r3], #1
+		tst	r2, #1
+		strneb	r1, [r3], #1
+		LOADREGS(fd, sp!, {pc})
+
+4:		movs	ip, r2, lsr #3
+		beq	3b
+		sub	r2, r2, ip, lsl #3
+		stmfd	sp!, {lr}
+		mov	lr, r1
+		subs	ip, ip, #4
+5:		stmgeia	r3!, {r1, lr}
+		stmgeia	r3!, {r1, lr}
+		stmgeia	r3!, {r1, lr}
+		stmgeia	r3!, {r1, lr}
+		subges	ip, ip, #4
+		bge	5b
+		tst	ip, #2
+		stmneia	r3!, {r1, lr}
+		stmneia	r3!, {r1, lr}
+		tst	ip, #1
+		stmneia	r3!, {r1, lr}
+		teq	r2, #0
+		LOADREGS(eqfd, sp!, {pc})
+		b	3b
+
+6:		subs	r2, r2, #1
+		strgeb	r1, [r3], #1
+		bgt	6b
+		RETINSTR(mov, pc, lr)
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/memzero.S linux/arch/arm/lib/memzero.S
--- linux-2.2.18-pre8/arch/arm/lib/memzero.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/memzero.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,80 @@
+/*
+ * linux/arch/arm/lib/memzero.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+/*
+ * Prototype: void memzero(void *d, size_t n)
+ */
+1:		@ 4 <= r1
+		cmp	ip, #2				@	1
+		strltb	r2, [r0], #1			@	1
+		strleb	r2, [r0], #1			@	1
+		strb	r2, [r0], #1			@	1
+		rsb	ip, ip, #4			@	1
+		sub	r1, r1, ip			@	1
+		cmp	r1, #3				@	1
+		bgt	2f				@	1	@ +8
+		b	4f				@	1	@ +9
+
+		.align	5
+
+ENTRY(__memzero)
+		mov	r2, #0				@	1
+		cmp	r1, #4				@	1
+		blt	4f				@	1	@ = 3
+
+		@ r1 >= 4
+
+		ands	ip, r0, #3			@	1
+		bne	1b				@	1	@ = 5
+
+2:		@ r1 >= 4 && (r0 & 3) = 0				@ = 5 or 11
+
+		str	lr, [sp, #-4]!			@	1
+		mov	r3, #0				@	1
+		mov	ip, #0				@	1
+		mov	lr, #0				@	1
+
+		@ 4 <= r1 <= 32						@ = 9 or 15
+
+3:		subs	r1, r1, #32			@	1
+		stmgeia	r0!, {r2, r3, ip, lr}		@	4
+		stmgeia	r0!, {r2, r3, ip, lr}		@	4
+		bgt	3b				@	1
+		LOADREGS(eqfd, sp!, {pc})		@	1/2
+
+		@ -28 <= r1 <= -1
+
+		cmp	r1, #-16			@	1
+		stmgeia	r0!, {r2, r3, ip, lr}		@	4
+		ldr	lr, [sp], #4			@	1
+		addlts	r1, r1, #16			@	1
+		RETINSTR(moveq,pc,lr)			@	1
+
+		@ -12 <= r1 <= -1
+
+		cmp	r1, #-8				@	1
+		stmgeia	r0!, {r2, r3}			@	2
+		addlts	r1, r1, #8			@	1
+		RETINSTR(moveq,pc,lr)			@	1
+
+		@ -4 <= r1 <= -1
+
+		cmp	r1, #-4				@	1
+		strge	r2, [r0], #4			@	1
+		adds	r1, r1, #4			@	1
+		RETINSTR(moveq,pc,lr)			@	1
+
+4:		@ 1 <= r1 <= 3
+		cmp	r1, #2				@	1
+		strgtb	r2, [r0], #1			@	1
+		strgeb	r2, [r0], #1			@	1
+		strb	r2, [r0], #1			@	1
+		RETINSTR(mov,pc,lr)			@	1
diff -urN linux-2.2.18-pre8/arch/arm/lib/semaphore.S linux/arch/arm/lib/semaphore.S
--- linux-2.2.18-pre8/arch/arm/lib/semaphore.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/semaphore.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,34 @@
+/*
+ *  linux/arch/arm/lib/semaphore.S
+ *
+ *  Idea from i386 code, Copyright Linus Torvalds.
+ *  Converted for ARM by Russell King
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * The semaphore operations have a special calling sequence
+ * that allows us to keep the distruption of the main code
+ * path to a minimum.  These routines save and restore the
+ * registers that will be touched by __down etc.
+ */
+ENTRY(__down_failed)
+	stmfd	sp!, {r0 - r3, ip, lr}
+	bl	SYMBOL_NAME(__down)
+	LOADREGS(fd, sp!, {r0 - r3, ip, pc})
+
+ENTRY(__down_interruptible_failed)
+	stmfd	sp!, {r1 - r3, ip, lr}
+	bl	SYMBOL_NAME(__down_interruptible)
+	LOADREGS(fd, sp!, {r1 - r3, ip, pc})
+
+ENTRY(__down_trylock_failed)
+	stmfd	sp!, {r1 - r3, ip, lr}
+	bl	SYMBOL_NAME(__down_trylock)
+	LOADREGS(fd, sp!, {r1 - r3, ip, pc})
+
+ENTRY(__up_wakeup)
+	stmfd	sp!, {r0 - r3, ip, lr}
+	bl	SYMBOL_NAME(__up)
+	LOADREGS(fd, sp!, {r0 - r3, ip, pc})
diff -urN linux-2.2.18-pre8/arch/arm/lib/setbit.S linux/arch/arm/lib/setbit.S
--- linux-2.2.18-pre8/arch/arm/lib/setbit.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/setbit.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/arm/lib/setbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+@ Purpose  : Function to set a bit
+@ Prototype: int set_bit(int bit,int *addr)
+
+ENTRY(set_bit)
+                and     r2, r0, #7
+                mov     r3, #1
+                mov     r3, r3, lsl r2
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1, r0, lsr #3]
+		orr	r2, r2, r3
+		strb	r2, [r1, r0, lsr #3]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/strchr.S linux/arch/arm/lib/strchr.S
--- linux-2.2.18-pre8/arch/arm/lib/strchr.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/strchr.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/strchr.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+ENTRY(strchr)
+		ldrb	r2, [r0], #1
+		stmfd	sp!,{lr}	@ StrongARM: LDR not followed by use
+1:		teq	r2, r1
+		teqne	r2, #0
+		ldrneb	r2, [r0], #1
+		bne	1b
+		teq	r2, r1		@ If they are the same we found char.
+		movne	r0, #0
+		subeq	r0, r0, #1	@ Even if we were looking for 0 :-)
+		LOADREGS(fd, sp!, {pc})
diff -urN linux-2.2.18-pre8/arch/arm/lib/string.S linux/arch/arm/lib/string.S
--- linux-2.2.18-pre8/arch/arm/lib/string.S	Fri May  8 00:42:38 1998
+++ linux/arch/arm/lib/string.S	Wed Dec 31 16:00:00 1969
@@ -1,170 +0,0 @@
-/*
- * linux/arch/arm/lib/string.S
- *
- * Copyright (C) 1995-1998 Russell King
- *
- * This is commonly used to clear the frame buffer and the frame
- * backing buffer.  As such, it will be rarely called with r2 < 32.
- *
- * Optimisations by Matthew Wilcox
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-		.text
-# Prototype: char *strrchr(const char *s,char c);
-
-/*
- * Prototype: void memzero(void *d, size_t n)
- */
-ENTRY(memzero)
-		mov	r2, r1
-		mov	r1, #0
-/*
- * Prototype: void memsetl(unsigned long *d, unsigned long c, size_t n)
- */
-ENTRY(memsetl)
-		teq	r2, #0
-		RETINSTR(moveq,pc,lr)
-		stmfd	sp!, {lr}
-		mov	lr, r1
-		mov	r3, r1
-		mov	ip, r1
-
-		@ r2 = {32 ... 4}
-
-1:		subs	r2, r2, #32
-		stmgeia	r0!, {r1, r3, ip, lr}
-		stmgeia	r0!, {r1, r3, ip, lr}
-		bgt	1b
-		LOADREGS(eqfd, sp!, {pc})
-
-		@ r2 can be {-4 ... -28}
-
-		cmp	r2, #-16
-		stmgeia	r0!, {r1, r3, ip, lr}
-		addlts	r2, r2, #16
-		LOADREGS(eqfd, sp!, {pc})
-
-		@ r2 can be {-4 ... -12}
-
-		cmp	r2, #-8
-		stmgeia	r0!, {r1, r3}
-		strne	r1, [r0]
-		LOADREGS(fd, sp!, {pc})
-
-		.global	__page_memcpy
-__page_memcpy:	stmfd	sp!, {r4, r5, lr}
-1:		subs	r2, r2, #4*8
-		ldmgeia	r1!, {r3, r4, r5, ip}
-		stmgeia	r0!, {r3, r4, r5, ip}
-		ldmgeia	r1!, {r3, r4, r5, ip}
-		stmgeia	r0!, {r3, r4, r5, ip}
-		bgt	1b
-		LOADREGS(fd, sp!, {r4, r5, pc})
-
-		.global	memset
-memset:		mov	r3, r0
-		cmp	r2, #16
-		blt	6f
-		ands	ip, r3, #3
-		beq	1f
-		cmp	ip, #2
-		strltb	r1, [r3], #1			@ Align destination
-		strleb	r1, [r3], #1
-		strb	r1, [r3], #1
-		rsb	ip, ip, #4
-		sub	r2, r2, ip
-1:		orr	r1, r1, r1, lsl #8
-		orr	r1, r1, r1, lsl #16
-		cmp	r2, #256
-		blt	4f
-		stmfd	sp!, {r4, r5, lr}
-		mov	r4, r1
-		mov	r5, r1
-		mov	lr, r1
-		mov	ip, r2, lsr #6
-		sub	r2, r2, ip, lsl #6
-2:		stmia	r3!, {r1, r4, r5, lr}		@ 64 bytes at a time.
-		stmia	r3!, {r1, r4, r5, lr}
-		stmia	r3!, {r1, r4, r5, lr}
-		stmia	r3!, {r1, r4, r5, lr}
-		subs	ip, ip, #1
-		bne	2b
-		teq	r2, #0
-		LOADREGS(eqfd, sp!, {r4, r5, pc})	@ Now <64 bytes to go.
-		tst	r2, #32
-		stmneia	r3!, {r1, r4, r5, lr}
-		stmneia	r3!, {r1, r4, r5, lr}
-		tst	r2, #16
-		stmneia	r3!, {r1, r4, r5, lr}
-		ldmia	sp!, {r4, r5}
-3:		tst	r2, #8
-		stmneia	r3!, {r1, lr}
-		tst	r2, #4
-		strne	r1, [r3], #4
-		tst	r2, #2
-		strneb	r1, [r3], #1
-		strneb	r1, [r3], #1
-		tst	r2, #1
-		strneb	r1, [r3], #1
-		LOADREGS(fd, sp!, {pc})
-
-4:		movs	ip, r2, lsr #3
-		beq	3b
-		sub	r2, r2, ip, lsl #3
-		stmfd	sp!, {lr}
-		mov	lr, r1
-		subs	ip, ip, #4
-5:		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		subges	ip, ip, #4
-		bge	5b
-		tst	ip, #2
-		stmneia	r3!, {r1, lr}
-		stmneia	r3!, {r1, lr}
-		tst	ip, #1
-		stmneia	r3!, {r1, lr}
-		teq	r2, #0
-		LOADREGS(eqfd, sp!, {pc})
-		b	3b
-
-6:		subs	r2, r2, #1
-		strgeb	r1, [r3], #1
-		bgt	6b
-		RETINSTR(mov, pc, lr)
-
-ENTRY(strrchr)
-		stmfd	sp!, {lr}
-		mov	r3, #0
-1:		ldrb	r2, [r0], #1
-		teq	r2, r1
-		moveq	r3, r0
-		teq	r2, #0
-		bne	1b
-		mov	r0, r3
-		LOADREGS(fd, sp!, {pc})
-
-ENTRY(strchr)
-		stmfd	sp!,{lr}
-		mov	r3, #0
-1:		ldrb	r2, [r0], #1
-		teq	r2, r1
-		teqne	r2, #0
-		bne	1b
-		teq	r2, #0
-		moveq	r0, #0
-		subne	r0, r0, #1
-		LOADREGS(fd, sp!, {pc})
-
-ENTRY(memchr)
-		stmfd	sp!, {lr}
-1:		ldrb	r3, [r0], #1
-		teq	r3, r1
-		beq	2f
-		subs	r2, r2, #1
-		bpl	1b
-2:		movne	r0, #0
-		subeq	r0, r0, #1
-		LOADREGS(fd, sp!, {pc})
diff -urN linux-2.2.18-pre8/arch/arm/lib/strrchr.S linux/arch/arm/lib/strrchr.S
--- linux-2.2.18-pre8/arch/arm/lib/strrchr.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/strrchr.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,29 @@
+/*
+ * linux/arch/arm/lib/strrchr.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+		@ Technically this will fail a regression test that tries
+		@ strrchr (buffer, 0), by returning NULL rather than
+		@ buffer + strlen (buffer). But even glibc doesnt test this
+ENTRY(strrchr)	@ 2nd argument is int not char
+		stmfd	sp!, {lr}
+		mov	r3, r0
+		ldrb	r2, [r3], #1
+		mov	r0, #0		@ prime return result.
+1:		teq	r2, r1		@ Found one?
+		subeq	r0, r3, #1
+		teq	r2, #0
+		ldrneb	r2, [r3], #1	@ Assume that well be looping at least
+		bne	1b		@ twice, so put LDM outside loop.
+		LOADREGS(fd, sp!, {pc})
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/testchangebit.S linux/arch/arm/lib/testchangebit.S
--- linux-2.2.18-pre8/arch/arm/lib/testchangebit.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/testchangebit.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/testchangebit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+ENTRY(test_and_change_bit)
+		add	r1, r1, r0, lsr #3
+		and	r3, r0, #7
+		mov	r0, #1
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		eor	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/testclearbit.S linux/arch/arm/lib/testclearbit.S
--- linux-2.2.18-pre8/arch/arm/lib/testclearbit.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/testclearbit.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/testclearbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+ENTRY(test_and_clear_bit)
+		add	r1, r1, r0, lsr #3	@ Get byte offset
+		and	r3, r0, #7	@ Get bit offset
+		mov	r0, #1
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		bic	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/testsetbit.S linux/arch/arm/lib/testsetbit.S
--- linux-2.2.18-pre8/arch/arm/lib/testsetbit.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/testsetbit.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/testsetbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+ENTRY(test_and_set_bit)
+		add	r1, r1, r0, lsr #3	@ Get byte offset
+		and	r3, r0, #7		@ Get bit offset
+		mov	r0, #1
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		orr	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -urN linux-2.2.18-pre8/arch/arm/lib/uaccess-armo.S linux/arch/arm/lib/uaccess-armo.S
--- linux-2.2.18-pre8/arch/arm/lib/uaccess-armo.S	Tue Jan 20 16:39:42 1998
+++ linux/arch/arm/lib/uaccess-armo.S	Mon Dec  3 20:25:13 2001
@@ -30,7 +30,7 @@
 		.word	__arch_copy_to_user
 		.word	__arch_clear_user
 		.word	__arch_strncpy_from_user
-		.word	__arch_strlen_user
+		.word	__arch_strnlen_user
 
 
 @ In : r0 = x, r1 = addr, r2 = error
@@ -99,7 +99,7 @@
 		.word	uaccess_kernel_copy
 		.word	uaccess_kernel_clear
 		.word	uaccess_kernel_strncpy_from
-		.word	uaccess_kernel_strlen
+		.word	uaccess_kernel_strnlen
 
 @ In : r0 = x, r1 = addr, r2 = error
 @ Out: r2 = error
@@ -214,17 +214,20 @@
 2:		subs	r0, ip, r2
 		ldmfd	sp!, {pc}^
 
-/* Prototype: int uaccess_kernel_strlen(char *str)
+/* Prototype: int uaccess_kernel_strnlen(char *str, long n)
  * Purpose  : get length of a string in kernel memory
  * Params   : str - address of string in kernel memory
  * Returns  : length of string *including terminator*, or zero on error
  */
-uaccess_kernel_strlen:
+uaccess_kernel_strnlen:
 		stmfd	sp!, {lr}
 		mov	r2, r0
-1:		ldrb	r1, [r0], #1
-		teq	r1, #0
+1:		ldrb	r3, [r0], #1
+		teq	r3, #0
+		beq	2f
+		subs	r1, r1, #1
 		bne	1b
-		sub	r0, r0, r2
+		add	r0, r0, #1
+2:		sub	r0, r0, r2
 		ldmfd	sp!, {pc}^
 
diff -urN linux-2.2.18-pre8/arch/arm/lib/uaccess.S linux/arch/arm/lib/uaccess.S
--- linux-2.2.18-pre8/arch/arm/lib/uaccess.S	Sun Sep  6 10:44:47 1998
+++ linux/arch/arm/lib/uaccess.S	Mon Dec  3 20:25:13 2001
@@ -42,11 +42,11 @@
 		rsb	ip, ip, #4
 		cmp	ip, #2
 		ldrb	r3, [r1], #1
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		ldrgeb	r3, [r1], #1
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		ldrgtb	r3, [r1], #1
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		sub	r2, r2, ip
 		b	.c2u_dest_aligned
 
@@ -69,8 +69,8 @@
 		addmi	ip, r2, #4
 		bmi	.c2u_0nowords
 		ldr	r3, [r1], #4
-USER(		strt	r3, [r0], #4)			// May fault
-		mov	ip, r0, lsl #32 - PAGE_SHIFT	// On each page, use a ld/st??t instruction
+USER(		strt	r3, [r0], #4)			@ May fault
+		mov	ip, r0, lsl #32 - PAGE_SHIFT	@ On each page, use a ld/st??t instruction
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
 		beq	.c2u_0fupi
@@ -84,31 +84,31 @@
 		blt	.c2u_0rem8lp
 
 .c2u_0cpy8lp:	ldmia	r1!, {r3 - r6}
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		ldmia	r1!, {r3 - r6}
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #32
 		bpl	.c2u_0cpy8lp
 .c2u_0rem8lp:	cmn	ip, #16
 		ldmgeia	r1!, {r3 - r6}
-		stmgeia	r0!, {r3 - r6}			// Shouldn't fault
+		stmgeia	r0!, {r3 - r6}			@ Shouldn't fault
 		tst	ip, #8
 		ldmneia	r1!, {r3 - r4}
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		ldrne	r3, [r1], #4
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_0fupi
 .c2u_0nowords:	teq	ip, #0
 		beq	.c2u_finished
 .c2u_nowords:	cmp	ip, #2
 		ldrb	r3, [r1], #1
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		ldrgeb	r3, [r1], #1
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		ldrgtb	r3, [r1], #1
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 .c2u_not_enough:
@@ -129,7 +129,7 @@
 		mov	r3, r7, lsr #8
 		ldr	r7, [r1], #4
 		orr	r3, r3, r7, lsl #24
-USER(		strt	r3, [r0], #4)			// May fault
+USER(		strt	r3, [r0], #4)			@ May fault
 		mov	ip, r0, lsl #32 - PAGE_SHIFT
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
@@ -149,7 +149,7 @@
 		orr	r5, r5, r6, lsl #24
 		mov	r6, r6, lsr #8
 		orr	r6, r6, r7, lsl #24
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #16
 		bpl	.c2u_1cpy8lp
 .c2u_1rem8lp:	tst	ip, #8
@@ -158,23 +158,23 @@
 		orrne	r3, r3, r4, lsl #24
 		movne	r4, r4, lsr #8
 		orrne	r4, r4, r7, lsl #24
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		movne	r3, r7, lsr #8
 		ldrne	r7, [r1], #4
 		orrne	r3, r3, r7, lsl #24
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_1fupi
 .c2u_1nowords:	mov	r3, r7, lsr #8
 		teq	ip, #0
 		beq	.c2u_finished
 		cmp	ip, #2
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		movge	r3, r3, lsr #8
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		movgt	r3, r3, lsr #8
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 .c2u_2fupi:	subs	r2, r2, #4
@@ -183,7 +183,7 @@
 		mov	r3, r7, lsr #16
 		ldr	r7, [r1], #4
 		orr	r3, r3, r7, lsl #16
-USER(		strt	r3, [r0], #4)			// May fault
+USER(		strt	r3, [r0], #4)			@ May fault
 		mov	ip, r0, lsl #32 - PAGE_SHIFT
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
@@ -203,7 +203,7 @@
 		orr	r5, r5, r6, lsl #16
 		mov	r6, r6, lsr #16
 		orr	r6, r6, r7, lsl #16
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #16
 		bpl	.c2u_2cpy8lp
 .c2u_2rem8lp:	tst	ip, #8
@@ -212,23 +212,23 @@
 		orrne	r3, r3, r4, lsl #16
 		movne	r4, r4, lsr #16
 		orrne	r4, r4, r7, lsl #16
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		movne	r3, r7, lsr #16
 		ldrne	r7, [r1], #4
 		orrne	r3, r3, r7, lsl #16
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_2fupi
 .c2u_2nowords:	mov	r3, r7, lsr #16
 		teq	ip, #0
 		beq	.c2u_finished
 		cmp	ip, #2
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		movge	r3, r3, lsr #8
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		ldrgtb	r3, [r1], #0
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 .c2u_3fupi:	subs	r2, r2, #4
@@ -237,7 +237,7 @@
 		mov	r3, r7, lsr #24
 		ldr	r7, [r1], #4
 		orr	r3, r3, r7, lsl #8
-USER(		strt	r3, [r0], #4)			// May fault
+USER(		strt	r3, [r0], #4)			@ May fault
 		mov	ip, r0, lsl #32 - PAGE_SHIFT
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
@@ -257,7 +257,7 @@
 		orr	r5, r5, r6, lsl #8
 		mov	r6, r6, lsr #24
 		orr	r6, r6, r7, lsl #8
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #16
 		bpl	.c2u_3cpy8lp
 .c2u_3rem8lp:	tst	ip, #8
@@ -266,23 +266,23 @@
 		orrne	r3, r3, r4, lsl #8
 		movne	r4, r4, lsr #24
 		orrne	r4, r4, r7, lsl #8
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		movne	r3, r7, lsr #24
 		ldrne	r7, [r1], #4
 		orrne	r3, r3, r7, lsl #8
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_3fupi
 .c2u_3nowords:	mov	r3, r7, lsr #24
 		teq	ip, #0
 		beq	.c2u_finished
 		cmp	ip, #2
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		ldrge	r3, [r1], #0
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		movgt	r3, r3, lsr #8
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 #ifndef TESTING
@@ -302,11 +302,11 @@
 .cfu_dest_not_aligned:
 		rsb	ip, ip, #4
 		cmp	ip, #2
-USER(		ldrbt	r3, [r1], #1)			// May fault
+USER(		ldrbt	r3, [r1], #1)			@ May fault
 		strb	r3, [r0], #1
-USER(		ldrgebt	r3, [r1], #1)			// May fault
+USER(		ldrgebt	r3, [r1], #1)			@ May fault
 		strgeb	r3, [r0], #1
-USER(		ldrgtbt	r3, [r1], #1)			// May fault
+USER(		ldrgtbt	r3, [r1], #1)			@ May fault
 		strgtb	r3, [r0], #1
 		sub	r2, r2, ip
 		b	.cfu_dest_aligned
@@ -330,7 +330,7 @@
 		bmi	.cfu_0nowords
 USER(		ldrt	r3, [r1], #4)
 		str	r3, [r0], #4
-		mov	ip, r1, lsl #32 - PAGE_SHIFT	// On each page, use a ld/st??t instruction
+		mov	ip, r1, lsl #32 - PAGE_SHIFT	@ On each page, use a ld/st??t instruction
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
 		beq	.cfu_0fupi
@@ -343,31 +343,31 @@
 		subs	ip, ip, #32
 		blt	.cfu_0rem8lp
 
-.cfu_0cpy8lp:	ldmia	r1!, {r3 - r6}			// Shouldn't fault
+.cfu_0cpy8lp:	ldmia	r1!, {r3 - r6}			@ Shouldn't fault
 		stmia	r0!, {r3 - r6}
-		ldmia	r1!, {r3 - r6}			// Shouldn't fault
+		ldmia	r1!, {r3 - r6}			@ Shouldn't fault
 		stmia	r0!, {r3 - r6}
 		subs	ip, ip, #32
 		bpl	.cfu_0cpy8lp
 .cfu_0rem8lp:	cmn	ip, #16
-		ldmgeia	r1!, {r3 - r6}			// Shouldn't fault
+		ldmgeia	r1!, {r3 - r6}			@ Shouldn't fault
 		stmgeia	r0!, {r3 - r6}
 		tst	ip, #8
-		ldmneia	r1!, {r3 - r4}			// Shouldn't fault
+		ldmneia	r1!, {r3 - r4}			@ Shouldn't fault
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
-		ldrnet	r3, [r1], #4			// Shouldn't fault
+		ldrnet	r3, [r1], #4			@ Shouldn't fault
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
 		beq	.cfu_0fupi
 .cfu_0nowords:	teq	ip, #0
 		beq	.cfu_finished
 .cfu_nowords:	cmp	ip, #2
-USER(		ldrbt	r3, [r1], #1)			// May fault
+USER(		ldrbt	r3, [r1], #1)			@ May fault
 		strb	r3, [r0], #1
-USER(		ldrgebt	r3, [r1], #1)			// May fault
+USER(		ldrgebt	r3, [r1], #1)			@ May fault
 		strgeb	r3, [r0], #1
-USER(		ldrgtbt	r3, [r1], #1)			// May fault
+USER(		ldrgtbt	r3, [r1], #1)			@ May fault
 		strgtb	r3, [r0], #1
 		b	.cfu_finished
 
@@ -380,7 +380,7 @@
 
 .cfu_src_not_aligned:
 		bic	r1, r1, #3
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		cmp	ip, #2
 		bgt	.cfu_3fupi
 		beq	.cfu_2fupi
@@ -388,7 +388,7 @@
 		addmi	ip, r2, #4
 		bmi	.cfu_1nowords
 		mov	r3, r7, lsr #8
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		orr	r3, r3, r7, lsl #24
 		str	r3, [r0], #4
 		mov	ip, r1, lsl #32 - PAGE_SHIFT
@@ -402,7 +402,7 @@
 		blt	.cfu_1rem8lp
 
 .cfu_1cpy8lp:	mov	r3, r7, lsr #8
-		ldmia	r1!, {r4 - r7}			// Shouldn't fault
+		ldmia	r1!, {r4 - r7}			@ Shouldn't fault
 		orr	r3, r3, r4, lsl #24
 		mov	r4, r4, lsr #8
 		orr	r4, r4, r5, lsl #24
@@ -415,14 +415,14 @@
 		bpl	.cfu_1cpy8lp
 .cfu_1rem8lp:	tst	ip, #8
 		movne	r3, r7, lsr #8
-		ldmneia	r1!, {r4, r7}			// Shouldn't fault
+		ldmneia	r1!, {r4, r7}			@ Shouldn't fault
 		orrne	r3, r3, r4, lsl #24
 		movne	r4, r4, lsr #8
 		orrne	r4, r4, r7, lsl #24
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
 		movne	r3, r7, lsr #8
-USER(		ldrnet	r7, [r1], #4)			// May fault
+USER(		ldrnet	r7, [r1], #4)			@ May fault
 		orrne	r3, r3, r7, lsl #24
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
@@ -442,7 +442,7 @@
 		addmi	ip, r2, #4
 		bmi	.cfu_2nowords
 		mov	r3, r7, lsr #16
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		orr	r3, r3, r7, lsl #16
 		str	r3, [r0], #4
 		mov	ip, r1, lsl #32 - PAGE_SHIFT
@@ -456,7 +456,7 @@
 		blt	.cfu_2rem8lp
 
 .cfu_2cpy8lp:	mov	r3, r7, lsr #16
-		ldmia	r1!, {r4 - r7}			// Shouldn't fault
+		ldmia	r1!, {r4 - r7}			@ Shouldn't fault
 		orr	r3, r3, r4, lsl #16
 		mov	r4, r4, lsr #16
 		orr	r4, r4, r5, lsl #16
@@ -469,14 +469,14 @@
 		bpl	.cfu_2cpy8lp
 .cfu_2rem8lp:	tst	ip, #8
 		movne	r3, r7, lsr #16
-		ldmneia	r1!, {r4, r7}			// Shouldn't fault
+		ldmneia	r1!, {r4, r7}			@ Shouldn't fault
 		orrne	r3, r3, r4, lsl #16
 		movne	r4, r4, lsr #16
 		orrne	r4, r4, r7, lsl #16
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
 		movne	r3, r7, lsr #16
-USER(		ldrnet	r7, [r1], #4)			// May fault
+USER(		ldrnet	r7, [r1], #4)			@ May fault
 		orrne	r3, r3, r7, lsl #16
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
@@ -488,7 +488,7 @@
 		strb	r3, [r0], #1
 		movge	r3, r3, lsr #8
 		strgeb	r3, [r0], #1
-USER(		ldrgtbt	r3, [r1], #0)			// May fault
+USER(		ldrgtbt	r3, [r1], #0)			@ May fault
 		strgtb	r3, [r0], #1
 		b	.cfu_finished
 
@@ -496,7 +496,7 @@
 		addmi	ip, r2, #4
 		bmi	.cfu_3nowords
 		mov	r3, r7, lsr #24
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		orr	r3, r3, r7, lsl #8
 		str	r3, [r0], #4
 		mov	ip, r1, lsl #32 - PAGE_SHIFT
@@ -510,7 +510,7 @@
 		blt	.cfu_3rem8lp
 
 .cfu_3cpy8lp:	mov	r3, r7, lsr #24
-		ldmia	r1!, {r4 - r7}			// Shouldn't fault
+		ldmia	r1!, {r4 - r7}			@ Shouldn't fault
 		orr	r3, r3, r4, lsl #8
 		mov	r4, r4, lsr #24
 		orr	r4, r4, r5, lsl #8
@@ -523,14 +523,14 @@
 		bpl	.cfu_3cpy8lp
 .cfu_3rem8lp:	tst	ip, #8
 		movne	r3, r7, lsr #24
-		ldmneia	r1!, {r4, r7}			// Shouldn't fault
+		ldmneia	r1!, {r4, r7}			@ Shouldn't fault
 		orrne	r3, r3, r4, lsl #8
 		movne	r4, r4, lsr #24
 		orrne	r4, r4, r7, lsl #8
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
 		movne	r3, r7, lsr #24
-USER(		ldrnet	r7, [r1], #4)			// May fault
+USER(		ldrnet	r7, [r1], #4)			@ May fault
 		orrne	r3, r3, r7, lsl #8
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
@@ -540,7 +540,7 @@
 		beq	.cfu_finished
 		cmp	ip, #2
 		strb	r3, [r0], #1
-USER(		ldrget	r3, [r1], #0)			// May fault
+USER(		ldrget	r3, [r1], #0)			@ May fault
 		strgeb	r3, [r0], #1
 		movgt	r3, r3, lsr #8
 		strgtb	r3, [r0], #1
@@ -553,7 +553,8 @@
 		   data was copied.  */
 9001:		ldr	r0, [sp], #4
 		ldr	r1, [sp]
-		bl	SYMBOL_NAME(memzero)
+		teq	r1, #0
+		blne	SYMBOL_NAME(__memzero)
 		LOADREGS(fd,sp!, {r0, r4 - r7, pc})
 		.previous
 #endif
@@ -597,19 +598,22 @@
 9001:		LOADREGS(fd,sp!, {r0, pc})
 		.previous
 
-/* Prototype: int __arch_strlen_user(char *str)
+/* Prototype: int __arch_strnlen_user(char *str, long n)
  * Purpose  : get length of a string in user memory
  * Params   : str - address of string in user memory
  * Returns  : length of string *including terminator*, or zero on error
  */
-ENTRY(__arch_strlen_user)
+ENTRY(__arch_strnlen_user)
 		stmfd	sp!, {lr}
 		mov	r2, r0
 1:
-USER(		ldrbt	r1, [r0], #1)
-		teq	r1, #0
+USER(		ldrbt	r3, [r0], #1)
+		teq	r3, #0
+		beq	2f
+		subs	r1, r1, #1
 		bne	1b
-		sub	r0, r0, r2
+		add	r0, r0, #1
+2:		sub	r0, r0, r2
 		LOADREGS(fd,sp!, {pc})
 
 		.section .fixup,"ax"
diff -urN linux-2.2.18-pre8/arch/arm/mm/Makefile linux/arch/arm/mm/Makefile
--- linux-2.2.18-pre8/arch/arm/mm/Makefile	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/Makefile	Mon Dec  3 20:25:13 2001
@@ -7,13 +7,11 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
-all:	lib first_rule
-
-O_TARGET := mm.o
-O_OBJS   := init.o extable.o fault-$(PROCESSOR).o small_page.o
+O_TARGET	:= mm.o
+O_OBJS		:= init.o extable.o fault-$(PROCESSOR).o small_page.o
 
 ifeq ($(PROCESSOR),armo)
- O_OBJS += proc-arm2,3.o
+ O_OBJS		+= proc-arm2,3.o
 endif
 
 ifeq ($(PROCESSOR),armv)
@@ -22,11 +20,8 @@
 
 include $(TOPDIR)/Rules.make
 
-%.o: %.S
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
-
-.PHONY:	lib
-lib:;	@$(MAKE) -C ../lib constants.h
+.S.o:
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_$@) -traditional -c -o $*.o $<
 
 # Special dependencies
 fault-armv.o: fault-common.c
diff -urN linux-2.2.18-pre8/arch/arm/mm/fault-armo.c linux/arch/arm/mm/fault-armo.c
--- linux-2.2.18-pre8/arch/arm/mm/fault-armo.c	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/fault-armo.c	Mon Dec  3 20:25:13 2001
@@ -23,7 +23,6 @@
 #define FAULT_CODE_FORCECOW	0x80
 #define FAULT_CODE_PREFETCH	0x04
 #define FAULT_CODE_WRITE	0x02
-#define FAULT_CODE_USER		0x01
 
 #define DO_COW(m)		((m) & (FAULT_CODE_WRITE|FAULT_CODE_FORCECOW))
 #define READ_FAULT(m)		(!((m) & FAULT_CODE_WRITE))
@@ -96,11 +95,11 @@
 do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
 {
 #if 0
-	if (the memc mapping for this page exists - can check now...) {
+	if (the memc mapping for this page exists) {
 		printk ("Page in, but got abort (undefined instruction?)\n");
 		return 0;
 	}
 #endif
-	do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_PREFETCH, regs);
+	do_page_fault(addr, FAULT_CODE_PREFETCH, regs);
 	return 1;
 }
diff -urN linux-2.2.18-pre8/arch/arm/mm/fault-armv.c linux/arch/arm/mm/fault-armv.c
--- linux-2.2.18-pre8/arch/arm/mm/fault-armv.c	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/fault-armv.c	Mon Dec  3 20:25:13 2001
@@ -25,7 +25,6 @@
 #include <asm/unaligned.h>
 
 #define FAULT_CODE_READ		0x02
-#define FAULT_CODE_USER		0x01
 
 #define DO_COW(m)		(!((m) & FAULT_CODE_READ))
 #define READ_FAULT(m)		((m) & FAULT_CODE_READ)
@@ -377,16 +376,30 @@
 
 #endif
 
+#define BUG_PROC_MSG \
+  "Weirdness detected (%08X)\n" \
+  "Please read http://www.arm.linux.org.uk/state.html for more information"
+
 asmlinkage void
 do_DataAbort(unsigned long addr, int fsr, int error_code, struct pt_regs *regs)
 {
-	if (user_mode(regs))
-		error_code |= FAULT_CODE_USER;
+	if (user_mode(regs)) {
+		if (addr == regs->ARM_pc) {
+			static int first = 1;
+			if (first) {
+				/*
+				 * I want statistical information on this problem!
+				 */
+				printk(KERN_ERR BUG_PROC_MSG, fsr);
+				first = 0;
+			}
+			return;
+		}
+	}
 
 #define DIE(signr,nam)\
 		force_sig(signr, current);\
 		die(nam, regs, fsr);\
-		do_exit(signr);\
 		break
 
 	switch (fsr & 15) {
@@ -395,10 +408,8 @@
 	 */
 	case 0:
 		force_sig(SIGSEGV, current);
-		if (!user_mode(regs)) {
+		if (!user_mode(regs))
 			die("vector exception", regs, fsr);
-			do_exit(SIGSEGV);
-		}
 		break;
 
 	/*
@@ -415,10 +426,9 @@
 	 */
 	case 13:
 		force_sig(SIGSEGV, current);
-		if (!user_mode(regs)) {
+		if (!user_mode(regs))
 			die("section permission fault", regs, fsr);
-			do_exit(SIGSEGV);
-		} else {
+		else {
 #ifdef CONFIG_DEBUG_USER
 			printk("%s: permission fault on section, "
 			       "address=0x%08lx, code %d\n",
@@ -476,6 +486,6 @@
 asmlinkage int
 do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
 {
-	do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_READ, regs);
+	do_page_fault(addr, FAULT_CODE_READ, regs);
 	return 1;
 }
diff -urN linux-2.2.18-pre8/arch/arm/mm/fault-common.c linux/arch/arm/mm/fault-common.c
--- linux-2.2.18-pre8/arch/arm/mm/fault-common.c	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/fault-common.c	Mon Dec  3 20:25:13 2001
@@ -26,25 +26,14 @@
 	set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE));
 }
 
-static void
-kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs,
-		  struct task_struct *tsk, struct mm_struct *mm)
+/*
+ * This is useful to dump out the page tables associated with
+ * 'addr' in mm 'mm'.
+ */
+void show_pte(struct mm_struct *mm, unsigned long addr)
 {
-	char *reason;
-	/*
-	 * Oops. The kernel tried to access some bad page. We'll have to
-	 * terminate things with extreme prejudice.
-	 */
 	pgd_t *pgd;
 
-	if (addr < PAGE_SIZE)
-		reason = "NULL pointer dereference";
-	else
-		reason = "paging request";
-
-	printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
-		reason, addr);
-	printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd);
 	pgd = pgd_offset(mm, addr);
 	printk(KERN_ALERT "*pgd = %08lx", pgd_val(*pgd));
 
@@ -73,11 +62,34 @@
 
 		pte = pte_offset(pmd, addr);
 		printk(", *pte = %08lx", pte_val(*pte));
+#ifdef CONFIG_CPU_32
 		printk(", *ppte = %08lx", pte_val(pte[-PTRS_PER_PTE]));
+#endif
 	} while(0);
 
 	printk("\n");
-	die("Oops", regs, mode);
+}
+
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+static void
+kernel_page_fault(unsigned long addr, int write_access, struct pt_regs *regs,
+		  struct task_struct *tsk, struct mm_struct *mm)
+{
+	char *reason;
+
+	if (addr < PAGE_SIZE)
+		reason = "NULL pointer dereference";
+	else
+		reason = "paging request";
+
+	printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
+		reason, addr);
+	printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd);
+	show_pte(mm, addr);
+	die("Oops", regs, write_access);
 
 	do_exit(SIGKILL);
 }
@@ -88,6 +100,7 @@
 	struct mm_struct *mm;
 	struct vm_area_struct *vma;
 	unsigned long fixup;
+	int fault;
 
 	tsk = current;
 	mm  = tsk->mm;
@@ -126,8 +139,12 @@
 	 * make sure we exit gracefully rather than endlessly redo
 	 * the fault.
 	 */
-	if (!handle_mm_fault(tsk, vma, addr & PAGE_MASK, DO_COW(mode)))
+survive:
+	fault = handle_mm_fault(tsk, vma, addr & PAGE_MASK, DO_COW(mode));
+	if (!fault)
 		goto do_sigbus;
+	if (fault < 0)
+		goto out_of_memory;
 
 	up(&mm->mmap_sem);
 	return;
@@ -140,7 +157,7 @@
 	up(&mm->mmap_sem);
 
 	/* User mode accesses just cause a SIGSEGV */
-	if (mode & FAULT_CODE_USER) {
+	if (user_mode(regs)) {
 		tsk->tss.error_code = mode;
 		tsk->tss.trap_no = 14;
 #ifdef CONFIG_DEBUG_USER
@@ -165,6 +182,23 @@
 	kernel_page_fault(addr, mode, regs, tsk, mm);
 	return;
 
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+	if (tsk->pid == 1) {
+		tsk->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	up(&mm->mmap_sem);
+	if (user_mode(regs)) {
+		printk("VM: killing process %s\n", tsk->comm);
+		do_exit(SIGKILL);
+	}
+	goto no_context;
+
 do_sigbus:
 	/*
 	 * We ran out of memory, or some other thing happened to us that made
@@ -181,7 +215,7 @@
 	force_sig(SIGBUS, tsk);
 
 	/* Kernel mode? Handle exceptions or die */
-	if (!(mode & FAULT_CODE_USER))
+	if (!user_mode(regs))
 		goto no_context;
 }
 
diff -urN linux-2.2.18-pre8/arch/arm/mm/init.c linux/arch/arm/mm/init.c
--- linux-2.2.18-pre8/arch/arm/mm/init.c	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/init.c	Mon Dec  3 20:25:13 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/arch/arm/mm/init.c
  *
- *  Copyright (C) 1995, 1996  Russell King
+ *  Copyright (C) 1995-1999  Russell King
  */
 
 #include <linux/config.h>
@@ -248,7 +248,7 @@
 			  (unsigned long)(&__netwinder_end),
 			  "netwinder");
 
-	if (!machine_is_ebsa285() && !machine_is_cats())
+	if (!machine_is_ebsa285() && !machine_is_cats() && !machine_is_co285())
 		free_area((unsigned long)(&__ebsa285_begin),
 			  (unsigned long)(&__ebsa285_end),
 			  "ebsa285/cats");
diff -urN linux-2.2.18-pre8/arch/arm/mm/ioremap.c linux/arch/arm/mm/ioremap.c
--- linux-2.2.18-pre8/arch/arm/mm/ioremap.c	Sat May  8 11:06:56 1999
+++ linux/arch/arm/mm/ioremap.c	Mon Dec  3 20:25:13 2001
@@ -28,7 +28,11 @@
  * flush this - flushing the area causes the bus to lock.
  */
 
+#include <linux/errno.h>
 #include <linux/vmalloc.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
 #include <asm/io.h>
 
 static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
@@ -111,23 +115,23 @@
  * 'flags' are the extra L_PTE_ flags that you want to specify for this
  * mapping.  See include/asm-arm/proc-armv/pgtable.h for more information.
  */
-void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
+void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)
 {
 	void * addr;
 	struct vm_struct * area;
-	unsigned long offset;
+	unsigned long offset, last_addr;
+
+	/* Don't allow wraparound or zero size */
+	last_addr = phys_addr + size - 1;
+	if (!size || last_addr < phys_addr)
+		return NULL;
 
 	/*
 	 * Mappings have to be page-aligned
 	 */
 	offset = phys_addr & ~PAGE_MASK;
-	size = PAGE_ALIGN(size + offset);
-
-	/*
-	 * Don't allow mappings that wrap..
-	 */
-	if (!size || size > phys_addr + size)
-		return NULL;
+	phys_addr &= PAGE_MASK;
+	size = PAGE_ALIGN(last_addr) - phys_addr;
 
 	/*
 	 * Ok, go for it..
@@ -143,7 +147,7 @@
 	return (void *) (offset + (char *)addr);
 }
 
-void iounmap(void *addr)
+void __iounmap(void *addr)
 {
-	return vfree((void *) (PAGE_MASK & (unsigned long) addr));
+	vfree((void *) (PAGE_MASK & (unsigned long) addr));
 }
diff -urN linux-2.2.18-pre8/arch/arm/mm/mm-armv.c linux/arch/arm/mm/mm-armv.c
--- linux-2.2.18-pre8/arch/arm/mm/mm-armv.c	Sat May  8 11:06:57 1999
+++ linux/arch/arm/mm/mm-armv.c	Mon Dec  3 20:25:13 2001
@@ -70,3 +70,27 @@
 
 	return start_mem;
 }
+
+/*
+ * In order to soft-boot, we need to insert a 1:1 mapping in place of
+ * the user-mode pages.  This will then ensure that we have predictable
+ * results when turning the mmu off
+ */
+void setup_mm_for_reboot(char mode)
+{
+	pgd_t *pgd;
+	pmd_t pmd;
+	int i;
+
+	if (current->mm && current->mm->pgd)
+		pgd = current->mm->pgd;
+	else
+		pgd = init_mm.pgd;
+
+	for (i = 0; i < USER_PTRS_PER_PGD; i++) {
+		pmd_val(pmd) = (i << PAGE_SHIFT) |
+			PMD_SECT_AP_WRITE | PMD_SECT_AP_READ |
+			PMD_TYPE_SECT;
+		set_pmd(pmd_offset(pgd + i, i << PGDIR_SHIFT), pmd);
+	}
+}
diff -urN linux-2.2.18-pre8/arch/arm/mm/proc-arm2,3.S linux/arch/arm/mm/proc-arm2,3.S
--- linux-2.2.18-pre8/arch/arm/mm/proc-arm2,3.S	Sat May  8 11:07:16 1999
+++ linux/arch/arm/mm/proc-arm2,3.S	Mon Dec  3 20:25:13 2001
@@ -202,21 +202,17 @@
 LC0:		.word	SYMBOL_NAME(page_nr)
 /*
  * Function: arm2_switch_to (struct task_struct *prev, struct task_struct *next)
- *
  * Params  : prev	Old task structure
  *	   : next	New task structure for process to run
- *
  * Returns : prev
- *
  * Purpose : Perform a task switch, saving the old processes state, and restoring
  *	     the new.
- *
  * Notes   : We don't fiddle with the FP registers here - we postpone this until
  *	     the new task actually uses FP.  This way, we don't swap FP for tasks
  *	     that do not require it.
  */
 _arm2_switch_to:
-		stmfd	sp!, {r4 - r9, fp, lr}		@ Store most regs on stack
+		stmfd	sp!, {r4 - r10, fp, lr}		@ Store most regs on stack
 		str	sp, [r0, #TSS_SAVE]		@ Save sp_SVC
 		ldr	sp, [r1, #TSS_SAVE]		@ Get saved sp_SVC
 		mov	r4, r1
@@ -235,7 +231,7 @@
 		strb	r6, [r6]
 		subs	r1, r1, #8
 		bhi	1b
-		ldmfd	sp!, {r4 - r9, fp, pc}^		@ Load all regs saved previously
+		ldmfd	sp!, {r4 - r10, fp, pc}^	@ Load all regs saved previously
 
 /*
  * Function: arm2_remap_memc (struct task_struct *tsk)
@@ -316,15 +312,11 @@
 _arm2_proc_fin:	movs	pc, lr
 /*
  * Function: arm3_switch_to (struct task_struct *prev, struct task_struct *next)
- *
  * Params  : prev	Old task structure
  *	   : next	New task structure for process to run
- *
  * Returns : prev
- *
  * Purpose : Perform a task switch, saving the old processes state, and restoring
  *	     the new.
- *
  * Notes   : We don't fiddle with the FP registers here - we postpone this until
  *	     the new task actually uses FP.  This way, we don't swap FP for tasks
  *	     that do not require it.
diff -urN linux-2.2.18-pre8/arch/arm/mm/proc-arm6,7.S linux/arch/arm/mm/proc-arm6,7.S
--- linux-2.2.18-pre8/arch/arm/mm/proc-arm6,7.S	Sat May  8 11:07:16 1999
+++ linux/arch/arm/mm/proc-arm6,7.S	Mon Dec  3 20:25:13 2001
@@ -1,13 +1,15 @@
 /*
- * linux/arch/arm/mm/arm6.S: MMU functions for ARM6
+ * linux/arch/arm/mm/proc-arm6,7.S: MMU functions for ARM6
  *
- * (C) 1997 Russell King
+ * (C) 1997-1999 Russell King
  *
  * These are the low level assembler for performing cache and TLB
  * functions on the ARM6 & ARM7.
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/procinfo.h>
+#include <asm/errno.h>
 #include "../lib/constants.h"
 
 /*
@@ -68,24 +70,21 @@
  *	     that do not require it.
  */
 _arm6_7_switch_to:
-		stmfd	sp!, {r4 - r9, fp, lr}		@ Store most regs on stack
+		stmfd	sp!, {r4 - r10, fp, lr}		@ Store most regs on stack
 		mrs	ip, cpsr
 		stmfd	sp!, {ip}			@ Save cpsr_SVC
 		str	sp, [r0, #TSS_SAVE]		@ Save sp_SVC
 		ldr	sp, [r1, #TSS_SAVE]		@ Get saved sp_SVC
-		ldr	r2, [r1, #TSK_ADDR_LIMIT]
-		teq	r2, #0
-		moveq	r2, #DOM_KERNELDOMAIN
-		movne	r2, #DOM_USERDOMAIN
+		ldr	r2, [r1, #TSS_DOMAIN]
+		ldr	r3, [r1, #TSS_MEMMAP]		@ Page table pointer
 		mcr	p15, 0, r2, c3, c0		@ Set domain reg
-		ldr	r2, [r1, #TSS_MEMMAP]		@ Page table pointer
 		mov	r1, #0
 		mcr	p15, 0, r1, c7, c0, 0		@ flush cache
-		mcr	p15, 0, r2, c2, c0, 0		@ update page table ptr
+		mcr	p15, 0, r3, c2, c0, 0		@ update page table ptr
 		mcr	p15, 0, r1, c5, c0, 0		@ flush TLBs
 		ldmfd	sp!, {ip}
 		msr	spsr, ip			@ Save tasks CPSR into SPSR for this return
-		ldmfd	sp!, {r4 - r9, fp, pc}^		@ Load all regs saved previously
+		ldmfd	sp!, {r4 - r10, fp, pc}^	@ Load all regs saved previously
 
 /*
  * Function: arm6_7_data_abort ()
@@ -109,40 +108,44 @@
 _arm6_data_abort:
 		ldr	r4, [r0]			@ read instruction causing problem
 		mov	r2, r4, lsr #19			@ r2 b1 = L
-		and	r1, r4, #15 << 24
-		add	pc, pc, r1, lsr #22		@ Now branch to the relevent processing routine
-		movs	pc, lr
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_earlyldrpost		@ ldr	rd, [rn], #m
-		b	Ldata_simple			@ ldr	rd, [rn, #m]	@ RegVal
-		b	Ldata_earlyldrpost		@ ldr	rd, [rn], rm
-		b	Ldata_simple			@ ldr	rd, [rn, rm]
-		b	Ldata_ldmstm			@ ldm*a	rn, <rlist>
-		b	Ldata_ldmstm			@ ldm*b	rn, <rlist>
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_simple			@ ldc	rd, [rn], #m	@ Same as ldr	rd, [rn], #m
-		b	Ldata_simple			@ ldc	rd, [rn, #m]
-		b	Ldata_unknown
-Ldata_unknown:	@ Part of jumptable
-		ldr	r3, [sp, #15 * 4]		@ Get PC
-		str	r3, [sp, #-4]!
-		mov	r1, r1, lsr #2
-		mov	r3, r4
-		mov	r2, r0
-		adr	r0, Lukabttxt
-		bl	SYMBOL_NAME(panic)
-Lstop:		b	Lstop
+		and	r1, r4, #14 << 24
+		and	r2, r2, #2			@ check read/write bit
+		teq	r1, #8 << 24
+		bne	Ldata_simple
+
+Ldata_ldmstm:	tst	r4, #1 << 21			@ check writeback bit
+		beq	Ldata_simple
+		mov	r7, #0x11
+		orr	r7, r7, r7, lsl #8
+		and	r0, r4, r7
+		and	r1, r4, r7, lsl #1
+		add	r0, r0, r1, lsr #1
+		and	r1, r4, r7, lsl #2
+		add	r0, r0, r1, lsr #2
+		and	r1, r4, r7, lsl #3
+		add	r0, r0, r1, lsr #3
+		add	r0, r0, r0, lsr #8
+		add	r0, r0, r0, lsr #4
+		and	r7, r0, #15			@ r7 = no. of registers to transfer.
+		and	r5, r4, #15 << 16		@ Get Rn
+		ldr	r0, [sp, r5, lsr #14]		@ Get register
+		tst	r4, #1 << 23			@ U bit
+		subne	r7, r0, r7, lsl #2
+		addeq	r7, r0, r7, lsl #2		@ Do correction (signed)
+Ldata_saver7:	str	r7, [sp, r5, lsr #14]		@ Put register
+Ldata_simple:	mrc	p15, 0, r0, c6, c0, 0		@ get FAR
+		mrc	p15, 0, r1, c5, c0, 0		@ get FSR
+		and	r1, r1, #255
+		mov	pc, lr
 
 _arm7_data_abort:
 		ldr	r4, [r0]			@ read instruction causing problem
 		mov	r2, r4, lsr #19			@ r2 b1 = L
 		and	r1, r4, #15 << 24
+		and	r2, r2, #2			@ check read/write bit
 		add	pc, pc, r1, lsr #22		@ Now branch to the relevent processing routine
 		movs	pc, lr
+
 		b	Ldata_unknown
 		b	Ldata_unknown
 		b	Ldata_unknown
@@ -158,123 +161,26 @@
 		b	Ldata_simple			@ ldc	rd, [rn], #m	@ Same as ldr	rd, [rn], #m
 		b	Ldata_simple			@ ldc	rd, [rn, #m]
 		b	Ldata_unknown
+Ldata_unknown:	@ Part of jumptable
 		b	Ldata_unknown
 
-Ldata_ldmstm:	tst	r4, #1 << 21			@ check writeback bit
+Ldata_lateldrpreconst:
+		tst	r4, #1 << 21			@ check writeback bit
 		beq	Ldata_simple
-
-		mov	r7, #0x11
-		orr	r7, r7, r7, lsl #8
-		and	r0, r4, r7
-		and	r1, r4, r7, lsl #1
-		add	r0, r0, r1, lsr #1
-		and	r1, r4, r7, lsl #2
-		add	r0, r0, r1, lsr #2
-		and	r1, r4, r7, lsl #3
-		add	r0, r0, r1, lsr #3
-		add	r0, r0, r0, lsr #8
-		add	r0, r0, r0, lsr #4
-		and	r7, r0, #15			@ r7 = no. of registers to transfer.
-		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]		@ Get register
-		eor	r6, r4, r4, lsl #2
-		tst	r6, #1 << 23			@ Check inc/dec ^ writeback
-		rsbeq	r7, r7, #0
-		add	r7, r0, r7, lsl #2		@ Do correction (signed)
-		str	r7, [sp, r5, lsr #14]		@ Put register
-
-Ldata_simple:	and	r2, r2, #2			@ check read/write bit
-		mrc	p15, 0, r0, c6, c0, 0		@ get FAR
-		mrc	p15, 0, r1, c5, c0, 0		@ get FSR
-		and	r1, r1, #15
-		mov	pc, lr
-
-Ldata_earlyldrpost:
-		tst	r2, #4
-		and	r2, r2, #2			@ check read/write bit
-		orrne	r2, r2, #1			@ T bit
-		mrc	p15, 0, r0, c6, c0, 0		@ get FAR
-		mrc	p15, 0, r1, c5, c0, 0		@ get FSR
-		and	r1, r1, #15
-		mov	pc, lr
-
 Ldata_lateldrpostconst:
 		movs	r1, r4, lsl #20			@ Get offset
-		beq	Ldata_earlyldrpost		@ if offset is zero, no effect
-		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]
-		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1, lsr #20
-		addeq	r0, r0, r1, lsr #20
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_earlyldrpost
-
-Ldata_lateldrpreconst:
-		tst	r4, #1 << 21			@ check writeback bit
-		movnes	r1, r4, lsl #20			@ Get offset
 		beq	Ldata_simple
 		and	r5, r4, #15 << 16		@ Get Rn
 		ldr	r0, [sp, r5, lsr #14]
 		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1, lsr #20
-		addeq	r0, r0, r1, lsr #20
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
-
-Ldata_lateldrpostreg:
-		and	r5, r4, #15
-		ldr	r1, [sp, r5, lsl #2]		@ Get Rm
-		mov	r3, r4, lsr #7
-		ands	r3, r3, #31
-		and	r6, r4, #0x70
-		orreq	r6, r6, #8
-		add	pc, pc, r6
-		mov	r0, r0
-
-		mov	r1, r1, lsl r3			@ 0: LSL #!0
-		b	1f
-		b	1f				@ 1: LSL #0
-		mov	r0, r0
-		b	1f				@ 2: MUL?
-		mov	r0, r0
-		b	1f				@ 3: MUL?
-		mov	r0, r0
-		mov	r1, r1, lsr r3			@ 4: LSR #!0
-		b	1f
-		mov	r1, r1, lsr #32			@ 5: LSR #32
-		b	1f
-		b	1f				@ 6: MUL?
-		mov	r0, r0
-		b	1f				@ 7: MUL?
-		mov	r0, r0
-		mov	r1, r1, asr r3			@ 8: ASR #!0
-		b	1f
-		mov	r1, r1, asr #32			@ 9: ASR #32
-		b	1f
-		b	1f				@ A: MUL?
-		mov	r0, r0
-		b	1f				@ B: MUL?
-		mov	r0, r0
-		mov	r1, r1, ror r3			@ C: ROR #!0
-		b	1f
-		mov	r1, r1, rrx			@ D: RRX
-		b	1f
-		mov	r0, r0				@ E: MUL?
-		mov	r0, r0
-		mov	r0, r0				@ F: MUL?
-
-
-1:		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]
-		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1
-		addeq	r0, r0, r1
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_earlyldrpost
+		subne	r7, r0, r1, lsr #20
+		addeq	r7, r0, r1, lsr #20
+		b	Ldata_saver7
 
 Ldata_lateldrprereg:
 		tst	r4, #1 << 21			@ check writeback bit
 		beq	Ldata_simple
+Ldata_lateldrpostreg:
 		and	r5, r4, #15
 		ldr	r1, [sp, r5, lsl #2]		@ Get Rm
 		mov	r3, r4, lsr #7
@@ -320,10 +226,9 @@
 1:		and	r5, r4, #15 << 16		@ Get Rn
 		ldr	r0, [sp, r5, lsr #14]
 		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1
-		addeq	r0, r0, r1
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
+		subne	r7, r0, r1
+		addeq	r7, r0, r1
+		b	Ldata_saver7
 
 /*
  * Function: arm6_7_check_bugs (void)
@@ -336,8 +241,22 @@
 		mrs	ip, cpsr
 		bic	ip, ip, #F_BIT
 		msr	cpsr, ip
+		mov	pc, lr
+
 _arm6_7_proc_init:
+		mov	pc, lr
+
 _arm6_7_proc_fin:
+		mrs	r0, cpsr
+		orr	r0, r0, #F_BIT | I_BIT
+		msr	cpsr, r0
+		mov	r0, #0x31			@ ....S..DP...M
+		mcr	p15, 0, r0, c1, c0, 0		@ disable caches
+		mov	pc, lr
+
+ENTRY(cpu_arm6_do_idle)
+ENTRY(cpu_arm7_do_idle)
+		mov	r0, #-EINVAL
 		mov	pc, lr
 
 /*
@@ -379,23 +298,22 @@
 _arm6_7_set_pte:
 		str	r1, [r0], #-1024		@ linux version
 
+		eor	r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY
+
 		bic	r2, r1, #0xff0
 		bic	r2, r2, #3
 		orr	r2, r2, #HPTE_TYPE_SMALL
 
-		tst	r1, #LPTE_USER | LPTE_EXEC
+		tst	r1, #LPTE_USER | LPTE_EXEC	@ User or Exec?
 		orrne	r2, r2, #HPTE_AP_READ
 
-		tst	r1, #LPTE_WRITE
-		tstne	r1, #LPTE_DIRTY
-		orrne	r2, r2, #HPTE_AP_WRITE
-
-		tst	r1, #LPTE_PRESENT
-		tstne	r1, #LPTE_YOUNG
-		moveq	r2, #0
+		tst	r1, #LPTE_WRITE | LPTE_DIRTY	@ Write and Dirty?
+		orreq	r2, r2, #HPTE_AP_WRITE
+
+		tst	r1, #LPTE_PRESENT | LPTE_YOUNG	@ Present and Young
+		movne	r2, #0
 
 		str	r2, [r0]			@ hardware version
-		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry	 (drain is done by TLB fns)
 		mov	pc, lr
 
 /*
@@ -403,24 +321,53 @@
  *
  * Notes   : This sets up everything for a reset
  */
-_arm6_7_reset:	mrs	r1, cpsr
-		orr	r1, r1, #F_BIT|I_BIT
-		msr	cpsr, r1
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c0, 0		@ flush cache
-		mcr	p15, 0, r0, c5, c0, 0		@ flush TLB
-		mov	r1, #F_BIT | I_BIT | 3
+_arm6_7_reset:
+		mov	r1, #0
+		mcr	p15, 0, r1, c7, c0, 0		@ flush cache
+		mcr	p15, 0, r1, c5, c0, 0		@ flush TLB
+		mov	r1, #0x30
+		mcr	p15, 0, r1, c1, c0, 0		@ turn off MMU etc
+		mov	pc, r0
+
+cpu_armvlsi_name:
+		.asciz	"ARM/VLSI"
+cpu_arm6_name:	.asciz	"ARM 6"
+cpu_arm610_name:
+		.asciz	"ARM 610"
+cpu_arm7_name:	.asciz	"ARM 7"
+cpu_arm710_name:
+		.asciz	"ARM 710"
+		.align
+
+		.section ".text.init", #alloc, #execinstr
+
+__arm6_setup:	mov	r0, #0
+		mcr	p15, 0, r0, c7, c0		@ flush caches on v3
+		mcr	p15, 0, r0, c5, c0		@ flush TLBs on v3
+		mcr	p15, 0, r4, c2, c0		@ load page table pointer
+		mov	r0, #0x1f			@ Domains 0, 1 = client
+		mcr	p15, 0, r0, c3, c0		@ load domain access register
+		mov	r0, #0x3d			@ ....S..DPWC.M
+		orr	r0, r0, #0x100
+		mov	pc, lr
+
+__arm7_setup:	mov	r0, #0
+		mcr	p15, 0, r0, c7, c0		@ flush caches on v3
+		mcr	p15, 0, r0, c5, c0		@ flush TLBs on v3
+		mcr	p15, 0, r4, c2, c0		@ load page table pointer
+		mov	r0, #0x1f			@ Domains 0, 1 = client
+		mcr	p15, 0, r0, c3, c0		@ load domain access register
+		mov	r0, #0x7d			@ ....S.LDPWC.M
+		orr	r0, r0, #0x100
 		mov	pc, lr
 
 /*
  * Purpose : Function pointers used to access above functions - all calls
  *	     come through these
  */
-_arm6_name:	.ascii	"arm6\0"
-		.align
-
-ENTRY(arm6_processor_functions)
-		.word	_arm6_name			@  0
+		.type	arm6_processor_functions, #object
+arm6_processor_functions:
+		.word	cpu_arm6_name			@  0
 		.word	_arm6_7_switch_to		@  4
 		.word	_arm6_data_abort		@  8
 		.word	_arm6_7_check_bugs		@ 12
@@ -441,16 +388,17 @@
 
 		.word	_arm6_7_flush_cache		@ 68
 		.word	_arm6_7_flush_cache		@ 72
+		.word	0
+		.word	cpu_arm6_do_idle
+		.size	arm6_processor_functions, . - arm6_processor_functions
 
 /*
  * Purpose : Function pointers used to access above functions - all calls
  *	     come through these
  */
-_arm7_name:	.ascii	"arm7\0"
-		.align
-
-ENTRY(arm7_processor_functions)
-		.word	_arm7_name			@  0
+		.type	arm7_processor_functions, #object
+arm7_processor_functions:
+		.word	cpu_arm7_name			@  0
 		.word	_arm6_7_switch_to		@  4
 		.word	_arm7_data_abort		@  8
 		.word	_arm6_7_check_bugs		@ 12
@@ -471,3 +419,93 @@
 
 		.word	_arm6_7_flush_cache		@ 68
 		.word	_arm6_7_flush_cache		@ 72
+		.word	0
+		.word	cpu_arm7_do_idle
+		.size	arm7_processor_functions, . - arm7_processor_functions
+
+		.type	cpu_arm6_info, #object
+cpu_arm6_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm6_name
+		.size	cpu_arm6_info, . - cpu_arm6_info
+
+		.type	cpu_arm610_info, #object
+cpu_arm610_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm610_name
+		.size	cpu_arm610_info, . - cpu_Arm610_info
+
+		.type	cpu_arm7_info, #object
+cpu_arm7_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm7_name
+		.size	cpu_arm7_info, . - cpu_arm7_info
+
+		.type	cpu_arm710_info, #object
+cpu_arm710_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm710_name
+		.size	cpu_arm710_info, . - cpu_arm710_info
+
+		.type	cpu_arch_name, #object
+cpu_arch_name:	.asciz	"armv3"
+		.size	cpu_arch_name, . - cpu_arch_name
+
+		.type	cpu_elf_name, #object
+cpu_elf_name:	.asciz	"v3"
+		.size	cpu_elf_name, . - cpu_elf_name
+		.align
+
+		.section ".proc.info", #alloc, #execinstr
+
+		.type   __arm6_proc_info, #object
+__arm6_proc_info:
+		.long	0x41560600
+		.long	0xfffffff0
+		.long	0x00000c12
+		b	__arm6_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm6_info
+		.long	arm6_processor_functions
+		.size	__arm6_proc_info, . - __arm6_proc_info
+
+		.type   __arm610_proc_info, #object
+__arm610_proc_info:
+		.long	0x41560610
+		.long	0xfffffff0
+		.long	0x00000c12
+		b	__arm6_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm610_info
+		.long	arm6_processor_functions
+		.size	__arm610_proc_info, . - __arm610_proc_info
+
+		.type   __arm7_proc_info, #object
+__arm7_proc_info:
+		.long	0x41007000
+		.long	0xffffff00
+		.long	0x00000c12
+		b	__arm7_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm7_info
+		.long	arm7_processor_functions
+		.size	__arm7_proc_info, . - __arm7_proc_info
+
+		.type   __arm710_proc_info, #object
+__arm710_proc_info:
+		.long	0x41007100
+		.long	0xfff8ff00
+		.long	0x00000c12
+		b	__arm7_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm710_info
+		.long	arm7_processor_functions
+		.size	__arm710_proc_info, . - __arm710_proc_info
diff -urN linux-2.2.18-pre8/arch/arm/mm/proc-sa110.S linux/arch/arm/mm/proc-sa110.S
--- linux-2.2.18-pre8/arch/arm/mm/proc-sa110.S	Sat May  8 11:07:16 1999
+++ linux/arch/arm/mm/proc-sa110.S	Mon Dec  3 20:25:13 2001
@@ -1,13 +1,14 @@
 /*
- * linux/arch/arm/mm/sa110.S: MMU functions for SA110
+ * linux/arch/arm/mm/proc-sa110.S: MMU functions for SA110
  *
- * (C) 1997 Russell King
+ * (C) 1997-1999 Russell King
  *
  * These are the low level assembler for performing cache and TLB
- * functions on the sa110.
+ * functions on the StrongARM-110.
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/procinfo.h>
 #include <asm/hardware.h>
 #include "../lib/constants.h"
 
@@ -15,6 +16,7 @@
  * is larger than this, then we flush the whole cache
  */
 #define MAX_AREA_SIZE	32768
+#define FLUSH_OFFSET	32768
 
 		.data
 Lclean_switch:	.long	0
@@ -29,12 +31,12 @@
 		mov	r2, #1
 _sa110_flush_cache_all_r2:
 		ldr	r3, =Lclean_switch
+		ldr	ip, =FLUSH_BASE
 		ldr	r1, [r3]
 		ands	r1, r1, #1
 		eor	r1, r1, #1
 		str	r1, [r3]
-		ldr	ip, =FLUSH_BASE
-		addne	ip, ip, #32768
+		addne	ip, ip, #FLUSH_OFFSET
 		add	r1, ip, #16384			@ only necessary for 16k
 1:		ldr	r3, [ip], #32
 		teq	r1, ip
@@ -152,16 +154,17 @@
 _sa110_flush_ram_page:
 		mov	r1, #4096
 1:		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
-		mcr	p15, 0, r0, c7, c6, 1		@ flush D entry
 		add	r0, r0, #32
 		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
-		mcr	p15, 0, r0, c7, c6, 1		@ flush D entry
 		add	r0, r0, #32
-		subs	r1, r1, #64
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		add	r0, r0, #32
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		add	r0, r0, #32
+		subs	r1, r1, #128
 		bne	1b
 		mov	r0, #0
 		mcr	p15, 0, r0, c7, c10, 4		@ drain WB
-		mcr	p15, 0, r0, c7, c5, 0		@ flush I cache
 		mov	pc, lr
 
 /*
@@ -179,7 +182,7 @@
  * Function: sa110_flush_tlb_area (unsigned long address, unsigned long end, int flags)
  * Params  : address	Area start address
  *	   : end	Area end address
- *	   : flags	b0 = I cache as well
+ *	   : flags	b0 = I-TLB as well
  * Purpose : flush a TLB entry
  */
 		.align	5
@@ -193,7 +196,7 @@
 		mcrlt	p15, 0, r0, c8, c6, 1		@ flush D TLB entry
 		addlt	r0, r0, #4096
 		blt	1b
-		tst	r2, #1
+		teq	r2, #0
 		mcrne	p15, 0, r3, c8, c5, 0		@ flush I TLB
 		mov	pc, lr
 
@@ -220,18 +223,15 @@
  */
 		.align	5
 _sa110_switch_to:
-		stmfd	sp!, {r4 - r9, fp, lr}		@ Store most regs on stack
+		stmfd	sp!, {r4 - r10, fp, lr}		@ Store most regs on stack
 		mrs	ip, cpsr
 		stmfd	sp!, {ip}			@ Save cpsr_SVC
 		ldr	r2, [r0, #TSS_MEMMAP]		@ Get old page tables
 		str	sp, [r0, #TSS_SAVE]		@ Save sp_SVC
 		ldr	sp, [r1, #TSS_SAVE]		@ Get saved sp_SVC
-		ldr	r4, [r1, #TSK_ADDR_LIMIT]
-		teq	r4, #0
-		moveq	r4, #DOM_KERNELDOMAIN
-		movne	r4, #DOM_USERDOMAIN
-		mcr	p15, 0, r4, c3, c0		@ Set segment
+		ldr	r5, [r1, #TSS_DOMAIN]
 		ldr	r4, [r1, #TSS_MEMMAP]		@ Page table pointer
+		mcr	p15, 0, r5, c3, c0		@ Set segment
 /*
  * Flushing the cache is nightmarishly slow, so we take any excuse
  * to get out of it.  If the old page table is the same as the new,
@@ -247,7 +247,7 @@
 		eor	r2, r2, #1
 		str	r2, [r3]
 		ldr	r2, =FLUSH_BASE
-		addne	r2, r2, #32768
+		addne	r2, r2, #FLUSH_OFFSET
 		add	r1, r2, #16384			@ only necessary for 16k
 1:		ldr	r3, [r2], #32
 		teq	r1, r2
@@ -259,7 +259,7 @@
 		mcr	p15, 0, r1, c8, c7, 0		@ flush TLBs
 2:		ldmfd	sp!, {ip}
 		msr	spsr, ip			@ Save tasks CPSR into SPSR for this return
-		ldmfd	sp!, {r4 - r9, fp, pc}^		@ Load all regs saved previously
+		ldmfd	sp!, {r4 - r10, fp, pc}^	@ Load all regs saved previously
 
 /*
  * Function: sa110_data_abort ()
@@ -288,7 +288,8 @@
  */
 		.align	5
 _sa110_set_pmd:	str	r1, [r0]
-		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry	 (drain is done by TLB fns)
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		mcr	p15, 0, r0, c7, c10, 4		@ drain WB
 		mov	pc, lr
 
 /*
@@ -317,7 +318,8 @@
 
 		str	r2, [r0]			@ hardware version
 		mov	r0, r0
-		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry	 (drain is done by TLB fns)
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		mcr	p15, 0, r0, c7, c10, 4		@ drain WB
 		mov	pc, lr
 
 /*
@@ -330,36 +332,83 @@
 		mrs	ip, cpsr
 		bic	ip, ip, #F_BIT
 		msr	cpsr, ip
+		mov	pc, lr
 
 _sa110_proc_init:
+		mov	r0, #0
+		mcr	p15, 0, r0, c15, c1, 2		@ Enable clock switching
+		mov	pc, lr
+
 _sa110_proc_fin:
+		stmfd	sp!, {r1, lr}
+		mrs	r0, cpsr
+		orr	r0, r0, #F_BIT | I_BIT
+		msr	cpsr, r0
+		bl	_sa110_flush_cache_all		@ clean caches
+		mov	r0, #0
+		mcr	p15, 0, r0, c15, c2, 2		@ Disable clock switching
+		mrc	p15, 0, r0, c1, c0, 0
+		bic	r0, r0, #0x1000			@ ...i............
+		bic	r0, r0, #0x000e			@ ............wca.
+		mcr	p15, 0, r0, c1, c0, 0		@ disable caches
+		ldmfd	sp!, {r1, pc}
+
+		.align	5
+ENTRY(cpu_sa110_do_idle)
+		mov	r0, #0
+		mcr	p15, 0, r0, c15, c2, 2		@ Disable clock switching
+		ldr	r1, =FLUSH_BASE+FLUSH_OFFSET*2	@ load from uncacheable loc
+		ldr	r1, [r1, #0]
+		b	1f
+
+		.align	5
+1:		mcr	p15, 0, r0, c15, c8, 2		@ Wait for interrupt
+		mcr	p15, 0, r0, c15, c1, 2		@ Enable clock switching
 		mov	pc, lr
 
 /*
  * Function: sa110_reset
  * Notes   : This sets up everything for a reset
  */
-_sa110_reset:	mrs	r1, cpsr
-		orr	r1, r1, #F_BIT | I_BIT
-		msr	cpsr, r1
-		stmfd	sp!, {r1, lr}
-		mov	r2, #1
-		bl	_sa110_flush_cache_all
-		bl	_sa110_flush_tlb_all
+_sa110_reset:
+		mov	ip, #0
 		mcr	p15, 0, ip, c7, c7, 0		@ flush I,D caches
+		mcr	p15, 0, ip, c7, c10, 4		@ drain WB
+		mcr	p15, 0, ip, c8, c7, 0		@ flush I & D tlbs
 		mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
-		bic	r0, r0, #0x1800
-		bic	r0, r0, #0x000f
-		ldmfd	sp!, {r1, pc}
+		bic	r0, r0, #0x000f			@ ............wcam
+		bic	r0, r0, #0x1100			@ ...i...s........
+		mcr	p15, 0, ip, c1, c0, 0
+		mov	pc, r0
 /*
  * Purpose : Function pointers used to access above functions - all calls
  *	     come through these
  */
-_sa110_name:	.ascii	"sa110\0"
+
+cpu_manu_name:	.asciz	"Intel"
+ENTRY(cpu_sa110_name)
+		.asciz	"StrongARM-110"
 		.align
 
-ENTRY(sa110_processor_functions)
-		.word	_sa110_name			@  0
+		.section ".text.init", #alloc, #execinstr
+
+__sa110_setup:	mov	r0, #0
+		mcr	p15, 0, r0, c7, c7		@ flush I,D caches on v4
+		mcr	p15, 0, r0, c7, c10, 4		@ drain write buffer on v4
+		mcr	p15, 0, r0, c8, c7		@ flush I,D TLBs on v4
+		mcr	p15, 0, r4, c2, c0		@ load page table pointer
+		mov	r0, #0x1f			@ Domains 0, 1 = client
+		mcr	p15, 0, r0, c3, c0		@ load domain access register
+		mrc	p15, 0, r0, c1, c0		@ get control register v4
+		bic	r0, r0, #0x0e00			@ ....??r.........
+		bic	r0, r0, #0x0002			@ ..............a.
+		orr	r0, r0, #0x003d			@ ..........DPWC.M
+		orr	r0, r0, #0x1100			@ ...I...S........
+		mov	pc, lr
+
+		.type	sa110_processor_functions, #object
+sa110_processor_functions:
+		.word	cpu_sa110_name			@  0
 		.word	_sa110_switch_to		@  4
 		.word	_sa110_data_abort		@  8
 		.word	_sa110_check_bugs		@ 12
@@ -381,3 +430,38 @@
 
 		.word	_sa110_cache_wback_area		@ 68
 		.word	_sa110_cache_purge_area		@ 72
+		.word	0
+		.word	cpu_sa110_do_idle
+		.size	sa110_processor_functions, . - sa110_processor_functions
+
+		.type	cpu_sa110_info, #object
+cpu_sa110_info:
+		.long	cpu_manu_name
+		.long	cpu_sa110_name
+		.size	cpu_sa110_info, . - cpu_sa110_info
+
+		.type	cpu_arch_name, #object
+cpu_arch_name:	.asciz	"armv4"
+		.size	cpu_arch_name, . - cpu_arch_name
+
+		.type	cpu_elf_name, #object
+cpu_elf_name:	.asciz	"v4"
+		.size	cpu_elf_name, . - cpu_elf_name
+		.align
+
+		.section ".proc.info", #alloc, #execinstr
+
+		.type	__sa110_proc_info,#object
+__sa110_proc_info:
+		.long	0x4401a100
+		.long	0xfffffff0
+		.long	0x00000c02
+		b	__sa110_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT
+		.long	cpu_sa110_info
+		.long	sa110_processor_functions
+		.size	__sa110_proc_info, . - __sa110_proc_info
+
+
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/ARM-gcc.h linux/arch/arm/nwfpe/ARM-gcc.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/ARM-gcc.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/ARM-gcc.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,128 @@
+
+/*
+-------------------------------------------------------------------------------
+One of the macros `BIGENDIAN' or `LITTLEENDIAN' must be defined.
+-------------------------------------------------------------------------------
+*/
+#define LITTLEENDIAN
+
+/*
+-------------------------------------------------------------------------------
+The macro `BITS64' can be defined to indicate that 64-bit integer types are
+supported by the compiler.
+-------------------------------------------------------------------------------
+*/
+#define BITS64
+
+/*
+-------------------------------------------------------------------------------
+Each of the following `typedef's defines the most convenient type that holds
+integers of at least as many bits as specified.  For example, `uint8' should
+be the most convenient type that can hold unsigned integers of as many as
+8 bits.  The `flag' type must be able to hold either a 0 or 1.  For most
+implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
+to the same as `int'.
+-------------------------------------------------------------------------------
+*/
+typedef char flag;
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef int uint16;
+typedef int int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+#ifdef BITS64
+typedef unsigned long long int bits64;
+typedef signed long long int sbits64;
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Each of the following `typedef's defines a type that holds integers
+of _exactly_ the number of bits specified.  For instance, for most
+implementation of C, `bits16' and `sbits16' should be `typedef'ed to
+`unsigned short int' and `signed short int' (or `short int'), respectively.
+-------------------------------------------------------------------------------
+*/
+typedef unsigned char bits8;
+typedef signed char sbits8;
+typedef unsigned short int bits16;
+typedef signed short int sbits16;
+typedef unsigned int bits32;
+typedef signed int sbits32;
+#ifdef BITS64
+typedef unsigned long long int uint64;
+typedef signed long long int int64;
+#endif
+
+#ifdef BITS64
+/*
+-------------------------------------------------------------------------------
+The `LIT64' macro takes as its argument a textual integer literal and if
+necessary ``marks'' the literal as having a 64-bit integer type.  For
+example, the Gnu C Compiler (`gcc') requires that 64-bit literals be
+appended with the letters `LL' standing for `long long', which is `gcc's
+name for the 64-bit integer type.  Some compilers may allow `LIT64' to be
+defined as the identity macro:  `#define LIT64( a ) a'.
+-------------------------------------------------------------------------------
+*/
+#define LIT64( a ) a##LL
+#endif
+
+/*
+-------------------------------------------------------------------------------
+The macro `INLINE' can be used before functions that should be inlined.  If
+a compiler does not support explicit inlining, this macro should be defined
+to be `static'.
+-------------------------------------------------------------------------------
+*/
+#define INLINE extern __inline__
+
+
+/* For use as a GCC soft-float library we need some special function names. */
+
+#ifdef __LIBFLOAT__
+
+/* Some 32-bit ops can be mapped straight across by just changing the name. */
+#define float32_add			__addsf3
+#define float32_sub			__subsf3
+#define float32_mul			__mulsf3
+#define float32_div			__divsf3
+#define int32_to_float32		__floatsisf
+#define float32_to_int32_round_to_zero	__fixsfsi
+#define float32_to_uint32_round_to_zero	__fixunssfsi
+
+/* These ones go through the glue code.  To avoid namespace pollution
+   we rename the internal functions too.  */
+#define float32_eq			___float32_eq
+#define float32_le			___float32_le
+#define float32_lt			___float32_lt
+
+/* All the 64-bit ops have to go through the glue, so we pull the same
+   trick.  */
+#define float64_add			___float64_add
+#define float64_sub			___float64_sub
+#define float64_mul			___float64_mul
+#define float64_div			___float64_div
+#define int32_to_float64		___int32_to_float64
+#define float64_to_int32_round_to_zero	___float64_to_int32_round_to_zero
+#define float64_to_uint32_round_to_zero	___float64_to_uint32_round_to_zero
+#define float64_to_float32		___float64_to_float32
+#define float32_to_float64		___float32_to_float64
+#define float64_eq			___float64_eq
+#define float64_le			___float64_le
+#define float64_lt			___float64_lt
+
+#if 0
+#define float64_add			__adddf3
+#define float64_sub			__subdf3
+#define float64_mul			__muldf3
+#define float64_div			__divdf3
+#define int32_to_float64		__floatsidf
+#define float64_to_int32_round_to_zero	__fixdfsi
+#define float64_to_uint32_round_to_zero	__fixunsdfsi
+#define float64_to_float32		__truncdfsf2
+#define float32_to_float64		__extendsfdf2
+#endif
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/ChangeLog linux/arch/arm/nwfpe/ChangeLog
--- linux-2.2.18-pre8/arch/arm/nwfpe/ChangeLog	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/ChangeLog	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,20 @@
+1998-11-23  Scott Bambrough  <scottb@corelcomputer.com>
+
+	* README.FPE - fix typo in description of lfm/sfm instructions
+	* NOTES - Added file to describe known bugs/problems 
+	* fpmodule.c - Changed version number to 0.94
+
+1998-11-20  Scott Bambrough  <scottb@corelcomputer.com>
+
+	* README.FPE - fix description of URD, NRM instructions
+	* TODO - remove URD, NRM instructions from TODO list
+	* single_cpdo.c - implement URD, NRM
+	* double_cpdo.c - implement URD, NRM
+	* extended_cpdo.c - implement URD, NRM
+
+1998-11-19  Scott Bambrough  <scottb@corelcomputer.com>
+
+	* ChangeLog - Added this file to track changes made.
+	* fpa11.c - added code to initialize register types to typeNone
+	* fpa11_cpdt.c - fixed bug in storeExtended (typeExtended changed to
+	  typeDouble in switch statement)
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/Makefile linux/arch/arm/nwfpe/Makefile
--- linux-2.2.18-pre8/arch/arm/nwfpe/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/Makefile	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,31 @@
+#
+# linux/arch/arm/nwfpe/Makefile
+#
+# Copyright (C) 1998, 1999 Philip Blundell
+#
+
+NWFPE_OBJS := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \
+	      fpmodule.o fpopcode.o softfloat.o \
+	      single_cpdo.o double_cpdo.o extended_cpdo.o
+
+ifeq ($(CONFIG_CPU_26),y)
+NWFPE_OBJS += entry26.o
+else
+NWFPE_OBJS += entry.o
+endif
+
+L_TARGET := math-emu.a
+
+ifeq ($(CONFIG_NWFPE),y)
+L_OBJS = $(NWFPE_OBJS)
+else
+  ifeq ($(CONFIG_NWFPE),m)
+    M_OBJS = nwfpe.o
+    MI_OBJS = $(NWFPE_OBJS)
+  endif
+endif    
+
+include $(TOPDIR)/Rules.make
+
+nwfpe.o: $(MI_OBJS) $(MIX_OBJS)
+	 $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/config.h linux/arch/arm/nwfpe/config.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/config.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/config.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,31 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#if 1
+#define C_SYMBOL_NAME(foo)	foo
+#else
+#define C_SYMBOL_NAME(foo)	_##foo
+#endif
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/double_cpdo.c linux/arch/arm/nwfpe/double_cpdo.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/double_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/double_cpdo.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,293 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+extern FPA11 *fpa11;
+
+float64 getDoubleConstant(unsigned int);
+
+float64 float64_exp(float64 Fm);
+float64 float64_ln(float64 Fm);
+float64 float64_sin(float64 rFm);
+float64 float64_cos(float64 rFm);
+float64 float64_arcsin(float64 rFm);
+float64 float64_arctan(float64 rFm);
+float64 float64_log(float64 rFm);
+float64 float64_tan(float64 rFm);
+float64 float64_arccos(float64 rFm);
+float64 float64_pow(float64 rFn,float64 rFm);
+float64 float64_pol(float64 rFn,float64 rFm);
+
+unsigned int DoubleCPDO(const unsigned int opcode)
+{
+   float64 rFm, rFn;
+   unsigned int Fd, Fm, Fn, nRc = 1;
+
+   //fp_printk("DoubleCPDO(0x%08x)\n",opcode);
+   
+   Fm = getFm(opcode);
+   if (CONSTANT_FM(opcode))
+   {
+     rFm = getDoubleConstant(Fm);
+   }
+   else
+   {  
+     switch (fpa11->fpreg[Fm].fType)
+     {
+        case typeSingle:
+          rFm = float32_to_float64(fpa11->fpreg[Fm].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFm = fpa11->fpreg[Fm].fValue.fDouble;
+          break;
+
+        case typeExtended:
+            // !! patb
+	    //fp_printk("not implemented! why not?\n");
+            //!! ScottB
+            // should never get here, if extended involved
+            // then other operand should be promoted then
+            // ExtendedCPDO called.
+            break;
+
+        default: return 0;
+     }
+   }
+
+   if (!MONADIC_INSTRUCTION(opcode))
+   {
+      Fn = getFn(opcode);
+      switch (fpa11->fpreg[Fn].fType)
+      {
+        case typeSingle:
+          rFn = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFn = fpa11->fpreg[Fn].fValue.fDouble;
+        break;
+        
+        default: return 0;
+      }
+   }
+
+   Fd = getFd(opcode);
+   /* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */
+   switch (opcode & MASK_ARITHMETIC_OPCODE)
+   {
+      /* dyadic opcodes */
+      case ADF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_add(rFn,rFm);
+      break;
+
+      case MUF_CODE:
+      case FML_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_mul(rFn,rFm);
+      break;
+
+   case SUF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFn,rFm);
+      break;
+
+      case RSF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFm,rFn);
+      break;
+
+      case DVF_CODE:
+      case FDV_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFn,rFm);
+      break;
+
+      case RDF_CODE:
+      case FRD_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFm,rFn);
+      break;
+
+#if 0
+      case POW_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFn,rFm);
+      break;
+
+      case RPW_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFm,rFn);
+      break;
+#endif
+
+      case RMF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_rem(rFn,rFm);
+      break;
+
+#if 0
+      case POL_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_pol(rFn,rFm);
+      break;
+#endif
+
+      /* monadic opcodes */
+      case MVF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = rFm;
+      break;
+
+      case MNF_CODE:
+      {
+         unsigned int *p = (unsigned int*)&rFm;
+         p[1] ^= 0x80000000;
+         fpa11->fpreg[Fd].fValue.fDouble = rFm;
+      }
+      break;
+
+      case ABS_CODE:
+      {
+         unsigned int *p = (unsigned int*)&rFm;
+         p[1] &= 0x7fffffff;
+         fpa11->fpreg[Fd].fValue.fDouble = rFm;
+      }
+      break;
+
+      case RND_CODE:
+      case URD_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = 
+             int32_to_float64(float64_to_int32(rFm));
+      break;
+
+      case SQT_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sqrt(rFm);
+      break;
+
+#if 0
+      case LOG_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_log(rFm);
+      break;
+
+      case LGN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_ln(rFm);
+      break;
+
+      case EXP_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_exp(rFm);
+      break;
+
+      case SIN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sin(rFm);
+      break;
+
+      case COS_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_cos(rFm);
+      break;
+
+      case TAN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_tan(rFm);
+      break;
+
+      case ASN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_arcsin(rFm);
+      break;
+
+      case ACS_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_arccos(rFm);
+      break;
+
+      case ATN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_arctan(rFm);
+      break;
+#endif
+
+      case NRM_CODE:
+      break;
+      
+      default:
+      {
+        nRc = 0;
+      }
+   }
+
+   if (0 != nRc) fpa11->fpreg[Fd].fType = typeDouble;
+   return nRc;
+}
+
+#if 0
+float64 float64_exp(float64 rFm)
+{
+  return rFm;
+//series
+}
+
+float64 float64_ln(float64 rFm)
+{
+  return rFm;
+//series
+}
+
+float64 float64_sin(float64 rFm)
+{
+  return rFm;
+//series
+}
+
+float64 float64_cos(float64 rFm)
+{
+   return rFm;
+   //series
+}
+
+#if 0
+float64 float64_arcsin(float64 rFm)
+{
+//series
+}
+
+float64 float64_arctan(float64 rFm)
+{
+  //series
+}
+#endif
+
+float64 float64_log(float64 rFm)
+{
+  return float64_div(float64_ln(rFm),getDoubleConstant(7));
+}
+
+float64 float64_tan(float64 rFm)
+{
+  return float64_div(float64_sin(rFm),float64_cos(rFm));
+}
+
+float64 float64_arccos(float64 rFm)
+{
+return rFm;
+   //return float64_sub(halfPi,float64_arcsin(rFm));
+}
+
+float64 float64_pow(float64 rFn,float64 rFm)
+{
+  return float64_exp(float64_mul(rFm,float64_ln(rFn))); 
+}
+
+float64 float64_pol(float64 rFn,float64 rFm)
+{
+  return float64_arctan(float64_div(rFn,rFm)); 
+}
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/entry.S linux/arch/arm/nwfpe/entry.S
--- linux-2.2.18-pre8/arch/arm/nwfpe/entry.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/entry.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,126 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell 1998-1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+/* This is the kernel's entry point into the floating point emulator.
+It is called from the kernel with code similar to this:
+
+	adrsvc	al, r9, ret_from_exception	@ r9  = normal FP return
+	adrsvc	al, lr, fpundefinstr		@ lr  = undefined instr return
+
+	get_current_task r10
+	mov	r8, #1
+	strb	r8, [r10, #TSK_USED_MATH]	@ set current->used_math
+	add	r10, r10, #TSS_FPESAVE		@ r10 = workspace
+	ldr	r4, .LC2
+	ldr	pc, [r4]			@ Call FP emulator entry point
+
+The kernel expects the emulator to return via one of two possible
+points of return it passes to the emulator.  The emulator, if
+successful in its emulation, jumps to ret_from_exception (passed in
+r9) and the kernel takes care of returning control from the trap to
+the user code.  If the emulator is unable to emulate the instruction,
+it returns via _fpundefinstr (passed via lr) and the kernel halts the
+user program with a core dump.
+
+On entry to the emulator r10 points to an area of private FP workspace
+reserved in the thread structure for this process.  This is where the
+emulator saves its registers across calls.  The first word of this area
+is used as a flag to detect the first time a process uses floating point,
+so that the emulator startup cost can be avoided for tasks that don't
+want it.
+
+This routine does three things:
+
+1) It saves SP into a variable called userRegisters.  The kernel has
+created a struct pt_regs on the stack and saved the user registers
+into it.  See /usr/include/asm/proc/ptrace.h for details.  The
+emulator code uses userRegisters as the base of an array of words from
+which the contents of the registers can be extracted.
+
+2) It calls EmulateAll to emulate a floating point instruction.
+EmulateAll returns 1 if the emulation was successful, or 0 if not.
+
+3) If an instruction has been emulated successfully, it looks ahead at
+the next instruction.  If it is a floating point instruction, it
+executes the instruction, without returning to user space.  In this
+way it repeatedly looks ahead and executes floating point instructions
+until it encounters a non floating point instruction, at which time it
+returns via _fpreturn.
+
+This is done to reduce the effect of the trap overhead on each
+floating point instructions.  GCC attempts to group floating point
+instructions to allow the emulator to spread the cost of the trap over
+several floating point instructions.  */
+
+	.globl	nwfpe_enter
+nwfpe_enter:
+	/* ?? Could put userRegisters and fpa11 into fixed regs during
+	   emulation.  This would reduce load/store overhead at the expense
+	   of stealing two regs from the register allocator.  Not sure if
+	   it's worth it.  */
+	ldr r4, =userRegisters
+        str sp, [r4]			@ save pointer to user regs
+	ldr r4, =fpa11
+	str r10, [r4]			@ store pointer to our state
+        mov r4, sp			@ use r4 for local pointer
+        mov r10, lr			@ save the failure-return addresses
+
+        ldr r5, [r4, #60]	 	@ get contents of PC;
+	ldr r0, [r5, #-4]		@ get actual instruction into r0
+emulate:
+	bl EmulateAll			@ emulate the instruction
+   	cmp r0, #0			@ was emulation successful
+        moveq pc, r10			@ no, return failure
+
+next:
+__x1:	ldrt	r6, [r5], #4		@ get the next instruction and
+					@ increment PC
+
+	and   r2, r6, #0x0F000000	@ test for FP insns
+        teq   r2, #0x0C000000
+        teqne r2, #0x0D000000
+        teqne r2, #0x0E000000
+        movne pc, r9			@ return ok if not a fp insn
+
+        str r5, [r4, #60]		@ update PC copy in regs
+
+        mov r0, r6			@ save a copy
+        ldr r1, [r4, #64]		@ fetch the condition codes
+   	bl  checkCondition		@ check the condition
+   	cmp r0, #0			@ r0 = 0 ==> condition failed
+
+        @ if condition code failed to match, next insn
+   	beq next			@ get the next instruction;
+   	    
+        mov r0, r6			@ prepare for EmulateAll()
+   	b emulate			@ if r0 != 0, goto EmulateAll
+
+	@ We need to be prepared for the instruction at __x1 to fault.
+	@ Emit the appropriate exception gunk to fix things up.
+	.section .fixup,"ax"
+	.align
+__f1:	mov	pc, r9
+	.previous
+	.section __ex_table,"a"
+	.align 3
+	.long	__x1, __f1
+	.previous
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/entry26.S linux/arch/arm/nwfpe/entry26.S
--- linux-2.2.18-pre8/arch/arm/nwfpe/entry26.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/entry26.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,112 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell 1998-1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "../lib/constants.h"
+
+/* This is the kernel's entry point into the floating point emulator.
+It is called from the kernel with code similar to this:
+
+	mov	fp, #0
+	teqp	pc, #I_BIT | MODE_SVC
+	ldr	r4, .LC2
+	ldr	pc, [r4]		@ Call FP module USR entry point
+
+The kernel expects the emulator to return via one of two possible
+points of return it passes to the emulator.  The emulator, if
+successful in its emulation, jumps to ret_from_exception and the
+kernel takes care of returning control from the trap to the user code.
+If the emulator is unable to emulate the instruction, it returns to
+fpundefinstr and the kernel halts the user program with a core dump.
+
+This routine does four things:
+
+1) It saves SP into a variable called userRegisters.  The kernel has
+created a struct pt_regs on the stack and saved the user registers
+into it.  See /usr/include/asm/proc/ptrace.h for details.  The
+emulator code uses userRegisters as the base of an array of words from
+which the contents of the registers can be extracted.
+
+2) It locates the FP emulator work area within the TSS structure and
+points `fpa11' to it.
+
+3) It calls EmulateAll to emulate a floating point instruction.
+EmulateAll returns 1 if the emulation was successful, or 0 if not.
+
+4) If an instruction has been emulated successfully, it looks ahead at
+the next instruction.  If it is a floating point instruction, it
+executes the instruction, without returning to user space.  In this
+way it repeatedly looks ahead and executes floating point instructions
+until it encounters a non floating point instruction, at which time it
+returns via _fpreturn.
+
+This is done to reduce the effect of the trap overhead on each
+floating point instructions.  GCC attempts to group floating point
+instructions to allow the emulator to spread the cost of the trap over
+several floating point instructions.  */
+
+	.globl	nwfpe_enter
+nwfpe_enter:
+	ldr	r4, =userRegisters
+	str	sp, [r4]		@ save pointer to user regs
+
+	mov	r10, sp, lsr #13	@ find workspace
+	mov	r10, r10, lsl #13
+	add	r10, r10, #TSS_FPESAVE
+
+	ldr	r4, =fpa11
+	str	r10, [r4]		@ store pointer to our state
+	mov	r4, sp			@ use r4 for local pointer
+
+	ldr	r5, [r4, #60]		@ get contents of PC
+	bic	r5, r5, #0xfc000003
+	ldr	r0, [r5, #-4]		@ get actual instruction into r0
+	bl	EmulateAll		@ emulate the instruction
+1:	cmp	r0, #0			@ was emulation successful
+	beq	fpundefinstr		@ no, return failure
+
+next:
+	ldrt	r6, [r5], #4		@ get the next instruction and
+					@ increment PC
+
+	and	r2, r6, #0x0F000000	@ test for FP insns
+	teq	r2, #0x0C000000
+	teqne	r2, #0x0D000000
+	teqne	r2, #0x0E000000
+	bne	ret_from_exception	@ return ok if not a fp insn
+
+	ldr	r9, [r4, #60]		@ get new condition codes
+	and	r9, r9, #0xfc000003
+	orr	r7, r5, r9
+	str	r7, [r4, #60]		@ update PC copy in regs
+
+	mov	r0, r6			@ save a copy
+	mov	r1, r9			@ fetch the condition codes
+	bl	checkCondition		@ check the condition
+	cmp	r0, #0			@ r0 = 0 ==> condition failed
+
+	@ if condition code failed to match, next insn
+	beq	next			@ get the next instruction;
+	    
+	mov	r0, r6			@ prepare for EmulateAll()
+	adr	lr, 1b
+	orr	lr, lr, #3
+	b	EmulateAll		@ if r0 != 0, goto EmulateAll
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/extended_cpdo.c linux/arch/arm/nwfpe/extended_cpdo.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/extended_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/extended_cpdo.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,276 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+floatx80 getExtendedConstant(unsigned int);
+
+floatx80 floatx80_exp(floatx80 Fm);
+floatx80 floatx80_ln(floatx80 Fm);
+floatx80 floatx80_sin(floatx80 rFm);
+floatx80 floatx80_cos(floatx80 rFm);
+floatx80 floatx80_arcsin(floatx80 rFm);
+floatx80 floatx80_arctan(floatx80 rFm);
+floatx80 floatx80_log(floatx80 rFm);
+floatx80 floatx80_tan(floatx80 rFm);
+floatx80 floatx80_arccos(floatx80 rFm);
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm);
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm);
+
+unsigned int ExtendedCPDO(const unsigned int opcode)
+{
+   floatx80 rFm, rFn;
+   unsigned int Fd, Fm, Fn, nRc = 1;
+
+   //fp_printk("ExtendedCPDO(0x%08x)\n",opcode);
+   
+   Fm = getFm(opcode);
+   if (CONSTANT_FM(opcode))
+   {
+     rFm = getExtendedConstant(Fm);
+   }
+   else
+   {  
+     switch (fpa11->fpreg[Fm].fType)
+     {
+        case typeSingle:
+          rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble);
+        break;
+        
+        case typeExtended:
+          rFm = fpa11->fpreg[Fm].fValue.fExtended;
+        break;
+        
+        default: return 0;
+     }
+   }
+   
+   if (!MONADIC_INSTRUCTION(opcode))
+   {
+      Fn = getFn(opcode);
+      switch (fpa11->fpreg[Fn].fType)
+      {
+        case typeSingle:
+          rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+        break;
+        
+        case typeExtended:
+          rFn = fpa11->fpreg[Fn].fValue.fExtended;
+        break;
+        
+        default: return 0;
+      }
+   }
+
+   Fd = getFd(opcode);
+   switch (opcode & MASK_ARITHMETIC_OPCODE)
+   {
+      /* dyadic opcodes */
+      case ADF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_add(rFn,rFm);
+      break;
+
+      case MUF_CODE:
+      case FML_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_mul(rFn,rFm);
+      break;
+
+      case SUF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFn,rFm);
+      break;
+
+      case RSF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFm,rFn);
+      break;
+
+      case DVF_CODE:
+      case FDV_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFn,rFm);
+      break;
+
+      case RDF_CODE:
+      case FRD_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFm,rFn);
+      break;
+
+#if 0
+      case POW_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFn,rFm);
+      break;
+
+      case RPW_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFm,rFn);
+      break;
+#endif
+
+      case RMF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_rem(rFn,rFm);
+      break;
+
+#if 0
+      case POL_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_pol(rFn,rFm);
+      break;
+#endif
+
+      /* monadic opcodes */
+      case MVF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = rFm;
+      break;
+
+      case MNF_CODE:
+         rFm.high ^= 0x8000;
+         fpa11->fpreg[Fd].fValue.fExtended = rFm;
+      break;
+
+      case ABS_CODE:
+         rFm.high &= 0x7fff;
+         fpa11->fpreg[Fd].fValue.fExtended = rFm;
+      break;
+
+      case RND_CODE:
+      case URD_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = 
+             int32_to_floatx80(floatx80_to_int32(rFm));
+      break;
+
+      case SQT_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sqrt(rFm);
+      break;
+
+#if 0
+      case LOG_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_log(rFm);
+      break;
+
+      case LGN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_ln(rFm);
+      break;
+
+      case EXP_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_exp(rFm);
+      break;
+
+      case SIN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sin(rFm);
+      break;
+
+      case COS_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_cos(rFm);
+      break;
+
+      case TAN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_tan(rFm);
+      break;
+
+      case ASN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_arcsin(rFm);
+      break;
+
+      case ACS_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_arccos(rFm);
+      break;
+
+      case ATN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_arctan(rFm);
+      break;
+#endif
+
+      case NRM_CODE:
+      break;
+      
+      default:
+      {
+        nRc = 0;
+      }
+   }
+   
+   if (0 != nRc) fpa11->fpreg[Fd].fType = typeExtended;
+   return nRc;
+}
+
+#if 0
+floatx80 floatx80_exp(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_ln(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_sin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_cos(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arcsin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arctan(floatx80 rFm)
+{
+  //series
+}
+
+floatx80 floatx80_log(floatx80 rFm)
+{
+  return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7));
+}
+
+floatx80 floatx80_tan(floatx80 rFm)
+{
+  return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm));
+}
+
+floatx80 floatx80_arccos(floatx80 rFm)
+{
+   //return floatx80_sub(halfPi,floatx80_arcsin(rFm));
+}
+
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm)
+{
+  return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn))); 
+}
+
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm)
+{
+  return floatx80_arctan(floatx80_div(rFn,rFm)); 
+}
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpa11.c linux/arch/arm/nwfpe/fpa11.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpa11.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,206 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "fpa11.h"
+#include "milieu.h"
+#include "fpopcode.h"
+
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+/* forward declarations */
+unsigned int EmulateCPDO(const unsigned int);
+unsigned int EmulateCPDT(const unsigned int);
+unsigned int EmulateCPRT(const unsigned int);
+
+/* Emulator registers */
+FPA11 *fpa11;
+
+/* Reset the FPA11 chip.  Called to initialize and reset the emulator. */
+void resetFPA11(void)
+{
+  int i;
+  /* initialize the registers */
+  for (i=0;i<=7;i++)
+  {
+    fpa11->fpreg[i].fType = typeNone;
+  }
+  
+  /* FPSR: set system id to FP_EMULATOR, clear all other bits */
+  fpa11->fpsr = FP_EMULATOR;
+  
+  /* FPCR: set SB, AB and DA bits, clear all others */
+#if MAINTAIN_FPCR         
+  fpa11->fpcr = MASK_RESET;
+#endif
+}
+
+void SetRoundingMode(const unsigned int opcode)
+{
+#if MAINTAIN_FPCR
+   fpa11->fpcr &= ~MASK_ROUNDING_MODE;
+#endif   
+   switch (opcode & MASK_ROUNDING_MODE)
+   {
+      default:
+      case ROUND_TO_NEAREST:
+         float_rounding_mode = float_round_nearest_even;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_NEAREST;
+#endif         
+      break;
+      
+      case ROUND_TO_PLUS_INFINITY:
+         float_rounding_mode = float_round_up;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_PLUS_INFINITY;
+#endif         
+      break;
+      
+      case ROUND_TO_MINUS_INFINITY:
+         float_rounding_mode = float_round_down;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
+#endif         
+      break;
+      
+      case ROUND_TO_ZERO:
+         float_rounding_mode = float_round_to_zero;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_ZERO;
+#endif         
+      break;
+  }
+}
+
+void SetRoundingPrecision(const unsigned int opcode)
+{
+#if MAINTAIN_FPCR
+   fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
+#endif   
+   switch (opcode & MASK_ROUNDING_PRECISION)
+   {
+      case ROUND_SINGLE:
+         floatx80_rounding_precision = 32;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_SINGLE;
+#endif         
+      break;
+      
+      case ROUND_DOUBLE:
+         floatx80_rounding_precision = 64;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_DOUBLE;
+#endif         
+      break;
+      
+      case ROUND_EXTENDED:
+         floatx80_rounding_precision = 80;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_EXTENDED;
+#endif         
+      break;
+      
+      default: floatx80_rounding_precision = 80;
+  }
+}
+
+/* Emulate the instruction in the opcode. */
+unsigned int EmulateAll(unsigned int opcode)
+{
+  unsigned int nRc = 0;
+
+  if (fpa11->initflag == 0)		/* good place for __builtin_expect */
+  {
+    resetFPA11();
+    SetRoundingMode(ROUND_TO_NEAREST);
+    SetRoundingPrecision(ROUND_EXTENDED);
+    fpa11->initflag = 1;
+  }
+
+  if (TEST_OPCODE(opcode,MASK_CPRT))
+  {
+    /* Emulate conversion opcodes. */
+    /* Emulate register transfer opcodes. */
+    /* Emulate comparison opcodes. */
+    nRc = EmulateCPRT(opcode);
+  }
+  else if (TEST_OPCODE(opcode,MASK_CPDO))
+  {
+    /* Emulate monadic arithmetic opcodes. */
+    /* Emulate dyadic arithmetic opcodes. */
+    nRc = EmulateCPDO(opcode);
+  }
+  else if (TEST_OPCODE(opcode,MASK_CPDT))
+  {
+    /* Emulate load/store opcodes. */
+    /* Emulate load/store multiple opcodes. */
+    nRc = EmulateCPDT(opcode);
+  }
+  else
+  {
+    /* Invalid instruction detected.  Return FALSE. */
+    nRc = 0;
+  }
+
+  return(nRc);
+}
+
+#if 0
+unsigned int EmulateAll1(unsigned int opcode)
+{
+  switch ((opcode >> 24) & 0xf)
+  {
+     case 0xc:
+     case 0xd:
+       if ((opcode >> 20) & 0x1)
+       {
+          switch ((opcode >> 8) & 0xf)
+          {
+             case 0x1: return PerformLDF(opcode); break;
+             case 0x2: return PerformLFM(opcode); break;
+             default: return 0;
+          }
+       }
+       else
+       {
+          switch ((opcode >> 8) & 0xf)
+          {
+             case 0x1: return PerformSTF(opcode); break;
+             case 0x2: return PerformSFM(opcode); break;
+             default: return 0;
+          }
+      }
+     break;
+     
+     case 0xe: 
+       if (opcode & 0x10)
+         return EmulateCPDO(opcode);
+       else
+         return EmulateCPRT(opcode);
+     break;
+  
+     default: return 0;
+  }
+}
+#endif
+
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpa11.h linux/arch/arm/nwfpe/fpa11.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpa11.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,61 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#ifndef __FPA11_H__
+#define __FPA11_H__
+
+/* includes */
+#include "fpsr.h"		/* FP control and status register definitions */
+#include "softfloat.h"
+
+#define		typeNone		0x00
+#define		typeSingle		0x01
+#define		typeDouble		0x02
+#define		typeExtended		0x03
+
+typedef struct tagFPREG {
+   unsigned int fType;
+   union {
+      float32  fSingle;
+      float64  fDouble;
+      floatx80 fExtended;
+   } fValue;
+} FPREG;
+
+/* FPA11 device model */
+typedef struct tagFPA11 {
+  int initflag;			/* this is special.  The kernel guarantees
+				   to set it to 0 when a thread is launched,
+				   so we can use it to detect whether this
+				   instance of the emulator needs to be
+				   initialised. */
+  FPREG fpreg[8];		/* 8 floating point registers */
+  FPSR fpsr;			/* floating point status register */
+  FPCR fpcr;			/* floating point control register */
+} FPA11;
+
+extern void resetFPA11(void);
+extern void SetRoundingMode(const unsigned int);
+extern void SetRoundingPrecision(const unsigned int);
+
+extern FPA11 *fpa11;
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpa11.inl linux/arch/arm/nwfpe/fpa11.inl
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpa11.inl	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11.inl	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,47 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "fpa11.h"
+
+/* Read and write floating point status register */
+extern __inline__ unsigned int readFPSR(void)
+{
+  return(fpa11->fpsr);
+}
+
+extern __inline__ void writeFPSR(FPSR reg)
+{
+  /* the sysid byte in the status register is readonly */
+  fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID);
+}
+
+/* Read and write floating point control register */
+extern __inline__ FPCR readFPCR(void)
+{
+  /* clear SB, AB and DA bits before returning FPCR */
+  return(fpa11->fpcr & ~MASK_RFC);
+}
+
+extern __inline__ void writeFPCR(FPCR reg)
+{
+  fpa11->fpcr &= ~MASK_WFC;		/* clear SB, AB and DA bits */
+  fpa11->fpcr |= (reg & MASK_WFC);	/* write SB, AB and DA bits */
+}
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpa11_cpdo.c linux/arch/arm/nwfpe/fpa11_cpdo.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpa11_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11_cpdo.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,117 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "fpa11.h"
+#include "fpopcode.h"
+
+unsigned int SingleCPDO(const unsigned int opcode);
+unsigned int DoubleCPDO(const unsigned int opcode);
+unsigned int ExtendedCPDO(const unsigned int opcode);
+
+unsigned int EmulateCPDO(const unsigned int opcode)
+{
+   unsigned int Fd, nType, nDest, nRc = 1;
+   
+   //fp_printk("EmulateCPDO(0x%08x)\n",opcode);
+
+   /* Get the destination size.  If not valid let Linux perform
+      an invalid instruction trap. */
+   nDest = getDestinationSize(opcode);
+   if (typeNone == nDest) return 0;
+   
+   SetRoundingMode(opcode);
+     
+   /* Compare the size of the operands in Fn and Fm.
+      Choose the largest size and perform operations in that size,
+      in order to make use of all the precision of the operands. 
+      If Fm is a constant, we just grab a constant of a size 
+      matching the size of the operand in Fn. */
+   if (MONADIC_INSTRUCTION(opcode))
+     nType = nDest;
+   else
+     nType = fpa11->fpreg[getFn(opcode)].fType;
+   
+   if (!CONSTANT_FM(opcode))
+   {
+     register unsigned int Fm = getFm(opcode);
+     if (nType < fpa11->fpreg[Fm].fType)
+     {
+        nType = fpa11->fpreg[Fm].fType;
+     }
+   }
+
+   switch (nType)
+   {
+      case typeSingle   : nRc = SingleCPDO(opcode);   break;
+      case typeDouble   : nRc = DoubleCPDO(opcode);   break;
+      case typeExtended : nRc = ExtendedCPDO(opcode); break;
+      default           : nRc = 0;
+   }
+
+   /* If the operation succeeded, check to see if the result in the
+      destination register is the correct size.  If not force it
+      to be. */
+   Fd = getFd(opcode);
+   nType = fpa11->fpreg[Fd].fType;
+   if ((0 != nRc) && (nDest != nType))
+   {
+     switch (nDest)
+     {
+       case typeSingle:
+       {
+         if (typeDouble == nType)
+           fpa11->fpreg[Fd].fValue.fSingle = 
+              float64_to_float32(fpa11->fpreg[Fd].fValue.fDouble);
+         else
+           fpa11->fpreg[Fd].fValue.fSingle = 
+              floatx80_to_float32(fpa11->fpreg[Fd].fValue.fExtended);
+       }
+       break;
+          
+       case typeDouble:
+       {
+         if (typeSingle == nType)
+           fpa11->fpreg[Fd].fValue.fDouble = 
+              float32_to_float64(fpa11->fpreg[Fd].fValue.fSingle);
+         else
+           fpa11->fpreg[Fd].fValue.fDouble = 
+              floatx80_to_float64(fpa11->fpreg[Fd].fValue.fExtended);
+       }
+       break;
+          
+       case typeExtended:
+       {
+         if (typeSingle == nType)
+           fpa11->fpreg[Fd].fValue.fExtended = 
+              float32_to_floatx80(fpa11->fpreg[Fd].fValue.fSingle);
+         else
+           fpa11->fpreg[Fd].fValue.fExtended = 
+              float64_to_floatx80(fpa11->fpreg[Fd].fValue.fDouble);
+       }
+       break;
+     }
+     
+     fpa11->fpreg[Fd].fType = nDest;
+   }
+   
+   return nRc;
+}
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpa11_cpdt.c linux/arch/arm/nwfpe/fpa11_cpdt.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpa11_cpdt.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11_cpdt.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,330 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+#include <asm/uaccess.h>
+
+extern __inline__
+void loadSingle(const unsigned int Fn,const unsigned int *pMem)
+{
+   fpa11->fpreg[Fn].fType = typeSingle;
+   get_user(fpa11->fpreg[Fn].fValue.fSingle, pMem);
+}
+
+extern __inline__
+void loadDouble(const unsigned int Fn,const unsigned int *pMem)
+{
+   unsigned int *p;
+   p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fDouble;
+   fpa11->fpreg[Fn].fType = typeDouble;
+   get_user(p[0], &pMem[1]);
+   get_user(p[1], &pMem[0]); /* sign & exponent */
+}   
+
+extern __inline__
+void loadExtended(const unsigned int Fn,const unsigned int *pMem)
+{
+   unsigned int *p;
+   p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fExtended;
+   fpa11->fpreg[Fn].fType = typeExtended;
+   get_user(p[0], &pMem[0]);  /* sign & exponent */
+   get_user(p[1], &pMem[2]);  /* ls bits */
+   get_user(p[2], &pMem[1]);  /* ms bits */
+}   
+
+extern __inline__
+void loadMultiple(const unsigned int Fn,const unsigned int *pMem)
+{
+   register unsigned int *p;
+   unsigned long x;
+
+   p = (unsigned int*)&(fpa11->fpreg[Fn].fValue);
+   get_user(x, &pMem[0]);
+   fpa11->fpreg[Fn].fType = (x >> 14) & 0x00000003;
+   
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle:
+      case typeDouble:
+      {
+         get_user(p[0], &pMem[2]);  /* Single */
+         get_user(p[1], &pMem[1]);  /* double msw */
+         p[2] = 0;        /* empty */
+      }
+      break; 
+   
+      case typeExtended:
+      {
+         get_user(p[1], &pMem[2]);
+         get_user(p[2], &pMem[1]);  /* msw */
+         p[0] = (x & 0x80003fff);      
+      }
+      break;
+   }
+}
+
+extern __inline__
+void storeSingle(const unsigned int Fn,unsigned int *pMem)
+{
+   float32 val;
+   register unsigned int *p = (unsigned int*)&val;
+   
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeDouble: 
+         val = float64_to_float32(fpa11->fpreg[Fn].fValue.fDouble);
+      break;
+
+      case typeExtended: 
+         val = floatx80_to_float32(fpa11->fpreg[Fn].fValue.fExtended);
+      break;
+
+      default: val = fpa11->fpreg[Fn].fValue.fSingle;
+   }
+  
+   put_user(p[0], pMem);
+}   
+
+extern __inline__
+void storeDouble(const unsigned int Fn,unsigned int *pMem)
+{
+   float64 val;
+   register unsigned int *p = (unsigned int*)&val;
+
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle: 
+         val = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle);
+      break;
+
+      case typeExtended:
+         val = floatx80_to_float64(fpa11->fpreg[Fn].fValue.fExtended);
+      break;
+
+      default: val = fpa11->fpreg[Fn].fValue.fDouble;
+   }
+   put_user(p[1], &pMem[0]);	/* msw */
+   put_user(p[0], &pMem[1]);	/* lsw */
+}   
+
+extern __inline__
+void storeExtended(const unsigned int Fn,unsigned int *pMem)
+{
+   floatx80 val;
+   register unsigned int *p = (unsigned int*)&val;
+   
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle: 
+         val = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+      break;
+
+      case typeDouble: 
+         val = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+      break;
+
+      default: val = fpa11->fpreg[Fn].fValue.fExtended;
+   }
+   
+   put_user(p[0], &pMem[0]); /* sign & exp */
+   put_user(p[1], &pMem[2]);
+   put_user(p[2], &pMem[1]); /* msw */
+}   
+
+extern __inline__
+void storeMultiple(const unsigned int Fn,unsigned int *pMem)
+{
+   register unsigned int nType, *p;
+   
+   p = (unsigned int*)&(fpa11->fpreg[Fn].fValue);
+   nType = fpa11->fpreg[Fn].fType;
+   
+   switch (nType)
+   {
+      case typeSingle:
+      case typeDouble:
+      {
+	 put_user(p[0], &pMem[2]); /* single */
+	 put_user(p[1], &pMem[1]); /* double msw */
+	 put_user(nType << 14, &pMem[0]);
+      }
+      break; 
+   
+      case typeExtended:
+      {
+	 put_user(p[2], &pMem[1]); /* msw */
+	 put_user(p[1], &pMem[2]);
+	 put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]);
+      }
+      break;
+   }
+}
+
+unsigned int PerformLDF(const unsigned int opcode)
+{
+   unsigned int *pBase, *pAddress, *pFinal, nRc = 1;
+   
+   //fp_printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   switch (opcode & MASK_TRANSFER_LENGTH)
+   {
+      case TRANSFER_SINGLE  : loadSingle(getFd(opcode),pAddress);   break;
+      case TRANSFER_DOUBLE  : loadDouble(getFd(opcode),pAddress);   break;
+      case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break;
+      default: nRc = 0;
+   }
+   
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return nRc;
+}
+
+unsigned int PerformSTF(const unsigned int opcode)
+{
+   unsigned int *pBase, *pAddress, *pFinal, nRc = 1;
+   
+   //fp_printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+   SetRoundingMode(ROUND_TO_NEAREST);
+   
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   switch (opcode & MASK_TRANSFER_LENGTH)
+   {
+      case TRANSFER_SINGLE  : storeSingle(getFd(opcode),pAddress);   break;
+      case TRANSFER_DOUBLE  : storeDouble(getFd(opcode),pAddress);   break;
+      case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break;
+      default: nRc = 0;
+   }
+   
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return nRc;
+}
+
+unsigned int PerformLFM(const unsigned int opcode)
+{
+   unsigned int i, Fd, *pBase, *pAddress, *pFinal;
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   Fd = getFd(opcode);
+   for (i=getRegisterCount(opcode);i>0;i--)
+   {
+     loadMultiple(Fd,pAddress);
+     pAddress += 3; Fd++;
+     if (Fd == 8) Fd = 0;
+   }
+
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return 1;
+}
+
+unsigned int PerformSFM(const unsigned int opcode)
+{
+   unsigned int i, Fd, *pBase, *pAddress, *pFinal;
+   
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+   
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   Fd = getFd(opcode);
+   for (i=getRegisterCount(opcode);i>0;i--)
+   {
+     storeMultiple(Fd,pAddress);
+     pAddress += 3; Fd++;
+     if (Fd == 8) Fd = 0;
+   }
+
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return 1;
+}
+
+#if 1
+unsigned int EmulateCPDT(const unsigned int opcode)
+{
+  unsigned int nRc = 0;
+
+  //fp_printk("EmulateCPDT(0x%08x)\n",opcode);
+  
+  if (LDF_OP(opcode))
+  {
+    nRc = PerformLDF(opcode);
+  }
+  else if (LFM_OP(opcode))
+  {
+    nRc = PerformLFM(opcode);
+  }
+  else if (STF_OP(opcode))
+  {
+    nRc = PerformSTF(opcode);
+  } 
+  else if (SFM_OP(opcode))
+  {
+    nRc = PerformSFM(opcode);
+  }
+  else
+  {
+    nRc = 0;
+  }
+  
+  return nRc;
+}
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpa11_cprt.c linux/arch/arm/nwfpe/fpa11_cprt.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpa11_cprt.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpa11_cprt.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,313 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell, 1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "milieu.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+#include "fpa11.inl"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+extern flag floatx80_is_nan(floatx80);
+extern flag float64_is_nan( float64);
+extern flag float32_is_nan( float32);
+
+void SetRoundingMode(const unsigned int opcode);
+
+unsigned int PerformFLT(const unsigned int opcode);
+unsigned int PerformFIX(const unsigned int opcode);
+
+static unsigned int
+PerformComparison(const unsigned int opcode);
+
+unsigned int EmulateCPRT(const unsigned int opcode)
+{
+  unsigned int nRc = 1;
+
+  //fp_printk("EmulateCPRT(0x%08x)\n",opcode);
+
+  if (opcode & 0x800000)
+  {
+     /* This is some variant of a comparison (PerformComparison will
+	sort out which one).  Since most of the other CPRT
+	instructions are oddball cases of some sort or other it makes
+	sense to pull this out into a fast path.  */
+     return PerformComparison(opcode);
+  }
+
+  /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
+  switch ((opcode & 0x700000) >> 20)
+  {
+    case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
+    case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
+    
+    case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
+    case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
+
+#if 0
+    /* ?? Not at all sure about the mode checks here.  Linux never
+       calls the emulator from a non-USR fault but we always run in SVC
+       mode.  Is there even any point trying to emulate the way FPA11
+       behaves in this respect?
+
+       No - and I quote: 'The FPCR may only be present in some
+       implementations: it is there to control the hardware in an
+       implementation-specific manner, ...  The user mode of the
+       ARM is not permitted to use this register, and the WFC and
+       RFC instructions will trap if tried from user mode.'
+       Therefore, we do not provide the RFC and WFC instructions.
+        (rmk, 3/05/1999)
+     */
+    case  WFC_CODE >> 20:
+    {
+       int mode = 0;
+       __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
+       nRc = (0x13 == mode) ? 1 : 0;	/* in SVC processor mode? */
+       if (nRc) writeFPCR(readRegister(getRd(opcode)));
+    }
+    break;
+    
+    case  RFC_CODE >> 20:
+    {
+       int mode = 0;
+       __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
+       nRc = (0x13 == mode) ? 1 : 0;	/* in SVC processor mode? */
+       if (nRc) writeRegister(getRd(opcode),readFPCR()); break;
+    }
+    break;
+#endif
+
+    default: nRc = 0;
+  }
+  
+  return nRc;
+}
+
+unsigned int PerformFLT(const unsigned int opcode)
+{
+   unsigned int nRc = 1;
+   SetRoundingMode(opcode);
+   SetRoundingPrecision(opcode);
+   
+   switch (opcode & MASK_ROUNDING_PRECISION)
+   {
+      case ROUND_SINGLE:
+      {
+        fpa11->fpreg[getFn(opcode)].fType = typeSingle;
+        fpa11->fpreg[getFn(opcode)].fValue.fSingle =
+	   int32_to_float32(readRegister(getRd(opcode)));
+      }
+      break;
+
+      case ROUND_DOUBLE:
+      {
+        fpa11->fpreg[getFn(opcode)].fType = typeDouble;
+        fpa11->fpreg[getFn(opcode)].fValue.fDouble =
+            int32_to_float64(readRegister(getRd(opcode)));
+      }
+      break;
+        
+      case ROUND_EXTENDED:
+      {
+        fpa11->fpreg[getFn(opcode)].fType = typeExtended;
+        fpa11->fpreg[getFn(opcode)].fValue.fExtended =
+	   int32_to_floatx80(readRegister(getRd(opcode)));
+      }
+      break;
+      
+      default: nRc = 0;
+  }
+  
+  return nRc;
+}
+
+unsigned int PerformFIX(const unsigned int opcode)
+{
+   unsigned int nRc = 1;
+   unsigned int Fn = getFm(opcode);
+   
+   SetRoundingMode(opcode);
+
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle:
+      {
+         writeRegister(getRd(opcode),
+	               float32_to_int32(fpa11->fpreg[Fn].fValue.fSingle));
+      }
+      break;
+
+      case typeDouble:
+      {
+         writeRegister(getRd(opcode),
+	               float64_to_int32(fpa11->fpreg[Fn].fValue.fDouble));
+      }
+      break;
+      	               
+      case typeExtended:
+      {
+         writeRegister(getRd(opcode),
+	               floatx80_to_int32(fpa11->fpreg[Fn].fValue.fExtended));
+      }
+      break;
+      
+      default: nRc = 0;
+  }
+  
+  return nRc;
+}
+
+   
+static unsigned int __inline__
+PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
+{
+   unsigned int flags = 0;
+
+   /* test for less than condition */
+   if (floatx80_lt(Fn,Fm))
+   {
+      flags |= CC_NEGATIVE;
+   }
+  
+   /* test for equal condition */
+   if (floatx80_eq(Fn,Fm))
+   {
+      flags |= CC_ZERO;
+   }
+
+   /* test for greater than or equal condition */
+   if (floatx80_lt(Fm,Fn))
+   {
+      flags |= CC_CARRY;
+   }
+   
+   writeConditionCodes(flags);
+   return 1;
+}
+
+/* This instruction sets the flags N, Z, C, V in the FPSR. */
+   
+static unsigned int PerformComparison(const unsigned int opcode)
+{
+   unsigned int Fn, Fm;
+   floatx80 rFn, rFm;
+   int e_flag = opcode & 0x400000;	/* 1 if CxFE */
+   int n_flag = opcode & 0x200000;	/* 1 if CNxx */
+   unsigned int flags = 0;
+
+   //fp_printk("PerformComparison(0x%08x)\n",opcode);
+
+   Fn = getFn(opcode);
+   Fm = getFm(opcode);
+
+   /* Check for unordered condition and convert all operands to 80-bit
+      format.
+      ?? Might be some mileage in avoiding this conversion if possible.
+      Eg, if both operands are 32-bit, detect this and do a 32-bit
+      comparison (cheaper than an 80-bit one).  */
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle: 
+        //fp_printk("single.\n");
+	if (float32_is_nan(fpa11->fpreg[Fn].fValue.fSingle))
+	   goto unordered;
+        rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+      break;
+
+      case typeDouble: 
+        //fp_printk("double.\n");
+	if (float64_is_nan(fpa11->fpreg[Fn].fValue.fDouble))
+	   goto unordered;
+        rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+      break;
+      
+      case typeExtended: 
+        //fp_printk("extended.\n");
+	if (floatx80_is_nan(fpa11->fpreg[Fn].fValue.fExtended))
+	   goto unordered;
+        rFn = fpa11->fpreg[Fn].fValue.fExtended;
+      break;
+      
+      default: return 0;
+   }
+
+   if (CONSTANT_FM(opcode))
+   {
+     //fp_printk("Fm is a constant: #%d.\n",Fm);
+     rFm = getExtendedConstant(Fm);
+     if (floatx80_is_nan(rFm))
+        goto unordered;
+   }
+   else
+   {
+     //fp_printk("Fm = r%d which contains a ",Fm);
+      switch (fpa11->fpreg[Fm].fType)
+      {
+         case typeSingle: 
+           //fp_printk("single.\n");
+	   if (float32_is_nan(fpa11->fpreg[Fm].fValue.fSingle))
+	      goto unordered;
+           rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle);
+         break;
+
+         case typeDouble: 
+           //fp_printk("double.\n");
+	   if (float64_is_nan(fpa11->fpreg[Fm].fValue.fDouble))
+	      goto unordered;
+           rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble);
+         break;
+      
+         case typeExtended: 
+           //fp_printk("extended.\n");
+	   if (floatx80_is_nan(fpa11->fpreg[Fm].fValue.fExtended))
+	      goto unordered;
+           rFm = fpa11->fpreg[Fm].fValue.fExtended;
+         break;
+      
+         default: return 0;
+      }
+   }
+
+   if (n_flag)
+   {
+      rFm.high ^= 0x8000;
+   }
+
+   return PerformComparisonOperation(rFn,rFm);
+
+ unordered:
+   /* ?? The FPA data sheet is pretty vague about this, in particular
+      about whether the non-E comparisons can ever raise exceptions.
+      This implementation is based on a combination of what it says in
+      the data sheet, observation of how the Acorn emulator actually
+      behaves (and how programs expect it to) and guesswork.  */
+   flags |= CC_OVERFLOW;
+
+   if (BIT_AC & readFPSR()) flags |= CC_CARRY;
+
+   if (e_flag) float_raise(float_flag_invalid);
+
+   writeConditionCodes(flags);
+   return 1;
+}
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpmodule.c linux/arch/arm/nwfpe/fpmodule.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpmodule.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpmodule.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,185 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell, 1998-1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+/* XXX */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/pgtable.h>
+/* XXX */
+
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpmodule.h"
+#include "fpa11.h"
+#include "fpa11.inl"
+
+/* external data */
+extern FPA11 *fpa11;
+
+/* kernel symbols required for signal handling */
+typedef struct task_struct*	PTASK;
+
+#ifdef MODULE
+int fp_printk(const char *,...);
+void fp_send_sig(unsigned long sig, PTASK p, int priv);
+#if LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Scott Bambrough <scottb@corelcomputer.com>");
+MODULE_DESCRIPTION("NWFPE floating point emulator");
+#endif
+
+#else
+#define fp_printk	printk
+#define fp_send_sig	send_sig
+#define kern_fp_enter	fp_enter
+#endif
+
+/* kernel function prototypes required */
+void C_SYMBOL_NAME(fp_setup)(void);
+
+/* external declarations for saved kernel symbols */
+extern unsigned int C_SYMBOL_NAME(kern_fp_enter);
+
+/* forward declarations */
+extern void nwfpe_enter(void);
+
+/* Original value of fp_enter from kernel before patched by fpe_init. */ 
+static unsigned int orig_fp_enter;
+
+/* Address of user registers on the kernel stack. */
+unsigned int *userRegisters;
+
+void __init C_SYMBOL_NAME(fpe_version)(void)
+{
+  static const char szTitle[] = "<4>NetWinder Floating Point Emulator ";
+  static const char szVersion[] = "V0.94.1 ";
+  static const char szCopyright[] = "(c) 1998 Corel Computer Corp.\n";
+  C_SYMBOL_NAME(fp_printk)(szTitle);
+  C_SYMBOL_NAME(fp_printk)(szVersion);
+  C_SYMBOL_NAME(fp_printk)(szCopyright);
+}
+
+int __init fpe_init(void)
+{
+  /* Display title, version and copyright information. */
+  C_SYMBOL_NAME(fpe_version)();
+
+  /* Save pointer to the old FP handler and then patch ourselves in */
+  orig_fp_enter = C_SYMBOL_NAME(kern_fp_enter);
+  C_SYMBOL_NAME(kern_fp_enter) = (unsigned int)C_SYMBOL_NAME(nwfpe_enter);
+
+  return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+  return(fpe_init());
+}
+
+void cleanup_module(void)
+{
+  /* Restore the values we saved earlier. */
+  C_SYMBOL_NAME(kern_fp_enter) = orig_fp_enter;
+}
+#endif
+
+#define _ARM_pc 60
+#define _ARM_cpsr 64
+
+/*
+ScottB:  November 4, 1998
+
+Moved this function out of softfloat-specialize into fpmodule.c.
+This effectively isolates all the changes required for integrating with the
+Linux kernel into fpmodule.c.  Porting to NetBSD should only require modifying
+fpmodule.c to integrate with the NetBSD kernel (I hope!).
+
+[1/1/99: Not quite true any more unfortunately.  There is Linux-specific
+code to access data in user space in some other source files at the 
+moment.  --philb]
+
+float_exception_flags is a global variable in SoftFloat.
+
+This function is called by the SoftFloat routines to raise a floating
+point exception.  We check the trap enable byte in the FPSR, and raise
+a SIGFPE exception if necessary.  If not the relevant bits in the 
+cumulative exceptions flag byte are set and we return.
+*/
+
+void float_raise(signed char flags)
+{
+  register unsigned int fpsr, cumulativeTraps;
+  
+#if 0
+  printk(KERN_DEBUG "NWFPE: exception %08x at %08x from %08x\n", flags,
+	 __builtin_return_address(0), userRegisters[15]);
+#endif
+
+  /* Keep SoftFloat exception flags up to date.  */
+  float_exception_flags |= flags;
+
+  /* Read fpsr and initialize the cumulativeTraps.  */
+  fpsr = readFPSR();
+  cumulativeTraps = 0;
+  
+  /* For each type of exception, the cumulative trap exception bit is only
+     set if the corresponding trap enable bit is not set.  */
+  if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC))
+     cumulativeTraps |= BIT_IXC;  
+  if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC))
+     cumulativeTraps |= BIT_UFC;  
+  if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC))
+     cumulativeTraps |= BIT_OFC;  
+  if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC))
+     cumulativeTraps |= BIT_DZC;  
+  if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC))
+     cumulativeTraps |= BIT_IOC;  
+
+  /* Set the cumulative exceptions flags.  */
+  if (cumulativeTraps)
+    writeFPSR(fpsr | cumulativeTraps);
+
+  /* Raise an exception if necessary.  */
+  if (fpsr & (flags << 16))
+    fp_send_sig(SIGFPE, current, 1);
+}
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpmodule.h linux/arch/arm/nwfpe/fpmodule.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpmodule.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpmodule.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,53 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#ifndef __FPMODULE_H__
+#define __FPMODULE_H__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_CPU_32
+#define REG_ORIG_R0	17
+#define REG_CPSR	16
+#else
+#define REG_ORIG_R0	16
+#define REG_CPSR	15
+#endif
+
+#define REG_PC		15
+#define REG_LR		14
+#define REG_SP		13
+#define REG_IP		12
+#define REG_FP		11
+#define REG_R10		10
+#define REG_R9		9
+#define REG_R9		9
+#define REG_R8		8
+#define REG_R7		7
+#define REG_R6		6
+#define REG_R5		5
+#define REG_R4		4
+#define REG_R3		3
+#define REG_R2		2
+#define REG_R1		1
+#define REG_R0		0
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpmodule.inl linux/arch/arm/nwfpe/fpmodule.inl
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpmodule.inl	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpmodule.inl	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,88 @@
+/*
+   NetWinder Floating Point Emulator
+   (c) Corel Computer Corporation, 1998
+
+   Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the 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.
+ */
+
+/* Address of user registers on the kernel stack. */
+extern unsigned int *userRegisters;
+
+extern __inline__
+unsigned int readRegister(const unsigned int nReg)
+{
+	/* Note: The CPU thinks it has dealt with the current instruction.  As
+	   a result the program counter has been advanced to the next
+	   instruction, and points 4 bytes beyond the actual instruction
+	   that caused the invalid instruction trap to occur.  We adjust
+	   for this in this routine.  LDF/STF instructions with Rn = PC
+	   depend on the PC being correct, as they use PC+8 in their 
+	   address calculations. */
+	unsigned int val = userRegisters[nReg];
+
+	if (REG_PC == nReg)
+		val -= 4;
+
+	return val;
+}
+
+extern __inline__
+void writeRegister(const unsigned int nReg, const unsigned int val)
+{
+	userRegisters[nReg] = val;
+}
+
+extern __inline__
+unsigned int readCPSR(void)
+{
+	return (readRegister(REG_CPSR));
+}
+
+extern __inline__
+void writeCPSR(const unsigned int val)
+{
+	writeRegister(REG_CPSR, val);
+}
+
+extern __inline__
+unsigned int readConditionCodes(void)
+{
+#ifdef __FPEM_TEST__
+	return (0);
+#else
+	return (readCPSR() & CC_MASK);
+#endif
+}
+
+extern __inline__
+void writeConditionCodes(const unsigned int val)
+{
+	unsigned int rval;
+
+	/*
+	 * Operate directly on userRegisters since
+	 * the CPSR may be the PC register itself.
+	 */
+	rval = userRegisters[REG_CPSR] & ~CC_MASK;
+	userRegisters[REG_CPSR] = rval | (val & CC_MASK);
+}
+
+extern __inline__
+unsigned int readMemoryInt(unsigned int *pMem)
+{
+	return *pMem;
+}
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpopcode.c linux/arch/arm/nwfpe/fpopcode.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpopcode.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpopcode.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,164 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpsr.h"
+#include "fpa11.h"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+static floatx80 floatx80Constant[] = {
+  { 0x0000, 0x0000000000000000ULL},	/* extended 0.0 */
+  { 0x3fff, 0x8000000000000000ULL},	/* extended 1.0 */
+  { 0x4000, 0x8000000000000000ULL},	/* extended 2.0 */
+  { 0x4000, 0xc000000000000000ULL},	/* extended 3.0 */
+  { 0x4001, 0x8000000000000000ULL},	/* extended 4.0 */
+  { 0x4001, 0xa000000000000000ULL},	/* extended 5.0 */
+  { 0x3ffe, 0x8000000000000000ULL},	/* extended 0.5 */
+  { 0x4002, 0xa000000000000000ULL}	/* extended 10.0 */
+};  
+
+static float64 float64Constant[] = {
+  0x0000000000000000ULL,		/* double 0.0 */
+  0x3ff0000000000000ULL,		/* double 1.0 */
+  0x4000000000000000ULL,		/* double 2.0 */
+  0x4008000000000000ULL,		/* double 3.0 */
+  0x4010000000000000ULL,		/* double 4.0 */
+  0x4014000000000000ULL,		/* double 5.0 */
+  0x3fe0000000000000ULL,		/* double 0.5 */
+  0x4024000000000000ULL			/* double 10.0 */
+};  
+
+static float32 float32Constant[] = {
+  0x00000000,				/* single 0.0 */
+  0x3f800000,				/* single 1.0 */
+  0x40000000,				/* single 2.0 */
+  0x40400000,				/* single 3.0 */
+  0x40800000,				/* single 4.0 */
+  0x40a00000,				/* single 5.0 */
+  0x3f000000,				/* single 0.5 */
+  0x41200000				/* single 10.0 */
+};  
+
+floatx80 getExtendedConstant(const unsigned int nIndex)
+{
+   return floatx80Constant[nIndex];
+} 
+
+float64 getDoubleConstant(const unsigned int nIndex)
+{
+   return float64Constant[nIndex];
+} 
+
+float32 getSingleConstant(const unsigned int nIndex)
+{
+   return float32Constant[nIndex];
+} 
+
+unsigned int getTransferLength(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_TRANSFER_LENGTH)
+  {
+    case 0x00000000: nRc = 1; break; /* single precision */
+    case 0x00008000: nRc = 2; break; /* double precision */
+    case 0x00400000: nRc = 3; break; /* extended precision */
+    default: nRc = 0;
+  }
+  
+  return(nRc);
+}
+
+unsigned int getRegisterCount(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_REGISTER_COUNT)
+  {
+    case 0x00000000: nRc = 4; break;
+    case 0x00008000: nRc = 1; break;
+    case 0x00400000: nRc = 2; break;
+    case 0x00408000: nRc = 3; break;
+    default: nRc = 0;
+  }
+  
+  return(nRc);
+}
+
+unsigned int getRoundingPrecision(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_ROUNDING_PRECISION)
+  {
+    case 0x00000000: nRc = 1; break;
+    case 0x00000080: nRc = 2; break;
+    case 0x00080000: nRc = 3; break;
+    default: nRc = 0;
+  }
+  
+  return(nRc);
+}
+
+unsigned int getDestinationSize(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_DESTINATION_SIZE)
+  {
+    case 0x00000000: nRc = typeSingle; break;
+    case 0x00000080: nRc = typeDouble; break;
+    case 0x00080000: nRc = typeExtended; break;
+    default: nRc = typeNone;
+  }
+  
+  return(nRc);
+}
+
+/* contition code lookup table
+ index into the table is test code: EQ, NE, ... LT, GT, AL, NV
+ bit position in short is condition code: NZCV */
+unsigned short aCC[16] = {
+    0xF0F0, // EQ == Z set
+    0x0F0F, // NE
+    0xCCCC, // CS == C set
+    0x3333, // CC
+    0xFF00, // MI == N set
+    0x00FF, // PL
+    0xAAAA, // VS == V set
+    0x5555, // VC
+    0x0C0C, // HI == C set && Z clear
+    0xF3F3, // LS == C clear || Z set
+    0xAA55, // GE == (N==V)
+    0x55AA, // LT == (N!=V)
+    0x0A05, // GT == (!Z && (N==V))
+    0xF5FA, // LE == (Z || (N!=V))
+    0xFFFF, // AL always
+    0 // NV
+};
+
+unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes)
+{
+  return (aCC[opcode>>28] >> (ccodes>>28)) & 1;
+}
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpopcode.h linux/arch/arm/nwfpe/fpopcode.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpopcode.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpopcode.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,376 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#ifndef __FPOPCODE_H__
+#define __FPOPCODE_H__
+
+/*
+ARM Floating Point Instruction Classes
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 
+|c o n d|1 1 0 P|U|u|W|L|   Rn  |v|  Fd |0|0|0|1|  o f f s e t  | CPDT
+|c o n d|1 1 0 P|U|w|W|L|   Rn  |x|  Fd |0|0|0|1|  o f f s e t  | CPDT
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 
+|c o n d|1 1 1 0|a|b|c|d|e|  Fn |j|  Fd |0|0|0|1|f|g|h|0|i|  Fm | CPDO
+|c o n d|1 1 1 0|a|b|c|L|e|  Fn |   Rd  |0|0|0|1|f|g|h|1|i|  Fm | CPRT
+|c o n d|1 1 1 0|a|b|c|1|e|  Fn |1|1|1|1|0|0|0|1|f|g|h|1|i|  Fm | comparisons
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 
+
+CPDT		data transfer instructions
+		LDF, STF, LFM, SFM
+		
+CPDO		dyadic arithmetic instructions
+		ADF, MUF, SUF, RSF, DVF, RDF,
+		POW, RPW, RMF, FML, FDV, FRD, POL
+
+CPDO		monadic arithmetic instructions
+		MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP,
+		SIN, COS, TAN, ASN, ACS, ATN, URD, NRM
+		
+CPRT		joint arithmetic/data transfer instructions
+		FIX (arithmetic followed by load/store)
+		FLT (load/store followed by arithmetic)
+		CMF, CNF CMFE, CNFE (comparisons)
+		WFS, RFS (write/read floating point status register)
+		WFC, RFC (write/read floating point control register)
+
+cond		condition codes
+P		pre/post index bit: 0 = postindex, 1 = preindex
+U		up/down bit: 0 = stack grows down, 1 = stack grows up
+W		write back bit: 1 = update base register (Rn)
+L		load/store bit: 0 = store, 1 = load
+Rn		base register
+Rd		destination/source register		
+Fd		floating point destination register
+Fn		floating point source register
+Fm		floating point source register or floating point constant
+
+uv		transfer length (TABLE 1)
+wx		register count (TABLE 2)
+abcd		arithmetic opcode (TABLES 3 & 4)
+ef		destination size (rounding precision) (TABLE 5)
+gh		rounding mode (TABLE 6)
+j		dyadic/monadic bit: 0 = dyadic, 1 = monadic
+i 		constant bit: 1 = constant (TABLE 6)
+*/
+
+/*
+TABLE 1
++-------------------------+---+---+---------+---------+
+|  Precision              | u | v | FPSR.EP | length  |
++-------------------------+---+---+---------+---------+
+| Single                  | 0 ü 0 |    x    | 1 words |
+| Double                  | 1 ü 1 |    x    | 2 words |
+| Extended                | 1 ü 1 |    x    | 3 words |
+| Packed decimal          | 1 ü 1 |    0    | 3 words |
+| Expanded packed decimal | 1 ü 1 |    1    | 4 words |
++-------------------------+---+---+---------+---------+
+Note: x = don't care
+*/
+
+/*
+TABLE 2
++---+---+---------------------------------+
+| w | x | Number of registers to transfer |
++---+---+---------------------------------+
+| 0 ü 1 |  1                              |
+| 1 ü 0 |  2                              |
+| 1 ü 1 |  3                              |
+| 0 ü 0 |  4                              |
++---+---+---------------------------------+
+*/
+
+/*
+TABLE 3: Dyadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description           | Operation             |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | ADF      | Add                   | Fd := Fn + Fm         |
+| 0 | 0 | 0 | 1 | MUF      | Multiply              | Fd := Fn * Fm         |
+| 0 | 0 | 1 | 0 | SUF      | Subtract              | Fd := Fn - Fm         |
+| 0 | 0 | 1 | 1 | RSF      | Reverse subtract      | Fd := Fm - Fn         |
+| 0 | 1 | 0 | 0 | DVF      | Divide                | Fd := Fn / Fm         |
+| 0 | 1 | 0 | 1 | RDF      | Reverse divide        | Fd := Fm / Fn         |
+| 0 | 1 | 1 | 0 | POW      | Power                 | Fd := Fn ^ Fm         |
+| 0 | 1 | 1 | 1 | RPW      | Reverse power         | Fd := Fm ^ Fn         |
+| 1 | 0 | 0 | 0 | RMF      | Remainder             | Fd := IEEE rem(Fn/Fm) |
+| 1 | 0 | 0 | 1 | FML      | Fast Multiply         | Fd := Fn * Fm         |
+| 1 | 0 | 1 | 0 | FDV      | Fast Divide           | Fd := Fn / Fm         |
+| 1 | 0 | 1 | 1 | FRD      | Fast reverse divide   | Fd := Fm / Fn         |
+| 1 | 1 | 0 | 0 | POL      | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm)  |
+| 1 | 1 | 0 | 1 |          | undefined instruction | trap                  |
+| 1 | 1 | 1 | 0 |          | undefined instruction | trap                  |
+| 1 | 1 | 1 | 1 |          | undefined instruction | trap                  |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: POW, RPW, POL are deprecated, and are available for backwards
+      compatibility only.
+*/
+
+/*
+TABLE 4: Monadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description           | Operation             |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | MVF      | Move                  | Fd := Fm              |
+| 0 | 0 | 0 | 1 | MNF      | Move negated          | Fd := - Fm            |
+| 0 | 0 | 1 | 0 | ABS      | Absolute value        | Fd := abs(Fm)         |
+| 0 | 0 | 1 | 1 | RND      | Round to integer      | Fd := int(Fm)         |
+| 0 | 1 | 0 | 0 | SQT      | Square root           | Fd := sqrt(Fm)        |
+| 0 | 1 | 0 | 1 | LOG      | Log base 10           | Fd := log10(Fm)       |
+| 0 | 1 | 1 | 0 | LGN      | Log base e            | Fd := ln(Fm)          |
+| 0 | 1 | 1 | 1 | EXP      | Exponent              | Fd := e ^ Fm          |
+| 1 | 0 | 0 | 0 | SIN      | Sine                  | Fd := sin(Fm)         |
+| 1 | 0 | 0 | 1 | COS      | Cosine                | Fd := cos(Fm)         |
+| 1 | 0 | 1 | 0 | TAN      | Tangent               | Fd := tan(Fm)         |
+| 1 | 0 | 1 | 1 | ASN      | Arc Sine              | Fd := arcsin(Fm)      |
+| 1 | 1 | 0 | 0 | ACS      | Arc Cosine            | Fd := arccos(Fm)      |
+| 1 | 1 | 0 | 1 | ATN      | Arc Tangent           | Fd := arctan(Fm)      |
+| 1 | 1 | 1 | 0 | URD      | Unnormalized round    | Fd := int(Fm)         |
+| 1 | 1 | 1 | 1 | NRM      | Normalize             | Fd := norm(Fm)        |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are
+      available for backwards compatibility only.
+*/
+
+/*
+TABLE 5
++-------------------------+---+---+
+|  Rounding Precision     | e | f |
++-------------------------+---+---+
+| IEEE Single precision   | 0 ü 0 |
+| IEEE Double precision   | 0 ü 1 |
+| IEEE Extended precision | 1 ü 0 |
+| undefined (trap)        | 1 ü 1 |
++-------------------------+---+---+
+*/
+
+/*
+TABLE 5
++---------------------------------+---+---+
+|  Rounding Mode                  | g | h |
++---------------------------------+---+---+
+| Round to nearest (default)      | 0 ü 0 |
+| Round toward plus infinity      | 0 ü 1 |
+| Round toward negative infinity  | 1 ü 0 |
+| Round toward zero               | 1 ü 1 |
++---------------------------------+---+---+
+*/
+
+/*
+===
+=== Definitions for load and store instructions
+===
+*/
+
+/* bit masks */
+#define BIT_PREINDEX	0x01000000
+#define BIT_UP		0x00800000
+#define BIT_WRITE_BACK	0x00200000
+#define BIT_LOAD	0x00100000
+
+/* masks for load/store */
+#define MASK_CPDT		0x0c000000  /* data processing opcode */
+#define MASK_OFFSET		0x000000ff
+#define MASK_TRANSFER_LENGTH	0x00408000
+#define MASK_REGISTER_COUNT	MASK_TRANSFER_LENGTH
+#define MASK_COPROCESSOR	0x00000f00
+
+/* Tests for transfer length */
+#define TRANSFER_SINGLE		0x00000000
+#define TRANSFER_DOUBLE		0x00008000
+#define TRANSFER_EXTENDED	0x00400000
+#define TRANSFER_PACKED		MASK_TRANSFER_LENGTH
+
+/* Get the coprocessor number from the opcode. */
+#define getCoprocessorNumber(opcode)	((opcode & MASK_COPROCESSOR) >> 8)
+
+/* Get the offset from the opcode. */
+#define getOffset(opcode)		(opcode & MASK_OFFSET)
+
+/* Tests for specific data transfer load/store opcodes. */
+#define TEST_OPCODE(opcode,mask)	(((opcode) & (mask)) == (mask))
+
+#define LOAD_OP(opcode)   TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD)
+#define STORE_OP(opcode)  ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT)
+
+#define LDF_OP(opcode)	(LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define LFM_OP(opcode)	(LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+#define STF_OP(opcode)	(STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define SFM_OP(opcode)	(STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+
+#define PREINDEXED(opcode)		((opcode & BIT_PREINDEX) != 0)
+#define POSTINDEXED(opcode)		((opcode & BIT_PREINDEX) == 0)
+#define BIT_UP_SET(opcode)		((opcode & BIT_UP) != 0)
+#define BIT_UP_CLEAR(opcode)		((opcode & BIT_DOWN) == 0)
+#define WRITE_BACK(opcode)		((opcode & BIT_WRITE_BACK) != 0)
+#define LOAD(opcode)			((opcode & BIT_LOAD) != 0)
+#define STORE(opcode)			((opcode & BIT_LOAD) == 0)
+
+/*
+===
+=== Definitions for arithmetic instructions
+===
+*/
+/* bit masks */
+#define BIT_MONADIC	0x00008000
+#define BIT_CONSTANT	0x00000008
+
+#define CONSTANT_FM(opcode)		((opcode & BIT_CONSTANT) != 0)
+#define MONADIC_INSTRUCTION(opcode)	((opcode & BIT_MONADIC) != 0)
+
+/* instruction identification masks */
+#define MASK_CPDO		0x0e000000  /* arithmetic opcode */
+#define MASK_ARITHMETIC_OPCODE	0x00f08000
+#define MASK_DESTINATION_SIZE	0x00080080
+
+/* dyadic arithmetic opcodes. */
+#define ADF_CODE	0x00000000
+#define MUF_CODE	0x00100000
+#define SUF_CODE	0x00200000
+#define RSF_CODE	0x00300000
+#define DVF_CODE	0x00400000
+#define RDF_CODE	0x00500000
+#define POW_CODE	0x00600000
+#define RPW_CODE	0x00700000
+#define RMF_CODE	0x00800000
+#define FML_CODE	0x00900000
+#define FDV_CODE	0x00a00000
+#define FRD_CODE	0x00b00000
+#define POL_CODE	0x00c00000
+/* 0x00d00000 is an invalid dyadic arithmetic opcode */
+/* 0x00e00000 is an invalid dyadic arithmetic opcode */
+/* 0x00f00000 is an invalid dyadic arithmetic opcode */
+
+/* monadic arithmetic opcodes. */
+#define MVF_CODE	0x00008000
+#define MNF_CODE	0x00108000
+#define ABS_CODE	0x00208000
+#define RND_CODE	0x00308000
+#define SQT_CODE	0x00408000
+#define LOG_CODE	0x00508000
+#define LGN_CODE	0x00608000
+#define EXP_CODE	0x00708000
+#define SIN_CODE	0x00808000
+#define COS_CODE	0x00908000
+#define TAN_CODE	0x00a08000
+#define ASN_CODE	0x00b08000
+#define ACS_CODE	0x00c08000
+#define ATN_CODE	0x00d08000
+#define URD_CODE	0x00e08000
+#define NRM_CODE	0x00f08000
+
+/*
+===
+=== Definitions for register transfer and comparison instructions
+===
+*/
+
+#define MASK_CPRT		0x0e000010  /* register transfer opcode */
+#define MASK_CPRT_CODE		0x00f00000
+#define FLT_CODE		0x00000000
+#define FIX_CODE		0x00100000
+#define WFS_CODE		0x00200000
+#define RFS_CODE		0x00300000
+#define WFC_CODE		0x00400000
+#define RFC_CODE		0x00500000
+#define CMF_CODE		0x00900000
+#define CNF_CODE		0x00b00000
+#define CMFE_CODE		0x00d00000
+#define CNFE_CODE		0x00f00000
+
+/*
+===
+=== Common definitions
+===
+*/
+
+/* register masks */
+#define MASK_Rd		0x0000f000
+#define MASK_Rn		0x000f0000
+#define MASK_Fd		0x00007000
+#define MASK_Fm		0x00000007
+#define MASK_Fn		0x00070000
+
+/* condition code masks */
+#define CC_MASK		0xf0000000
+#define CC_NEGATIVE	0x80000000
+#define CC_ZERO		0x40000000
+#define CC_CARRY	0x20000000
+#define CC_OVERFLOW	0x10000000
+#define CC_EQ		0x00000000
+#define CC_NE		0x10000000
+#define CC_CS		0x20000000
+#define CC_HS		CC_CS
+#define CC_CC		0x30000000
+#define CC_LO		CC_CC
+#define CC_MI		0x40000000
+#define CC_PL		0x50000000
+#define CC_VS		0x60000000
+#define CC_VC		0x70000000
+#define CC_HI		0x80000000
+#define CC_LS		0x90000000
+#define CC_GE		0xa0000000
+#define CC_LT		0xb0000000
+#define CC_GT		0xc0000000
+#define CC_LE		0xd0000000
+#define CC_AL		0xe0000000
+#define CC_NV		0xf0000000
+
+/* rounding masks/values */
+#define MASK_ROUNDING_MODE	0x00000060
+#define ROUND_TO_NEAREST	0x00000000
+#define ROUND_TO_PLUS_INFINITY	0x00000020
+#define ROUND_TO_MINUS_INFINITY	0x00000040
+#define ROUND_TO_ZERO		0x00000060
+
+#define MASK_ROUNDING_PRECISION	0x00080080
+#define ROUND_SINGLE		0x00000000
+#define ROUND_DOUBLE		0x00000080
+#define ROUND_EXTENDED		0x00080000
+
+/* Get the condition code from the opcode. */
+#define getCondition(opcode)		(opcode >> 28)
+
+/* Get the source register from the opcode. */
+#define getRn(opcode)			((opcode & MASK_Rn) >> 16)
+
+/* Get the destination floating point register from the opcode. */
+#define getFd(opcode)			((opcode & MASK_Fd) >> 12)
+
+/* Get the first source floating point register from the opcode. */
+#define getFn(opcode)		((opcode & MASK_Fn) >> 16)
+
+/* Get the second source floating point register from the opcode. */
+#define getFm(opcode)		(opcode & MASK_Fm)
+
+/* Get the destination register from the opcode. */
+#define getRd(opcode)		((opcode & MASK_Rd) >> 12)
+
+/* Get the rounding mode from the opcode. */
+#define getRoundingMode(opcode)		((opcode & MASK_ROUNDING_MODE) >> 5)
+
+float32 getSingleConstant(const unsigned int nIndex);
+float64 getDoubleConstant(const unsigned int nIndex);
+floatx80 getExtendedConstant(const unsigned int nIndex);
+
+unsigned int getRegisterCount(const unsigned int opcode);
+unsigned int getDestinationSize(const unsigned int opcode);
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/fpsr.h linux/arch/arm/nwfpe/fpsr.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/fpsr.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/fpsr.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,108 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#ifndef __FPSR_H__
+#define __FPSR_H__
+
+/*
+The FPSR is a 32 bit register consisting of 4 parts, each exactly
+one byte.
+
+	SYSTEM ID
+	EXCEPTION TRAP ENABLE BYTE
+	SYSTEM CONTROL BYTE
+	CUMULATIVE EXCEPTION FLAGS BYTE
+	
+The FPCR is a 32 bit register consisting of bit flags.
+*/
+
+/* SYSTEM ID
+------------
+Note: the system id byte is read only  */
+
+typedef unsigned int FPSR;  /* type for floating point status register */
+typedef unsigned int FPCR;  /* type for floating point control register */
+
+#define MASK_SYSID		0xff000000
+#define BIT_HARDWARE		0x80000000
+#define FP_EMULATOR		0x01000000	/* System ID for emulator */ 
+#define FP_ACCELERATOR		0x81000000	/* System ID for FPA11 */
+
+/* EXCEPTION TRAP ENABLE BYTE
+----------------------------- */
+
+#define MASK_TRAP_ENABLE	0x00ff0000
+#define MASK_TRAP_ENABLE_STRICT	0x001f0000
+#define BIT_IXE		0x00100000   /* inexact exception enable */
+#define BIT_UFE		0x00080000   /* underflow exception enable */
+#define BIT_OFE		0x00040000   /* overflow exception enable */
+#define BIT_DZE		0x00020000   /* divide by zero exception enable */
+#define BIT_IOE		0x00010000   /* invalid operation exception enable */
+
+/* SYSTEM CONTROL BYTE
+---------------------- */
+
+#define MASK_SYSTEM_CONTROL	0x0000ff00
+#define MASK_TRAP_STRICT	0x00001f00
+
+#define BIT_AC	0x00001000	/* use alternative C-flag definition
+				   for compares */
+#define BIT_EP	0x00000800	/* use expanded packed decimal format */
+#define BIT_SO	0x00000400	/* select synchronous operation of FPA */
+#define BIT_NE	0x00000200	/* NaN exception bit */
+#define BIT_ND	0x00000100	/* no denormalized numbers bit */
+
+/* CUMULATIVE EXCEPTION FLAGS BYTE
+---------------------------------- */
+
+#define MASK_EXCEPTION_FLAGS		0x000000ff
+#define MASK_EXCEPTION_FLAGS_STRICT	0x0000001f
+
+#define BIT_IXC		0x00000010	/* inexact exception flag */
+#define BIT_UFC		0x00000008	/* underflow exception flag */
+#define BIT_OFC		0x00000004	/* overfloat exception flag */
+#define BIT_DZC		0x00000002	/* divide by zero exception flag */
+#define BIT_IOC		0x00000001	/* invalid operation exception flag */
+
+/* Floating Point Control Register
+----------------------------------*/
+
+#define BIT_RU		0x80000000	/* rounded up bit */
+#define BIT_IE		0x10000000	/* inexact bit */
+#define BIT_MO		0x08000000	/* mantissa overflow bit */
+#define BIT_EO		0x04000000	/* exponent overflow bit */
+#define BIT_SB		0x00000800	/* store bounce */
+#define BIT_AB		0x00000400	/* arithmetic bounce */
+#define BIT_RE		0x00000200	/* rounding exception */
+#define BIT_DA		0x00000100	/* disable FPA */
+
+#define MASK_OP		0x00f08010	/* AU operation code */
+#define MASK_PR		0x00080080	/* AU precision */
+#define MASK_S1		0x00070000	/* AU source register 1 */
+#define MASK_S2		0x00000007	/* AU source register 2 */
+#define MASK_DS		0x00007000	/* AU destination register */
+#define MASK_RM		0x00000060	/* AU rounding mode */
+#define MASK_ALU	0x9cfff2ff	/* only ALU can write these bits */
+#define MASK_RESET	0x00000d00	/* bits set on reset, all others cleared */
+#define MASK_WFC	MASK_RESET
+#define MASK_RFC	~MASK_RESET
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/milieu.h linux/arch/arm/nwfpe/milieu.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/milieu.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/milieu.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,48 @@
+
+/*
+===============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Include common integer types and flags.
+-------------------------------------------------------------------------------
+*/
+#include "ARM-gcc.h"
+
+/*
+-------------------------------------------------------------------------------
+Symbolic Boolean literals.
+-------------------------------------------------------------------------------
+*/
+enum {
+    FALSE = 0,
+    TRUE  = 1
+};
+
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/single_cpdo.c linux/arch/arm/nwfpe/single_cpdo.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/single_cpdo.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/single_cpdo.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,259 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the 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.
+*/
+
+#include "config.h"
+#include "milieu.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+float32 getSingleConstant(unsigned int);
+
+float32 float32_exp(float32 Fm);
+float32 float32_ln(float32 Fm);
+float32 float32_sin(float32 rFm);
+float32 float32_cos(float32 rFm);
+float32 float32_arcsin(float32 rFm);
+float32 float32_arctan(float32 rFm);
+float32 float32_log(float32 rFm);
+float32 float32_tan(float32 rFm);
+float32 float32_arccos(float32 rFm);
+float32 float32_pow(float32 rFn,float32 rFm);
+float32 float32_pol(float32 rFn,float32 rFm);
+
+unsigned int SingleCPDO(const unsigned int opcode)
+{
+   float32 rFm, rFn;
+   unsigned int Fd, Fm, Fn, nRc = 1;
+
+   Fm = getFm(opcode);
+   if (CONSTANT_FM(opcode))
+   {
+     rFm = getSingleConstant(Fm);
+   }
+   else
+   {  
+     switch (fpa11->fpreg[Fm].fType)
+     {
+        case typeSingle:
+          rFm = fpa11->fpreg[Fm].fValue.fSingle;
+        break;
+        
+        default: return 0;
+     }
+   }
+
+   if (!MONADIC_INSTRUCTION(opcode))
+   {
+      Fn = getFn(opcode);
+      switch (fpa11->fpreg[Fn].fType)
+      {
+        case typeSingle:
+          rFn = fpa11->fpreg[Fn].fValue.fSingle;
+        break;
+
+        default: return 0;
+      }
+   }
+
+   Fd = getFd(opcode);
+   switch (opcode & MASK_ARITHMETIC_OPCODE)
+   {
+      /* dyadic opcodes */
+      case ADF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_add(rFn,rFm);
+      break;
+
+      case MUF_CODE:
+      case FML_CODE:
+        fpa11->fpreg[Fd].fValue.fSingle = float32_mul(rFn,rFm);
+      break;
+
+      case SUF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFn,rFm);
+      break;
+
+      case RSF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFm,rFn);
+      break;
+
+      case DVF_CODE:
+      case FDV_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFn,rFm);
+      break;
+
+      case RDF_CODE:
+      case FRD_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFm,rFn);
+      break;
+
+#if 0
+      case POW_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFn,rFm);
+      break;
+
+      case RPW_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFm,rFn);
+      break;
+#endif
+
+      case RMF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_rem(rFn,rFm);
+      break;
+
+#if 0
+      case POL_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_pol(rFn,rFm);
+      break;
+#endif
+
+      /* monadic opcodes */
+      case MVF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = rFm;
+      break;
+
+      case MNF_CODE:
+         rFm ^= 0x80000000;
+         fpa11->fpreg[Fd].fValue.fSingle = rFm;
+      break;
+
+      case ABS_CODE:
+         rFm &= 0x7fffffff;
+         fpa11->fpreg[Fd].fValue.fSingle = rFm;
+      break;
+
+      case RND_CODE:
+      case URD_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = 
+             int32_to_float32(float32_to_int32(rFm));
+      break;
+
+      case SQT_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sqrt(rFm);
+      break;
+
+#if 0
+      case LOG_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_log(rFm);
+      break;
+
+      case LGN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_ln(rFm);
+      break;
+
+      case EXP_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_exp(rFm);
+      break;
+
+      case SIN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sin(rFm);
+      break;
+
+      case COS_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_cos(rFm);
+      break;
+
+      case TAN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_tan(rFm);
+      break;
+
+      case ASN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_arcsin(rFm);
+      break;
+
+      case ACS_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_arccos(rFm);
+      break;
+
+      case ATN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_arctan(rFm);
+      break;
+#endif
+
+      case NRM_CODE:
+      break;
+      
+      default:
+      {
+        nRc = 0;
+      }
+   }
+
+   if (0 != nRc) fpa11->fpreg[Fd].fType = typeSingle;
+   return nRc;
+}
+
+#if 0
+float32 float32_exp(float32 Fm)
+{
+//series
+}
+
+float32 float32_ln(float32 Fm)
+{
+//series
+}
+
+float32 float32_sin(float32 rFm)
+{
+//series
+}
+
+float32 float32_cos(float32 rFm)
+{
+//series
+}
+
+float32 float32_arcsin(float32 rFm)
+{
+//series
+}
+
+float32 float32_arctan(float32 rFm)
+{
+  //series
+}
+
+float32 float32_arccos(float32 rFm)
+{
+   //return float32_sub(halfPi,float32_arcsin(rFm));
+}
+
+float32 float32_log(float32 rFm)
+{
+  return float32_div(float32_ln(rFm),getSingleConstant(7));
+}
+
+float32 float32_tan(float32 rFm)
+{
+  return float32_div(float32_sin(rFm),float32_cos(rFm));
+}
+
+float32 float32_pow(float32 rFn,float32 rFm)
+{
+  return float32_exp(float32_mul(rFm,float32_ln(rFn))); 
+}
+
+float32 float32_pol(float32 rFn,float32 rFm)
+{
+  return float32_arctan(float32_div(rFn,rFm)); 
+}
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/softfloat-macros linux/arch/arm/nwfpe/softfloat-macros
--- linux-2.2.18-pre8/arch/arm/nwfpe/softfloat-macros	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat-macros	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,740 @@
+
+/*
+===============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Shifts `a' right by the number of bits given in `count'.  If any nonzero
+bits are shifted off, they are ``jammed'' into the least significant bit of
+the result by setting the least significant bit to 1.  The value of `count'
+can be arbitrarily large; in particular, if `count' is greater than 32, the
+result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+The result is stored in the location pointed to by `zPtr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
+{
+    bits32 z;
+    if ( count == 0 ) {
+        z = a;
+    }
+    else if ( count < 32 ) {
+        z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
+    }
+    else {
+        z = ( a != 0 );
+    }
+    *zPtr = z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts `a' right by the number of bits given in `count'.  If any nonzero
+bits are shifted off, they are ``jammed'' into the least significant bit of
+the result by setting the least significant bit to 1.  The value of `count'
+can be arbitrarily large; in particular, if `count' is greater than 64, the
+result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+The result is stored in the location pointed to by `zPtr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
+{
+    bits64 z;
+
+ __asm__("@shift64RightJamming -- start");   
+    if ( count == 0 ) {
+        z = a;
+    }
+    else if ( count < 64 ) {
+        z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
+    }
+    else {
+        z = ( a != 0 );
+    }
+ __asm__("@shift64RightJamming -- end");   
+    *zPtr = z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
+_plus_ the number of bits given in `count'.  The shifted result is at most
+64 nonzero bits; this is stored at the location pointed to by `z0Ptr'.  The
+bits shifted off form a second 64-bit result as follows:  The _last_ bit
+shifted off is the most-significant bit of the extra result, and the other
+63 bits of the extra result are all zero if and only if _all_but_the_last_
+bits shifted off were all zero.  This extra result is stored in the location
+pointed to by `z1Ptr'.  The value of `count' can be arbitrarily large.
+    (This routine makes more sense if `a0' and `a1' are considered to form a
+fixed-point value with binary point between `a0' and `a1'.  This fixed-point
+value is shifted right by the number of bits given in `count', and the
+integer part of the result is returned at the location pointed to by
+`z0Ptr'.  The fractional part of the result may be slightly corrupted as
+described above, and is returned at the location pointed to by `z1Ptr'.)
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift64ExtraRightJamming(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1 != 0 );
+        z0 = a0>>count;
+    }
+    else {
+        if ( count == 64 ) {
+            z1 = a0 | ( a1 != 0 );
+        }
+        else {
+            z1 = ( ( a0 | a1 ) != 0 );
+        }
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+number of bits given in `count'.  Any bits shifted off are lost.  The value
+of `count' can be arbitrarily large; in particular, if `count' is greater
+than 128, the result will be 0.  The result is broken into two 64-bit pieces
+which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128Right(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1>>count );
+        z0 = a0>>count;
+    }
+    else {
+        z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+number of bits given in `count'.  If any nonzero bits are shifted off, they
+are ``jammed'' into the least significant bit of the result by setting the
+least significant bit to 1.  The value of `count' can be arbitrarily large;
+in particular, if `count' is greater than 128, the result will be either 0
+or 1, depending on whether the concatenation of `a0' and `a1' is zero or
+nonzero.  The result is broken into two 64-bit pieces which are stored at
+the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128RightJamming(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
+        z0 = a0>>count;
+    }
+    else {
+        if ( count == 64 ) {
+            z1 = a0 | ( a1 != 0 );
+        }
+        else if ( count < 128 ) {
+            z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
+        }
+        else {
+            z1 = ( ( a0 | a1 ) != 0 );
+        }
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
+by 64 _plus_ the number of bits given in `count'.  The shifted result is
+at most 128 nonzero bits; these are broken into two 64-bit pieces which are
+stored at the locations pointed to by `z0Ptr' and `z1Ptr'.  The bits shifted
+off form a third 64-bit result as follows:  The _last_ bit shifted off is
+the most-significant bit of the extra result, and the other 63 bits of the
+extra result are all zero if and only if _all_but_the_last_ bits shifted off
+were all zero.  This extra result is stored in the location pointed to by
+`z2Ptr'.  The value of `count' can be arbitrarily large.
+    (This routine makes more sense if `a0', `a1', and `a2' are considered
+to form a fixed-point value with binary point between `a1' and `a2'.  This
+fixed-point value is shifted right by the number of bits given in `count',
+and the integer part of the result is returned at the locations pointed to
+by `z0Ptr' and `z1Ptr'.  The fractional part of the result may be slightly
+corrupted as described above, and is returned at the location pointed to by
+`z2Ptr'.)
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128ExtraRightJamming(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     int16 count,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z2 = a2;
+        z1 = a1;
+        z0 = a0;
+    }
+    else {
+        if ( count < 64 ) {
+            z2 = a1<<negCount;
+            z1 = ( a0<<negCount ) | ( a1>>count );
+            z0 = a0>>count;
+        }
+        else {
+            if ( count == 64 ) {
+                z2 = a1;
+                z1 = a0;
+            }
+            else {
+                a2 |= a1;
+                if ( count < 128 ) {
+                    z2 = a0<<negCount;
+                    z1 = a0>>( count & 63 );
+                }
+                else {
+                    z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
+                    z1 = 0;
+                }
+            }
+            z0 = 0;
+        }
+        z2 |= ( a2 != 0 );
+    }
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
+number of bits given in `count'.  Any bits shifted off are lost.  The value
+of `count' must be less than 64.  The result is broken into two 64-bit
+pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shortShift128Left(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+    *z1Ptr = a1<<count;
+    *z0Ptr =
+        ( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
+by the number of bits given in `count'.  Any bits shifted off are lost.
+The value of `count' must be less than 64.  The result is broken into three
+64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+`z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shortShift192Left(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     int16 count,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 negCount;
+
+    z2 = a2<<count;
+    z1 = a1<<count;
+    z0 = a0<<count;
+    if ( 0 < count ) {
+        negCount = ( ( - count ) & 63 );
+        z1 |= a2>>negCount;
+        z0 |= a1>>negCount;
+    }
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
+value formed by concatenating `b0' and `b1'.  Addition is modulo 2^128, so
+any carry out is lost.  The result is broken into two 64-bit pieces which
+are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ add128(
+     bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z1;
+
+    z1 = a1 + b1;
+    *z1Ptr = z1;
+    *z0Ptr = a0 + b0 + ( z1 < a1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
+192-bit value formed by concatenating `b0', `b1', and `b2'.  Addition is
+modulo 2^192, so any carry out is lost.  The result is broken into three
+64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+`z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ add192(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     bits64 b0,
+     bits64 b1,
+     bits64 b2,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 carry0, carry1;
+
+    z2 = a2 + b2;
+    carry1 = ( z2 < a2 );
+    z1 = a1 + b1;
+    carry0 = ( z1 < a1 );
+    z0 = a0 + b0;
+    z1 += carry1;
+    z0 += ( z1 < carry1 );
+    z0 += carry0;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
+128-bit value formed by concatenating `a0' and `a1'.  Subtraction is modulo
+2^128, so any borrow out (carry out) is lost.  The result is broken into two
+64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
+`z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ sub128(
+     bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+    *z1Ptr = a1 - b1;
+    *z0Ptr = a0 - b0 - ( a1 < b1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
+from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
+Subtraction is modulo 2^192, so any borrow out (carry out) is lost.  The
+result is broken into three 64-bit pieces which are stored at the locations
+pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ sub192(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     bits64 b0,
+     bits64 b1,
+     bits64 b2,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 borrow0, borrow1;
+
+    z2 = a2 - b2;
+    borrow1 = ( a2 < b2 );
+    z1 = a1 - b1;
+    borrow0 = ( a1 < b1 );
+    z0 = a0 - b0;
+    z0 -= ( z1 < borrow1 );
+    z1 -= borrow1;
+    z0 -= borrow0;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies `a' by `b' to obtain a 128-bit product.  The product is broken
+into two 64-bit pieces which are stored at the locations pointed to by
+`z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits32 aHigh, aLow, bHigh, bLow;
+    bits64 z0, zMiddleA, zMiddleB, z1;
+
+    aLow = a;
+    aHigh = a>>32;
+    bLow = b;
+    bHigh = b>>32;
+    z1 = ( (bits64) aLow ) * bLow;
+    zMiddleA = ( (bits64) aLow ) * bHigh;
+    zMiddleB = ( (bits64) aHigh ) * bLow;
+    z0 = ( (bits64) aHigh ) * bHigh;
+    zMiddleA += zMiddleB;
+    z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
+    zMiddleA <<= 32;
+    z1 += zMiddleA;
+    z0 += ( z1 < zMiddleA );
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies the 128-bit value formed by concatenating `a0' and `a1' by `b' to
+obtain a 192-bit product.  The product is broken into three 64-bit pieces
+which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
+`z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ mul128By64To192(
+     bits64 a0,
+     bits64 a1,
+     bits64 b,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2, more1;
+
+    mul64To128( a1, b, &z1, &z2 );
+    mul64To128( a0, b, &z0, &more1 );
+    add128( z0, more1, 0, z1, &z0, &z1 );
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
+128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
+product.  The product is broken into four 64-bit pieces which are stored at
+the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ mul128To256(
+     bits64 a0,
+     bits64 a1,
+     bits64 b0,
+     bits64 b1,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr,
+     bits64 *z3Ptr
+ )
+{
+    bits64 z0, z1, z2, z3;
+    bits64 more1, more2;
+
+    mul64To128( a1, b1, &z2, &z3 );
+    mul64To128( a1, b0, &z1, &more2 );
+    add128( z1, more2, 0, z2, &z1, &z2 );
+    mul64To128( a0, b0, &z0, &more1 );
+    add128( z0, more1, 0, z1, &z0, &z1 );
+    mul64To128( a0, b1, &more1, &more2 );
+    add128( more1, more2, 0, z2, &more1, &z2 );
+    add128( z0, z1, 0, more1, &z0, &z1 );
+    *z3Ptr = z3;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns an approximation to the 64-bit integer quotient obtained by dividing
+`b' into the 128-bit value formed by concatenating `a0' and `a1'.  The
+divisor `b' must be at least 2^63.  If q is the exact quotient truncated
+toward zero, the approximation returned lies between q and q + 2 inclusive.
+If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
+unsigned integer is returned.
+-------------------------------------------------------------------------------
+*/
+static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
+{
+    bits64 b0, b1;
+    bits64 rem0, rem1, term0, term1;
+    bits64 z;
+    if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
+    b0 = b>>32;
+    z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
+    mul64To128( b, z, &term0, &term1 );
+    sub128( a0, a1, term0, term1, &rem0, &rem1 );
+    while ( ( (sbits64) rem0 ) < 0 ) {
+        z -= LIT64( 0x100000000 );
+        b1 = b<<32;
+        add128( rem0, rem1, b0, b1, &rem0, &rem1 );
+    }
+    rem0 = ( rem0<<32 ) | ( rem1>>32 );
+    z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns an approximation to the square root of the 32-bit significand given
+by `a'.  Considered as an integer, `a' must be at least 2^31.  If bit 0 of
+`aExp' (the least significant bit) is 1, the integer returned approximates
+2^31*sqrt(`a'/2^31), where `a' is considered an integer.  If bit 0 of `aExp'
+is 0, the integer returned approximates 2^31*sqrt(`a'/2^30).  In either
+case, the approximation returned lies strictly within +/-2 of the exact
+value.
+-------------------------------------------------------------------------------
+*/
+static bits32 estimateSqrt32( int16 aExp, bits32 a )
+{
+    static const bits16 sqrtOddAdjustments[] = {
+        0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
+        0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
+    };
+    static const bits16 sqrtEvenAdjustments[] = {
+        0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
+        0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
+    };
+    int8 index;
+    bits32 z;
+
+    index = ( a>>27 ) & 15;
+    if ( aExp & 1 ) {
+        z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
+        z = ( ( a / z )<<14 ) + ( z<<15 );
+        a >>= 1;
+    }
+    else {
+        z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
+        z = a / z + z;
+        z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
+        if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
+    }
+    return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the number of leading 0 bits before the most-significant 1 bit
+of `a'.  If `a' is zero, 32 is returned.
+-------------------------------------------------------------------------------
+*/
+static int8 countLeadingZeros32( bits32 a )
+{
+    static const int8 countLeadingZerosHigh[] = {
+        8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    int8 shiftCount;
+
+    shiftCount = 0;
+    if ( a < 0x10000 ) {
+        shiftCount += 16;
+        a <<= 16;
+    }
+    if ( a < 0x1000000 ) {
+        shiftCount += 8;
+        a <<= 8;
+    }
+    shiftCount += countLeadingZerosHigh[ a>>24 ];
+    return shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the number of leading 0 bits before the most-significant 1 bit
+of `a'.  If `a' is zero, 64 is returned.
+-------------------------------------------------------------------------------
+*/
+static int8 countLeadingZeros64( bits64 a )
+{
+    int8 shiftCount;
+
+    shiftCount = 0;
+    if ( a < ( (bits64) 1 )<<32 ) {
+        shiftCount += 32;
+    }
+    else {
+        a >>= 32;
+    }
+    shiftCount += countLeadingZeros32( a );
+    return shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
+is equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 == b0 ) && ( a1 == b1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+than the 128-bit value formed by concatenating `b0' and `b1'.  Otherwise,
+returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
+not equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 != b0 ) || ( a1 != b1 );
+
+}
+
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/softfloat-specialize linux/arch/arm/nwfpe/softfloat-specialize
--- linux-2.2.18-pre8/arch/arm/nwfpe/softfloat-specialize	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat-specialize	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,471 @@
+
+/*
+===============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Underflow tininess-detection mode, statically initialized to default value.
+(The declaration in `softfloat.h' must match the `int8' type here.)
+-------------------------------------------------------------------------------
+*/
+int8 float_detect_tininess = float_tininess_after_rounding;
+
+/*
+-------------------------------------------------------------------------------
+Raises the exceptions specified by `flags'.  Floating-point traps can be
+defined here if desired.  It is currently not possible for such a trap to
+substitute a result value.  If traps are not implemented, this routine
+should be simply `float_exception_flags |= flags;'.
+
+ScottB:  November 4, 1998
+Moved this function out of softfloat-specialize into fpmodule.c.
+This effectively isolates all the changes required for integrating with the
+Linux kernel into fpmodule.c.  Porting to NetBSD should only require modifying
+fpmodule.c to integrate with the NetBSD kernel (I hope!).
+-------------------------------------------------------------------------------
+void float_raise( int8 flags )
+{
+    float_exception_flags |= flags;
+}
+*/
+
+/*
+-------------------------------------------------------------------------------
+Internal canonical NaN format.
+-------------------------------------------------------------------------------
+*/
+typedef struct {
+    flag sign;
+    bits64 high, low;
+} commonNaNT;
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated single-precision NaN.
+-------------------------------------------------------------------------------
+*/
+#define float32_default_nan 0xFFFFFFFF
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float32_is_nan( float32 a )
+{
+
+    return ( 0xFF000000 < (bits32) ( a<<1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is a signaling
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float32_is_signaling_nan( float32 a )
+{
+
+    return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point NaN
+`a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float32ToCommonNaN( float32 a )
+{
+    commonNaNT z;
+
+    if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a>>31;
+    z.low = 0;
+    z.high = ( (bits64) a )<<41;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the single-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float32 commonNaNToFloat32( commonNaNT a )
+{
+
+    return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two single-precision floating-point values `a' and `b', one of which
+is a NaN, and returns the appropriate NaN result.  If either `a' or `b' is a
+signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float32 propagateFloat32NaN( float32 a, float32 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = float32_is_nan( a );
+    aIsSignalingNaN = float32_is_signaling_nan( a );
+    bIsNaN = float32_is_nan( b );
+    bIsSignalingNaN = float32_is_signaling_nan( b );
+    a |= 0x00400000;
+    b |= 0x00400000;
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated double-precision NaN.
+-------------------------------------------------------------------------------
+*/
+#define float64_default_nan LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float64_is_nan( float64 a )
+{
+
+    return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is a signaling
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float64_is_signaling_nan( float64 a )
+{
+
+    return
+           ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+        && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point NaN
+`a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float64ToCommonNaN( float64 a )
+{
+    commonNaNT z;
+
+    if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a>>63;
+    z.low = 0;
+    z.high = a<<12;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the double-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float64 commonNaNToFloat64( commonNaNT a )
+{
+
+    return
+          ( ( (bits64) a.sign )<<63 )
+        | LIT64( 0x7FF8000000000000 )
+        | ( a.high>>12 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two double-precision floating-point values `a' and `b', one of which
+is a NaN, and returns the appropriate NaN result.  If either `a' or `b' is a
+signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float64 propagateFloat64NaN( float64 a, float64 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = float64_is_nan( a );
+    aIsSignalingNaN = float64_is_signaling_nan( a );
+    bIsNaN = float64_is_nan( b );
+    bIsSignalingNaN = float64_is_signaling_nan( b );
+    a |= LIT64( 0x0008000000000000 );
+    b |= LIT64( 0x0008000000000000 );
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated extended double-precision NaN.  The
+`high' and `low' values hold the most- and least-significant bits,
+respectively.
+-------------------------------------------------------------------------------
+*/
+#define floatx80_default_nan_high 0xFFFF
+#define floatx80_default_nan_low  LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is a
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_is_nan( floatx80 a )
+{
+
+    return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is a
+signaling NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_is_signaling_nan( floatx80 a )
+{
+    //register int lr;
+    bits64 aLow;
+
+    //__asm__("mov %0, lr" : : "g" (lr));
+    //fp_printk("floatx80_is_signalling_nan() called from 0x%08x\n",lr);
+    aLow = a.low & ~ LIT64( 0x4000000000000000 );
+    return
+           ( ( a.high & 0x7FFF ) == 0x7FFF )
+        && (bits64) ( aLow<<1 )
+        && ( a.low == aLow );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point NaN `a' to the canonical NaN format.  If `a' is a signaling NaN, the
+invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT floatx80ToCommonNaN( floatx80 a )
+{
+    commonNaNT z;
+
+    if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a.high>>15;
+    z.low = 0;
+    z.high = a.low<<1;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the extended
+double-precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static floatx80 commonNaNToFloatx80( commonNaNT a )
+{
+    floatx80 z;
+
+    z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
+    z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two extended double-precision floating-point values `a' and `b', one
+of which is a NaN, and returns the appropriate NaN result.  If either `a' or
+`b' is a signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = floatx80_is_nan( a );
+    aIsSignalingNaN = floatx80_is_signaling_nan( a );
+    bIsNaN = floatx80_is_nan( b );
+    bIsSignalingNaN = floatx80_is_signaling_nan( b );
+    a.low |= LIT64( 0xC000000000000000 );
+    b.low |= LIT64( 0xC000000000000000 );
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated quadruple-precision NaN.  The `high' and
+`low' values hold the most- and least-significant bits, respectively.
+-------------------------------------------------------------------------------
+*/
+#define float128_default_nan_high LIT64( 0xFFFFFFFFFFFFFFFF )
+#define float128_default_nan_low  LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float128_is_nan( float128 a )
+{
+
+    return
+           ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+        && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is a
+signaling NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float128_is_signaling_nan( float128 a )
+{
+
+    return
+           ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+        && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point NaN
+`a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float128ToCommonNaN( float128 a )
+{
+    commonNaNT z;
+
+    if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a.high>>63;
+    shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the quadruple-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float128 commonNaNToFloat128( commonNaNT a )
+{
+    float128 z;
+
+    shift128Right( a.high, a.low, 16, &z.high, &z.low );
+    z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two quadruple-precision floating-point values `a' and `b', one of
+which is a NaN, and returns the appropriate NaN result.  If either `a' or
+`b' is a signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float128 propagateFloat128NaN( float128 a, float128 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = float128_is_nan( a );
+    aIsSignalingNaN = float128_is_signaling_nan( a );
+    bIsNaN = float128_is_nan( b );
+    bIsSignalingNaN = float128_is_signaling_nan( b );
+    a.high |= LIT64( 0x0000800000000000 );
+    b.high |= LIT64( 0x0000800000000000 );
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+#endif
+
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/softfloat.c linux/arch/arm/nwfpe/softfloat.c
--- linux-2.2.18-pre8/arch/arm/nwfpe/softfloat.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat.c	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,4877 @@
+/*
+===============================================================================
+
+This C source file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+#include "milieu.h"
+#include "softfloat.h"
+
+/*
+-------------------------------------------------------------------------------
+Floating-point rounding mode, extended double-precision rounding precision,
+and exception flags.
+-------------------------------------------------------------------------------
+*/
+int8 float_rounding_mode = float_round_nearest_even;
+int8 floatx80_rounding_precision = 80;
+int8 float_exception_flags = 0;
+
+/*
+-------------------------------------------------------------------------------
+Primitive arithmetic functions, including multi-word arithmetic, and
+division and square root approximations.  (Can be specialized to target if
+desired.)
+-------------------------------------------------------------------------------
+*/
+#include "softfloat-macros"
+
+/*
+-------------------------------------------------------------------------------
+Functions and definitions to determine:  (1) whether tininess for underflow
+is detected before or after rounding by default, (2) what (if anything)
+happens when exceptions are raised, (3) how signaling NaNs are distinguished
+from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs
+are propagated from function inputs to output.  These details are target-
+specific.
+-------------------------------------------------------------------------------
+*/
+#include "softfloat-specialize"
+
+/*
+-------------------------------------------------------------------------------
+Takes a 64-bit fixed-point value `absZ' with binary point between bits 6
+and 7, and returns the properly rounded 32-bit integer corresponding to the
+input.  If `zSign' is nonzero, the input is negated before being converted
+to an integer.  Bit 63 of `absZ' must be zero.  Ordinarily, the fixed-point
+input is simply rounded to an integer, with the inexact exception raised if
+the input cannot be represented exactly as an integer.  If the fixed-point
+input is too large, however, the invalid exception is raised and the largest
+positive or negative integer is returned.
+-------------------------------------------------------------------------------
+*/
+static int32 roundAndPackInt32( flag zSign, bits64 absZ )
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int8 roundIncrement, roundBits;
+    int32 z;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x40;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x7F;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = absZ & 0x7F;
+    absZ = ( absZ + roundIncrement )>>7;
+    absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+    z = absZ;
+    if ( zSign ) z = - z;
+    if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) {
+        float_exception_flags |= float_flag_invalid;
+        return zSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits32 extractFloat32Frac( float32 a )
+{
+
+    return a & 0x007FFFFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int16 extractFloat32Exp( float32 a )
+{
+
+    return ( a>>23 ) & 0xFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat32Sign( float32 a )
+{
+
+    return a>>31;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal single-precision floating-point value represented
+by the denormalized significand `aSig'.  The normalized exponent and
+significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros32( aSig ) - 8;
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+single-precision floating-point value, returning the result.  After being
+shifted into the proper positions, the three fields are simply added
+together to form the result.  This means that any integer portion of `zSig'
+will be added into the exponent.  Since a properly normalized significand
+will have an integer portion equal to 1, the `zExp' input should be 1 less
+than the desired result exponent whenever `zSig' is a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+#if 0
+   float32 f;
+   __asm__("@ packFloat32;
+   	    mov %0, %1, asl #31;
+   	    orr %0, %2, asl #23;
+   	    orr %0, %3"
+   	    : /* no outputs */
+   	    : "g" (f), "g" (zSign), "g" (zExp), "g" (zSig)
+   	    : "cc");
+   return f;
+#else
+    return ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig;
+#endif 
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper single-precision floating-
+point value corresponding to the abstract input.  Ordinarily, the abstract
+value is simply rounded and packed into the single-precision format, with
+the inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal single-
+precision floating-point number.
+    The input significand `zSig' has its binary point between bits 30
+and 29, which is 7 bits to the left of the usual location.  This shifted
+significand must be normalized or smaller.  If `zSig' is not normalized,
+`zExp' must be 0; in that case, the result returned is a subnormal number,
+and it must not require rounding.  In the usual case that `zSig' is
+normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+The handling of underflow and overflow follows the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int8 roundIncrement, roundBits;
+    flag isTiny;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x40;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x7F;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig & 0x7F;
+    if ( 0xFD <= (bits16) zExp ) {
+        if (    ( 0xFD < zExp )
+             || (    ( zExp == 0xFD )
+                  && ( (sbits32) ( zSig + roundIncrement ) < 0 ) )
+           ) {
+            float_raise( float_flag_overflow | float_flag_inexact );
+            return packFloat32( zSign, 0xFF, 0 ) - ( roundIncrement == 0 );
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ( zSig + roundIncrement < 0x80000000 );
+            shift32RightJamming( zSig, - zExp, &zSig );
+            zExp = 0;
+            roundBits = zSig & 0x7F;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+        }
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    zSig = ( zSig + roundIncrement )>>7;
+    zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+    if ( zSig == 0 ) zExp = 0;
+    return packFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper single-precision floating-
+point value corresponding to the abstract input.  This routine is just like
+`roundAndPackFloat32' except that `zSig' does not have to be normalized in
+any way.  In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float32
+ normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros32( zSig ) - 1;
+    return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<<shiftCount );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat64Frac( float64 a )
+{
+
+    return a & LIT64( 0x000FFFFFFFFFFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int16 extractFloat64Exp( float64 a )
+{
+
+    return ( a>>52 ) & 0x7FF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat64Sign( float64 a )
+{
+
+    return a>>63;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal double-precision floating-point value represented
+by the denormalized significand `aSig'.  The normalized exponent and
+significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( aSig ) - 11;
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+double-precision floating-point value, returning the result.  After being
+shifted into the proper positions, the three fields are simply added
+together to form the result.  This means that any integer portion of `zSig'
+will be added into the exponent.  Since a properly normalized significand
+will have an integer portion equal to 1, the `zExp' input should be 1 less
+than the desired result exponent whenever `zSig' is a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+
+    return ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper double-precision floating-
+point value corresponding to the abstract input.  Ordinarily, the abstract
+value is simply rounded and packed into the double-precision format, with
+the inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal double-
+precision floating-point number.
+    The input significand `zSig' has its binary point between bits 62
+and 61, which is 10 bits to the left of the usual location.  This shifted
+significand must be normalized or smaller.  If `zSig' is not normalized,
+`zExp' must be 0; in that case, the result returned is a subnormal number,
+and it must not require rounding.  In the usual case that `zSig' is
+normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+The handling of underflow and overflow follows the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int16 roundIncrement, roundBits;
+    flag isTiny;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x200;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x3FF;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig & 0x3FF;
+    if ( 0x7FD <= (bits16) zExp ) {
+        if (    ( 0x7FD < zExp )
+             || (    ( zExp == 0x7FD )
+                  && ( (sbits64) ( zSig + roundIncrement ) < 0 ) )
+           ) {
+            //register int lr;
+            //__asm__("mov %0, lr" :: "g" (lr));
+            //fp_printk("roundAndPackFloat64 called from 0x%08x\n",lr);
+            float_raise( float_flag_overflow | float_flag_inexact );
+            return packFloat64( zSign, 0x7FF, 0 ) - ( roundIncrement == 0 );
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) );
+            shift64RightJamming( zSig, - zExp, &zSig );
+            zExp = 0;
+            roundBits = zSig & 0x3FF;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+        }
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    zSig = ( zSig + roundIncrement )>>10;
+    zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven );
+    if ( zSig == 0 ) zExp = 0;
+    return packFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper double-precision floating-
+point value corresponding to the abstract input.  This routine is just like
+`roundAndPackFloat64' except that `zSig' does not have to be normalized in
+any way.  In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float64
+ normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( zSig ) - 1;
+    return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the extended double-precision floating-point
+value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloatx80Frac( floatx80 a )
+{
+
+    return a.low;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the extended double-precision floating-point
+value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int32 extractFloatx80Exp( floatx80 a )
+{
+
+    return a.high & 0x7FFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the extended double-precision floating-point value
+`a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloatx80Sign( floatx80 a )
+{
+
+    return a.high>>15;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal extended double-precision floating-point value
+represented by the denormalized significand `aSig'.  The normalized exponent
+and significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( aSig );
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into an
+extended double-precision floating-point value, returning the result.
+-------------------------------------------------------------------------------
+*/
+INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig )
+{
+    floatx80 z;
+
+    z.low = zSig;
+    z.high = ( ( (bits16) zSign )<<15 ) + zExp;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and extended significand formed by the concatenation of `zSig0' and `zSig1',
+and returns the proper extended double-precision floating-point value
+corresponding to the abstract input.  Ordinarily, the abstract value is
+rounded and packed into the extended double-precision format, with the
+inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal extended
+double-precision floating-point number.
+    If `roundingPrecision' is 32 or 64, the result is rounded to the same
+number of bits as single or double precision, respectively.  Otherwise, the
+result is rounded to the full precision of the extended double-precision
+format.
+    The input significand must be normalized or smaller.  If the input
+significand is not normalized, `zExp' must be 0; in that case, the result
+returned is a subnormal number, and it must not require rounding.  The
+handling of underflow and overflow follows the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80
+ roundAndPackFloatx80(
+     int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ )
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment, isTiny;
+    int64 roundIncrement, roundMask, roundBits;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    if ( roundingPrecision == 80 ) goto precision80;
+    if ( roundingPrecision == 64 ) {
+        roundIncrement = LIT64( 0x0000000000000400 );
+        roundMask = LIT64( 0x00000000000007FF );
+    }
+    else if ( roundingPrecision == 32 ) {
+        roundIncrement = LIT64( 0x0000008000000000 );
+        roundMask = LIT64( 0x000000FFFFFFFFFF );
+    }
+    else {
+        goto precision80;
+    }
+    zSig0 |= ( zSig1 != 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = roundMask;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig0 & roundMask;
+    if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+        if (    ( 0x7FFE < zExp )
+             || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) )
+           ) {
+            goto overflow;
+        }
+        if ( zExp <= 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < 0 )
+                || ( zSig0 <= zSig0 + roundIncrement );
+            shift64RightJamming( zSig0, 1 - zExp, &zSig0 );
+            zExp = 0;
+            roundBits = zSig0 & roundMask;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+            if ( roundBits ) float_exception_flags |= float_flag_inexact;
+            zSig0 += roundIncrement;
+            if ( (sbits64) zSig0 < 0 ) zExp = 1;
+            roundIncrement = roundMask + 1;
+            if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+                roundMask |= roundIncrement;
+            }
+            zSig0 &= ~ roundMask;
+            return packFloatx80( zSign, zExp, zSig0 );
+        }
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    zSig0 += roundIncrement;
+    if ( zSig0 < roundIncrement ) {
+        ++zExp;
+        zSig0 = LIT64( 0x8000000000000000 );
+    }
+    roundIncrement = roundMask + 1;
+    if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+        roundMask |= roundIncrement;
+    }
+    zSig0 &= ~ roundMask;
+    if ( zSig0 == 0 ) zExp = 0;
+    return packFloatx80( zSign, zExp, zSig0 );
+ precision80:
+    increment = ( (sbits64) zSig1 < 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            increment = 0;
+        }
+        else {
+            if ( zSign ) {
+                increment = ( roundingMode == float_round_down ) && zSig1;
+            }
+            else {
+                increment = ( roundingMode == float_round_up ) && zSig1;
+            }
+        }
+    }
+    if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+        if (    ( 0x7FFE < zExp )
+             || (    ( zExp == 0x7FFE )
+                  && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) )
+                  && increment
+                )
+           ) {
+            roundMask = 0;
+ overflow:
+            float_raise( float_flag_overflow | float_flag_inexact );
+            if (    ( roundingMode == float_round_to_zero )
+                 || ( zSign && ( roundingMode == float_round_up ) )
+                 || ( ! zSign && ( roundingMode == float_round_down ) )
+               ) {
+                return packFloatx80( zSign, 0x7FFE, ~ roundMask );
+            }
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        if ( zExp <= 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < 0 )
+                || ! increment
+                || ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) );
+            shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 );
+            zExp = 0;
+            if ( isTiny && zSig1 ) float_raise( float_flag_underflow );
+            if ( zSig1 ) float_exception_flags |= float_flag_inexact;
+            if ( roundNearestEven ) {
+                increment = ( (sbits64) zSig1 < 0 );
+            }
+            else {
+                if ( zSign ) {
+                    increment = ( roundingMode == float_round_down ) && zSig1;
+                }
+                else {
+                    increment = ( roundingMode == float_round_up ) && zSig1;
+                }
+            }
+            if ( increment ) {
+                ++zSig0;
+                zSig0 &= ~ ( ( zSig1 + zSig1 == 0 ) & roundNearestEven );
+                if ( (sbits64) zSig0 < 0 ) zExp = 1;
+            }
+            return packFloatx80( zSign, zExp, zSig0 );
+        }
+    }
+    if ( zSig1 ) float_exception_flags |= float_flag_inexact;
+    if ( increment ) {
+        ++zSig0;
+        if ( zSig0 == 0 ) {
+            ++zExp;
+            zSig0 = LIT64( 0x8000000000000000 );
+        }
+        else {
+            zSig0 &= ~ ( ( zSig1 + zSig1 == 0 ) & roundNearestEven );
+        }
+    }
+    else {
+        if ( zSig0 == 0 ) zExp = 0;
+    }
+    
+    return packFloatx80( zSign, zExp, zSig0 );
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent
+`zExp', and significand formed by the concatenation of `zSig0' and `zSig1',
+and returns the proper extended double-precision floating-point value
+corresponding to the abstract input.  This routine is just like
+`roundAndPackFloatx80' except that the input significand does not have to be
+normalized.
+-------------------------------------------------------------------------------
+*/
+static floatx80
+ normalizeRoundAndPackFloatx80(
+     int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ )
+{
+    int8 shiftCount;
+
+    if ( zSig0 == 0 ) {
+        zSig0 = zSig1;
+        zSig1 = 0;
+        zExp -= 64;
+    }
+    shiftCount = countLeadingZeros64( zSig0 );
+    shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+    zExp -= shiftCount;
+    return
+        roundAndPackFloatx80( roundingPrecision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the least-significant 64 fraction bits of the quadruple-precision
+floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat128Frac1( float128 a )
+{
+
+    return a.low;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the most-significant 48 fraction bits of the quadruple-precision
+floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat128Frac0( float128 a )
+{
+
+    return a.high & LIT64( 0x0000FFFFFFFFFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the quadruple-precision floating-point value
+`a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int32 extractFloat128Exp( float128 a )
+{
+
+    return ( a.high>>48 ) & 0x7FFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the quadruple-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat128Sign( float128 a )
+{
+
+    return a.high>>63;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal quadruple-precision floating-point value
+represented by the denormalized significand formed by the concatenation of
+`aSig0' and `aSig1'.  The normalized exponent is stored at the location
+pointed to by `zExpPtr'.  The most significant 49 bits of the normalized
+significand are stored at the location pointed to by `zSig0Ptr', and the
+least significant 64 bits of the normalized significand are stored at the
+location pointed to by `zSig1Ptr'.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat128Subnormal(
+     bits64 aSig0,
+     bits64 aSig1,
+     int32 *zExpPtr,
+     bits64 *zSig0Ptr,
+     bits64 *zSig1Ptr
+ )
+{
+    int8 shiftCount;
+
+    if ( aSig0 == 0 ) {
+        shiftCount = countLeadingZeros64( aSig1 ) - 15;
+        if ( shiftCount < 0 ) {
+            *zSig0Ptr = aSig1>>( - shiftCount );
+            *zSig1Ptr = aSig1<<( shiftCount & 63 );
+        }
+        else {
+            *zSig0Ptr = aSig1<<shiftCount;
+            *zSig1Ptr = 0;
+        }
+        *zExpPtr = - shiftCount - 63;
+    }
+    else {
+        shiftCount = countLeadingZeros64( aSig0 ) - 15;
+        shortShift128Left( aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr );
+        *zExpPtr = 1 - shiftCount;
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', the exponent `zExp', and the significand formed
+by the concatenation of `zSig0' and `zSig1' into a quadruple-precision
+floating-point value, returning the result.  After being shifted into the
+proper positions, the three fields `zSign', `zExp', and `zSig0' are simply
+added together to form the most significant 32 bits of the result.  This
+means that any integer portion of `zSig0' will be added into the exponent.
+Since a properly normalized significand will have an integer portion equal
+to 1, the `zExp' input should be 1 less than the desired result exponent
+whenever `zSig0' and `zSig1' concatenated form a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float128
+ packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+    float128 z;
+
+    z.low = zSig1;
+    z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and extended significand formed by the concatenation of `zSig0', `zSig1',
+and `zSig2', and returns the proper quadruple-precision floating-point value
+corresponding to the abstract input.  Ordinarily, the abstract value is
+simply rounded and packed into the quadruple-precision format, with the
+inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal quadruple-
+precision floating-point number.
+    The input significand must be normalized or smaller.  If the input
+significand is not normalized, `zExp' must be 0; in that case, the result
+returned is a subnormal number, and it must not require rounding.  In the
+usual case that the input significand is normalized, `zExp' must be 1 less
+than the ``true'' floating-point exponent.  The handling of underflow and
+overflow follows the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128
+ roundAndPackFloat128(
+     flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 )
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment, isTiny;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    increment = ( (sbits64) zSig2 < 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            increment = 0;
+        }
+        else {
+            if ( zSign ) {
+                increment = ( roundingMode == float_round_down ) && zSig2;
+            }
+            else {
+                increment = ( roundingMode == float_round_up ) && zSig2;
+            }
+        }
+    }
+    if ( 0x7FFD <= (bits32) zExp ) {
+        if (    ( 0x7FFD < zExp )
+             || (    ( zExp == 0x7FFD )
+                  && eq128(
+                         LIT64( 0x0001FFFFFFFFFFFF ),
+                         LIT64( 0xFFFFFFFFFFFFFFFF ),
+                         zSig0,
+                         zSig1
+                     )
+                  && increment
+                )
+           ) {
+            float_raise( float_flag_overflow | float_flag_inexact );
+            if (    ( roundingMode == float_round_to_zero )
+                 || ( zSign && ( roundingMode == float_round_up ) )
+                 || ( ! zSign && ( roundingMode == float_round_down ) )
+               ) {
+                return
+                    packFloat128(
+                        zSign,
+                        0x7FFE,
+                        LIT64( 0x0000FFFFFFFFFFFF ),
+                        LIT64( 0xFFFFFFFFFFFFFFFF )
+                    );
+            }
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ! increment
+                || lt128(
+                       zSig0,
+                       zSig1,
+                       LIT64( 0x0001FFFFFFFFFFFF ),
+                       LIT64( 0xFFFFFFFFFFFFFFFF )
+                   );
+            shift128ExtraRightJamming(
+                zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 );
+            zExp = 0;
+            if ( isTiny && zSig2 ) float_raise( float_flag_underflow );
+            if ( roundNearestEven ) {
+                increment = ( (sbits64) zSig2 < 0 );
+            }
+            else {
+                if ( zSign ) {
+                    increment = ( roundingMode == float_round_down ) && zSig2;
+                }
+                else {
+                    increment = ( roundingMode == float_round_up ) && zSig2;
+                }
+            }
+        }
+    }
+    if ( zSig2 ) float_exception_flags |= float_flag_inexact;
+    if ( increment ) {
+        add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 );
+        zSig1 &= ~ ( ( zSig2 + zSig2 == 0 ) & roundNearestEven );
+    }
+    else {
+        if ( ( zSig0 | zSig1 ) == 0 ) zExp = 0;
+    }
+    return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand formed by the concatenation of `zSig0' and `zSig1', and
+returns the proper quadruple-precision floating-point value corresponding to
+the abstract input.  This routine is just like `roundAndPackFloat128' except
+that the input significand has fewer bits and does not have to be normalized
+in any way.  In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float128
+ normalizeRoundAndPackFloat128(
+     flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+    int8 shiftCount;
+    bits64 zSig2;
+
+    if ( zSig0 == 0 ) {
+        zSig0 = zSig1;
+        zSig1 = 0;
+        zExp -= 64;
+    }
+    shiftCount = countLeadingZeros64( zSig0 ) - 15;
+    if ( 0 <= shiftCount ) {
+        zSig2 = 0;
+        shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+    }
+    else {
+        shift128ExtraRightJamming(
+            zSig0, zSig1, 0, - shiftCount, &zSig0, &zSig1, &zSig2 );
+    }
+    zExp -= shiftCount;
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the single-precision floating-point format.  The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 int32_to_float32( int32 a )
+{
+    flag zSign;
+
+    if ( a == 0 ) return 0;
+    if ( a == 0x80000000 ) return packFloat32( 1, 0x9E, 0 );
+    zSign = ( a < 0 );
+    return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the double-precision floating-point format.  The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 int32_to_float64( int32 a )
+{
+    flag aSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig;
+
+    if ( a == 0 ) return 0;
+    aSign = ( a < 0 );
+    absA = aSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 21;
+    zSig = absA;
+    return packFloat64( aSign, 0x432 - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a'
+to the extended double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 int32_to_floatx80( int32 a )
+{
+    flag zSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig;
+
+    if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 32;
+    zSig = absA;
+    return packFloatx80( zSign, 0x403E - shiftCount, zSig<<shiftCount );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the quadruple-precision floating-point format.  The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 int32_to_float128( int32 a )
+{
+    flag zSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig0;
+
+    if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 17;
+    zSig0 = absA;
+    return packFloat128( zSign, 0x402E - shiftCount, zSig0<<shiftCount, 0 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float32_to_int32( float32 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    bits64 zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= 0x00800000;
+    shiftCount = 0xAF - aExp;
+    zSig = aSig;
+    zSig <<= 32;
+    if ( 0 < shiftCount ) shift64RightJamming( zSig, shiftCount, &zSig );
+    return roundAndPackInt32( aSign, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float32_to_int32_round_to_zero( float32 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    int32 z;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    shiftCount = aExp - 0x9E;
+    if ( 0 <= shiftCount ) {
+        if ( a == 0xCF000000 ) return 0x80000000;
+        float_raise( float_flag_invalid );
+        if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF;
+        return 0x80000000;
+    }
+    else if ( aExp <= 0x7E ) {
+        if ( aExp | aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig = ( aSig | 0x00800000 )<<8;
+    z = aSig>>( - shiftCount );
+    if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return aSign ? - z : z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the double-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float32_to_float64( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a ) );
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( aSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the extended double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float32_to_floatx80( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a ) );
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    aSig |= 0x00800000;
+    return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the double-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float32_to_float128( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a ) );
+        return packFloat128( aSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the single-precision floating-point value `a' to an integer, and
+returns the result as a single-precision floating-point value.  The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_round_to_int( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    float32 z;
+
+    aExp = extractFloat32Exp( a );
+    if ( 0x96 <= aExp ) {
+        if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) {
+            return propagateFloat32NaN( a, a );
+        }
+        return a;
+    }
+    if ( aExp <= 0x7E ) {
+        if ( (bits32) ( a<<1 ) == 0 ) return a;
+        float_exception_flags |= float_flag_inexact;
+        aSign = extractFloat32Sign( a );
+        switch ( float_rounding_mode ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) {
+                return packFloat32( aSign, 0x7F, 0 );
+            }
+            break;
+         case float_round_down:
+            return aSign ? 0xBF800000 : 0;
+         case float_round_up:
+            return aSign ? 0x80000000 : 0x3F800000;
+        }
+        return packFloat32( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x96 - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = a;
+    roundingMode = float_rounding_mode;
+    if ( roundingMode == float_round_nearest_even ) {
+        z += lastBitMask>>1;
+        if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloat32Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+            z += roundBitsMask;
+        }
+    }
+    z &= ~ roundBitsMask;
+    if ( z != a ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the single-precision
+floating-point values `a' and `b'.  If `zSign' is true, the sum is negated
+before being returned.  `zSign' is ignored if the result is a NaN.  The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 addFloat32Sigs( float32 a, float32 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 6;
+    bSig <<= 6;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0xFF ) {
+            if ( aSig ) return propagateFloat32NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig |= 0x20000000;
+        }
+        shift32RightJamming( bSig, expDiff, &bSig );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0xFF ) {
+            if ( bSig ) return propagateFloat32NaN( a, b );
+            return packFloat32( zSign, 0xFF, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig |= 0x20000000;
+        }
+        shift32RightJamming( aSig, - expDiff, &aSig );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0xFF ) {
+            if ( aSig | bSig ) return propagateFloat32NaN( a, b );
+            return a;
+        }
+        if ( aExp == 0 ) return packFloat32( zSign, 0, ( aSig + bSig )>>6 );
+        zSig = 0x40000000 + aSig + bSig;
+        zExp = aExp;
+        goto roundAndPack;
+    }
+    aSig |= 0x20000000;
+    zSig = ( aSig + bSig )<<1;
+    --zExp;
+    if ( (sbits32) zSig < 0 ) {
+        zSig = aSig + bSig;
+        ++zExp;
+    }
+ roundAndPack:
+    return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the single-
+precision floating-point values `a' and `b'.  If `zSign' is true, the
+difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 subFloat32Sigs( float32 a, float32 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 7;
+    bSig <<= 7;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0xFF ) {
+        if ( aSig | bSig ) return propagateFloat32NaN( a, b );
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloat32( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        return packFloat32( zSign ^ 1, 0xFF, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig |= 0x40000000;
+    }
+    shift32RightJamming( aSig, - expDiff, &aSig );
+    bSig |= 0x40000000;
+ bBigger:
+    zSig = bSig - aSig;
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig |= 0x40000000;
+    }
+    shift32RightJamming( bSig, expDiff, &bSig );
+    aSig |= 0x40000000;
+ aBigger:
+    zSig = aSig - bSig;
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the single-precision floating-point values `a'
+and `b'.  The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_add( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat32Sigs( a, b, aSign );
+    }
+    else {
+        return subFloat32Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the single-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_sub( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat32Sigs( a, b, aSign );
+    }
+    else {
+        return addFloat32Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the single-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_mul( float32 a, float32 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig;
+    bits64 zSig64;
+    bits32 zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0xFF ) {
+        if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+            return propagateFloat32NaN( a, b );
+        }
+        if ( ( bExp | bSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        if ( ( aExp | aSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x7F;
+    aSig = ( aSig | 0x00800000 )<<7;
+    bSig = ( bSig | 0x00800000 )<<8;
+    shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 );
+    zSig = zSig64;
+    if ( 0 <= (sbits32) ( zSig<<1 ) ) {
+        zSig <<= 1;
+        --zExp;
+    }
+    return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the single-precision floating-point value `a'
+by the corresponding value `b'.  The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_div( float32 a, float32 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, b );
+        if ( bExp == 0xFF ) {
+            if ( bSig ) return propagateFloat32NaN( a, b );
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        return packFloat32( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+                float_raise( float_flag_invalid );
+                return float32_default_nan;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloat32( zSign, 0xFF, 0 );
+        }
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x7D;
+    aSig = ( aSig | 0x00800000 )<<7;
+    bSig = ( bSig | 0x00800000 )<<8;
+    if ( bSig <= ( aSig + aSig ) ) {
+        aSig >>= 1;
+        ++zExp;
+    }
+    zSig = ( ( (bits64) aSig )<<32 ) / bSig;
+    if ( ( zSig & 0x3F ) == 0 ) {
+        zSig |= ( ( (bits64) bSig ) * zSig != ( (bits64) aSig )<<32 );
+    }
+    return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the single-precision floating-point value `a'
+with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_rem( float32 a, float32 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, expDiff;
+    bits32 aSig, bSig;
+    bits32 q;
+    bits64 aSig64, bSig64, q64;
+    bits32 alternateASig;
+    sbits32 sigMean;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    if ( aExp == 0xFF ) {
+        if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+            return propagateFloat32NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return a;
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    expDiff = aExp - bExp;
+    aSig |= 0x00800000;
+    bSig |= 0x00800000;
+    if ( expDiff < 32 ) {
+        aSig <<= 8;
+        bSig <<= 8;
+        if ( expDiff < 0 ) {
+            if ( expDiff < -1 ) return a;
+            aSig >>= 1;
+        }
+        q = ( bSig <= aSig );
+        if ( q ) aSig -= bSig;
+        if ( 0 < expDiff ) {
+            q = ( ( (bits64) aSig )<<32 ) / bSig;
+            q >>= 32 - expDiff;
+            bSig >>= 2;
+            aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+        }
+        else {
+            aSig >>= 2;
+            bSig >>= 2;
+        }
+    }
+    else {
+        if ( bSig <= aSig ) aSig -= bSig;
+        aSig64 = ( (bits64) aSig )<<40;
+        bSig64 = ( (bits64) bSig )<<40;
+        expDiff -= 64;
+        while ( 0 < expDiff ) {
+            q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+            q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+            aSig64 = - ( ( bSig * q64 )<<38 );
+            expDiff -= 62;
+        }
+        expDiff += 64;
+        q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+        q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+        q = q64>>( 64 - expDiff );
+        bSig <<= 6;
+        aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q;
+    }
+    do {
+        alternateASig = aSig;
+        ++q;
+        aSig -= bSig;
+    } while ( 0 <= (sbits32) aSig );
+    sigMean = aSig + alternateASig;
+    if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+        aSig = alternateASig;
+    }
+    zSign = ( (sbits32) aSig < 0 );
+    if ( zSign ) aSig = - aSig;
+    return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the single-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_sqrt( float32 a )
+{
+    flag aSign;
+    int16 aExp, zExp;
+    bits32 aSig, zSig;
+    bits64 rem, term;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, 0 );
+        if ( ! aSign ) return a;
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig ) == 0 ) return a;
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return 0;
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E;
+    aSig = ( aSig | 0x00800000 )<<8;
+    zSig = estimateSqrt32( aExp, aSig ) + 2;
+    if ( ( zSig & 0x7F ) <= 5 ) {
+        if ( zSig < 2 ) {
+            zSig = 0xFFFFFFFF;
+        }
+        else {
+            aSig >>= aExp & 1;
+            term = ( (bits64) zSig ) * zSig;
+            rem = ( ( (bits64) aSig )<<32 ) - term;
+            while ( (sbits64) rem < 0 ) {
+                --zSig;
+                rem += ( ( (bits64) zSig )<<1 ) | 1;
+            }
+            zSig |= ( rem != 0 );
+        }
+    }
+    shift32RightJamming( zSig, 1, &zSig );
+    return roundAndPackFloat32( 0, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_eq( float32 a, float32 b )
+{
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_le( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_lt( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The invalid exception is raised
+if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_eq_signaling( float32 a, float32 b )
+{
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+cause an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_le_quiet( float32 a, float32 b )
+{
+    flag aSign, bSign;
+    //int16 aExp, bExp;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_lt_quiet( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_int32( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = 0x42C - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_int32_round_to_zero( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    shiftCount = 0x433 - aExp;
+    if ( shiftCount < 21 ) {
+        if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 52 < shiftCount ) {
+        if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig |= LIT64( 0x0010000000000000 );
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement unsigned integer format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest positive integer is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_uint32( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = 0; //extractFloat64Sign( a );
+    //if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = 0x42C - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig );
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest positive integer is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_uint32_round_to_zero( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    shiftCount = 0x433 - aExp;
+    if ( shiftCount < 21 ) {
+        if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 52 < shiftCount ) {
+        if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig |= LIT64( 0x0010000000000000 );
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the single-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float64_to_float32( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+    bits32 zSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a ) );
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    shift64RightJamming( aSig, 22, &aSig );
+    zSig = aSig;
+    if ( aExp || zSig ) {
+        zSig |= 0x40000000;
+        aExp -= 0x381;
+    }
+    return roundAndPackFloat32( aSign, aExp, zSig );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the extended double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float64_to_floatx80( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a ) );
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    return
+        packFloatx80(
+            aSign, aExp + 0x3C00, ( aSig | LIT64( 0x0010000000000000 ) )<<11 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the quadruple-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float64_to_float128( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig, zSig0, zSig1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a ) );
+        return packFloat128( aSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    shift128Right( aSig, 0, 4, &zSig0, &zSig1 );
+    return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the double-precision floating-point value `a' to an integer, and
+returns the result as a double-precision floating-point value.  The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_round_to_int( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    float64 z;
+
+    aExp = extractFloat64Exp( a );
+    if ( 0x433 <= aExp ) {
+        if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) {
+            return propagateFloat64NaN( a, a );
+        }
+        return a;
+    }
+    if ( aExp <= 0x3FE ) {
+        if ( (bits64) ( a<<1 ) == 0 ) return a;
+        float_exception_flags |= float_flag_inexact;
+        aSign = extractFloat64Sign( a );
+        switch ( float_rounding_mode ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) {
+                return packFloat64( aSign, 0x3FF, 0 );
+            }
+            break;
+         case float_round_down:
+            return aSign ? LIT64( 0xBFF0000000000000 ) : 0;
+         case float_round_up:
+            return
+            aSign ? LIT64( 0x8000000000000000 ) : LIT64( 0x3FF0000000000000 );
+        }
+        return packFloat64( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x433 - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = a;
+    roundingMode = float_rounding_mode;
+    if ( roundingMode == float_round_nearest_even ) {
+        z += lastBitMask>>1;
+        if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloat64Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+            z += roundBitsMask;
+        }
+    }
+    z &= ~ roundBitsMask;
+    if ( z != a ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the double-precision
+floating-point values `a' and `b'.  If `zSign' is true, the sum is negated
+before being returned.  `zSign' is ignored if the result is a NaN.  The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 addFloat64Sigs( float64 a, float64 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 9;
+    bSig <<= 9;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FF ) {
+            if ( aSig ) return propagateFloat64NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig |= LIT64( 0x2000000000000000 );
+        }
+        shift64RightJamming( bSig, expDiff, &bSig );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FF ) {
+            if ( bSig ) return propagateFloat64NaN( a, b );
+            return packFloat64( zSign, 0x7FF, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig |= LIT64( 0x2000000000000000 );
+        }
+        shift64RightJamming( aSig, - expDiff, &aSig );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FF ) {
+            if ( aSig | bSig ) return propagateFloat64NaN( a, b );
+            return a;
+        }
+        if ( aExp == 0 ) return packFloat64( zSign, 0, ( aSig + bSig )>>9 );
+        zSig = LIT64( 0x4000000000000000 ) + aSig + bSig;
+        zExp = aExp;
+        goto roundAndPack;
+    }
+    aSig |= LIT64( 0x2000000000000000 );
+    zSig = ( aSig + bSig )<<1;
+    --zExp;
+    if ( (sbits64) zSig < 0 ) {
+        zSig = aSig + bSig;
+        ++zExp;
+    }
+ roundAndPack:
+    return roundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the double-
+precision floating-point values `a' and `b'.  If `zSign' is true, the
+difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 subFloat64Sigs( float64 a, float64 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 10;
+    bSig <<= 10;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FF ) {
+        if ( aSig | bSig ) return propagateFloat64NaN( a, b );
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloat64( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        return packFloat64( zSign ^ 1, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig |= LIT64( 0x4000000000000000 );
+    }
+    shift64RightJamming( aSig, - expDiff, &aSig );
+    bSig |= LIT64( 0x4000000000000000 );
+ bBigger:
+    zSig = bSig - aSig;
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig |= LIT64( 0x4000000000000000 );
+    }
+    shift64RightJamming( bSig, expDiff, &bSig );
+    aSig |= LIT64( 0x4000000000000000 );
+ aBigger:
+    zSig = aSig - bSig;
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the double-precision floating-point values `a'
+and `b'.  The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_add( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat64Sigs( a, b, aSign );
+    }
+    else {
+        return subFloat64Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the double-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_sub( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat64Sigs( a, b, aSign );
+    }
+    else {
+        return addFloat64Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the double-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_mul( float64 a, float64 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FF ) {
+        if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+            return propagateFloat64NaN( a, b );
+        }
+        if ( ( bExp | bSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        if ( ( aExp | aSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x3FF;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    mul64To128( aSig, bSig, &zSig0, &zSig1 );
+    zSig0 |= ( zSig1 != 0 );
+    if ( 0 <= (sbits64) ( zSig0<<1 ) ) {
+        zSig0 <<= 1;
+        --zExp;
+    }
+    return roundAndPackFloat64( zSign, zExp, zSig0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the double-precision floating-point value `a'
+by the corresponding value `b'.  The operation is performed according to
+the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_div( float64 a, float64 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    bits64 rem0, rem1;
+    bits64 term0, term1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, b );
+        if ( bExp == 0x7FF ) {
+            if ( bSig ) return propagateFloat64NaN( a, b );
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        return packFloat64( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+                float_raise( float_flag_invalid );
+                return float64_default_nan;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloat64( zSign, 0x7FF, 0 );
+        }
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x3FD;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    if ( bSig <= ( aSig + aSig ) ) {
+        aSig >>= 1;
+        ++zExp;
+    }
+    zSig = estimateDiv128To64( aSig, 0, bSig );
+    if ( ( zSig & 0x1FF ) <= 2 ) {
+        mul64To128( bSig, zSig, &term0, &term1 );
+        sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+        while ( (sbits64) rem0 < 0 ) {
+            --zSig;
+            add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+        }
+        zSig |= ( rem1 != 0 );
+    }
+    return roundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the double-precision floating-point value `a'
+with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_rem( float64 a, float64 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, expDiff;
+    bits64 aSig, bSig;
+    bits64 q, alternateASig;
+    sbits64 sigMean;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    if ( aExp == 0x7FF ) {
+        if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+            return propagateFloat64NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return a;
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    expDiff = aExp - bExp;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    if ( expDiff < 0 ) {
+        if ( expDiff < -1 ) return a;
+        aSig >>= 1;
+    }
+    q = ( bSig <= aSig );
+    if ( q ) aSig -= bSig;
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig, 0, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        aSig = - ( ( bSig>>2 ) * q );
+        expDiff -= 62;
+    }
+    expDiff += 64;
+    if ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig, 0, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        q >>= 64 - expDiff;
+        bSig >>= 2;
+        aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+    }
+    else {
+        aSig >>= 2;
+        bSig >>= 2;
+    }
+    do {
+        alternateASig = aSig;
+        ++q;
+        aSig -= bSig;
+    } while ( 0 <= (sbits64) aSig );
+    sigMean = aSig + alternateASig;
+    if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+        aSig = alternateASig;
+    }
+    zSign = ( (sbits64) aSig < 0 );
+    if ( zSign ) aSig = - aSig;
+    return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the double-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_sqrt( float64 a )
+{
+    flag aSign;
+    int16 aExp, zExp;
+    bits64 aSig, zSig;
+    bits64 rem0, rem1, term0, term1; //, shiftedRem;
+    //float64 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, a );
+        if ( ! aSign ) return a;
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig ) == 0 ) return a;
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return 0;
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE;
+    aSig |= LIT64( 0x0010000000000000 );
+    zSig = estimateSqrt32( aExp, aSig>>21 );
+    zSig <<= 31;
+    aSig <<= 9 - ( aExp & 1 );
+    zSig = estimateDiv128To64( aSig, 0, zSig ) + zSig + 2;
+    if ( ( zSig & 0x3FF ) <= 5 ) {
+        if ( zSig < 2 ) {
+            zSig = LIT64( 0xFFFFFFFFFFFFFFFF );
+        }
+        else {
+            aSig <<= 2;
+            mul64To128( zSig, zSig, &term0, &term1 );
+            sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+            while ( (sbits64) rem0 < 0 ) {
+                --zSig;
+                shortShift128Left( 0, zSig, 1, &term0, &term1 );
+                term1 |= 1;
+                add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+            }
+            zSig |= ( ( rem0 | rem1 ) != 0 );
+        }
+    }
+    shift64RightJamming( zSig, 1, &zSig );
+    return roundAndPackFloat64( 0, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_eq( float64 a, float64 b )
+{
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_le( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_lt( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The invalid exception is raised
+if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_eq_signaling( float64 a, float64 b )
+{
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+cause an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_le_quiet( float64 a, float64 b )
+{
+    flag aSign, bSign;
+    //int16 aExp, bExp;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_lt_quiet( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the 32-bit two's complement integer format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic---which means in particular that the conversion
+is rounded according to the current rounding mode.  If `a' is a NaN, the
+largest positive integer is returned.  Otherwise, if the conversion
+overflows, the largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 floatx80_to_int32( floatx80 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+    shiftCount = 0x4037 - aExp;
+    if ( shiftCount <= 0 ) shiftCount = 1;
+    shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the 32-bit two's complement integer format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic, except that the conversion is always rounded
+toward zero.  If `a' is a NaN, the largest positive integer is returned.
+Otherwise, if the conversion overflows, the largest integer with the same
+sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 floatx80_to_int32_round_to_zero( floatx80 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    shiftCount = 0x403E - aExp;
+    if ( shiftCount < 32 ) {
+        if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 63 < shiftCount ) {
+        if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the single-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 floatx80_to_float32( floatx80 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) {
+            return commonNaNToFloat32( floatx80ToCommonNaN( a ) );
+        }
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    shift64RightJamming( aSig, 33, &aSig );
+    if ( aExp || aSig ) aExp -= 0x3F81;
+    return roundAndPackFloat32( aSign, aExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the double-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 floatx80_to_float64( floatx80 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig, zSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) {
+            return commonNaNToFloat64( floatx80ToCommonNaN( a ) );
+        }
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    shift64RightJamming( aSig, 1, &zSig );
+    if ( aExp || aSig ) aExp -= 0x3C01;
+    return roundAndPackFloat64( aSign, aExp, zSig );
+
+}
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the quadruple-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 floatx80_to_float128( floatx80 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig, zSig0, zSig1;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) {
+        return commonNaNToFloat128( floatx80ToCommonNaN( a ) );
+    }
+    shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
+    return packFloat128( aSign, aExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the extended double-precision floating-point value `a' to an integer,
+and returns the result as an extended quadruple-precision floating-point
+value.  The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_round_to_int( floatx80 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    floatx80 z;
+
+    aExp = extractFloatx80Exp( a );
+    if ( 0x403E <= aExp ) {
+        if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) {
+            return propagateFloatx80NaN( a, a );
+        }
+        return a;
+    }
+    if ( aExp <= 0x3FFE ) {
+        if (    ( aExp == 0 )
+             && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+            return a;
+        }
+        float_exception_flags |= float_flag_inexact;
+        aSign = extractFloatx80Sign( a );
+        switch ( float_rounding_mode ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 )
+               ) {
+                return
+                    packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
+            }
+            break;
+         case float_round_down:
+            return
+                  aSign ?
+                      packFloatx80( 1, 0x3FFF, LIT64( 0x8000000000000000 ) )
+                : packFloatx80( 0, 0, 0 );
+         case float_round_up:
+            return
+                  aSign ? packFloatx80( 1, 0, 0 )
+                : packFloatx80( 0, 0x3FFF, LIT64( 0x8000000000000000 ) );
+        }
+        return packFloatx80( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x403E - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = a;
+    roundingMode = float_rounding_mode;
+    if ( roundingMode == float_round_nearest_even ) {
+        z.low += lastBitMask>>1;
+        if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+            z.low += roundBitsMask;
+        }
+    }
+    z.low &= ~ roundBitsMask;
+    if ( z.low == 0 ) {
+        ++z.high;
+        z.low = LIT64( 0x8000000000000000 );
+    }
+    if ( z.low != a.low ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the extended double-
+precision floating-point values `a' and `b'.  If `zSign' is true, the sum is
+negated before being returned.  `zSign' is ignored if the result is a NaN.
+The addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    int32 expDiff;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FFF ) {
+            if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) --expDiff;
+        shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FFF ) {
+            if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        if ( aExp == 0 ) ++expDiff;
+        shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FFF ) {
+            if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+                return propagateFloatx80NaN( a, b );
+            }
+            return a;
+        }
+        zSig1 = 0;
+        zSig0 = aSig + bSig;
+        if ( aExp == 0 ) {
+            normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 );
+            goto roundAndPack;
+        }
+        zExp = aExp;
+        goto shiftRight1;
+    }
+    
+    zSig0 = aSig + bSig;
+
+    if ( (sbits64) zSig0 < 0 ) goto roundAndPack; 
+ shiftRight1:
+    shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 );
+    zSig0 |= LIT64( 0x8000000000000000 );
+    ++zExp;
+ roundAndPack:
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the extended
+double-precision floating-point values `a' and `b'.  If `zSign' is true,
+the difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    int32 expDiff;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+            return propagateFloatx80NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        z.low = floatx80_default_nan_low;
+        z.high = floatx80_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    zSig1 = 0;
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloatx80( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) ++expDiff;
+    shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ bBigger:
+    sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 );
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) --expDiff;
+    shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ aBigger:
+    sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 );
+    zExp = aExp;
+ normalizeRoundAndPack:
+    return
+        normalizeRoundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the extended double-precision floating-point
+values `a' and `b'.  The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_add( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+    
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign == bSign ) {
+        return addFloatx80Sigs( a, b, aSign );
+    }
+    else {
+        return subFloatx80Sigs( a, b, aSign );
+    }
+    
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the extended double-precision floating-
+point values `a' and `b'.  The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_sub( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign == bSign ) {
+        return subFloatx80Sigs( a, b, aSign );
+    }
+    else {
+        return addFloatx80Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the extended double-precision floating-
+point values `a' and `b'.  The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_mul( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if (    (bits64) ( aSig<<1 )
+             || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+            return propagateFloatx80NaN( a, b );
+        }
+        if ( ( bExp | bSig ) == 0 ) goto invalid;
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = floatx80_default_nan_low;
+            z.high = floatx80_default_nan_high;
+            return z;
+        }
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x3FFE;
+    mul64To128( aSig, bSig, &zSig0, &zSig1 );
+    if ( 0 < (sbits64) zSig0 ) {
+        shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 );
+        --zExp;
+    }
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the extended double-precision floating-point
+value `a' by the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_div( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    bits64 rem0, rem1, rem2, term0, term1, term2;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        if ( bExp == 0x7FFF ) {
+            if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+            goto invalid;
+        }
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return packFloatx80( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+                float_raise( float_flag_invalid );
+                z.low = floatx80_default_nan_low;
+                z.high = floatx80_default_nan_high;
+                return z;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x3FFE;
+    rem1 = 0;
+    if ( bSig <= aSig ) {
+        shift128Right( aSig, 0, 1, &aSig, &rem1 );
+        ++zExp;
+    }
+    zSig0 = estimateDiv128To64( aSig, rem1, bSig );
+    mul64To128( bSig, zSig0, &term0, &term1 );
+    sub128( aSig, rem1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+    }
+    zSig1 = estimateDiv128To64( rem1, 0, bSig );
+    if ( (bits64) ( zSig1<<1 ) <= 8 ) {
+        mul64To128( bSig, zSig1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            add128( rem1, rem2, 0, bSig, &rem1, &rem2 );
+        }
+        zSig1 |= ( ( rem1 | rem2 ) != 0 );
+    }
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the extended double-precision floating-point value
+`a' with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_rem( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, expDiff;
+    bits64 aSig0, aSig1, bSig;
+    bits64 q, term0, term1, alternateASig0, alternateASig1;
+    floatx80 z;
+
+    aSig0 = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    if ( aExp == 0x7FFF ) {
+        if (    (bits64) ( aSig0<<1 )
+             || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+            return propagateFloatx80NaN( a, b );
+        }
+        goto invalid;
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = floatx80_default_nan_low;
+            z.high = floatx80_default_nan_high;
+            return z;
+        }
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( (bits64) ( aSig0<<1 ) == 0 ) return a;
+        normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+    }
+    bSig |= LIT64( 0x8000000000000000 );
+    zSign = aSign;
+    expDiff = aExp - bExp;
+    aSig1 = 0;
+    if ( expDiff < 0 ) {
+        if ( expDiff < -1 ) return a;
+        shift128Right( aSig0, 0, 1, &aSig0, &aSig1 );
+        expDiff = 0;
+    }
+    q = ( bSig <= aSig0 );
+    if ( q ) aSig0 -= bSig;
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        mul64To128( bSig, q, &term0, &term1 );
+        sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 );
+        expDiff -= 62;
+    }
+    expDiff += 64;
+    if ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        q >>= 64 - expDiff;
+        mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 );
+        sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 );
+        while ( le128( term0, term1, aSig0, aSig1 ) ) {
+            ++q;
+            sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        }
+    }
+    else {
+        term1 = 0;
+        term0 = bSig;
+    }
+    sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 );
+    if (    lt128( alternateASig0, alternateASig1, aSig0, aSig1 )
+         || (    eq128( alternateASig0, alternateASig1, aSig0, aSig1 )
+              && ( q & 1 ) )
+       ) {
+        aSig0 = alternateASig0;
+        aSig1 = alternateASig1;
+        zSign = ! zSign;
+    }
+    return
+        normalizeRoundAndPackFloatx80(
+            80, zSign, bExp + expDiff, aSig0, aSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the extended double-precision floating-point
+value `a'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_sqrt( floatx80 a )
+{
+    flag aSign;
+    int32 aExp, zExp;
+    bits64 aSig0, aSig1, zSig0, zSig1;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    bits64 shiftedRem0, shiftedRem1;
+    floatx80 z;
+
+    aSig0 = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a );
+        if ( ! aSign ) return a;
+        goto invalid;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig0 ) == 0 ) return a;
+ invalid:
+        float_raise( float_flag_invalid );
+        z.low = floatx80_default_nan_low;
+        z.high = floatx80_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 );
+        normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+    }
+    zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF;
+    zSig0 = estimateSqrt32( aExp, aSig0>>32 );
+    zSig0 <<= 31;
+    aSig1 = 0;
+    shift128Right( aSig0, 0, ( aExp & 1 ) + 2, &aSig0, &aSig1 );
+    zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4;
+    if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF );
+    shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 );
+    mul64To128( zSig0, zSig0, &term0, &term1 );
+    sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        shortShift128Left( 0, zSig0, 1, &term0, &term1 );
+        term1 |= 1;
+        add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+    }
+    shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 );
+    zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 );
+    if ( (bits64) ( zSig1<<1 ) <= 10 ) {
+        if ( zSig1 == 0 ) zSig1 = 1;
+        mul64To128( zSig0, zSig1, &term1, &term2 );
+        shortShift128Left( term1, term2, 1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        mul64To128( zSig1, zSig1, &term2, &term3 );
+        sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 );
+            term3 |= 1;
+            add192(
+                rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, 0, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+equal to the corresponding value `b', and 0 otherwise.  The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_eq( floatx80 a, floatx80 b )
+{
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+less than or equal to the corresponding value `b', and 0 otherwise.  The
+comparison is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_le( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+less than the corresponding value `b', and 0 otherwise.  The comparison
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_lt( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is equal
+to the corresponding value `b', and 0 otherwise.  The invalid exception is
+raised if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_eq_signaling( floatx80 a, floatx80 b )
+{
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is less
+than or equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs
+do not cause an exception.  Otherwise, the comparison is performed according
+to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_le_quiet( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is less
+than the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause
+an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_lt_quiet( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the 32-bit two's complement integer format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float128_to_int32( float128 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0;
+    if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+    aSig0 |= ( aSig1 != 0 );
+    shiftCount = 0x4028 - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 );
+    return roundAndPackInt32( aSign, aSig0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the 32-bit two's complement integer format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float128_to_int32_round_to_zero( float128 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1, savedASig;
+    int32 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    aSig0 |= ( aSig1 != 0 );
+    shiftCount = 0x402F - aExp;
+    if ( shiftCount < 17 ) {
+        if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 48 < shiftCount ) {
+        if ( aExp || aSig0 ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig0 |= LIT64( 0x0001000000000000 );
+    savedASig = aSig0;
+    aSig0 >>= shiftCount;
+    z = aSig0;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig0<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the single-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float128_to_float32( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+    bits32 zSig;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloat32( float128ToCommonNaN( a ) );
+        }
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    aSig0 |= ( aSig1 != 0 );
+    shift64RightJamming( aSig0, 18, &aSig0 );
+    zSig = aSig0;
+    if ( aExp || zSig ) {
+        zSig |= 0x40000000;
+        aExp -= 0x3F81;
+    }
+    return roundAndPackFloat32( aSign, aExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float128_to_float64( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloat64( float128ToCommonNaN( a ) );
+        }
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+    aSig0 |= ( aSig1 != 0 );
+    if ( aExp || aSig0 ) {
+        aSig0 |= LIT64( 0x4000000000000000 );
+        aExp -= 0x3C01;
+    }
+    return roundAndPackFloat64( aSign, aExp, aSig0 );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the extended double-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float128_to_floatx80( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloatx80( float128ToCommonNaN( a ) );
+        }
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    else {
+        aSig0 |= LIT64( 0x0001000000000000 );
+    }
+    shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 );
+    return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the quadruple-precision floating-point value `a' to an integer, and
+returns the result as a quadruple-precision floating-point value.  The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_round_to_int( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    float128 z;
+
+    aExp = extractFloat128Exp( a );
+    if ( 0x402F <= aExp ) {
+        if ( 0x406F <= aExp ) {
+            if (    ( aExp == 0x7FFF )
+                 && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) )
+               ) {
+                return propagateFloat128NaN( a, a );
+            }
+            return a;
+        }
+        lastBitMask = 1;
+        lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1;
+        roundBitsMask = lastBitMask - 1;
+        z = a;
+        roundingMode = float_rounding_mode;
+        if ( roundingMode == float_round_nearest_even ) {
+            if ( lastBitMask ) {
+                add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low );
+                if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+            }
+            else {
+                if ( (sbits64) z.low < 0 ) {
+                    ++z.high;
+                    if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1;
+                }
+            }
+        }
+        else if ( roundingMode != float_round_to_zero ) {
+            if (   extractFloat128Sign( z )
+                 ^ ( roundingMode == float_round_up ) ) {
+                add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
+            }
+        }
+        z.low &= ~ roundBitsMask;
+    }
+    else {
+        if ( aExp <= 0x3FFE ) {
+            if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a;
+            float_exception_flags |= float_flag_inexact;
+            aSign = extractFloat128Sign( a );
+            switch ( float_rounding_mode ) {
+             case float_round_nearest_even:
+                if (    ( aExp == 0x3FFE )
+                     && (   extractFloat128Frac0( a )
+                          | extractFloat128Frac1( a ) )
+                   ) {
+                    return packFloat128( aSign, 0x3FFF, 0, 0 );
+                }
+                break;
+             case float_round_down:
+                return
+                      aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
+                    : packFloat128( 0, 0, 0, 0 );
+             case float_round_up:
+                return
+                      aSign ? packFloat128( 1, 0, 0, 0 )
+                    : packFloat128( 0, 0x3FFF, 0, 0 );
+            }
+            return packFloat128( aSign, 0, 0, 0 );
+        }
+        lastBitMask = 1;
+        lastBitMask <<= 0x402F - aExp;
+        roundBitsMask = lastBitMask - 1;
+        z.low = 0;
+        z.high = a.high;
+        roundingMode = float_rounding_mode;
+        if ( roundingMode == float_round_nearest_even ) {
+            z.high += lastBitMask>>1;
+            if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
+                z.high &= ~ lastBitMask;
+            }
+        }
+        else if ( roundingMode != float_round_to_zero ) {
+            if (   extractFloat128Sign( z )
+                 ^ ( roundingMode == float_round_up ) ) {
+                z.high |= ( a.low != 0 );
+                z.high += roundBitsMask;
+            }
+        }
+        z.high &= ~ roundBitsMask;
+    }
+    if ( ( z.low != a.low ) || ( z.high != a.high ) ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the quadruple-precision
+floating-point values `a' and `b'.  If `zSign' is true, the sum is negated
+before being returned.  `zSign' is ignored if the result is a NaN.  The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128 addFloat128Sigs( float128 a, float128 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+    int32 expDiff;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FFF ) {
+            if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig0 |= LIT64( 0x0001000000000000 );
+        }
+        shift128ExtraRightJamming(
+            bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FFF ) {
+            if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig0 |= LIT64( 0x0001000000000000 );
+        }
+        shift128ExtraRightJamming(
+            aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FFF ) {
+            if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+                return propagateFloat128NaN( a, b );
+            }
+            return a;
+        }
+        add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+        if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 );
+        zSig2 = 0;
+        zSig0 |= LIT64( 0x0002000000000000 );
+        zExp = aExp;
+        goto shiftRight1;
+    }
+    aSig0 |= LIT64( 0x0001000000000000 );
+    add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+    --zExp;
+    if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack;
+    ++zExp;
+ shiftRight1:
+    shift128ExtraRightJamming(
+        zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ roundAndPack:
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the quadruple-
+precision floating-point values `a' and `b'.  If `zSign' is true, the
+difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128 subFloat128Sigs( float128 a, float128 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1;
+    int32 expDiff;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    expDiff = aExp - bExp;
+    shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+    shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 );
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+            return propagateFloat128NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        z.low = float128_default_nan_low;
+        z.high = float128_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig0 < aSig0 ) goto aBigger;
+    if ( aSig0 < bSig0 ) goto bBigger;
+    if ( bSig1 < aSig1 ) goto aBigger;
+    if ( aSig1 < bSig1 ) goto bBigger;
+    return packFloat128( float_rounding_mode == float_round_down, 0, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig0 |= LIT64( 0x4000000000000000 );
+    }
+    shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+    bSig0 |= LIT64( 0x4000000000000000 );
+ bBigger:
+    sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 );
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig0 |= LIT64( 0x4000000000000000 );
+    }
+    shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 );
+    aSig0 |= LIT64( 0x4000000000000000 );
+ aBigger:
+    sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the quadruple-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_add( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat128Sigs( a, b, aSign );
+    }
+    else {
+        return subFloat128Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the quadruple-precision floating-point
+values `a' and `b'.  The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_sub( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat128Sigs( a, b, aSign );
+    }
+    else {
+        return addFloat128Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the quadruple-precision floating-point
+values `a' and `b'.  The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_mul( float128 a, float128 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if (    ( aSig0 | aSig1 )
+             || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+            return propagateFloat128NaN( a, b );
+        }
+        if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid;
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = float128_default_nan_low;
+            z.high = float128_default_nan_high;
+            return z;
+        }
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    zExp = aExp + bExp - 0x4000;
+    aSig0 |= LIT64( 0x0001000000000000 );
+    shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 );
+    mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 );
+    add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 );
+    zSig2 |= ( zSig3 != 0 );
+    if ( LIT64( 0x0002000000000000 ) <= zSig0 ) {
+        shift128ExtraRightJamming(
+            zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+        ++zExp;
+    }
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the quadruple-precision floating-point value
+`a' by the corresponding value `b'.  The operation is performed according to
+the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_div( float128 a, float128 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+        if ( bExp == 0x7FFF ) {
+            if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+            goto invalid;
+        }
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        return packFloat128( zSign, 0, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) {
+            if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+                float_raise( float_flag_invalid );
+                z.low = float128_default_nan_low;
+                z.high = float128_default_nan_high;
+                return z;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    zExp = aExp - bExp + 0x3FFD;
+    shortShift128Left(
+        aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 );
+    shortShift128Left(
+        bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+    if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) {
+        shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 );
+        ++zExp;
+    }
+    zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 );
+    mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 );
+    sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 );
+    }
+    zSig1 = estimateDiv128To64( rem1, rem2, bSig0 );
+    if ( ( zSig1 & 0x3FFF ) <= 4 ) {
+        mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 );
+        sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the quadruple-precision floating-point value `a'
+with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_rem( float128 a, float128 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, expDiff;
+    bits64 aSig0, aSig1, bSig0, bSig1;
+    bits64 q, term0, term1, term2, allZero, alternateASig0, alternateASig1;
+    bits64 sigMean1;
+    sbits64 sigMean0;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    if ( aExp == 0x7FFF ) {
+        if (    ( aSig0 | aSig1 )
+             || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+            return propagateFloat128NaN( a, b );
+        }
+        goto invalid;
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = float128_default_nan_low;
+            z.high = float128_default_nan_high;
+            return z;
+        }
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return a;
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    expDiff = aExp - bExp;
+    if ( expDiff < -1 ) return a;
+    shortShift128Left(
+        aSig0 | LIT64( 0x0001000000000000 ),
+        aSig1,
+        15 - ( expDiff < 0 ),
+        &aSig0,
+        &aSig1
+    );
+    shortShift128Left(
+        bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+    q = le128( bSig0, bSig1, aSig0, aSig1 );
+    if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+        q = ( 4 < q ) ? q - 4 : 0;
+        mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+        shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero );
+        shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero );
+        sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 );
+        expDiff -= 61;
+    }
+    if ( -64 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+        q = ( 4 < q ) ? q - 4 : 0;
+        q >>= - expDiff;
+        shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+        expDiff += 52;
+        if ( expDiff < 0 ) {
+            shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+        }
+        else {
+            shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 );
+        }
+        mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+        sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 );
+    }
+    else {
+        shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 );
+        shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+    }
+    do {
+        alternateASig0 = aSig0;
+        alternateASig1 = aSig1;
+        ++q;
+        sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+    } while ( 0 <= (sbits64) aSig0 );
+    add128(
+        aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 );
+    if (    ( sigMean0 < 0 )
+         || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) {
+        aSig0 = alternateASig0;
+        aSig1 = alternateASig1;
+    }
+    zSign = ( (sbits64) aSig0 < 0 );
+    if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 );
+    return
+        normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the quadruple-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_sqrt( float128 a )
+{
+    flag aSign;
+    int32 aExp, zExp;
+    bits64 aSig0, aSig1, zSig0, zSig1, zSig2;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    bits64 shiftedRem0, shiftedRem1;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a );
+        if ( ! aSign ) return a;
+        goto invalid;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a;
+ invalid:
+        float_raise( float_flag_invalid );
+        z.low = float128_default_nan_low;
+        z.high = float128_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE;
+    aSig0 |= LIT64( 0x0001000000000000 );
+    zSig0 = estimateSqrt32( aExp, aSig0>>17 );
+    zSig0 <<= 31;
+    shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 );
+    zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4;
+    if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF );
+    shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 );
+    mul64To128( zSig0, zSig0, &term0, &term1 );
+    sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        shortShift128Left( 0, zSig0, 1, &term0, &term1 );
+        term1 |= 1;
+        add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+    }
+    shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 );
+    zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 );
+    if ( ( zSig1 & 0x3FFF ) <= 5 ) {
+        if ( zSig1 == 0 ) zSig1 = 1;
+        mul64To128( zSig0, zSig1, &term1, &term2 );
+        shortShift128Left( term1, term2, 1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        mul64To128( zSig1, zSig1, &term2, &term3 );
+        sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 );
+            term3 |= 1;
+            add192(
+                rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+    return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is equal to
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_eq( float128 a, float128 b )
+{
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+or equal to the corresponding value `b', and 0 otherwise.  The comparison
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_le( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_lt( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is equal to
+the corresponding value `b', and 0 otherwise.  The invalid exception is
+raised if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_eq_signaling( float128 a, float128 b )
+{
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+or equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+cause an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_le_quiet( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_lt_quiet( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
diff -urN linux-2.2.18-pre8/arch/arm/nwfpe/softfloat.h linux/arch/arm/nwfpe/softfloat.h
--- linux-2.2.18-pre8/arch/arm/nwfpe/softfloat.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/nwfpe/softfloat.h	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,290 @@
+
+/*
+===============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+#ifndef __SOFTFLOAT_H__
+#define __SOFTFLOAT_H__
+
+/*
+-------------------------------------------------------------------------------
+The macro `FLOATX80' must be defined to enable the extended double-precision
+floating-point format `floatx80'.  If this macro is not defined, the
+`floatx80' type will not be defined, and none of the functions that either
+input or output the `floatx80' type will be defined.  The same applies to
+the `FLOAT128' macro and the quadruple-precision format `float128'.
+-------------------------------------------------------------------------------
+*/
+#define FLOATX80
+/* #define FLOAT128 */
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point types.
+-------------------------------------------------------------------------------
+*/
+typedef unsigned long int float32;
+typedef unsigned long long float64;
+#ifdef FLOATX80
+typedef struct {
+    unsigned short high;
+    unsigned long long low;
+} floatx80;
+#endif
+#ifdef FLOAT128
+typedef struct {
+    unsigned long long high, low;
+} float128;
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point underflow tininess-detection mode.
+-------------------------------------------------------------------------------
+*/
+extern signed char float_detect_tininess;
+enum {
+    float_tininess_after_rounding  = 0,
+    float_tininess_before_rounding = 1
+};
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point rounding mode.
+-------------------------------------------------------------------------------
+*/
+extern signed char float_rounding_mode;
+enum {
+    float_round_nearest_even = 0,
+    float_round_to_zero      = 1,
+    float_round_down         = 2,
+    float_round_up           = 3
+};
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point exception flags.
+-------------------------------------------------------------------------------
+extern signed char float_exception_flags;
+enum {
+    float_flag_inexact   =  1,
+    float_flag_underflow =  2,
+    float_flag_overflow  =  4,
+    float_flag_divbyzero =  8,
+    float_flag_invalid   = 16
+};
+
+ScottB: November 4, 1998
+Changed the enumeration to match the bit order in the FPA11.
+*/
+
+extern signed char float_exception_flags;
+enum {
+    float_flag_invalid   =  1,
+    float_flag_divbyzero =  2,
+    float_flag_overflow  =  4,
+    float_flag_underflow =  8,
+    float_flag_inexact   = 16
+};
+
+/*
+-------------------------------------------------------------------------------
+Routine to raise any or all of the software IEC/IEEE floating-point
+exception flags.
+-------------------------------------------------------------------------------
+*/
+void float_raise( signed char );
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE integer-to-floating-point conversion routines.
+-------------------------------------------------------------------------------
+*/
+float32 int32_to_float32( signed int );
+float64 int32_to_float64( signed int );
+#ifdef FLOATX80
+floatx80 int32_to_floatx80( signed int );
+#endif
+#ifdef FLOAT128
+float128 int32_to_float128( signed int );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE single-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float32_to_int32( float32 );
+signed int float32_to_int32_round_to_zero( float32 );
+float64 float32_to_float64( float32 );
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 );
+#endif
+#ifdef FLOAT128
+float128 float32_to_float128( float32 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE single-precision operations.
+-------------------------------------------------------------------------------
+*/
+float32 float32_round_to_int( float32 );
+float32 float32_add( float32, float32 );
+float32 float32_sub( float32, float32 );
+float32 float32_mul( float32, float32 );
+float32 float32_div( float32, float32 );
+float32 float32_rem( float32, float32 );
+float32 float32_sqrt( float32 );
+char float32_eq( float32, float32 );
+char float32_le( float32, float32 );
+char float32_lt( float32, float32 );
+char float32_eq_signaling( float32, float32 );
+char float32_le_quiet( float32, float32 );
+char float32_lt_quiet( float32, float32 );
+char float32_is_signaling_nan( float32 );
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE double-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float64_to_int32( float64 );
+signed int float64_to_int32_round_to_zero( float64 );
+float32 float64_to_float32( float64 );
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 );
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE double-precision operations.
+-------------------------------------------------------------------------------
+*/
+float64 float64_round_to_int( float64 );
+float64 float64_add( float64, float64 );
+float64 float64_sub( float64, float64 );
+float64 float64_mul( float64, float64 );
+float64 float64_div( float64, float64 );
+float64 float64_rem( float64, float64 );
+float64 float64_sqrt( float64 );
+char float64_eq( float64, float64 );
+char float64_le( float64, float64 );
+char float64_lt( float64, float64 );
+char float64_eq_signaling( float64, float64 );
+char float64_le_quiet( float64, float64 );
+char float64_lt_quiet( float64, float64 );
+char float64_is_signaling_nan( float64 );
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int floatx80_to_int32( floatx80 );
+signed int floatx80_to_int32_round_to_zero( floatx80 );
+float32 floatx80_to_float32( floatx80 );
+float64 floatx80_to_float64( floatx80 );
+#ifdef FLOAT128
+float128 floatx80_to_float128( floatx80 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision rounding precision.  Valid
+values are 32, 64, and 80.
+-------------------------------------------------------------------------------
+*/
+extern signed char floatx80_rounding_precision;
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision operations.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_round_to_int( floatx80 );
+floatx80 floatx80_add( floatx80, floatx80 );
+floatx80 floatx80_sub( floatx80, floatx80 );
+floatx80 floatx80_mul( floatx80, floatx80 );
+floatx80 floatx80_div( floatx80, floatx80 );
+floatx80 floatx80_rem( floatx80, floatx80 );
+floatx80 floatx80_sqrt( floatx80 );
+char floatx80_eq( floatx80, floatx80 );
+char floatx80_le( floatx80, floatx80 );
+char floatx80_lt( floatx80, floatx80 );
+char floatx80_eq_signaling( floatx80, floatx80 );
+char floatx80_le_quiet( floatx80, floatx80 );
+char floatx80_lt_quiet( floatx80, floatx80 );
+char floatx80_is_signaling_nan( floatx80 );
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE quadruple-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float128_to_int32( float128 );
+signed int float128_to_int32_round_to_zero( float128 );
+float32 float128_to_float32( float128 );
+float64 float128_to_float64( float128 );
+#ifdef FLOATX80
+floatx80 float128_to_floatx80( float128 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE quadruple-precision operations.
+-------------------------------------------------------------------------------
+*/
+float128 float128_round_to_int( float128 );
+float128 float128_add( float128, float128 );
+float128 float128_sub( float128, float128 );
+float128 float128_mul( float128, float128 );
+float128 float128_div( float128, float128 );
+float128 float128_rem( float128, float128 );
+float128 float128_sqrt( float128 );
+char float128_eq( float128, float128 );
+char float128_le( float128, float128 );
+char float128_lt( float128, float128 );
+char float128_eq_signaling( float128, float128 );
+char float128_le_quiet( float128, float128 );
+char float128_lt_quiet( float128, float128 );
+char float128_is_signaling_nan( float128 );
+
+#endif
+
+#endif
diff -urN linux-2.2.18-pre8/arch/arm/vmlinux-armo.lds.in linux/arch/arm/vmlinux-armo.lds.in
--- linux-2.2.18-pre8/arch/arm/vmlinux-armo.lds.in	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/vmlinux-armo.lds.in	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,60 @@
+/* ld script to make ARM Linux kernel
+ * taken from the i386 version by Russell King
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+  _text = .;			/* Text and read-only data */
+  .text : {
+	*(.text)
+	*(.fixup)
+	*(.gnu.warning)
+	} = 0x9090
+  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
+  .rodata : { *(.rodata) }
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
+
+  _etext = .;			/* End of text section */
+
+  . = ALIGN(8192);
+  .data : {			/* Data */
+	*(.init.task)
+	*(.data)
+	CONSTRUCTORS
+	}
+
+  _edata = .;			/* End of data section */
+
+  . = ALIGN(32768);		/* Init code and data */
+  __init_begin = .;
+  .text.init : { *(.text.init) }
+  .data.init : { *(.data.init) }
+  . = ALIGN(32768);
+  __init_end = .;
+
+  __bss_start = .;		/* BSS */
+  .bss : {
+	*(.bss)
+	}
+  _end = . ;
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
diff -urN linux-2.2.18-pre8/arch/arm/vmlinux-armv.lds.in linux/arch/arm/vmlinux-armv.lds.in
--- linux-2.2.18-pre8/arch/arm/vmlinux-armv.lds.in	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/vmlinux-armv.lds.in	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,93 @@
+/* ld script to make ARM Linux kernel
+ * taken from the i386 version by Russell King
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+OUTPUT_ARCH(arm)
+ENTRY(stext)
+SECTIONS
+{
+	. = TEXTADDR;
+	.init : {			/* Init code and data		*/
+		__init_begin = .;
+			*(.text.init)
+		__proc_info_begin = .;
+			*(.proc.info)
+		__proc_info_end = .;
+			*(.data.init)
+		. = ALIGN(4096);
+		__init_end = .;
+	}
+
+	__ebsa285_begin = .;
+	.text.ebsa285 : { *(.text.ebsa285) }
+	.data.ebsa285 : { *(.data.ebsa285) }
+	. = ALIGN(4096);
+	__ebsa285_end = .;
+
+	__netwinder_begin = .;
+	.text.netwinder : { *(.text.netwinder) }
+	.data.netwinder : { *(.data.netwinder) }
+	. = ALIGN(4096);
+	__netwinder_end = .;
+
+	.text : {			/* Real text segment		*/
+		_text = .;		/* Text and read-only data	*/
+			*(.text)
+			*(.fixup)
+			*(.gnu.warning)
+			*(.text.lock)	/* out-of-line lock text */
+			*(.rodata)
+			*(.kstrtab)
+		. = ALIGN(16);		/* Exception table		*/
+		__start___ex_table = .;
+			*(__ex_table)
+		__stop___ex_table = .;
+
+		__start___ksymtab = .;	/* Kernel symbol table		*/
+			*(__ksymtab)
+		__stop___ksymtab = .;
+
+		*(.got)			/* Global offset table		*/
+
+		_etext = .;		/* End of text section		*/
+	}
+
+	. = ALIGN(8192);
+
+	.data : {
+		/*
+		 * first, the init task union, aligned
+		 * to an 8192 byte boundary.
+		 */
+		*(.init.task)
+
+		/*
+		 * then the cacheline aligned data
+		 */
+		. = ALIGN(32);
+		*(.data.cacheline_aligned)
+
+		/*
+		 * and the usual data section
+		 */
+		*(.data)
+		CONSTRUCTORS
+
+		_edata = .;
+	}
+
+	.bss : {
+		__bss_start = .;	/* BSS				*/
+		*(.bss)
+		*(COMMON)
+		_end = . ;
+	}
+					/* Stabs debugging sections.	*/
+	.stab 0 : { *(.stab) }
+	.stabstr 0 : { *(.stabstr) }
+	.stab.excl 0 : { *(.stab.excl) }
+	.stab.exclstr 0 : { *(.stab.exclstr) }
+	.stab.index 0 : { *(.stab.index) }
+	.stab.indexstr 0 : { *(.stab.indexstr) }
+	.comment 0 : { *(.comment) }
+}
diff -urN linux-2.2.18-pre8/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- linux-2.2.18-pre8/arch/i386/boot/setup.S	Wed May  3 17:16:30 2000
+++ linux/arch/i386/boot/setup.S	Mon Dec  3 20:25:13 2001
@@ -30,6 +30,10 @@
 ! Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
 ! parsons) to avoid loadlin confusion, July 1997
 !
+!
+! A20 gating fiddled to work on AMD Elan AmSC4xx series by kira@linuxgrrls.org
+! july 1999
+!
 
 #define __ASSEMBLY__
 #include <linux/config.h>
@@ -548,6 +552,14 @@
 	lgdt	gdt_48		! load gdt with whatever appropriate
 
 ! that was painless, now we enable A20
+
+! if this is SC410 or a few other bits we need to do it with the 'fast' method
+
+        in      al,#0x92        ! read "System Control Port A"
+        or      al,#0x02        ! Set "Alternate Gate A20" - Bit
+        out     #0x92,al        ! write "System Control Port A"
+
+! do it the normal way too, so as not to upset normal machines
 
 	call	empty_8042
 	mov	al,#0xD1		! command write
diff -urN linux-2.2.18-pre8/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- linux-2.2.18-pre8/arch/sparc/kernel/sparc_ksyms.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Mon Dec  3 20:25:13 2001
@@ -65,6 +65,7 @@
 extern int __ashrdi3(int, int);
 extern int __ashldi3(int, int);
 extern int __lshrdi3(int, int);
+extern int __muldi3(int, int);
 
 extern void dump_thread(struct pt_regs *, struct user *);
 
@@ -272,6 +273,7 @@
 EXPORT_SYMBOL_NOVERS(__ashrdi3);
 EXPORT_SYMBOL_NOVERS(__ashldi3);
 EXPORT_SYMBOL_NOVERS(__lshrdi3);
+EXPORT_SYMBOL_NOVERS(__muldi3);
 
 EXPORT_SYMBOL_DOT(rem);
 EXPORT_SYMBOL_DOT(urem);
diff -urN linux-2.2.18-pre8/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- linux-2.2.18-pre8/arch/sparc/kernel/sys_sunos.c	Wed May  3 17:16:32 2000
+++ linux/arch/sparc/kernel/sys_sunos.c	Mon Dec  3 20:25:13 2001
@@ -707,7 +707,7 @@
 #define SMNT_SYS5         128
 
 struct sunos_fh_t {
-	char fh_data [NFS_FHSIZE];
+	char fh_data [NFS2_FHSIZE];
 };
 
 struct sunos_nfs_mount_args {
@@ -774,7 +774,7 @@
 
 	server.sin_family = AF_INET;
 	server.sin_addr = addr->sin_addr;
-	server.sin_port = NFS_PORT;
+	server.sin_port = NFS2_PORT;
 
 	/* Call sys_connect */
 	ret = socket->ops->connect (socket, (struct sockaddr *) &server,
diff -urN linux-2.2.18-pre8/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile
--- linux-2.2.18-pre8/arch/sparc/lib/Makefile	Tue Jan  4 10:12:13 2000
+++ linux/arch/sparc/lib/Makefile	Mon Dec  3 20:25:13 2001
@@ -6,7 +6,7 @@
         strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
 	strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
 	copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o \
-	ashldi3.o
+	ashldi3.o muldi3.o
 
 ifdef CONFIG_SMP
 OBJS += irqlock.o
diff -urN linux-2.2.18-pre8/arch/sparc/lib/muldi3.S linux/arch/sparc/lib/muldi3.S
--- linux-2.2.18-pre8/arch/sparc/lib/muldi3.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/lib/muldi3.S	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,76 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+	.text
+	.align 4
+	.globl __muldi3
+__muldi3:
+	save  %sp, -104, %sp
+	wr  %g0, %i1, %y
+	sra  %i3, 0x1f, %g2
+	and  %i1, %g2, %g2
+	andcc  %g0, 0, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, 0, %g1
+	add  %g1, %g2, %l2
+	rd  %y, %o1
+	mov  %o1, %l3
+	mov  %i1, %o0
+	call  .umul
+	mov  %i2, %o1
+	mov  %o0, %l0
+	mov  %i0, %o0
+	call  .umul
+	mov  %i3, %o1
+	add  %l0, %o0, %l0
+	mov  %l2, %i0
+	add  %l2, %l0, %i0
+	ret 
+	restore  %g0, %l3, %o1
diff -urN linux-2.2.18-pre8/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- linux-2.2.18-pre8/arch/sparc64/kernel/sys_sunos32.c	Wed May  3 17:16:32 2000
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Mon Dec  3 20:25:13 2001
@@ -672,7 +672,7 @@
 #define SMNT_SYS5         128
 
 struct sunos_fh_t {
-	char fh_data [NFS_FHSIZE];
+	char fh_data [NFS2_FHSIZE];
 };
 
 struct sunos_nfs_mount_args {
@@ -746,7 +746,7 @@
 
 	server.sin_family = AF_INET;
 	server.sin_addr = addr->sin_addr;
-	server.sin_port = NFS_PORT;
+	server.sin_port = NFS2_PORT;
 
 	/* Call sys_connect */
 	ret = socket->ops->connect (socket, (struct sockaddr *) &server,
diff -urN linux-2.2.18-pre8/drivers/acorn/Makefile linux/drivers/acorn/Makefile
--- linux-2.2.18-pre8/drivers/acorn/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/Makefile	Mon Dec  3 20:25:13 2001
@@ -0,0 +1,12 @@
+#
+# Makefile for the Acorn-specific Linux kernel device 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 (not a .c file).
+
+SUB_DIRS     := block char net scsi
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre8/drivers/acorn/block/Config.in linux/drivers/acorn/block/Config.in
--- linux-2.2.18-pre8/drivers/acorn/block/Config.in	Fri May  8 00:42:38 1998
+++ linux/drivers/acorn/block/Config.in	Mon Dec  3 20:25:13 2001
@@ -10,9 +10,12 @@
   dep_tristate '    RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_BLK_DEV_IDE
 fi
 
-tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM
-if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then
-  bool '   Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT
+if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
+  tristate 'Old Archimedes floppy (1772) support' CONFIG_BLK_DEV_FD1772
+  tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM
+  if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then
+    bool '   Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT
+  fi
 fi
 
 endmenu
diff -urN linux-2.2.18-pre8/drivers/acorn/block/Makefile linux/drivers/acorn/block/Makefile
--- linux-2.2.18-pre8/drivers/acorn/block/Makefile	Mon Feb 16 13:49:36 1998
+++ linux/drivers/acorn/block/Makefile	Mon Dec  3 20:25:13 2001
@@ -14,13 +14,11 @@
 M_OBJS   :=
 MOD_LIST_NAME := ACORN_BLOCK_MODULES
 
-ifeq ($(CONFIG_ARCH_ARC),y)
-  ifeq ($(CONFIG_BLK_DEV_FD),y)
-    L_OBJS += fd1772.o fd1772dma.o
-  else
-    ifeq ($(CONFIG_BLK_DEV_FD),m)
-      M_OBJS += fd1772_mod.o
-    endif
+ifeq ($(CONFIG_BLK_DEV_FD1772),y)
+  L_OBJS += fd1772.o fd1772dma.o
+else
+  ifeq ($(CONFIG_BLK_DEV_FD1772),m)
+    M_OBJS += fd1772_mod.o
   endif
 endif
 
diff -urN linux-2.2.18-pre8/drivers/acorn/block/fd1772.c linux/drivers/acorn/block/fd1772.c
--- linux-2.2.18-pre8/drivers/acorn/block/fd1772.c	Thu Jan  7 08:46:58 1999
+++ linux/drivers/acorn/block/fd1772.c	Mon Dec  3 20:25:13 2001
@@ -114,6 +114,10 @@
  *		  I wish I knew why that timer didn't work.....
  *
  *     16/11/96 - Fiddled and frigged for 2.0.18
+ *
+ * DAG 30/01/99 - Started frobbing for 2.2.1
+ * DAG 20/06/99 - A little more frobbing:
+       Included include/asm/uaccess.h for get_user/put_user
  */
 
 #include <linux/sched.h>
@@ -136,14 +140,16 @@
 #include <asm/dma.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
+#include <asm/ioc.h>
 #include <asm/irq.h>
-#include <asm/irq-no.h>
 #include <asm/pgtable.h>
 #include <asm/segment.h>
+#include <asm/uaccess.h>
+
 
 #define MAJOR_NR FLOPPY_MAJOR
 #define FLOPPY_DMA 0
-#include "blk.h"
+#include <linux/blk.h>
 
 /* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with
  * little additional rework in this file). But I'm not yet sure if
@@ -1598,7 +1604,7 @@
 };
 
 
-int floppy_init(void)
+int fd1772_init(void)
 {
 	int i;
 
diff -urN linux-2.2.18-pre8/drivers/acorn/block/fd1772dma.S linux/drivers/acorn/block/fd1772dma.S
--- linux-2.2.18-pre8/drivers/acorn/block/fd1772dma.S	Fri May  8 00:42:38 1998
+++ linux/drivers/acorn/block/fd1772dma.S	Mon Dec  3 20:25:13 2001
@@ -4,45 +4,45 @@
 .text
 
 
-  .global _fdc1772_dataaddr
-_fdc1772_fiqdata:
+  .global fdc1772_dataaddr
+fdc1772_fiqdata:
 @ Number of bytes left to DMA
-  .global _fdc1772_bytestogo
-_fdc1772_bytestogo:
+  .global fdc1772_bytestogo
+fdc1772_bytestogo:
   .word 0
 @ Place to put/get data from in DMA
-  .global _fdc1772_dataaddr
-_fdc1772_dataaddr:
+  .global fdc1772_dataaddr
+fdc1772_dataaddr:
   .word 0
   
-  .global _fdc1772_fdc_int_done
-_fdc1772_fdc_int_done:
+  .global fdc1772_fdc_int_done
+fdc1772_fdc_int_done:
   .word 0
-  .global _fdc1772_comendstatus
-_fdc1772_comendstatus:
+  .global fdc1772_comendstatus
+fdc1772_comendstatus:
   .word 0
 
 @ We hang this off DMA channel 1
-    .global _fdc1772_comendhandler
-_fdc1772_comendhandler:
+    .global fdc1772_comendhandler
+fdc1772_comendhandler:
   mov      r8,#IOC_BASE
   ldrb     r9,[r8,#0x34]    @ IOC FIQ status
   tst      r9,#2
   subeqs   pc,r14,#4        @ should I leave a space here
   orr      r9,r8,#0x10000   @ FDC base
-  adr      r8,_fdc1772_fdc_int_done
+  adr      r8,fdc1772_fdc_int_done
   ldrb     r10,[r9,#0]  @ FDC status
   mov      r9,#1        @ Got a FIQ flag
   stmia    r8,{r9,r10}
   subs     pc,r14,#4
 
 
-    .global _fdc1772_dma_read
-_fdc1772_dma_read:
+    .global fdc1772_dma_read
+fdc1772_dma_read:
   mov      r8,#IOC_BASE
   ldrb     r9,[r8,#0x34]    @ IOC FIQ status
   tst      r9,#1
-  beq      _fdc1772_dma_read_notours
+  beq      fdc1772_dma_read_notours
   orr      r8,r8,#0x10000   @ FDC base
   ldrb     r10,[r8,#0xc]   @ Read from FDC data reg (also clears interrupt)
   ldmia    r11,{r8,r9}
@@ -51,19 +51,19 @@
   strplb   r10,[r9],#1     @ Store the data and increment the pointer
   stmplia  r11,{r8,r9}     @ Update count/pointers
   @ Handle any other interrupts if there are any
-_fdc1772_dma_read_notours:
+fdc1772_dma_read_notours:
   @ Cant branch because this code has been copied down to the FIQ vector
   ldr pc,[pc,#-4]
-  .word _fdc1772_comendhandler
-  .global _fdc1772_dma_read_end
-_fdc1772_dma_read_end:
+  .word fdc1772_comendhandler
+  .global fdc1772_dma_read_end
+fdc1772_dma_read_end:
 
-    .global _fdc1772_dma_write
-_fdc1772_dma_write:
+    .global fdc1772_dma_write
+fdc1772_dma_write:
   mov      r8,#IOC_BASE
   ldrb     r9,[r8,#0x34]    @ IOC FIQ status
   tst      r9,#1
-  beq      _fdc1772_dma_write_notours
+  beq      fdc1772_dma_write_notours
   orr      r8,r8,#0x10000   @ FDC base
   ldmia    r11,{r9,r10}
   subs     r9,r9,#1        @ One less byte to go
@@ -72,23 +72,23 @@
   strplb   r12,[r8,#0xc]   @ write it to FDC data reg
   stmplia  r11,{r9,r10}    @ Update count and pointer - should clear interrupt
   @ Handle any other interrupts
-_fdc1772_dma_write_notours:
+fdc1772_dma_write_notours:
   @ Cant branch because this code has been copied down to the FIQ vector
   ldr pc,[pc,#-4]
-  .word _fdc1772_comendhandler
+  .word fdc1772_comendhandler
 
-  .global _fdc1772_dma_write_end
-_fdc1772_dma_write_end:
+  .global fdc1772_dma_write_end
+fdc1772_dma_write_end:
   
 
 @ Setup the FIQ R11 to point to the data and store the count, address
 @ for this dma
 @ R0=count
 @ R1=address
-  .global _fdc1772_setupdma
-_fdc1772_setupdma:
+  .global fdc1772_setupdma
+fdc1772_setupdma:
 	@ The big job is flipping in and out of FIQ mode
-	adr	r2,_fdc1772_fiqdata	@ This is what we really came here for
+	adr	r2,fdc1772_fiqdata	@ This is what we really came here for
   stmia  r2,{r0,r1}
 	mov	r3, pc
 	teqp	pc,#0x0c000001	@ Disable FIQs, IRQs and switch to FIQ mode
diff -urN linux-2.2.18-pre8/drivers/acorn/block/ide-ics.c linux/drivers/acorn/block/ide-ics.c
--- linux-2.2.18-pre8/drivers/acorn/block/ide-ics.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/block/ide-ics.c	Mon Dec  3 20:25:13 2001
@@ -4,8 +4,9 @@
  * Copyright (c) 1996,1997 Russell King.
  *
  * Changelog:
- *  08-06-1996	RMK	Created
- *  12-09-1997	RMK	Added interrupt enable/disable
+ *  08-Jun-1996	RMK	Created
+ *  12-Sep-1997	RMK	Added interrupt enable/disable
+ *  17-Apr-1999	RMK	Support for V6 EASI
  */
 
 #include <linux/module.h>
@@ -44,7 +45,8 @@
 #define ICS_ARCIN_V6_IDESTEPPING	4
 
 static const card_ids icside_cids[] = {
-	{ MANU_ICS, PROD_ICS_IDE },
+	{ MANU_ICS,  PROD_ICS_IDE  },
+	{ MANU_ICS2, PROD_ICS2_IDE },
 	{ 0xffff, 0xffff }
 };
 
@@ -81,6 +83,8 @@
 	icside_irqenable_arcin_v5,
 	icside_irqdisable_arcin_v5,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -106,9 +110,22 @@
 	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
 }
 
+/* Prototype: icside_irqprobe(struct expansion_card *ec)
+ * Purpose  : detect an active interrupt from card
+ */
+static int icside_irqpending_arcin_v6(struct expansion_card *ec)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+	return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+	       inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+}
+
 static const expansioncard_ops_t icside_ops_arcin_v6 = {
 	icside_irqenable_arcin_v6,
 	icside_irqdisable_arcin_v6,
+	icside_irqpending_arcin_v6,
+	NULL,
 	NULL,
 	NULL
 };
@@ -157,7 +174,7 @@
 		printk ("icside: ***********************************\n");
 		printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
 		printk ("icside: ***********************************\n");
-		printk ("icside: please report this to: linux@arm.uk.linux.org\n");
+		printk ("icside: please report this to linux@arm.linux.org.uk\n");
 		printk ("icside: defaulting to ARCIN V5\n");
 		iftype = ics_if_arcin_v5;
 		break;
@@ -166,7 +183,9 @@
 	return iftype;
 }
 
-static int icside_register_port(unsigned long dataport, unsigned long ctrlport, int stepping, int irq)
+static int
+icside_register_port(unsigned long dataport, unsigned long ctrlport,
+		     int stepping, int irq, int dma)
 {
 	hw_regs_t hw;
 	int i;
@@ -179,6 +198,7 @@
 	}
 	hw.io_ports[IDE_CONTROL_OFFSET] = ctrlport;
 	hw.irq = irq;
+	hw.dma = dma;
 
 	return ide_register_hw(&hw, NULL);
 }
@@ -189,7 +209,7 @@
  */
 static inline void icside_register (struct expansion_card *ec, int index)
 {
-	unsigned long port;
+	unsigned long port, slot_port;
 
 	result[index][0] = -1;
 	result[index][1] = -1;
@@ -201,44 +221,48 @@
 		break;
 
 	case ics_if_arcin_v5:
-		port = ecard_address (ec, ECARD_MEMC, 0);
-		ec->irqaddr = ioaddr(port + ICS_ARCIN_V5_INTRSTAT);
+		slot_port = ecard_address (ec, ECARD_MEMC, 0);
+		ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT);
 		ec->irqmask = 1;
-		ec->irq_data = (void *)port;
+		ec->irq_data = (void *)slot_port;
 		ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
 
 		/*
 		 * Be on the safe side - disable interrupts
 		 */
-		inb (port + ICS_ARCIN_V5_INTROFFSET);
-		result[index][0] = icside_register_port(port + ICS_ARCIN_V5_IDEOFFSET,
-							port + ICS_ARCIN_V5_IDEALTOFFSET,
+		inb (slot_port + ICS_ARCIN_V5_INTROFFSET);
+		result[index][0] = icside_register_port(slot_port + ICS_ARCIN_V5_IDEOFFSET,
+							slot_port + ICS_ARCIN_V5_IDEALTOFFSET,
 							ICS_ARCIN_V5_IDESTEPPING,
-							ec->irq);
+							ec->irq, ec->dma);
 		result[index][1] = -1;
 		break;
 
 	case ics_if_arcin_v6:
-		port = ecard_address (ec, ECARD_IOC, ECARD_FAST);
-		ec->irqaddr = ioaddr(port + ICS_ARCIN_V6_INTRSTAT_1);
-		ec->irqmask = 1;
+		slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+		port = ecard_address(ec, ECARD_EASI, ECARD_FAST);
+		if (port == 0)
+			port = slot_port;
+		else
+			outb(1 << 5, slot_port);
+
 		ec->irq_data = (void *)port;
 		ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
 
 		/*
 		 * Be on the safe side - disable interrupts
 		 */
-		inb (port + ICS_ARCIN_V6_INTROFFSET_1);
-		inb (port + ICS_ARCIN_V6_INTROFFSET_2);
+		inb(port + ICS_ARCIN_V6_INTROFFSET_1);
+		inb(port + ICS_ARCIN_V6_INTROFFSET_2);
 
 		result[index][0] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1,
 							port + ICS_ARCIN_V6_IDEALTOFFSET_1,
 							ICS_ARCIN_V6_IDESTEPPING,
-							ec->irq);
+							ec->irq, ec->dma);
 		result[index][1] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2,
 							port + ICS_ARCIN_V6_IDEALTOFFSET_2,
 							ICS_ARCIN_V6_IDESTEPPING,
-							ec->irq);
+							ec->irq, ec->dma);
 		break;
 	}		
 }
diff -urN linux-2.2.18-pre8/drivers/acorn/block/ide-rapide.c linux/drivers/acorn/block/ide-rapide.c
--- linux-2.2.18-pre8/drivers/acorn/block/ide-rapide.c	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/block/ide-rapide.c	Mon Dec  3 20:25:13 2001
@@ -13,7 +13,6 @@
 #include <linux/blkdev.h>
 #include <linux/errno.h>
 #include <asm/ecard.h>
-#include <asm/ide.h>
 
 #include "../../block/ide.h"
 
@@ -28,14 +27,20 @@
 static inline int rapide_register(struct expansion_card *ec)
 {
 	unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
-	ide_ioregspec_t spec;
+	hw_regs_t hw;
 
-	spec.base = port;
-	spec.ctrl = port + 0x206;
-	spec.offset = 1 << 4;
-	spec.irq = ec->irq;
+	int i;
 
-	return ide_register_port(&spec);
+	memset(&hw, 0, sizeof(hw));
+
+	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+		hw.io_ports[i] = (ide_ioreg_t)port;
+		port += 1 << 4;
+	}
+	hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206;
+	hw.irq = ec->irq;
+
+	return ide_register_hw(&hw, NULL);
 }
 
 int rapide_init(void)
diff -urN linux-2.2.18-pre8/drivers/acorn/block/mfm.S linux/drivers/acorn/block/mfm.S
--- linux-2.2.18-pre8/drivers/acorn/block/mfm.S	Wed May 20 18:54:37 1998
+++ linux/drivers/acorn/block/mfm.S	Mon Dec  3 20:25:14 2001
@@ -1,44 +1,43 @@
-@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400
-@   motherboard on ST506 podules.
-@ (c) David Alan Gilbert (gilbertd@cs.man.ac.uk) 1996
+@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes
+@   motherboard and on ST506 expansion podules.
+@ (c) David Alan Gilbert (linux@treblig.org) 1996-1999
 
 #include <asm/assembler.h>
  
-_hdc63463_irqdata:
+hdc63463_irqdata:
 @ Controller base address
-  .global _hdc63463_baseaddress
-_hdc63463_baseaddress:
+  .global hdc63463_baseaddress
+hdc63463_baseaddress:
   .word 0
 
-  .global _hdc63463_irqpolladdress
-_hdc63463_irqpolladdress:
+  .global hdc63463_irqpolladdress
+hdc63463_irqpolladdress:
   .word 0
  
-  .global _hdc63463_irqpollmask
-_hdc63463_irqpollmask:
+  .global hdc63463_irqpollmask
+hdc63463_irqpollmask:
   .word 0
 
 @ where to read/write data  from the kernel data space
-  .global _hdc63463_dataptr
-_hdc63463_dataptr:
+  .global hdc63463_dataptr
+hdc63463_dataptr:
   .word 0
 
 @ Number of bytes left to transfer
-  .global _hdc63463_dataleft
-_hdc63463_dataleft:
+  .global hdc63463_dataleft
+hdc63463_dataleft:
   .word 0
 
 @ -------------------------------------------------------------------------
 @ hdc63463_writedma: DMA from host to controller
 @  internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
 @                      r3=data ptr, r4=data left, r5,r6=temporary
-  .global _hdc63463_writedma
-_hdc63463_writedma:
+  .global hdc63463_writedma
+hdc63463_writedma:
   stmfd sp!,{r4-r7}
-  adr r5,_hdc63463_irqdata
+  adr r5,hdc63463_irqdata
   ldmia r5,{r0,r1,r2,r3,r4}
 
-
 writedma_again:
 
   @ test number of remaining bytes to transfer
@@ -89,12 +88,12 @@
 
   @ If we were too slow we had better go through again - DAG - took out with new interrupt routine
   @ sub r0,r0,#32+8
-  @ adr r2,_hdc63463_irqdata
+  @ adr r2,hdc63463_irqdata
   @ ldr r2,[r2,#8]
   @ b writedma_again
 
 writedma_end:
-  adr r5,_hdc63463_irqdata+12
+  adr r5,hdc63463_irqdata+12
   stmia r5,{r3,r4}
   ldmfd sp!,{r4-r7}
   RETINSTR(mov,pc,lr)
@@ -103,10 +102,10 @@
 @ hdc63463_readdma: DMA from controller to host
 @  internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
 @                      r3=data ptr, r4=data left, r5,r6=temporary
-  .global _hdc63463_readdma
-_hdc63463_readdma:
+  .global hdc63463_readdma
+hdc63463_readdma:
   stmfd sp!,{r4-r7}
-  adr r5,_hdc63463_irqdata
+  adr r5,hdc63463_irqdata
   ldmia r5,{r0,r1,r2,r3,r4}
 
 readdma_again:
@@ -157,7 +156,7 @@
   @ b readdma_again
 
 readdma_end:
-  adr r5,_hdc63463_irqdata+12
+  adr r5,hdc63463_irqdata+12
   stmia r5,{r3,r4}
   ldmfd sp!,{r4-r7}
   RETINSTR(mov,pc,lr)
diff -urN linux-2.2.18-pre8/drivers/acorn/block/mfmhd.c linux/drivers/acorn/block/mfmhd.c
--- linux-2.2.18-pre8/drivers/acorn/block/mfmhd.c	Mon Aug 24 13:14:09 1998
+++ linux/drivers/acorn/block/mfmhd.c	Mon Dec  3 20:25:14 2001
@@ -123,6 +123,7 @@
 #include <asm/dma.h>
 #include <asm/hardware.h>
 #include <asm/ecard.h>
+#include <asm/ioc.h>
 
 /*
  * This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc
@@ -261,7 +262,9 @@
 	void (*done) (int st);	/* done handler */
 } *cont = NULL;
 
+#if 0
 static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0};
+#endif
 
 int number_mfm_drives = 1;
 
diff -urN linux-2.2.18-pre8/drivers/acorn/char/Config.in linux/drivers/acorn/char/Config.in
--- linux-2.2.18-pre8/drivers/acorn/char/Config.in	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/char/Config.in	Wed Dec 31 16:00:00 1969
@@ -1,15 +0,0 @@
-if [ "$CONFIG_SERIAL" != "n" ]; then
-  tristate '   Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
-  tristate '   Dual serial port support' CONFIG_DUALSP_SERIAL
-fi
-
-if [ "$CONFIG_MOUSE" = "y" ]; then
-  if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-    if [ "$CONFIG_ARCH_RPC" != "y" ]; then
-      define_bool CONFIG_KBDMOUSE y
-    else
-      define_bool CONFIG_RPCMOUSE y
-    fi
-  fi
-fi
-
diff -urN linux-2.2.18-pre8/drivers/acorn/char/Makefile linux/drivers/acorn/char/Makefile
--- linux-2.2.18-pre8/drivers/acorn/char/Makefile	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/char/Makefile	Mon Dec  3 20:25:14 2001
@@ -9,20 +9,19 @@
 # parent makes..
 #
 
-L_TARGET := acorn-char.a
-M_OBJS   :=
-L_OBJS   :=
+L_TARGET 	:= acorn-char.a
+M_OBJS   	:=
+L_OBJS   	:=
 
-ifeq ($(MACHINE),rpc)
-  MOUSE_OBJS += mouse_rpc.o
-  L_OBJS += keyb_ps2.o
-endif
+L_OBJS_arc	:= keyb_arc.o
+L_OBJS_a5k	:= keyb_arc.o
+L_OBJS_rpc	:= keyb_ps2.o
 
-ifeq ($(CONFIG_MOUSE),y)
-  LX_OBJS += $(MOUSE_OBJS)
+ifeq ($(CONFIG_RPCMOUSE),y)
+  LX_OBJS += mouse_rpc.o
 else
-  ifeq ($(CONFIG_MOUSE),m)
-    MX_OBJS += $(MOUSE_OBJS)
+  ifeq ($(CONFIG_RPCMOUSE),m)
+    MX_OBJS += mouse_rpc.o
   endif
 endif
 
@@ -41,5 +40,7 @@
     M_OBJS += serial-dualsp.o
   endif
 endif
+
+L_OBJS += $(L_OBJS_$(MACHINE))
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre8/drivers/acorn/char/keyb_arc.c linux/drivers/acorn/char/keyb_arc.c
--- linux-2.2.18-pre8/drivers/acorn/char/keyb_arc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/char/keyb_arc.c	Mon Dec  3 20:25:14 2001
@@ -0,0 +1,451 @@
+/*
+ * linux/arch/arm/drivers/char1/keyb_arc.c
+ *
+ * Acorn keyboard driver for ARM Linux.
+ *
+ * The Acorn keyboard appears to have a ***very*** buggy reset protocol -
+ * every reset behaves differently.  We try to get round this by attempting
+ * a few things...
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/ptrace.h>
+#include <linux/signal.h>
+#include <linux/timer.h>
+#include <linux/random.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/kbd_ll.h>
+#include <linux/tty.h>
+#include <linux/kbd_kern.h>
+#include <linux/delay.h>
+
+#include <asm/bitops.h>
+#include <asm/keyboard.h>
+#include <asm/irq.h>
+#include <asm/ioc.h>
+#include <asm/hardware.h>
+
+#include "../../char/mouse.h"
+
+extern void kbd_reset_kdown(void);
+
+#define VERSION 108
+
+#define KBD_REPORT_ERR
+#define KBD_REPORT_UNKN
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+static char kbd_txval[4];
+static unsigned char kbd_txhead, kbd_txtail;
+#define KBD_INCTXPTR(ptr) ((ptr) = ((ptr) + 1) & 3)
+static int kbd_id = -1;
+static struct wait_queue *kbd_waitq;
+#ifdef CONFIG_KBDMOUSE
+static int mousedev;
+#endif
+
+/*
+ * Protocol codes to send the keyboard.
+ */
+#define HRST 0xff	/* reset keyboard */
+#define RAK1 0xfe	/* reset response */
+#define RAK2 0xfd	/* reset response */
+#define BACK 0x3f	/* Ack for first keyboard pair */
+#define SMAK 0x33	/* Last data byte ack (key scanning + mouse movement scanning) */
+#define MACK 0x32	/* Last data byte ack (mouse movement scanning) */
+#define SACK 0x31	/* Last data byte ack (key scanning) */
+#define NACK 0x30	/* Last data byte ack (no scanning, mouse data) */
+#define RQMP 0x22	/* Request mouse data */
+#define PRST 0x21	/* nothing */
+#define RQID 0x20	/* Request ID */
+
+#define UP_FLAG 1
+
+#ifdef CONFIG_MAGIC_SYSRQ
+unsigned char a5kkbd_sysrq_xlate[] = 
+{
+    27,    0,    0,    0,    0,    0,    0,    0,
+     0,    0,    0,    0,    0,    0,    0,    0,
+   '`',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+   '8',  '9',  '0',  '-',  '=',  '£',  127,    0,
+     0,    0,    0,  '/',  '*',  '#',    9,  'q',
+   'w',  'e',  'r',  't',  'y',  'u',  'i',  'o',
+   'p',  '[',  ']', '\\',  22,    23,   25,  '7',
+   '8',  '9',  '-',    0,  'a',  's',  'd',  'f',
+   'g',  'h',  'j',  'k',  'l',  ';', '\'',   13,
+   '4',  '5',  '6',  '+',    0,    0,  'z',  'x',
+   'c',  'v',  'b',  'n',  'm',  ',',  '.',  '/',
+     0,    0,  '1',  '2',  '3',    0,    0,  ' ',
+     0,    0,    0,    0,    0,  '0',  '.',   10,
+     0,    0,    0,    0,    0,    0,    0,    0,
+     0,    0,    0,    0,    0,    0,    0,    0,
+     0,    0,    0,    0,    0,    0,    0,    0,
+};
+#endif
+
+/*
+ * This array converts the scancode that we get from the keyboard to the
+ * real rows/columns on the A5000 keyboard.  This might be keyboard specific...
+ *
+ * It is these values that we use to maintain the key down array.  That way, we
+ * should pick up on the ghost key presses (which is what happens when you press
+ * three keys, and the keyboard thinks you have pressed four!)
+ *
+ * Row 8 (0x80+c) is actually a column with one key per row.  It is isolated from
+ * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc).
+ *
+ * Illegal scancodes are denoted by an 0xff (in other words, we don't know about
+ * them, and can't process them for ghosts).  This does however, cause problems with
+ * autorepeat processing...
+ */
+static unsigned char scancode_2_colrow[256] = {
+  0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60,
+  0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79,
+  0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a,
+  0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34,
+  0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12,
+  0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03,
+  0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff,
+};
+
+#define BITS_PER_SHORT (8*sizeof(unsigned short))
+static unsigned short ghost_down[128/BITS_PER_SHORT];
+
+static void a5kkbd_key(unsigned int keycode, unsigned int up_flag)
+{
+	unsigned int real_keycode;
+
+	if (keycode > 0x72) {
+#ifdef KBD_REPORT_UNKN
+		printk ("kbd: unknown scancode 0x%04x\n", keycode);
+#endif
+		return;
+	}
+	if (keycode >= 0x70) {
+#ifdef CONFIG_KBDMOUSE
+		if (mousedev >= 0)
+			switch (keycode) {
+			case 0x70: /* Left mouse button */
+				busmouse_add_buttons(mousedev, 4, up_flag ? 4 : 0);
+				break;
+
+			case 0x71: /* Middle mouse button */
+				busmouse_add_buttons(mousedev, 2, up_flag ? 2 : 0);
+				break;
+
+			case 0x72:/* Right mouse button */
+				busmouse_add_buttons(mousedev, 1, up_flag ? 1 : 0);
+				break;
+			}
+#endif
+		return;
+	}
+
+	/*
+	 * We have to work out if we accept this key press as a real key, or
+	 * if it is a ghost.  IE. If you press three keys, the keyboard will think
+	 * that you've pressed a fourth: (@ = key down, # = ghost)
+	 *
+	 *   0 1 2 3 4 5 6 7
+	 *   | | | | | | | |
+	 * 0-+-+-+-+-+-+-+-+-
+	 *   | | | | | | | |
+	 * 1-+-@-+-+-+-@-+-+-
+	 *   | | | | | | | |
+	 * 2-+-+-+-+-+-+-+-+-
+	 *   | | | | | | | |
+	 * 3-+-@-+-+-+-#-+-+-
+	 *   | | | | | | | |
+	 *
+	 * This is what happens when you have a matrix keyboard...
+	 */
+
+	real_keycode = scancode_2_colrow[keycode];
+
+	if ((real_keycode & 0x80) == 0) {
+		int rr, kc = (real_keycode >> 4) & 7;
+		int cc;
+		unsigned short res, kdownkc;
+
+		kdownkc = ghost_down[kc] | (1 << (real_keycode & 15));
+
+		for (rr = 0; rr < 128/BITS_PER_SHORT; rr++)
+			if (rr != kc && (res = ghost_down[rr] & kdownkc)) {
+			    	/*
+				 * we have found a second row with at least one key pressed in the
+			    	 * same column.
+			    	 */
+			    	for (cc = 0; res; res >>= 1)
+					cc += (res & 1);
+				if (cc > 1)
+					return; /* ignore it */
+			}
+		if (up_flag)
+			clear_bit (real_keycode, ghost_down);
+		else
+			set_bit (real_keycode, ghost_down);
+	}
+
+	handle_scancode(keycode, !up_flag);
+}
+
+static inline void a5kkbd_sendbyte(unsigned char val)
+{
+	kbd_txval[kbd_txhead] = val;
+	KBD_INCTXPTR(kbd_txhead);
+	enable_irq(IRQ_KEYBOARDTX);
+}
+
+static inline void a5kkbd_reset(void)
+{
+	int i;
+
+	for (i = 0; i < NR_SCANCODES/BITS_PER_SHORT; i++)
+		ghost_down[i] = 0;
+
+	kbd_reset_kdown();
+}
+
+void a5kkbd_leds(unsigned char leds)
+{
+	leds =  ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) |
+		((leds & (1<<VC_CAPSLOCK))?1:0);
+	a5kkbd_sendbyte(leds);
+}
+
+/* Keyboard states:
+ *  0 initial reset condition, receive HRST, send RRAK1
+ *  1 Sent RAK1, wait for RAK1, send RRAK2
+ *  2 Sent RAK2, wait for RAK2, send SMAK or RQID
+ *  3 Sent RQID, expect KBID, send SMAK
+ *  4 Sent SMAK, wait for anything
+ *  5 Wait for second keyboard nibble for key pressed
+ *  6 Wait for second keyboard nibble for key released
+ *  7 Wait for second part of mouse data
+ *
+ * This function returns 1 when we successfully enter the IDLE state
+ * (and hence need to do some keyboard processing).
+ */
+#define KBD_INITRST	0
+#define KBD_RAK1	1
+#define KBD_RAK2	2
+#define KBD_ID		3
+#define KBD_IDLE	4
+#define KBD_KEYDOWN	5
+#define KBD_KEYUP	6
+#define KBD_MOUSE	7
+
+static int handle_rawcode(unsigned int keyval)
+{
+	static signed char kbd_mousedx = 0;
+	       signed char kbd_mousedy;
+	static unsigned char kbd_state = KBD_INITRST;
+	static unsigned char kbd_keyhigh = 0;
+
+	if (keyval == HRST && kbd_state != KBD_INITRST && kbd_state != KBD_ID) {
+		a5kkbd_sendbyte (HRST);
+		a5kkbd_reset ();
+		kbd_state = KBD_INITRST;
+	} else switch(kbd_state) {
+	case KBD_INITRST:			/* hard reset - sent HRST */
+		if (keyval == HRST) {
+			a5kkbd_sendbyte (RAK1);
+			kbd_state = KBD_RAK1;
+		} else if (keyval == RAK1) {
+			/* Some A5000 keyboards are very fussy and don't follow Acorn's
+			 * specs - this appears to fix them, but them it might stop
+			 * them from being initialised.
+			 *  fix by Philip Blundell
+			 */
+			printk(KERN_DEBUG "keyboard sent early RAK1 -- ignored\n");
+		} else
+			goto kbd_wontreset;
+		break;
+
+	case KBD_RAK1:				/* sent RAK1 - expect RAK1 and send RAK2 */
+		if (keyval == RAK1) {
+			a5kkbd_sendbyte (RAK2);
+			kbd_state = KBD_RAK2;
+		} else
+			goto kbd_wontreset;
+		break;
+
+	case KBD_RAK2:				/* Sent RAK2 - expect RAK2 and send either RQID or SMAK */
+		if (keyval == RAK2) {
+			if (kbd_id == -1) {
+				a5kkbd_sendbyte (NACK);
+				a5kkbd_sendbyte (RQID);
+				kbd_state = KBD_ID;
+			} else {
+				a5kkbd_sendbyte (SMAK);
+				kbd_state = KBD_IDLE;
+			}
+		} else
+			goto kbd_wontreset;
+		break;
+
+	case KBD_ID:				/* Sent RQID - expect KBID */
+		if (keyval == HRST) {
+			kbd_id = -2;
+			a5kkbd_reset ();
+			a5kkbd_sendbyte (HRST);
+			kbd_state = KBD_INITRST;
+			wake_up (&kbd_waitq);
+		} else if ((keyval & 0xc0) == 0x80) {
+			kbd_id = keyval & 0x3f;
+			a5kkbd_sendbyte (SMAK);
+			kbd_state = KBD_IDLE;
+			wake_up (&kbd_waitq);
+		}
+		break;
+
+	case KBD_IDLE:				/* Send SMAK, ready for any reply */
+		switch (keyval & 0xf0) {
+		default:	/* 0x00 - 0x7f */
+			kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval;
+			kbd_state   = KBD_MOUSE;
+			a5kkbd_sendbyte (BACK);
+			break;
+
+		case 0x80:
+		case 0x90:
+		case 0xa0:
+		case 0xb0:
+		    	if (kbd_id == -1)
+				kbd_id = keyval & 0x3f;
+			break;
+
+		case 0xc0:
+			kbd_keyhigh = keyval;
+			kbd_state   = KBD_KEYDOWN;
+			a5kkbd_sendbyte (BACK);
+			break;
+
+		case 0xd0:
+			kbd_keyhigh = keyval;
+			kbd_state   = KBD_KEYUP;
+			a5kkbd_sendbyte (BACK);
+			break;
+
+		case 0xe0:
+		case 0xf0:
+			goto kbd_error;
+		}
+		break;
+
+	case KBD_KEYDOWN:
+		if ((keyval & 0xf0) != 0xc0)
+			goto kbd_error;
+		else {
+			kbd_state = KBD_IDLE;
+			a5kkbd_sendbyte (SMAK);
+			if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
+				a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), 0);
+		}
+		break;
+
+	case KBD_KEYUP:
+		if ((keyval & 0xf0) != 0xd0)
+			goto kbd_error;
+		else {
+			kbd_state = KBD_IDLE;
+			a5kkbd_sendbyte (SMAK);
+			if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
+				a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), UP_FLAG);
+		}
+		break;
+
+	case KBD_MOUSE:
+		if (keyval & 0x80)
+			goto kbd_error;
+		else {
+			kbd_state = KBD_IDLE;
+			a5kkbd_sendbyte (SMAK);
+			kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval);
+#ifdef CONFIG_KBDMOUSE
+			if (mousedev >= 0)
+				busmouse_add_movement(mousedev, (int)kbd_mousedx, (int)kbd_mousedy);
+#endif
+		}
+	}
+	return kbd_state == KBD_IDLE ? 1 : 0;
+
+kbd_wontreset:
+#ifdef KBD_REPORT_ERR
+	printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n",
+		kbd_state, keyval);
+#endif
+	mdelay(1);
+	inb(IOC_KARTRX);
+	a5kkbd_sendbyte (HRST);
+	kbd_state = KBD_INITRST;
+	return 0;
+
+kbd_error:
+#ifdef KBD_REPORT_ERR
+	printk ("kbd: keyboard out of sync - resetting\n");
+#endif
+	a5kkbd_sendbyte (HRST);
+	kbd_state = KBD_INITRST;
+	return 0;
+}
+
+static void a5kkbd_rx(int irq, void *dev_id, struct pt_regs *regs)
+{
+	kbd_pt_regs = regs;
+	if (handle_rawcode(inb(IOC_KARTRX)))
+		mark_bh (KEYBOARD_BH);
+}
+
+static void a5kkbd_tx(int irq, void *dev_id, struct pt_regs *regs)
+{
+	outb (kbd_txval[kbd_txtail], IOC_KARTTX);
+	KBD_INCTXPTR(kbd_txtail);
+	if (kbd_txtail == kbd_txhead)
+		disable_irq(irq);
+}
+
+#ifdef CONFIG_KBDMOUSE
+static struct busmouse a5kkbd_mouse = {
+	6, "kbdmouse", NULL, NULL, 7
+};
+#endif
+
+__initfunc(void a5kkbd_init_hw (void))
+{
+	unsigned long flags;
+
+	save_flags_cli (flags);
+	if (request_irq (IRQ_KEYBOARDTX, a5kkbd_tx, 0, "keyboard", NULL) != 0)
+		panic("Could not allocate keyboard transmit IRQ!");
+	disable_irq (IRQ_KEYBOARDTX);
+	if (request_irq (IRQ_KEYBOARDRX, a5kkbd_rx, 0, "keyboard", NULL) != 0)
+		panic("Could not allocate keyboard receive IRQ!");
+	(void)inb(IOC_KARTRX);
+	restore_flags (flags);
+
+	a5kkbd_sendbyte (HRST);	/* send HRST (expect HRST) */
+
+	/* wait 1s for keyboard to initialise */
+	interruptible_sleep_on_timeout(&kbd_waitq, HZ);
+
+#ifdef CONFIG_KBDMOUSE
+	mousedev = register_busmouse(&a5kkbd_mouse);
+	if (mousedev < 0)
+		printk(KERN_ERR "Unable to register mouse driver\n");
+#endif
+
+	printk (KERN_INFO "Keyboard driver v%d.%02d. (", VERSION/100, VERSION%100);
+	if (kbd_id != -1)
+	      printk ("id=%d ", kbd_id);
+	printk ("English)\n");
+}
diff -urN linux-2.2.18-pre8/drivers/acorn/char/keyb_ps2.c linux/drivers/acorn/char/keyb_ps2.c
--- linux-2.2.18-pre8/drivers/acorn/char/keyb_ps2.c	Mon Apr 26 13:31:49 1999
+++ linux/drivers/acorn/char/keyb_ps2.c	Mon Dec  3 20:25:14 2001
@@ -25,6 +25,7 @@
 #include <asm/irq.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
+#include <asm/iomd.h>
 #include <asm/system.h>
 
 extern void kbd_reset_kdown(void);
@@ -221,14 +222,7 @@
 };
 #endif
 
-int ps2kbd_translate(unsigned char scancode, unsigned char *keycode_p, char *uf_p)
-{
-	*uf_p = scancode & 0200;
-	*keycode_p = scancode & 0x7f;
-	return 1;
-}
-
-static void ps2kbd_key(unsigned int keycode, unsigned int up_flag)
+static inline void ps2kbd_key(unsigned int keycode, unsigned int up_flag)
 {
 	handle_scancode(keycode, !up_flag);
 }
diff -urN linux-2.2.18-pre8/drivers/acorn/char/mouse_rpc.c linux/drivers/acorn/char/mouse_rpc.c
--- linux-2.2.18-pre8/drivers/acorn/char/mouse_rpc.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/char/mouse_rpc.c	Mon Dec  3 20:25:14 2001
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/char/rpcmouse.c
+ * linux/drivers/char/mouse_rpc.c
  *
  * Copyright (C) 1996-1998 Russell King
  *
@@ -16,6 +16,7 @@
 #include <asm/hardware.h>
 #include <asm/irq.h>
 #include <asm/io.h>
+#include <asm/iomd.h>
 
 #include "../../char/mouse.h"
 
diff -urN linux-2.2.18-pre8/drivers/acorn/char/serial-card.c linux/drivers/acorn/char/serial-card.c
--- linux-2.2.18-pre8/drivers/acorn/char/serial-card.c	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/char/serial-card.c	Mon Dec  3 20:25:14 2001
@@ -33,14 +33,20 @@
 #ifdef MODULE
 static int __serial_ports[NUM_SERIALS];
 static int __serial_pcount;
+static int __serial_addr[NUM_SERIALS];
 static struct expansion_card *expcard[MAX_ECARDS];
 #define ADD_ECARD(ec,card) expcard[(card)] = (ec)
-#define ADD_PORT(port) __serial_ports[__serial_pcount++] = (port)
+#define ADD_PORT(port,addr)					\
+	do {							\
+		__serial_ports[__serial_pcount] = (port);	\
+		__serial_addr[__serial_pcount] = (addr);	\
+		__serial_pcount += 1;				\
+	} while (0)
 #undef MY_INIT
 #define MY_INIT init_module
 #else
 #define ADD_ECARD(ec,card)
-#define ADD_PORT(port)
+#define ADD_PORT(port,addr)
 #endif
 
 static const card_ids serial_cids[] = { MY_CARD_LIST, { 0xffff, 0xffff } };
@@ -75,12 +81,15 @@
 	cardaddr = MY_BASE_ADDRESS(ec);
 
 	for (port = 0; port < MY_NUMPORTS; port ++) {
+	    unsigned long address;
 	    int line;
 
-	    line = serial_register_onedev (MY_PORT_ADDRESS(port, cardaddr), ec->irq);
+	    address = MY_PORT_ADDRESS(port, cardaddr);
+
+	    line = serial_register_onedev (address, ec->irq);
 	    if (line < 0)
 		break;
-	    ADD_PORT(line);
+	    ADD_PORT(line, address);
 	}
 
 	if (port) {
@@ -97,8 +106,10 @@
 {
     int i;
 
-    for (i = 0; i < __serial_pcount; i++)
-	unregister_serial (__serial_ports[i]);
+    for (i = 0; i < __serial_pcount; i++) {
+	unregister_serial(__serial_ports[i]);
+	release_region(__serial_addr[i], 8);
+    }
 
     for (i = 0; i < MAX_ECARDS; i++)
 	if (expcard[i])
diff -urN linux-2.2.18-pre8/drivers/acorn/net/ether1.c linux/drivers/acorn/net/ether1.c
--- linux-2.2.18-pre8/drivers/acorn/net/ether1.c	Wed Dec 23 09:44:41 1998
+++ linux/drivers/acorn/net/ether1.c	Mon Dec  3 20:25:14 2001
@@ -71,7 +71,7 @@
 #define BUS_16 16
 #define BUS_8  8
 
-static const card_ids ether1_cids[] = {
+static const card_ids __init ether1_cids[] = {
 	{ MANU_ACORN, PROD_ACORN_ETHER1 },
 	{ 0xffff, 0xffff }
 };
@@ -128,7 +128,7 @@
 {
 	int used;
 
-	addr = IO_BASE + (addr << 2);
+	addr = ioaddr(addr);
 
 	__asm__ __volatile__(
 		"subs	%3, %3, #2
@@ -171,7 +171,7 @@
 {
 	int used;
 
-	addr = IO_BASE + (addr << 2);
+	addr = ioaddr(addr);
 
 	__asm__ __volatile__(
 		"subs	%3, %3, #2
@@ -659,12 +659,6 @@
 	/* Fill in the fields of the device structure with ethernet values */
 	ether_setup (dev);
 
-#ifndef CLAIM_IRQ_AT_OPEN
-	if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) {
-		kfree (dev->priv);
-		return -EAGAIN;
-	}
-#endif
 	return 0;
 }	
     
@@ -751,6 +745,8 @@
 
 	if (ether1_init_for_open (dev))
 		printk (KERN_ERR "%s: unable to restart interface\n", dev->name);
+	else
+		priv->restart = 0;
 
 	dev->start = 1;
 }
@@ -759,18 +755,16 @@
 ether1_open (struct device *dev)
 {
 	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
-#ifdef CLAIM_IRQ_AT_OPEN
+
 	if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev))
 		return -EAGAIN;
-#endif
+
 	MOD_INC_USE_COUNT;
 
 	memset (&priv->stats, 0, sizeof (struct enet_statistics));
 
 	if (ether1_init_for_open (dev)) {
-#ifdef CLAIM_IRQ_AT_OPEN
 		free_irq (dev->irq, dev);
-#endif
 		MOD_DEC_USE_COUNT;
 		return -EAGAIN;
 	}
@@ -1080,12 +1074,10 @@
 static int
 ether1_close (struct device *dev)
 {
-#ifdef CLAIM_IRQ_AT_OPEN
-	free_irq (dev->irq, dev);
-#endif
-
 	ether1_reset (dev);
 
+	free_irq(dev->irq, dev);
+
 	dev->start = 0;
 	dev->tbusy = 0;
 
@@ -1117,56 +1109,46 @@
 
 #ifdef MODULE
 
-static char ethernames[MAX_ECARDS][9];
-static struct device *my_ethers[MAX_ECARDS];
-static struct expansion_card *ec[MAX_ECARDS];
+static struct ether_dev {
+	struct expansion_card	*ec;
+	char			name[9];
+	struct device		dev;
+} ether_devs[MAX_ECARDS];
 
 int
 init_module (void)
 {
-	int i;
+	struct expansion_card *ec;
+	int i, ret = -ENODEV;
 
-	for (i = 0; i < MAX_ECARDS; i++) {
-		my_ethers[i] = NULL;
-		ec[i] = NULL;
-		strcpy (ethernames[i], "        ");
-	}
+	memset(ether_devs, 0, sizeof(ether_devs));
 
+	ecard_startfind ();
+	ec = ecard_find(0, ether1_cids);
 	i = 0;
 
-	ecard_startfind ();
+	while (ec && i < MAX_ECARDS) {
+		ecard_claim(ec);
 
-	do {
-		if ((ec[i] = ecard_find(0, ether1_cids)) == NULL)
+		ether_devs[i].ec	    = ec;
+		ether_devs[i].dev.irq	    = ec->irq;
+		ether_devs[i].dev.base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+		ether_devs[i].dev.init	    = ether1_probe;
+		ether_devs[i].dev.name	    = ether_devs[i].name;
+
+		ret = register_netdev(&ether_devs[i].dev);
+
+		if (ret) {
+			ecard_release(ec);
+			ether_devs[i].ec = NULL;
 			break;
-
-		my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL);
-		memset (my_ethers[i], 0, sizeof (struct device));
-
-		my_ethers[i]->irq = ec[i]->irq;
-		my_ethers[i]->base_addr = ecard_address (ec[i], ECARD_IOC, ECARD_FAST);
-		my_ethers[i]->init = ether1_probe;
-		my_ethers[i]->name = ethernames[i];
-
-		ecard_claim (ec[i]);
-
-		if (register_netdev (my_ethers[i]) != 0) {
-			for (i = 0; i < 4; i++) {
-				if (my_ethers[i]) {
-					kfree (my_ethers[i]);
-					my_ethers[i] = NULL;
-				}
-				if (ec[i]) {
-					ecard_release (ec[i]);
-					ec[i] = NULL;
-				}
-			}
-			return -EIO;
 		}
-		i++;
-	} while (i < MAX_ECARDS);
 
-	return i != 0 ? 0 : -ENODEV;
+		i += 1;
+		ec = ecard_find(0, ether1_cids);
+	}
+
+	return i != 0 ? 0 : ret;
 }
 
 void
@@ -1175,18 +1157,15 @@
 	int i;
 
 	for (i = 0; i < MAX_ECARDS; i++) {
-		if (my_ethers[i]) {
-			unregister_netdev (my_ethers[i]);
-			release_region (my_ethers[i]->base_addr, 16);
-			release_region (my_ethers[i]->base_addr + 0x800, 4096);
-#ifndef CLAIM_IRQ_AT_OPEN
-			free_irq (my_ethers[i]->irq, my_ethers[i]);
-#endif
-			my_ethers[i] = NULL;
-		}
-		if (ec[i]) {
-			ecard_release (ec[i]);
-			ec[i] = NULL;
+		if (ether_devs[i].ec) {
+			unregister_netdev(&ether_devs[i].dev);
+
+			release_region(ether_devs[i].dev.base_addr, 16);
+			release_region(ether_devs[i].dev.base_addr + 0x800, 4096);
+
+			ecard_release(ether_devs[i].ec);
+
+			ether_devs[i].ec = NULL;
 		}
 	}
 }
diff -urN linux-2.2.18-pre8/drivers/acorn/net/ether3.c linux/drivers/acorn/net/ether3.c
--- linux-2.2.18-pre8/drivers/acorn/net/ether3.c	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/net/ether3.c	Mon Dec  3 20:25:14 2001
@@ -33,11 +33,12 @@
  *				packet starts two bytes from the end of the
  *				buffer, it corrupts the receiver chain, and
  *				never updates the transmit status correctly.
- * TODO:
- *  When we detect a fatal error on the interface, we should restart it.
+ * 1.14	RMK	07/01/1998	Added initial code for ETHERB addressing.
+ * 1.15	RMK	30/04/1999	More fixes to the transmit routine for buggy
+ *				hardware.
  */
 
-static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.13\n";
+static char *version = "ether3 ethernet driver (c) 1995-1999 R.M.King v1.15\n";
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -66,7 +67,7 @@
 #include "ether3.h"
 
 static unsigned int net_debug = NET_DEBUG;
-static const card_ids ether3_cids[] = {
+static const card_ids __init ether3_cids[] = {
 	{ MANU_ANT2, PROD_ANT_ETHER3 },
 	{ MANU_ANT,  PROD_ANT_ETHER3 },
 	{ MANU_ANT,  PROD_ANT_ETHERB }, /* trial - will etherb work? */
@@ -77,9 +78,6 @@
 static int  ether3_rx(struct device *dev, struct dev_priv *priv, unsigned int maxcnt);
 static void ether3_tx(struct device *dev, struct dev_priv *priv);
 
-extern int inswb(int reg, void *buffer, int len);
-extern int outswb(int reg, void *buffer, int len);
-
 #define BUS_16		2
 #define BUS_8		1
 #define BUS_UNKNOWN	0
@@ -88,7 +86,7 @@
  * I'm not sure what address we should default to if the internal one
  * is corrupted...
  */
-unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+unsigned char def_eth_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
 
 /* --------------------------------------------------------------------------- */
 
@@ -99,10 +97,20 @@
 
 /*
  * ether3 read/write.  Slow things down a bit...
+ * The SEEQ8005 doesn't like us writing to it's registers
+ * too quickly.
  */
-#define ether3_outb(v,r)	{ outb((v),(r)); udelay(1); }
-#define ether3_outw(v,r)	{ outw((v),(r)); udelay(1); }
+static inline void ether3_outb(int v, const int r)
+{
+	outb(v, r);
+	udelay(1);
+}
 
+static inline void ether3_outw(int v, const int r)
+{
+	outw(v, r);
+	udelay(1);
+}
 #define ether3_inb(r)		({ unsigned int __v = inb((r)); udelay(1); __v; })
 #define ether3_inw(r)		({ unsigned int __v = inw((r)); udelay(1); __v; })
 
@@ -138,7 +146,7 @@
  * write data to the buffer memory
  */
 #define ether3_writebuffer(dev,data,length)			\
-	outswb(REG_BUFWIN, (data), (length))
+	outsw(REG_BUFWIN, (data), (length) >> 1)
 
 #define ether3_writeword(dev,data)				\
 	outw((data), REG_BUFWIN)
@@ -153,7 +161,7 @@
  * read data from the buffer memory
  */
 #define ether3_readbuffer(dev,data,length)			\
-	inswb(REG_BUFWIN, (data), (length))
+	insw(REG_BUFWIN, (data), (length) >> 1)
 
 #define ether3_readword(dev)					\
 	inw(REG_BUFWIN)
@@ -249,7 +257,7 @@
 			}
 		} else {
 			if (bad != -1) {
-			    	if (bad != i - 1)
+				if (bad != i - 1)
 					printk(" - 0x%04X\n", i - 1);
 				printk("\n");
 				bad = -1;
@@ -335,7 +343,6 @@
 	for (i = 0; i < 6; i++)
 		ether3_outb(dev->dev_addr[i], REG_BUFWIN);
 
-	priv->tx_used	= 0;
 	priv->tx_head	= 0;
 	priv->tx_tail	= 0;
 	priv->regs.config2 |= CFG2_CTRLO;
@@ -471,6 +478,25 @@
 	return error;
 }
 
+__initfunc(static void
+ether3_get_dev(struct device *dev, struct expansion_card *ec))
+{
+	ecard_claim(ec);
+
+	dev->base_addr = ecard_address(ec, ECARD_MEMC, 0);
+	dev->irq = ec->irq;
+
+	if (ec->cid.manufacturer == MANU_ANT &&
+	    ec->cid.product == PROD_ANT_ETHERB) {
+		dev->base_addr += 0x200;
+	}
+
+	ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr);
+	ec->irqmask = 0xf0;
+
+	ether3_addr(dev->dev_addr, ec);
+}
+
 #ifndef MODULE
 __initfunc(int
 ether3_probe(struct device *dev))
@@ -485,12 +511,8 @@
 	if ((ec = ecard_find(0, ether3_cids)) == NULL)
 		return ENODEV;
 
-	dev->base_addr = ecard_address(ec, ECARD_MEMC, 0);
-	dev->irq = ec->irq;
-
-	ecard_claim(ec);
+	ether3_get_dev(dev, ec);
 
-	ether3_addr(dev->dev_addr, ec);
 	return ether3_probe1(dev);
 }
 #endif
@@ -581,33 +603,6 @@
 }
 
 /*
- * Allocate memory in transmitter ring buffer.
- */
-static int
-ether3_alloc_tx(struct dev_priv *priv, int length, int alloc)
-{
-	int start, head, tail;
-
-	tail = priv->tx_tail;
-	start = priv->tx_head;
-	head = start + length + 4;
-
-	if (head >= TX_END) {
-		if (tail > priv->tx_head)
-			return -1;
-		head -= TX_END - TX_START;
-		if (tail < head)
-			return -1;
-	} else if (start < tail && tail < head)
-		return -1;
-
-	if (alloc)
-		priv->tx_head = head;
-
-	return start;
-}
-
-/*
  * Transmit a packet
  */
 static int
@@ -622,7 +617,7 @@
 		if (!test_and_set_bit(0, (void *)&dev->tbusy)) {
 			unsigned long flags;
 			unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-			int ptr;
+			unsigned int ptr, next_ptr;
 
 			length = (length + 1) & ~1;
 
@@ -633,23 +628,31 @@
 				return 0;
 			}
 
+			next_ptr = (priv->tx_head + 1) & 15;
+
 			save_flags_cli(flags);
 
-			ptr = ether3_alloc_tx(priv, length, 1);
-			if (ptr == -1)
+			if (priv->tx_tail == next_ptr) {
+				restore_flags(flags);
 				return 1;	/* unable to queue */
+			}
+
+			dev->trans_start = jiffies;
+			ptr		 = 0x600 * priv->tx_head;
+			priv->tx_head	 = next_ptr;
+			next_ptr	*= 0x600;
 
 #define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS)
 
-			ether3_setbuffer(dev, buffer_write, priv->tx_head);
+			ether3_setbuffer(dev, buffer_write, next_ptr);
 			ether3_writelong(dev, 0);
-
 			ether3_setbuffer(dev, buffer_write, ptr);
 			ether3_writelong(dev, 0);
 			ether3_writebuffer(dev, skb->data, length);
-
+			ether3_writeword(dev, htons(next_ptr));
+			ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16);
 			ether3_setbuffer(dev, buffer_write, ptr);
-			ether3_writeword(dev, htons(priv->tx_head));
+			ether3_writeword(dev, htons((ptr + length + 4)));
 			ether3_writeword(dev, TXHDR_FLAGS >> 16);
 			ether3_ledon(dev, priv);
 
@@ -658,11 +661,10 @@
 				ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND);
 			}
 
-			if (ether3_alloc_tx(priv, 2044, 0) != -1)
+			next_ptr = (priv->tx_head + 1) & 15;
+			if (priv->tx_tail != next_ptr)
 				dev->tbusy = 0;
 
-			dev->trans_start = jiffies;
-
 			restore_flags(flags);
 
 			dev_kfree_skb(skb);
@@ -689,7 +691,7 @@
 			ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2));
 		printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name,
 			ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR));
-		printk(KERN_ERR "%s: tx head=%04X tx tail=%04X\n", dev->name,
+		printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name,
 			priv->tx_head, priv->tx_tail);
 		ether3_setbuffer(dev, buffer_read, priv->tx_tail);
 		printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev));
@@ -698,8 +700,9 @@
 		dev->tbusy = 0;
 		priv->regs.config2 |= CFG2_CTRLO;
 		priv->stats.tx_errors += 1;
-		ether3_outw(priv->regs.config2 , REG_CONFIG2);
+		ether3_outw(priv->regs.config2, REG_CONFIG2);
 		dev->trans_start = jiffies;
+		priv->tx_head = priv->tx_tail = 0;
 		goto retry;
 	}
 }
@@ -867,6 +870,7 @@
 ether3_tx(struct device *dev, struct dev_priv *priv)
 {
 	unsigned int tx_tail = priv->tx_tail;
+	int max_work = 14;
 
 	do {
 	    	unsigned long status;
@@ -874,7 +878,7 @@
     		/*
 	    	 * Read the packet header
     		 */
-	    	ether3_setbuffer(dev, buffer_read, tx_tail);
+	    	ether3_setbuffer(dev, buffer_read, tx_tail * 0x600);
     		status = ether3_readlong(dev);
 
 		/*
@@ -895,95 +899,72 @@
 			if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++;
 		}
 
-		tx_tail = htons(status & TX_NEXT);
-		if (tx_tail < TX_START || tx_tail >= TX_END) {
-			printk("%s: transmit error: next pointer = %04X\n", dev->name, tx_tail);
-			tx_tail = TX_START;
-			priv->tx_head = TX_START;
-			priv->tx_tail = TX_END;
-		}
-	} while (1);
+		tx_tail = (tx_tail + 1) & 15;
+	} while (--max_work);
 
 	if (priv->tx_tail != tx_tail) {
 		priv->tx_tail = tx_tail;
-		if (priv->tx_used <= MAX_TX_BUFFERED) {
-			dev->tbusy = 0;
-			mark_bh(NET_BH);	/* Inform upper layers. */
-		}
+		dev->tbusy = 0;
+		mark_bh(NET_BH);	/* Inform upper layers. */
 	}
 }
 
 #ifdef MODULE
 
-char ethernames[MAX_ECARDS][9];
-
-static struct device *my_ethers[MAX_ECARDS];
-static struct expansion_card *ec[MAX_ECARDS];
+static struct ether_dev {
+	struct expansion_card	*ec;
+	char			name[9];
+	struct device		dev;
+} ether_devs[MAX_ECARDS];
 
 int
 init_module(void)
 {
-	int i;
+	struct expansion_card *ec;
+	int i, ret = -ENODEV;
 
-	for(i = 0; i < MAX_ECARDS; i++) {
-		my_ethers[i] = NULL;
-		ec[i] = NULL;
-		strcpy(ethernames[i], "        ");
-	}
+	memset(ether_devs, 0, sizeof(ether_devs));
 
+	ecard_startfind ();
+	ec = ecard_find(0, ether3_cids);
 	i = 0;
 
-	ecard_startfind();
+	while (ec && i < MAX_ECARDS) {
+		ecard_claim(ec);
 
-	do {
-		if ((ec[i] = ecard_find(0, ether3_cids)) == NULL)
-			break;
-
-		my_ethers[i] = (struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
-		memset(my_ethers[i], 0, sizeof(struct device));
+		ether_devs[i].ec	    = ec;
+		ether_devs[i].dev.init	    = ether3_probe1;
+		ether_devs[i].dev.name	    = ether_devs[i].name;
+		ether3_get_dev(&ether_devs[i].dev, ec);
+
+		ret = register_netdev(&ether_devs[i].dev);
+
+		if (ret) {
+			ecard_release(ec);
+			ether_devs[i].ec = NULL;
+		} else
+			i += 1;
 
-		my_ethers[i]->irq = ec[i]->irq;
-		my_ethers[i]->base_addr= ecard_address(ec[i], ECARD_MEMC, 0);
-		my_ethers[i]->init = ether3_probe1;
-		my_ethers[i]->name = ethernames[i];
-
-		ether3_addr(my_ethers[i]->dev_addr, ec[i]);
-
-		ecard_claim(ec[i]);
-
-		if(register_netdev(my_ethers[i]) != 0) {
-			for (i = 0; i < 4; i++) {
-				if(my_ethers[i]) {
-					kfree(my_ethers[i]);
-					my_ethers[i] = NULL;
-				}
-				if(ec[i]) {
-					ecard_release(ec[i]);
-					ec[i] = NULL;
-				}
-			}
-			return -EIO;
-		}
-		i++;
+		ec = ecard_find(0, ether3_cids);
 	}
-	while(i < MAX_ECARDS);
 
-	return i != 0 ? 0 : -ENODEV;
+	return i != 0 ? 0 : ret;
 }
 
 void
 cleanup_module(void)
 {
 	int i;
+
 	for (i = 0; i < MAX_ECARDS; i++) {
-		if (my_ethers[i]) {
-		  	release_region(my_ethers[i]->base_addr, 128);
-			unregister_netdev(my_ethers[i]);
-			my_ethers[i] = NULL;
-		}
-		if (ec[i]) {
-			ecard_release(ec[i]);
-			ec[i] = NULL;
+		if (ether_devs[i].ec) {
+			unregister_netdev(&ether_devs[i].dev);
+
+			release_region(ether_devs[i].dev.base_addr, 128);
+
+			ecard_release(ether_devs[i].ec);
+
+			ether_devs[i].ec = NULL;
 		}
 	}
 }
diff -urN linux-2.2.18-pre8/drivers/acorn/net/ether3.h linux/drivers/acorn/net/ether3.h
--- linux-2.2.18-pre8/drivers/acorn/net/ether3.h	Sun Sep  6 10:46:07 1998
+++ linux/drivers/acorn/net/ether3.h	Mon Dec  3 20:25:14 2001
@@ -151,9 +151,8 @@
 	unsigned int config1;
 	unsigned int config2;
     } regs;
-    unsigned int tx_head;		/* address to insert next packet	 */
-    unsigned int tx_tail;		/* address of transmitting packet	 */
-    unsigned int tx_used;		/* number of 'slots' used		 */
+    unsigned char tx_head;		/* buffer nr to insert next packet	 */
+    unsigned char tx_tail;		/* buffer nr of transmitting packet	 */
     unsigned int rx_head;		/* address to fetch next packet from	 */
     struct enet_statistics stats;
     struct timer_list timer;
diff -urN linux-2.2.18-pre8/drivers/acorn/net/etherh.c linux/drivers/acorn/net/etherh.c
--- linux-2.2.18-pre8/drivers/acorn/net/etherh.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/net/etherh.c	Mon Dec  3 20:25:14 2001
@@ -37,6 +37,7 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -50,13 +51,16 @@
 #define DEBUG_INIT 2
 
 static unsigned int net_debug = NET_DEBUG;
-static const card_ids etherh_cids[] = {
+static const card_ids __init etherh_cids[] = {
 	{ MANU_I3, PROD_I3_ETHERLAN500 },
 	{ MANU_I3, PROD_I3_ETHERLAN600 },
 	{ MANU_I3, PROD_I3_ETHERLAN600A },
 	{ 0xffff, 0xffff }
 };
 
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("i3 EtherH driver");
+
 static char *version = "etherh [500/600/600A] ethernet driver (c) 1998 R.M.King v1.05\n";
 
 #define ETHERH500_DATAPORT	0x200	/* MEMC */
@@ -80,8 +84,8 @@
  * Read the ethernet address string from the on board rom.
  * This is an ascii string...
  */
-static int
-etherh_addr(char *addr, struct expansion_card *ec)
+__initfunc(static int
+etherh_addr(char *addr, struct expansion_card *ec))
 {
 	struct in_chunk_dir cd;
 	char *s;
@@ -216,10 +220,8 @@
 
 	if (ei_status.word16)
 		outsw (dma_addr, buf, count >> 1);
-#ifdef BIT8
 	else
 		outsb (dma_addr, buf, count);
-#endif
 
 	dma_start = jiffies;
 
@@ -268,11 +270,8 @@
 		insw (dma_addr, buf, count >> 1);
 		if (count & 1)
 			buf[count - 1] = inb (dma_addr);
-	}
-#ifdef BIT8
-	else
+	} else
 		insb (dma_addr, buf, count);
-#endif
 
 	outb (ENISR_RDC, addr + EN0_ISR);
 	ei_status.dmaing &= ~1;
@@ -307,10 +306,8 @@
 
 	if (ei_status.word16)
 		insw (dma_addr, hdr, sizeof (*hdr) >> 1);
-#ifdef BIT8
 	else
 		insb (dma_addr, hdr, sizeof (*hdr));
-#endif
 
 	outb (ENISR_RDC, addr + EN0_ISR);
 	ei_status.dmaing &= ~1;
@@ -355,8 +352,8 @@
 /*
  * This is the real probe routine.
  */
-static int
-etherh_probe1(struct device *dev)
+__initfunc(static int
+etherh_probe1(struct device *dev))
 {
 	static int version_printed;
 	unsigned int addr, i, reg0, tmp;
@@ -461,10 +458,13 @@
 	etherh_irq_enable,
 	etherh_irq_disable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
-static void etherh_initdev (ecard_t *ec, struct device *dev)
+__initfunc(static void
+etherh_initdev(ecard_t *ec, struct device *dev))
 {
 	ecard_claim (ec);
 	
@@ -492,27 +492,27 @@
 	}
 	ec->ops = &etherh_ops;
 
-	etherh_addr (dev->dev_addr, ec);
+	etherh_addr(dev->dev_addr, ec);
 }
 
 #ifndef MODULE
-int
-etherh_probe(struct device *dev)
+__initfunc(int
+etherh_probe(struct device *dev))
 {
 	if (!dev)
 		return ENODEV;
 
-	ecard_startfind ();
-
-	if (!dev->base_addr) {
+	if (!dev->base_addr || dev->base_addr == 0xffe0) {
 		struct expansion_card *ec;
 
+		ecard_startfind();
+
 		if ((ec = ecard_find (0, etherh_cids)) == NULL)
 			return ENODEV;
 
-		etherh_initdev (ec, dev);
+		etherh_initdev(ec, dev);
 	}
-	return etherh_probe1 (dev);
+	return etherh_probe1(dev);
 }
 #endif
 
@@ -529,12 +529,10 @@
 init_all_cards(void)
 {
 	struct device *dev = NULL;
-	struct expansion_card *boguscards[MAX_ETHERH_CARDS];
 	int i, found = 0;
 
 	for (i = 0; i < MAX_ETHERH_CARDS; i++) {
 		my_ethers[i] = NULL;
-		boguscards[i] = NULL;
 		ec[i] = NULL;
 		strcpy (ethernames[i], "        ");
 	}
@@ -571,7 +569,7 @@
 		if (register_netdev(dev) != 0) {
 			printk (KERN_WARNING "No etherh card found at %08lX\n", dev->base_addr);
 			if (ec[i]) {
-				boguscards[i] = ec[i];
+				ecard_release(ec[i]);
 				ec[i] = NULL;
 			}
 			continue;
@@ -582,12 +580,6 @@
 
 	if (dev)
 		kfree (dev);
-
-	for (i = 0; i < MAX_ETHERH_CARDS; i++)
-		if (boguscards[i]) {
-			boguscards[i]->ops = NULL;
-			ecard_release (boguscards[i]);
-		}
 
 	return found ? 0 : -ENODEV;
 }
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/Config.in linux/drivers/acorn/scsi/Config.in
--- linux-2.2.18-pre8/drivers/acorn/scsi/Config.in	Fri May  8 00:42:39 1998
+++ linux/drivers/acorn/scsi/Config.in	Mon Dec  3 20:25:14 2001
@@ -7,11 +7,12 @@
   bool '  Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  dep_tristate 'ARXE SCSI support (Experimental)' CONFIG_SCSI_ARXESCSI $CONFIG_SCSI
   dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI
   dep_tristate 'EESOX support (Experimental)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI
   dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI
 
-  comment 'The following drives are not fully supported'
+  comment 'The following drivers are not fully supported'
 
   dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI
   if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/Makefile linux/drivers/acorn/scsi/Makefile
--- linux-2.2.18-pre8/drivers/acorn/scsi/Makefile	Fri May  8 00:42:39 1998
+++ linux/drivers/acorn/scsi/Makefile	Mon Dec  3 20:25:14 2001
@@ -24,6 +24,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_ARXESCSI),y)
+  L_OBJS += arxescsi.o
+  CONFIG_FAS216_BUILTIN=y
+else
+  ifeq ($(CONFIG_SCSI_ARXESCSI),m)
+    M_OBJS += arxescsi.o
+    CONFIG_FAS216_MODULE=y
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_CUMANA_1),y)
   L_OBJS += cumana_1.o
 else
@@ -34,12 +44,10 @@
 
 ifeq ($(CONFIG_SCSI_CUMANA_2),y)
   L_OBJS += cumana_2.o
-  CONFIG_QUEUE_BUILTIN=y
   CONFIG_FAS216_BUILTIN=y
 else
   ifeq ($(CONFIG_SCSI_CUMANA_2),m)
     M_OBJS += cumana_2.o
-    CONFIG_QUEUE_MODULE=y
     CONFIG_FAS216_MODULE=y
   endif
 endif
@@ -62,41 +70,39 @@
 
 ifeq ($(CONFIG_SCSI_POWERTECSCSI),y)
   L_OBJS += powertec.o
-  CONFIG_QUEUE_BUILTIN=y
   CONFIG_FAS216_BUILTIN=y
 else
   ifeq ($(CONFIG_SCSI_POWERTECSCSI),m)
     M_OBJS += powertec.o
-    CONFIG_QUEUE_MODULE=y
     CONFIG_FAS216_MODULE=y
   endif
 endif
 
 ifeq ($(CONFIG_SCSI_EESOXSCSI),y)
   L_OBJS += eesox.o
-  CONFIG_QUEUE_BUILTIN=y
   CONFIG_FAS216_BUILTIN=y
 else
   ifeq ($(CONFIG_SCSI_EESOXSCSI),m)
     M_OBJS += eesox.o
-    CONFIG_QUEUE_MODULE=y
     CONFIG_FAS216_MODULE=y
   endif
 endif
 
-ifeq ($(CONFIG_QUEUE_BUILTIN),y)
-  LX_OBJS += queue.o msgqueue.o
-else
-  ifeq ($(CONFIG_QUEUE_MODULE),y)
-    MX_OBJS += queue.o msgqueue.o
-  endif
-endif
-
 ifeq ($(CONFIG_FAS216_BUILTIN),y)
   LX_OBJS += fas216.o
+  CONFIG_QUEUE_BUILTIN=y
 else
   ifeq ($(CONFIG_FAS216_MODULE),y)
     MX_OBJS += fas216.o
+    CONFIG_QUEUE_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_QUEUE_BUILTIN),y)
+  LX_OBJS += queue.o msgqueue.o
+else
+  ifeq ($(CONFIG_QUEUE_MODULE),y)
+    MX_OBJS += queue.o msgqueue.o
   endif
 endif
 
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/acornscsi.c linux/drivers/acorn/scsi/acornscsi.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/acornscsi.c	Wed Dec 23 09:44:41 1998
+++ linux/drivers/acorn/scsi/acornscsi.c	Mon Dec  3 20:25:14 2001
@@ -21,6 +21,8 @@
  *  12-Oct-1997	RMK	Added catch for re-entering interrupt routine.
  *  15-Oct-1997	RMK	Improved handling of commands.
  *  27-Jun-1998	RMK	Changed asm/delay.h to linux/delay.h.
+ *  13-Dec-1998	RMK	Better abort code and command handling.  Extra state
+ *			transitions added to allow dodgy devices to work.
  */
 #define DEBUG_NO_WRITE	1
 #define DEBUG_QUEUES	2
@@ -35,7 +37,7 @@
 #define DEBUG_RESET	1024
 #define DEBUG_ALL	(DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\
 			 DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\
-			 DEBUG_DMA|DEBUG_QUEUES|DEBUG_NO_WRITE)
+			 DEBUG_DMA|DEBUG_QUEUES)
 
 /* DRIVER CONFIGURATION
  *
@@ -123,8 +125,8 @@
 #ifndef STRINGIFY
 #define STRINGIFY(x) #x
 #endif
-#define STR(x) STRINGIFY(x)
-#define NO_WRITE_STR STR(NO_WRITE)
+#define STRx(x) STRINGIFY(x)
+#define NO_WRITE_STR STRx(NO_WRITE)
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -142,6 +144,7 @@
 #include <asm/bitops.h>
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 #include <asm/ecard.h>
 
 #include "../../scsi/scsi.h"
@@ -160,10 +163,6 @@
 #error "Yippee!  ABORT TAG is now defined!  Remove this error!"
 #endif
 
-#ifndef NO_IRQ
-#define NO_IRQ 255
-#endif
-
 #ifdef CONFIG_SCSI_ACORNSCSI_LINK
 #error SCSI2 LINKed commands not supported (yet)!
 #endif
@@ -186,26 +185,7 @@
 #define DMAC_BUFFER_SIZE	65536
 #endif
 
-/*
- * This is used to dump the previous states of the SBIC
- */
-static struct status_entry {
-	unsigned long	when;
-	unsigned char	ssr;
-	unsigned char	ph;
-	unsigned char	irq;
-	unsigned char	unused;
-} status[9][16];
-static unsigned char status_ptr[9];
-
-#define ADD_STATUS(_q,_ssr,_ph,_irq) \
-({							\
-	status[(_q)][status_ptr[(_q)]].when = jiffies;	\
-	status[(_q)][status_ptr[(_q)]].ssr  = (_ssr);	\
-	status[(_q)][status_ptr[(_q)]].ph   = (_ph);	\
-	status[(_q)][status_ptr[(_q)]].irq  = (_irq);	\
-	status_ptr[(_q)] = (status_ptr[(_q)] + 1) & 15;	\
-})
+#define STATUS_BUFFER_TO_PRINT	24
 
 unsigned int sdtr_period = SDTR_PERIOD;
 unsigned int sdtr_size   = SDTR_SIZE;
@@ -214,31 +194,31 @@
 	PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-static void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
-static int acornscsi_reconnect_finish (AS_Host *host);
-static void acornscsi_dma_cleanup (AS_Host *host);
-static void acornscsi_abortcmd (AS_Host *host, unsigned char tag);
+static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
+static int acornscsi_reconnect_finish(AS_Host *host);
+static void acornscsi_dma_cleanup(AS_Host *host);
+static void acornscsi_abortcmd(AS_Host *host, unsigned char tag);
 
 /* ====================================================================================
  * Miscellaneous
  */
 
 static inline void
-sbic_arm_write (unsigned int io_port, int reg, int value)
+sbic_arm_write(unsigned int io_port, int reg, int value)
 {
-    outb_t (reg, io_port);
-    outb_t (value, io_port + 4);
+    outb_t(reg, io_port);
+    outb_t(value, io_port + 4);
 }
 
 #define sbic_arm_writenext(io,val) \
-	outb_t ((val), (io) + 4)
+	outb_t((val), (io) + 4)
 
 static inline
-int sbic_arm_read (unsigned int io_port, int reg)
+int sbic_arm_read(unsigned int io_port, int reg)
 {
     if(reg == ASR)
 	   return inl_t(io_port) & 255;
-    outb_t (reg, io_port);
+    outb_t(reg, io_port);
     return inl_t(io_port + 4) & 255;
 }
 
@@ -247,129 +227,165 @@
 
 #ifdef USE_DMAC
 #define dmac_read(io_port,reg) \
-	inb ((io_port) + (reg))
+	inb((io_port) + (reg))
 
 #define dmac_write(io_port,reg,value) \
-	({ outb ((value), (io_port) + (reg)); })
+	({ outb((value), (io_port) + (reg)); })
 
 #define dmac_clearintr(io_port) \
-	({ outb (0, (io_port)); })
+	({ outb(0, (io_port)); })
 
 static inline
-unsigned int dmac_address (unsigned int io_port)
+unsigned int dmac_address(unsigned int io_port)
 {
-    return dmac_read (io_port, TXADRHI) << 16 |
-	   dmac_read (io_port, TXADRMD) << 8 |
-	   dmac_read (io_port, TXADRLO);
+    return dmac_read(io_port, TXADRHI) << 16 |
+	   dmac_read(io_port, TXADRMD) << 8 |
+	   dmac_read(io_port, TXADRLO);
 }
 
 static
-void acornscsi_dumpdma (AS_Host *host, char *where)
+void acornscsi_dumpdma(AS_Host *host, char *where)
 {
 	unsigned int mode, addr, len;
 
-	mode = dmac_read (host->dma.io_port, MODECON);
-	addr = dmac_address (host->dma.io_port);
-	len  = dmac_read (host->dma.io_port, TXCNTHI) << 8 |
-	       dmac_read (host->dma.io_port, TXCNTLO);
+	mode = dmac_read(host->dma.io_port, MODECON);
+	addr = dmac_address(host->dma.io_port);
+	len  = dmac_read(host->dma.io_port, TXCNTHI) << 8 |
+	       dmac_read(host->dma.io_port, TXCNTLO);
 
-	printk ("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
+	printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
 		host->host->host_no, where,
 		mode, addr, (len + 1) & 0xffff,
-		dmac_read (host->dma.io_port, MASKREG));
+		dmac_read(host->dma.io_port, MASKREG));
 
-	printk ("DMA @%06x, ", host->dma.start_addr);
-	printk ("BH @%p +%04x, ", host->scsi.SCp.ptr,
+	printk("DMA @%06x, ", host->dma.start_addr);
+	printk("BH @%p +%04x, ", host->scsi.SCp.ptr,
 		host->scsi.SCp.this_residual);
-	printk ("DT @+%04x ST @+%04x", host->dma.transferred,
+	printk("DT @+%04x ST @+%04x", host->dma.transferred,
 		host->scsi.SCp.scsi_xferred);
-	printk ("\n");
+	printk("\n");
 }
 #endif
 
 static
-unsigned long acornscsi_sbic_xfcount (AS_Host *host)
+unsigned long acornscsi_sbic_xfcount(AS_Host *host)
 {
     unsigned long length;
 
-    length = sbic_arm_read (host->scsi.io_port, TRANSCNTH) << 16;
-    length |= sbic_arm_readnext (host->scsi.io_port) << 8;
-    length |= sbic_arm_readnext (host->scsi.io_port);
+    length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16;
+    length |= sbic_arm_readnext(host->scsi.io_port) << 8;
+    length |= sbic_arm_readnext(host->scsi.io_port);
 
     return length;
 }
 
-static
-int acornscsi_sbic_issuecmd (AS_Host *host, int command)
+static int
+acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg)
 {
-    int asr;
+	int asr;
 
-    do {
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-    } while (asr & ASR_CIP);
+	do {
+		asr = sbic_arm_read(host->scsi.io_port, ASR);
+
+		if ((asr & stat_mask) == stat)
+			return 0;
+
+		udelay(1);
+	} while (--timeout);
+
+	printk("scsi%d: timeout while %s\n", host->host->host_no, msg);
+
+	return -1;
+}
 
-    sbic_arm_write (host->scsi.io_port, CMND, command);
+static
+int acornscsi_sbic_issuecmd(AS_Host *host, int command)
+{
+    if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command"))
+	return -1;
+
+    sbic_arm_write(host->scsi.io_port, CMND, command);
 
     return 0;
 }
 
 static void
-acornscsi_csdelay (unsigned int cs)
+acornscsi_csdelay(unsigned int cs)
 {
     unsigned long target_jiffies, flags;
 
     target_jiffies = jiffies + 1 + cs * HZ / 100;
 
-    save_flags (flags);
-    sti ();
+    save_flags(flags);
+    sti();
 
     while (time_before(jiffies, target_jiffies)) barrier();
 
-    restore_flags (flags);
+    restore_flags(flags);
 }
 
 static
-void acornscsi_resetcard (AS_Host *host)
+void acornscsi_resetcard(AS_Host *host)
 {
-    unsigned int i;
+    unsigned int i, timeout;
 
     /* assert reset line */
     host->card.page_reg = 0x80;
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 
     /* wait 3 cs.  SCSI standard says 25ms. */
-    acornscsi_csdelay (3);
+    acornscsi_csdelay(3);
 
     host->card.page_reg = 0;
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 
     /*
      * Should get a reset from the card
      */
-    while (!(inb (host->card.io_intr) & 8));
-    sbic_arm_read (host->scsi.io_port, ASR);
-    sbic_arm_read (host->scsi.io_port, SSR);
+    timeout = 1000;
+    do {
+	if (inb(host->card.io_intr) & 8)
+	    break;
+	udelay(1);
+    } while (--timeout);
+
+    if (timeout == 0)
+	printk("scsi%d: timeout while resetting card\n",
+		host->host->host_no);
+
+    sbic_arm_read(host->scsi.io_port, ASR);
+    sbic_arm_read(host->scsi.io_port, SSR);
 
     /* setup sbic - WD33C93A */
-    sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
-    sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+    sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+    sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET);
 
     /*
      * Command should cause a reset interrupt
      */
-    while (!(inb (host->card.io_intr) & 8));
-    sbic_arm_read (host->scsi.io_port, ASR);
-    if (sbic_arm_read (host->scsi.io_port, SSR) != 0x01)
-	printk (KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+    timeout = 1000;
+    do {
+	if (inb(host->card.io_intr) & 8)
+	    break;
+	udelay(1);
+    } while (--timeout);
+
+    if (timeout == 0)
+	printk("scsi%d: timeout while resetting card\n",
 		host->host->host_no);
 
-    sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
-    sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
-    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
-    sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+    sbic_arm_read(host->scsi.io_port, ASR);
+    if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01)
+	printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+		host->host->host_no);
+
+    sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+    sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+    sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
 
     host->card.page_reg = 0x40;
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 
     /* setup dmac - uPC71071 */
     dmac_write(host->dma.io_port, INIT, 0);
@@ -391,7 +407,7 @@
     }
 
     /* wait 25 cs.  SCSI standard says 250ms. */
-    acornscsi_csdelay (25);
+    acornscsi_csdelay(25);
 }
 
 /*=============================================================================================
@@ -461,80 +477,101 @@
 };
 
 static
-void print_scsi_status (unsigned int ssr)
+void print_scsi_status(unsigned int ssr)
 {
     if (acornscsi_map[ssr] != -1)
-	printk ("%s:%s",
+	printk("%s:%s",
 		acornscsi_interrupttype[(ssr >> 4)],
 		acornscsi_interruptcode[acornscsi_map[ssr]]);
     else
-	printk ("%X:%X", ssr >> 4, ssr & 0x0f);    
+	printk("%X:%X", ssr >> 4, ssr & 0x0f);    
 }    
 #endif
 
 static
-void print_sbic_status (int asr, int ssr, int cmdphase)
+void print_sbic_status(int asr, int ssr, int cmdphase)
 {
 #ifdef CONFIG_ACORNSCSI_CONSTANTS
-    printk ("sbic: %c%c%c%c%c%c ",
+    printk("sbic: %c%c%c%c%c%c ",
 	    asr & ASR_INT ? 'I' : 'i',
 	    asr & ASR_LCI ? 'L' : 'l',
 	    asr & ASR_BSY ? 'B' : 'b',
 	    asr & ASR_CIP ? 'C' : 'c',
 	    asr & ASR_PE  ? 'P' : 'p',
 	    asr & ASR_DBR ? 'D' : 'd');
-    printk ("scsi: ");
-    print_scsi_status (ssr);
-    printk (" ph %02X\n", cmdphase);
+    printk("scsi: ");
+    print_scsi_status(ssr);
+    printk(" ph %02X\n", cmdphase);
 #else
-    printk ("sbic: %02X scsi: %X:%X ph: %02X\n",
+    printk("sbic: %02X scsi: %X:%X ph: %02X\n",
 	    asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
 #endif
 }
 
-static
-void acornscsi_dumplog (AS_Host *host, int target)
+static void
+acornscsi_dumplogline(AS_Host *host, int target, int line)
 {
-    unsigned int prev;
-    do {
-	signed int statptr;
+	unsigned long prev;
+	signed int ptr;
 
-	printk ("%c:", target == 8 ? 'H' : ('0' + target));
-	statptr = status_ptr[target] - 10;
+	ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT;
+	if (ptr < 0)
+		ptr += STATUS_BUFFER_SIZE;
 
-	if (statptr < 0)
-	    statptr += 16;
+	printk("%c: %3s:", target == 8 ? 'H' : '0' + target,
+		line == 0 ? "ph" : line == 1 ? "ssr" : "int");
 
-	prev = status[target][statptr].when;
+	prev = host->status[target][ptr].when;
 
-	for (; statptr != status_ptr[target]; statptr = (statptr + 1) & 15) {
-	    if (status[target][statptr].when) {
-#ifdef CONFIG_ACORNSCSI_CONSTANTS
-		printk ("%c%02X:S=",
-			status[target][statptr].irq ? '-' : ' ',
-			status[target][statptr].ph);
-		print_scsi_status (status[target][statptr].ssr);
-#else
-		printk ("%c%02X:%02X",
-			status[target][statptr].irq ? '-' : ' ',
-			status[target][statptr].ph,
-			status[target][statptr].ssr);
-#endif
-		printk ("+%02ld",
-			(status[target][statptr].when - prev) < 100 ?
-				(status[target][statptr].when - prev) : 99);
-		prev = status[target][statptr].when;
-	    }
+	for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+		unsigned long time_diff;
+
+		if (!host->status[target][ptr].when)
+			continue;
+
+		switch (line) {
+		case 0:
+			printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ',
+					 host->status[target][ptr].ph);
+			break;
+
+		case 1:
+			printk(" %02X", host->status[target][ptr].ssr);
+			break;
+
+		case 2:
+			time_diff = host->status[target][ptr].when - prev;
+			prev = host->status[target][ptr].when;
+			if (time_diff == 0)
+				printk("==^");
+			else if (time_diff >= 100)
+				printk("   ");
+			else
+				printk(" %02ld", time_diff);
+			break;
+		}
 	}
-	printk ("\n");
+
+	printk("\n");
+}
+
+static
+void acornscsi_dumplog(AS_Host *host, int target)
+{
+    do {
+	acornscsi_dumplogline(host, target, 0);
+	acornscsi_dumplogline(host, target, 1);
+	acornscsi_dumplogline(host, target, 2);
+
 	if (target == 8)
 	    break;
+
 	target = 8;
     } while (1);
 }
 
 static
-char acornscsi_target (AS_Host *host)
+char acornscsi_target(AS_Host *host)
 {
 	if (host->SCpnt)
 		return '0' + host->SCpnt->target;
@@ -542,7 +579,7 @@
 }
 
 /*
- * Prototype: cmdtype_t acornscsi_cmdtype (int command)
+ * Prototype: cmdtype_t acornscsi_cmdtype(int command)
  * Purpose  : differentiate READ from WRITE from other commands
  * Params   : command - command to interpret
  * Returns  : CMD_READ	- command reads data,
@@ -550,7 +587,7 @@
  *	      CMD_MISC	- everything else
  */
 static inline
-cmdtype_t acornscsi_cmdtype (int command)
+cmdtype_t acornscsi_cmdtype(int command)
 {
     switch (command) {
     case WRITE_6:  case WRITE_10:  case WRITE_12:
@@ -563,7 +600,7 @@
 }
 
 /*
- * Prototype: int acornscsi_datadirection (int command)
+ * Prototype: int acornscsi_datadirection(int command)
  * Purpose  : differentiate between commands that have a DATA IN phase
  *	      and a DATA OUT phase
  * Params   : command - command to interpret
@@ -571,7 +608,7 @@
  *	      DATADIR_IN  - data in phase expected
  */
 static
-datadir_t acornscsi_datadirection (int command)
+datadir_t acornscsi_datadirection(int command)
 {
     switch (command) {
     case CHANGE_DEFINITION:	case COMPARE:		case COPY:
@@ -605,13 +642,13 @@
 };
 
 /*
- * Prototype: int acornscsi_getperiod (unsigned char syncxfer)
+ * Prototype: int acornscsi_getperiod(unsigned char syncxfer)
  * Purpose  : period for the synchronous transfer setting
  * Params   : syncxfer SYNCXFER register value
  * Returns  : period in ns.
  */
 static
-int acornscsi_getperiod (unsigned char syncxfer)
+int acornscsi_getperiod(unsigned char syncxfer)
 {
     int i;
 
@@ -626,14 +663,14 @@
 }
 
 /*
- * Prototype: int round_period (unsigned int period)
+ * Prototype: int round_period(unsigned int period)
  * Purpose  : return index into above table for a required REQ period
  * Params   : period - time (ns) for REQ
  * Returns  : table index
  * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
  */
 static inline
-int round_period (unsigned int period)
+int round_period(unsigned int period)
 {
     int i;
 
@@ -646,7 +683,7 @@
 }
 
 /*
- * Prototype: unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+ * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
  * Purpose  : calculate value for 33c93s SYNC register
  * Params   : period - time (ns) for REQ
  *	      offset - offset in bytes between REQ/ACK
@@ -654,7 +691,7 @@
  * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
  */
 static
-unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
 {
     return sync_xfer_table[round_period(period)].reg_value |
 		((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
@@ -664,14 +701,14 @@
  * Command functions
  */
 /*
- * Function: acornscsi_kick (AS_Host *host)
+ * Function: acornscsi_kick(AS_Host *host)
  * Purpose : kick next command to interface
  * Params  : host - host to send command to
  * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING
  * Notes   : interrupts are always disabled!
  */
 static
-intr_ret_t acornscsi_kick (AS_Host *host)
+intr_ret_t acornscsi_kick(AS_Host *host)
 {
     int from_queue = 0;
     Scsi_Cmnd *SCpnt;
@@ -682,7 +719,7 @@
 
     /* retrieve next command */
     if (!SCpnt) {
-	SCpnt = queue_remove_exclude (&host->queues.issue, host->busyluns);
+	SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns);
 	if (!SCpnt)
 	    return INTR_IDLE;
 
@@ -690,11 +727,11 @@
     }
 
     if (host->scsi.disconnectable && host->SCpnt) {
-	queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+	queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
 	host->scsi.disconnectable = 0;
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	DBG(host->SCpnt, printk ("scsi%d.%c: moved command to disconnected queue\n",
-		host->host->host_no, acornscsi_target (host)));
+	DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n",
+		host->host->host_no, acornscsi_target(host)));
 #endif
 	host->SCpnt = NULL;
     }
@@ -703,9 +740,9 @@
      * If we have an interrupt pending, then we may have been reselected.
      * In this case, we don't want to write to the registers
      */
-    if (!(sbic_arm_read (host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
-	sbic_arm_write (host->scsi.io_port, DESTID, SCpnt->target);
-	sbic_arm_write (host->scsi.io_port, CMND, CMND_SELWITHATN);
+    if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+	sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->target);
+	sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN);
     }
 
     /*
@@ -717,9 +754,10 @@
     host->scsi.SCp = SCpnt->SCp;
     host->dma.xfer_setup = 0;
     host->dma.xfer_required = 0;
+    host->dma.xfer_done = 0;
 
 #if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))
-    DBG(SCpnt,printk ("scsi%d.%c: starting cmd %02X\n",
+    DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n",
 	    host->host->host_no, '0' + SCpnt->target,
 	    SCpnt->cmnd[0]));
 #endif
@@ -736,11 +774,11 @@
 	    SCpnt->tag = SCpnt->device->current_tag;
 	} else
 #endif
-	    set_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+	    set_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
 
 	host->stats.removes += 1;
 
-	switch (acornscsi_cmdtype (SCpnt->cmnd[0])) {
+	switch (acornscsi_cmdtype(SCpnt->cmnd[0])) {
 	case CMD_WRITE:
 	    host->stats.writes += 1;
 	    break;
@@ -757,25 +795,25 @@
 }    
 
 /*
- * Function: void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+ * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
  * Purpose : complete processing for command
  * Params  : host   - interface that completed
  *	     result - driver byte of result
  */
 static
-void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
 {
     Scsi_Cmnd *SCpnt = *SCpntp;
 
     /* clean up */
-    sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+    sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
 
     host->stats.fins += 1;
 
     if (SCpnt) {
 	*SCpntp = NULL;
 
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 
 	SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
 
@@ -787,35 +825,63 @@
 	 * It doesn't appear to be set to something meaningful by the higher
 	 * levels all the time.
 	 */
-	if (host->scsi.SCp.ptr && result == DID_OK &&
-		acornscsi_cmdtype (SCpnt->cmnd[0]) != CMD_MISC) {
-	    switch (status_byte (SCpnt->result)) {
-	    case CHECK_CONDITION:
-	    case COMMAND_TERMINATED:
-	    case BUSY:
-	    case QUEUE_FULL:
-	    case RESERVATION_CONFLICT:
-		break;
+	if (result == DID_OK) {
+		int xfer_warn = 0;
 
-	    default:
-		printk (KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
-			host->host->host_no, SCpnt->result);
-		print_command (SCpnt->cmnd);
-		acornscsi_dumpdma (host, "done");
-	 	acornscsi_dumplog (host, SCpnt->target);
-		SCpnt->result &= 0xffff;
-		SCpnt->result |= DID_ERROR << 16;
-	    }
+		if (SCpnt->underflow == 0) {
+			if (host->scsi.SCp.ptr &&
+			    acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC)
+				xfer_warn = 1;
+		} else {
+			if (host->scsi.SCp.scsi_xferred < SCpnt->underflow ||
+			    host->scsi.SCp.scsi_xferred != host->dma.transferred)
+				xfer_warn = 1;
+		}
+
+		/* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6)
+		 *  Targets which break data transfers into multiple
+		 *  connections shall end each successful connection
+		 *  (except possibly the last) with a SAVE DATA
+		 *  POINTER - DISCONNECT message sequence.
+		 *
+		 * This makes it difficult to ensure that a transfer has
+		 * completed.  If we reach the end of a transfer during
+		 * the command, then we can only have finished the transfer.
+		 * therefore, if we seem to have some data remaining, this
+		 * is not a problem.
+		 */
+		if (host->dma.xfer_done)
+			xfer_warn = 0;
+
+		if (xfer_warn) {
+		    switch (status_byte(SCpnt->result)) {
+		    case CHECK_CONDITION:
+		    case COMMAND_TERMINATED:
+		    case BUSY:
+		    case QUEUE_FULL:
+		    case RESERVATION_CONFLICT:
+			break;
+
+		    default:
+			printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
+				host->host->host_no, SCpnt->result);
+			print_command(SCpnt->cmnd);
+			acornscsi_dumpdma(host, "done");
+		 	acornscsi_dumplog(host, SCpnt->target);
+			SCpnt->result &= 0xffff;
+			SCpnt->result |= DID_ERROR << 16;
+		    }
+		}
 	}
 
 	if (!SCpnt->scsi_done)
-	    panic ("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
+	    panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
 
-	clear_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+	clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
 
-	SCpnt->scsi_done (SCpnt);
+	SCpnt->scsi_done(SCpnt);
     } else
-	printk ("scsi%d: null command in acornscsi_done", host->host->host_no);
+	printk("scsi%d: null command in acornscsi_done", host->host->host_no);
 
     host->scsi.phase = PHASE_IDLE;
 }
@@ -828,7 +894,7 @@
  * Notes    : this will only be one SG entry or less
  */
 static
-void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
+void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
 {
     SCp->ptr += length;
     SCp->this_residual -= length;
@@ -839,13 +905,15 @@
 	    SCp->buffers_residual--;
 	    SCp->ptr = (char *)SCp->buffer->address;
 	    SCp->this_residual = SCp->buffer->length;
-	} else
+	} else {
 	    SCp->ptr = NULL;
+	    host->dma.xfer_done = 1;
+	}
     }
 }
 
 /*
- * Prototype: void acornscsi_data_read (AS_Host *host, char *ptr,
+ * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr,
  *				unsigned int start_addr, unsigned int length)
  * Purpose  : read data from DMA RAM
  * Params   : host - host to transfer from
@@ -855,16 +923,16 @@
  * Notes    : this will only be one SG entry or less
  */
 static
-void acornscsi_data_read (AS_Host *host, char *ptr,
+void acornscsi_data_read(AS_Host *host, char *ptr,
 				 unsigned int start_addr, unsigned int length)
 {
-    extern void __acornscsi_in (int port, char *buf, int len);
+    extern void __acornscsi_in(int port, char *buf, int len);
     unsigned int page, offset, len = length;
 
     page = (start_addr >> 12);
     offset = start_addr & ((1 << 12) - 1);
 
-    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 
     while (len > 0) {
 	unsigned int this_len;
@@ -874,7 +942,7 @@
 	else
 	    this_len = len;
 
-	__acornscsi_in (host->card.io_ram + (offset << 1), ptr, this_len);
+	__acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len);
 
 	offset += this_len;
 	ptr += this_len;
@@ -883,14 +951,14 @@
 	if (offset == (1 << 12)) {
 	    offset = 0;
 	    page ++;
-	    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+	    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 	}
     }
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 }
 
 /*
- * Prototype: void acornscsi_data_write (AS_Host *host, char *ptr,
+ * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr,
  *				unsigned int start_addr, unsigned int length)
  * Purpose  : write data to DMA RAM
  * Params   : host - host to transfer from
@@ -900,16 +968,16 @@
  * Notes    : this will only be one SG entry or less
  */
 static
-void acornscsi_data_write (AS_Host *host, char *ptr,
+void acornscsi_data_write(AS_Host *host, char *ptr,
 				 unsigned int start_addr, unsigned int length)
 {
-    extern void __acornscsi_out (int port, char *buf, int len);
+    extern void __acornscsi_out(int port, char *buf, int len);
     unsigned int page, offset, len = length;
 
     page = (start_addr >> 12);
     offset = start_addr & ((1 << 12) - 1);
 
-    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 
     while (len > 0) {
 	unsigned int this_len;
@@ -919,7 +987,7 @@
 	else
 	    this_len = len;
 
-	__acornscsi_out (host->card.io_ram + (offset << 1), ptr, this_len);
+	__acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len);
 
 	offset += this_len;
 	ptr += this_len;
@@ -928,10 +996,10 @@
 	if (offset == (1 << 12)) {
 	    offset = 0;
 	    page ++;
-	    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+	    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 	}
     }
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 }
 
 /* =========================================================================================
@@ -939,25 +1007,25 @@
  */
 #ifdef USE_DMAC
 /*
- * Prototype: void acornscsi_dmastop (AS_Host *host)
+ * Prototype: void acornscsi_dmastop(AS_Host *host)
  * Purpose  : stop all DMA
  * Params   : host - host on which to stop DMA
  * Notes    : This is called when leaving DATA IN/OUT phase,
  *	      or when interface is RESET
  */
 static inline
-void acornscsi_dma_stop (AS_Host *host)
+void acornscsi_dma_stop(AS_Host *host)
 {
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
-    dmac_clearintr (host->dma.io_intr_clear);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr(host->dma.io_intr_clear);
 
 #if (DEBUG & DEBUG_DMA)
-    DBG(host->SCpnt, acornscsi_dumpdma (host, "stop"));
+    DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));
 #endif
 }
 
 /*
- * Function: void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+ * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
  * Purpose : setup DMA controller for data transfer
  * Params  : host - host to setup
  *	     direction - data transfer direction
@@ -965,19 +1033,19 @@
  *	     while we're in a DATA I/O phase
  */
 static
-void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
 {
     unsigned int address, length, mode;
 
     host->dma.direction = direction;
 
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
 
     if (direction == DMA_OUT) {
 #if (DEBUG & DEBUG_NO_WRITE)
 	if (NO_WRITE & (1 << host->SCpnt->target)) {
-	    printk (KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
-		    host->host->host_no, acornscsi_target (host));
+	    printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
+		    host->host->host_no, acornscsi_target(host));
 	    return;
 	}
 #endif
@@ -988,7 +1056,7 @@
     /*
      * Allocate some buffer space, limited to half the buffer size
      */
-    length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+    length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
     if (length) {
 	host->dma.start_addr = address = host->dma.free_addr;
 	host->dma.free_addr = (host->dma.free_addr + length) &
@@ -998,27 +1066,27 @@
 	 * Transfer data to DMA memory
 	 */
 	if (direction == DMA_OUT)
-	    acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+	    acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
 				length);
 
 	length -= 1;
-	dmac_write (host->dma.io_port, TXCNTLO, length);
-	dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
-	dmac_write (host->dma.io_port, TXADRLO, address);
-	dmac_write (host->dma.io_port, TXADRMD, address >> 8);
-	dmac_write (host->dma.io_port, TXADRHI, 0);
-	dmac_write (host->dma.io_port, MODECON, mode);
-	dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+	dmac_write(host->dma.io_port, TXCNTLO, length);
+	dmac_write(host->dma.io_port, TXCNTHI, length >> 8);
+	dmac_write(host->dma.io_port, TXADRLO, address);
+	dmac_write(host->dma.io_port, TXADRMD, address >> 8);
+	dmac_write(host->dma.io_port, TXADRHI, 0);
+	dmac_write(host->dma.io_port, MODECON, mode);
+	dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
 
 #if (DEBUG & DEBUG_DMA)
-	DBG(host->SCpnt, acornscsi_dumpdma (host, "strt"));
+	DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));
 #endif
 	host->dma.xfer_setup = 1;
     }
 }
 
 /*
- * Function: void acornscsi_dma_cleanup (AS_Host *host)
+ * Function: void acornscsi_dma_cleanup(AS_Host *host)
  * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
  * Params  : host - host to finish
  * Notes   : This is called when a command is:
@@ -1026,10 +1094,10 @@
  *	   : This must not return until all transfers are completed.
  */
 static
-void acornscsi_dma_cleanup (AS_Host *host)
+void acornscsi_dma_cleanup(AS_Host *host)
 {
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
-    dmac_clearintr (host->dma.io_intr_clear);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr(host->dma.io_intr_clear);
 
     /*
      * Check for a pending transfer
@@ -1037,7 +1105,7 @@
     if (host->dma.xfer_required) {
 	host->dma.xfer_required = 0;
 	if (host->dma.direction == DMA_IN)
-	    acornscsi_data_read (host, host->dma.xfer_ptr,
+	    acornscsi_data_read(host, host->dma.xfer_ptr,
 				 host->dma.xfer_start, host->dma.xfer_length);
     }
 
@@ -1056,17 +1124,17 @@
 	/*
 	 * Calculate number of bytes transferred from DMA.
 	 */
-	transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+	transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
 	host->dma.transferred += transferred;
 
 	if (host->dma.direction == DMA_IN)
-	    acornscsi_data_read (host, host->scsi.SCp.ptr,
+	    acornscsi_data_read(host, host->scsi.SCp.ptr,
 				 host->dma.start_addr, transferred);
 
 	/*
 	 * Update SCSI pointers
 	 */
-	acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+	acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
 #if (DEBUG & DEBUG_DMA)
 	DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));
 #endif
@@ -1074,7 +1142,7 @@
 }
 
 /*
- * Function: void acornscsi_dmacintr (AS_Host *host)
+ * Function: void acornscsi_dmacintr(AS_Host *host)
  * Purpose : handle interrupts from DMAC device
  * Params  : host - host to process
  * Notes   : If reading, we schedule the read to main memory &
@@ -1084,21 +1152,21 @@
  *	   : Called whenever DMAC finished it's current transfer.
  */
 static
-void acornscsi_dma_intr (AS_Host *host)
+void acornscsi_dma_intr(AS_Host *host)
 {
     unsigned int address, length, transferred;
 
 #if (DEBUG & DEBUG_DMA)
-    DBG(host->SCpnt, acornscsi_dumpdma (host, "inti"));
+    DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));
 #endif
 
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
-    dmac_clearintr (host->dma.io_intr_clear);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr(host->dma.io_intr_clear);
 
     /*
      * Calculate amount transferred via DMA
      */
-    transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+    transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
     host->dma.transferred += transferred;
 
     /*
@@ -1111,12 +1179,12 @@
 	host->dma.xfer_required = 1;
     }
 
-    acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+    acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
 
     /*
      * Allocate some buffer space, limited to half the on-board RAM size
      */
-    length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+    length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
     if (length) {
 	host->dma.start_addr = address = host->dma.free_addr;
 	host->dma.free_addr = (host->dma.free_addr + length) &
@@ -1126,19 +1194,19 @@
 	 * Transfer data to DMA memory
 	 */
 	if (host->dma.direction == DMA_OUT)
-	    acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+	    acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
 				length);
 
 	length -= 1;
-	dmac_write (host->dma.io_port, TXCNTLO, length);
-	dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
-	dmac_write (host->dma.io_port, TXADRLO, address);
-	dmac_write (host->dma.io_port, TXADRMD, address >> 8);
-	dmac_write (host->dma.io_port, TXADRHI, 0);
-	dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+	dmac_write(host->dma.io_port, TXCNTLO, length);
+	dmac_write(host->dma.io_port, TXCNTHI, length >> 8);
+	dmac_write(host->dma.io_port, TXADRLO, address);
+	dmac_write(host->dma.io_port, TXADRMD, address >> 8);
+	dmac_write(host->dma.io_port, TXADRHI, 0);
+	dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
 
 #if (DEBUG & DEBUG_DMA)
-	DBG(host->SCpnt, acornscsi_dumpdma (host, "into"));
+	DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));
 #endif
     } else {
 	host->dma.xfer_setup = 0;
@@ -1149,48 +1217,48 @@
 	 * attention condition.  We continue giving one byte until
 	 * the device recognises the attention.
 	 */
-	if (dmac_read (host->dma.io_port, STATUS) & STATUS_RQ0) {
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) {
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 
-	    dmac_write (host->dma.io_port, TXCNTLO, 0);
-	    dmac_write (host->dma.io_port, TXCNTHI, 0);
-	    dmac_write (host->dma.io_port, TXADRLO, 0);
-	    dmac_write (host->dma.io_port, TXADRMD, 0);
-	    dmac_write (host->dma.io_port, TXADRHI, 0);
-	    dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+	    dmac_write(host->dma.io_port, TXCNTLO, 0);
+	    dmac_write(host->dma.io_port, TXCNTHI, 0);
+	    dmac_write(host->dma.io_port, TXADRLO, 0);
+	    dmac_write(host->dma.io_port, TXADRMD, 0);
+	    dmac_write(host->dma.io_port, TXADRHI, 0);
+	    dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
 	}
 #endif
     }
 }
 
 /*
- * Function: void acornscsi_dma_xfer (AS_Host *host)
+ * Function: void acornscsi_dma_xfer(AS_Host *host)
  * Purpose : transfer data between AcornSCSI and memory
  * Params  : host - host to process
  */
 static
-void acornscsi_dma_xfer (AS_Host *host)
+void acornscsi_dma_xfer(AS_Host *host)
 {
     host->dma.xfer_required = 0;
 
     if (host->dma.direction == DMA_IN)
-	acornscsi_data_read (host, host->dma.xfer_ptr,
+	acornscsi_data_read(host, host->dma.xfer_ptr,
 				host->dma.xfer_start, host->dma.xfer_length);
 }
 
 /*
- * Function: void acornscsi_dma_adjust (AS_Host *host)
+ * Function: void acornscsi_dma_adjust(AS_Host *host)
  * Purpose : adjust DMA pointers & count for bytes transfered to
  *	     SBIC but not SCSI bus.
  * Params  : host - host to adjust DMA count for
  */
 static
-void acornscsi_dma_adjust (AS_Host *host)
+void acornscsi_dma_adjust(AS_Host *host)
 {
     if (host->dma.xfer_setup) {
 	signed long transferred;
 #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
-	DBG(host->SCpnt, acornscsi_dumpdma (host, "adji"));
+	DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));
 #endif
 	/*
 	 * Calculate correct DMA address - DMA is ahead of SCSI bus while
@@ -1205,17 +1273,17 @@
 	 */
 	transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred;
 	if (transferred < 0)
-	    printk ("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
-		    host->host->host_no, acornscsi_target (host), transferred);
+	    printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
+		    host->host->host_no, acornscsi_target(host), transferred);
 	else if (transferred == 0)
 	    host->dma.xfer_setup = 0;
 	else {
 	    transferred += host->dma.start_addr;
-	    dmac_write (host->dma.io_port, TXADRLO, transferred);
-	    dmac_write (host->dma.io_port, TXADRMD, transferred >> 8);
-	    dmac_write (host->dma.io_port, TXADRHI, transferred >> 16);
+	    dmac_write(host->dma.io_port, TXADRLO, transferred);
+	    dmac_write(host->dma.io_port, TXADRMD, transferred >> 8);
+	    dmac_write(host->dma.io_port, TXADRHI, transferred >> 16);
 #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
-	    DBG(host->SCpnt, acornscsi_dumpdma (host, "adjo"));
+	    DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));
 #endif
 	}
     }
@@ -1225,66 +1293,88 @@
 /* =========================================================================================
  * Data I/O
  */
+static int
+acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout)
+{
+	unsigned int asr, timeout = max_timeout;
+	int my_ptr = *ptr;
+
+	while (my_ptr < len) {
+		asr = sbic_arm_read(host->scsi.io_port, ASR);
+
+		if (asr & ASR_DBR) {
+			timeout = max_timeout;
+
+			sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]);
+		} else if (asr & ASR_INT)
+			break;
+		else if (--timeout == 0)
+			break;
+		udelay(1);
+	}
+
+	*ptr = my_ptr;
+
+	return (timeout == 0) ? -1 : 0;
+}
+
 /*
- * Function: void acornscsi_sendcommand (AS_Host *host)
+ * Function: void acornscsi_sendcommand(AS_Host *host)
  * Purpose : send a command to a target
  * Params  : host - host which is connected to target
  */
-static
-void acornscsi_sendcommand (AS_Host *host)
+static void
+acornscsi_sendcommand(AS_Host *host)
 {
     Scsi_Cmnd *SCpnt = host->SCpnt;
-    unsigned int asr;
-    unsigned char *cmdptr, *cmdend;
 
-    sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
-    sbic_arm_writenext (host->scsi.io_port, 0);
-    sbic_arm_writenext (host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
-
-    cmdptr = SCpnt->cmnd + host->scsi.SCp.sent_command;
-    cmdend = SCpnt->cmnd + SCpnt->cmd_len;
-
-    while (cmdptr < cmdend) {
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-	if (asr & ASR_DBR)
-	    sbic_arm_write (host->scsi.io_port, DATA, *cmdptr++);
-	else if (asr & ASR_INT)
-	    break;
-    }
-    if (cmdptr >= cmdend)
-	host->scsi.SCp.sent_command = cmdptr - SCpnt->cmnd;
+    sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0);
+    sbic_arm_writenext(host->scsi.io_port, 0);
+    sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
+
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
+
+    if (acornscsi_write_pio(host, SCpnt->cmnd,
+	(int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000))
+	printk("scsi%d: timeout while sending command\n", host->host->host_no);
+
     host->scsi.phase = PHASE_COMMAND;
 }
 
 static
-void acornscsi_sendmessage (AS_Host *host)
+void acornscsi_sendmessage(AS_Host *host)
 {
-    unsigned int message_length = msgqueue_msglength (&host->scsi.msgs);
-    int msgnr;
+    unsigned int message_length = msgqueue_msglength(&host->scsi.msgs);
+    unsigned int msgnr;
     struct message *msg;
 
 #if (DEBUG & DEBUG_MESSAGES)
-    printk ("scsi%d.%c: sending message ",
-	    host->host->host_no, acornscsi_target (host));
+    printk("scsi%d.%c: sending message ",
+	    host->host->host_no, acornscsi_target(host));
 #endif
 
     switch (message_length) {
     case 0:
-	acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
-	while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
-	sbic_arm_write (host->scsi.io_port, DATA, NOP);
+	acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+	acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1");
+
+	sbic_arm_write(host->scsi.io_port, DATA, NOP);
+
 	host->scsi.last_message = NOP;
 #if (DEBUG & DEBUG_MESSAGES)
-	printk ("NOP");
+	printk("NOP");
 #endif
 	break;
 
     case 1:
-	acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
+	acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
 	msg = msgqueue_getmsg(&host->scsi.msgs, 0);
-	while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
-	sbic_arm_write (host->scsi.io_port, DATA, msg->msg[0]);
+
+	acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2");
+
+	sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]);
+
 	host->scsi.last_message = msg->msg[0];
 #if (DEBUG & DEBUG_MESSAGES)
 	print_msg(msg->msg);
@@ -1300,86 +1390,85 @@
 	 *  initiator.  This provides an interlock so that the
 	 *  initiator can determine which message byte is rejected.
 	 */
-	sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
-	sbic_arm_writenext (host->scsi.io_port, 0);
-	sbic_arm_writenext (host->scsi.io_port, message_length);
-	acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+	sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0);
+	sbic_arm_writenext(host->scsi.io_port, 0);
+	sbic_arm_writenext(host->scsi.io_port, message_length);
+	acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
 
 	msgnr = 0;
 	while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) {
-	    unsigned int asr, i;
+	    unsigned int i;
 #if (DEBUG & DEBUG_MESSAGES)
-	    print_msg (msg);
+	    print_msg(msg);
 #endif
-	    for (i = 0; i < msg->length;) {
-		asr = sbic_arm_read (host->scsi.io_port, ASR);
-		if (asr & ASR_DBR)
-		    sbic_arm_write (host->scsi.io_port, DATA, msg->msg[i++]);
-		if (asr & ASR_INT)
-		    break;
-	    }
+	    i = 0;
+	    if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000))
+		printk("scsi%d: timeout while sending message\n", host->host->host_no);
+
 	    host->scsi.last_message = msg->msg[0];
 	    if (msg->msg[0] == EXTENDED_MESSAGE)
 		host->scsi.last_message |= msg->msg[2] << 8;
-	    if (asr & ASR_INT)
+
+	    if (i != msg->length)
 		break;
 	}
 	break;
     }
 #if (DEBUG & DEBUG_MESSAGES)
-    printk ("\n");
+    printk("\n");
 #endif
 }
 
 /*
- * Function: void acornscsi_readstatusbyte (AS_Host *host)
+ * Function: void acornscsi_readstatusbyte(AS_Host *host)
  * Purpose : Read status byte from connected target
  * Params  : host - host connected to target
  */
 static
-void acornscsi_readstatusbyte (AS_Host *host)
+void acornscsi_readstatusbyte(AS_Host *host)
 {
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO|CMND_SBT);
-    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
-
-    host->scsi.SCp.Status = sbic_arm_read (host->scsi.io_port, DATA);
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT);
+    acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte");
+    host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA);
 }
 
 /*
- * Function: unsigned char acornscsi_readmessagebyte (AS_Host *host)
+ * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host)
  * Purpose : Read one message byte from connected target
  * Params  : host - host connected to target
  */
 static
-unsigned char acornscsi_readmessagebyte (AS_Host *host)
+unsigned char acornscsi_readmessagebyte(AS_Host *host)
 {
     unsigned char message;
 
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
-    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+    acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte");
 
-    message = sbic_arm_read (host->scsi.io_port, DATA);
+    message = sbic_arm_read(host->scsi.io_port, DATA);
 
     /* wait for MSGIN-XFER-PAUSED */
-    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
-    sbic_arm_read (host->scsi.io_port, SSR);
+    acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte");
+
+    sbic_arm_read(host->scsi.io_port, SSR);
 
     return message;
 }
 
 /*
- * Function: void acornscsi_message (AS_Host *host)
+ * Function: void acornscsi_message(AS_Host *host)
  * Purpose : Read complete message from connected target & action message
  * Params  : host - host connected to target
  */
 static
-void acornscsi_message (AS_Host *host)
+void acornscsi_message(AS_Host *host)
 {
     unsigned char message[16];
     unsigned int msgidx = 0, msglen = 1;
 
     do {
-	message[msgidx] = acornscsi_readmessagebyte (host);
+	message[msgidx] = acornscsi_readmessagebyte(host);
 
 	switch (msgidx) {
 	case 0:
@@ -1395,17 +1484,17 @@
 	}
 	msgidx += 1;
 	if (msgidx < msglen) {
-	    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+	    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
 
 	    /* wait for next msg-in */
-	    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
-	    sbic_arm_read (host->scsi.io_port, SSR);
+	    acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack");
+	    sbic_arm_read(host->scsi.io_port, SSR);
 	}
     } while (msgidx < msglen);
 
 #if (DEBUG & DEBUG_MESSAGES)
     printk("scsi%d.%c: message in: ",
-	    host->host->host_no, acornscsi_target (host));
+	    host->host->host_no, acornscsi_target(host));
     print_msg(message);
     printk("\n");
 #endif
@@ -1419,7 +1508,7 @@
 	 */
 	if (message[0] == SIMPLE_QUEUE_TAG)
 	    host->scsi.reconnected.tag = message[1];
-	if (acornscsi_reconnect_finish (host))
+	if (acornscsi_reconnect_finish(host))
 	    host->scsi.phase = PHASE_MSGIN;
     }
 
@@ -1429,7 +1518,7 @@
     case COMMAND_COMPLETE:
 	if (host->scsi.phase != PHASE_STATUSIN) {
 	    printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
-		    host->host->host_no, acornscsi_target (host));
+		    host->host->host_no, acornscsi_target(host));
 	    acornscsi_dumplog(host, host->SCpnt->target);
 	}
 	host->scsi.phase = PHASE_DONE;
@@ -1443,7 +1532,7 @@
 	 *  direct the initiator to copy the active data pointer to
 	 *  the saved data pointer for the current I/O process.
 	 */
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 	host->SCpnt->SCp = host->scsi.SCp;
 	host->SCpnt->SCp.sent_command = 0;
 	host->scsi.phase = PHASE_MSGIN;
@@ -1459,7 +1548,7 @@
 	 *  status pointers shall be restored to the beginning of
 	 *  the present command and status areas.'
 	 */
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 	host->scsi.SCp = host->SCpnt->SCp;
 	host->scsi.phase = PHASE_MSGIN;
 	break;
@@ -1474,7 +1563,7 @@
 	 *  message.  When reconnection is completed, the most recent
 	 *  saved pointer values are restored.'
 	 */
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 	host->scsi.phase = PHASE_DISCONNECT;
 	break;
 
@@ -1493,8 +1582,8 @@
 	/*
 	 * If we have any messages waiting to go out, then assert ATN now
 	 */
-	if (msgqueue_msglength (&host->scsi.msgs))
-	    acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+	if (msgqueue_msglength(&host->scsi.msgs))
+	    acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
 
 	switch (host->scsi.last_message) {
 #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
@@ -1507,21 +1596,21 @@
 	     *  message is received, it shall respond with a MESSAGE REJECT
 	     *  message and accept the I/O process as if it were untagged.
 	     */
-	    printk (KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
-		    host->host->host_no, acornscsi_target (host));
+	    printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
+		    host->host->host_no, acornscsi_target(host));
 	    host->SCpnt->device->tagged_queue = 0;
-	    set_bit (host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns);
+	    set_bit(host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns);
 	    break;
 #endif
 	case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
 	    /*
 	     * Target can't handle synchronous transfers
 	     */
-	    printk (KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
-		    host->host->host_no, acornscsi_target (host));
+	    printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
+		    host->host->host_no, acornscsi_target(host));
 	    host->device[host->SCpnt->target].sync_xfer = SYNCHTRANSFER_2DBA;
 	    host->device[host->SCpnt->target].sync_state = SYNC_ASYNCHRONOUS;
-	    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+	    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
 	    break;
 
 	default:
@@ -1535,8 +1624,8 @@
 
     case SIMPLE_QUEUE_TAG:
 	/* tag queue reconnect... message[1] = queue tag.  Print something to indicate something happened! */
-	printk ("scsi%d.%c: reconnect queue tag %02X\n",
-		host->host->host_no, acornscsi_target (host),
+	printk("scsi%d.%c: reconnect queue tag %02X\n",
+		host->host->host_no, acornscsi_target(host),
 		message[1]);
 	break;
 
@@ -1552,26 +1641,26 @@
 		 * and the target retries fail, then we fallback to asynchronous mode
 		 */
 		host->device[host->SCpnt->target].sync_state = SYNC_COMPLETED;
-		printk (KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
+		printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
 			host->host->host_no, acornscsi_target(host),
 			message[4], message[3] * 4);
 		host->device[host->SCpnt->target].sync_xfer =
-			calc_sync_xfer (message[3] * 4, message[4]);
+			calc_sync_xfer(message[3] * 4, message[4]);
 	    } else {
 		unsigned char period, length;
 		/*
 		 * Target requested synchronous transfers.  The agreement is only
 		 * to be in operation AFTER the target leaves message out phase.
 		 */
-		acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-		period = max (message[3], sdtr_period / 4);
-		length = min (message[4], sdtr_size);
-		msgqueue_addmsg (&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
+		acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+		period = max(message[3], sdtr_period / 4);
+		length = min(message[4], sdtr_size);
+		msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
 				 EXTENDED_SDTR, period, length);
 		host->device[host->SCpnt->target].sync_xfer =
-			calc_sync_xfer (period * 4, length);
+			calc_sync_xfer(period * 4, length);
 	    }
-	    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+	    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
 	    break;
 #else
 	    /* We do not accept synchronous transfers.  Respond with a
@@ -1584,9 +1673,9 @@
 	     * to a wide data transfer request.
 	     */
 	default:
-	    acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-	    msgqueue_flush (&host->scsi.msgs);
-	    msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+	    acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+	    msgqueue_flush(&host->scsi.msgs);
+	    msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
 	    break;
 	}
 	break;
@@ -1607,19 +1696,19 @@
 	     * if there are more linked commands available.
 	     */
 	    if (!host->SCpnt->next_link) {
-		printk (KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
-			instance->host_no, acornscsi_target (host), host->SCpnt->tag);
-		acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-		msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+		printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
+			instance->host_no, acornscsi_target(host), host->SCpnt->tag);
+		acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+		msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
 	    } else {
 		Scsi_Cmnd *SCpnt = host->SCpnt;
 
-		acornscsi_dma_cleanup (host);
+		acornscsi_dma_cleanup(host);
 
 		host->SCpnt = host->SCpnt->next_link;
 		host->SCpnt->tag = SCpnt->tag;
 		SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status;
-		SCpnt->done (SCpnt);
+		SCpnt->done(SCpnt);
 
 		/* initialise host->SCpnt->SCp */
 	    }
@@ -1628,42 +1717,42 @@
 #endif
 
     default: /* reject message */
-	printk (KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
-		host->host->host_no, acornscsi_target (host),
+	printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
+		host->host->host_no, acornscsi_target(host),
 		message[0]);
-	acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-	msgqueue_flush (&host->scsi.msgs);
-	msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+	acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+	msgqueue_flush(&host->scsi.msgs);
+	msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
 	host->scsi.phase = PHASE_MSGIN;
 	break;
     }
-    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
 }
 
 /*
- * Function: int acornscsi_buildmessages (AS_Host *host)
+ * Function: int acornscsi_buildmessages(AS_Host *host)
  * Purpose : build the connection messages for a host
  * Params  : host - host to add messages to
  */
 static
-void acornscsi_buildmessages (AS_Host *host)
+void acornscsi_buildmessages(AS_Host *host)
 {
 #if 0
     /* does the device need resetting? */
     if (cmd_reset) {
-	msgqueue_addmsg (&host->scsi.msgs, 1, BUS_DEVICE_RESET);
+	msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET);
 	return;
     }
 #endif
 
-    msgqueue_addmsg (&host->scsi.msgs, 1,
+    msgqueue_addmsg(&host->scsi.msgs, 1,
 		     IDENTIFY(host->device[host->SCpnt->target].disconnect_ok,
 			     host->SCpnt->lun));
 
 #if 0
     /* does the device need the current command aborted */
     if (cmd_aborted) {
-	acornscsi_abortcmd (host->SCpnt->tag);
+	acornscsi_abortcmd(host->SCpnt->tag);
 	return;
     }
 #endif
@@ -1678,14 +1767,14 @@
 	    tag_type = HEAD_OF_QUEUE_TAG;
 	else
 	    tag_type = SIMPLE_QUEUE_TAG;
-	msgqueue_addmsg (&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
+	msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
     }
 #endif
 
 #ifdef CONFIG_SCSI_ACORNSCSI_SYNC
     if (host->device[host->SCpnt->target].sync_state == SYNC_NEGOCIATE) {
 	host->device[host->SCpnt->target].sync_state = SYNC_SENT_REQUEST;
-	msgqueue_addmsg (&host->scsi.msgs, 5,
+	msgqueue_addmsg(&host->scsi.msgs, 5,
 			 EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
 			 sdtr_period / 4, sdtr_size);
     }
@@ -1693,29 +1782,29 @@
 }
 
 /*
- * Function: int acornscsi_starttransfer (AS_Host *host)
+ * Function: int acornscsi_starttransfer(AS_Host *host)
  * Purpose : transfer data to/from connected target
  * Params  : host - host to which target is connected
  * Returns : 0 if failure
  */
 static
-int acornscsi_starttransfer (AS_Host *host)
+int acornscsi_starttransfer(AS_Host *host)
 {
     int residual;
 
     if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {
-	printk (KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
-		host->host->host_no, acornscsi_target (host));
+	printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
+		host->host->host_no, acornscsi_target(host));
 	return 0;
     }
 
     residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred;
 
-    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
-    sbic_arm_writenext (host->scsi.io_port, residual >> 16);
-    sbic_arm_writenext (host->scsi.io_port, residual >> 8);
-    sbic_arm_writenext (host->scsi.io_port, residual);
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+    sbic_arm_writenext(host->scsi.io_port, residual >> 16);
+    sbic_arm_writenext(host->scsi.io_port, residual >> 8);
+    sbic_arm_writenext(host->scsi.io_port, residual);
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
     return 1;
 }
 
@@ -1723,7 +1812,7 @@
  * Connection & Disconnection
  */
 /*
- * Function : acornscsi_reconnect (AS_Host *host)
+ * Function : acornscsi_reconnect(AS_Host *host)
  * Purpose  : reconnect a previously disconnected command
  * Params   : host - host specific data
  * Remarks  : SCSI spec says:
@@ -1731,27 +1820,27 @@
  *		 of saved pointers upon reconnection of the I/O process'
  */
 static
-int acornscsi_reconnect (AS_Host *host)
+int acornscsi_reconnect(AS_Host *host)
 {
     unsigned int target, lun, ok = 0;
 
-    target = sbic_arm_read (host->scsi.io_port, SOURCEID);
+    target = sbic_arm_read(host->scsi.io_port, SOURCEID);
 
     if (!(target & 8))
-	printk (KERN_ERR "scsi%d: invalid source id after reselection "
+	printk(KERN_ERR "scsi%d: invalid source id after reselection "
 		"- device fault?\n",
 		host->host->host_no);
 
     target &= 7;
 
     if (host->SCpnt && !host->scsi.disconnectable) {
-	printk (KERN_ERR "scsi%d.%d: reconnected while command in "
+	printk(KERN_ERR "scsi%d.%d: reconnected while command in "
 		"progress to target %d?\n",
 		host->host->host_no, target, host->SCpnt->target);
 	host->SCpnt = NULL;
     }
 
-    lun = sbic_arm_read (host->scsi.io_port, DATA) & 7;
+    lun = sbic_arm_read(host->scsi.io_port, DATA) & 7;
 
     host->scsi.reconnected.target = target;
     host->scsi.reconnected.lun = lun;
@@ -1761,7 +1850,7 @@
 	host->SCpnt->target == target && host->SCpnt->lun == lun)
 	ok = 1;
 
-    if (!ok && queue_probetgtlun (&host->queues.disconnected, target, lun))
+    if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun))
 	ok = 1;
 
     ADD_STATUS(target, 0x81, host->scsi.phase, 0);
@@ -1770,26 +1859,28 @@
 	host->scsi.phase = PHASE_RECONNECTED;
     } else {
 	/* this doesn't seem to work */
-	printk (KERN_ERR "scsi%d.%c: reselected with no command "
+	printk(KERN_ERR "scsi%d.%c: reselected with no command "
 		"to reconnect with\n",
 		host->host->host_no, '0' + target);
-	acornscsi_dumplog (host, target);
-	acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-	msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
-	host->scsi.phase = PHASE_ABORTED;
+	acornscsi_dumplog(host, target);
+	acornscsi_abortcmd(host, 0);
+	if (host->SCpnt) {
+	    queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
+	    host->SCpnt = NULL;
+	}
     }
-    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
     return !ok;
 }
 
 /*
- * Function: int acornscsi_reconect_finish (AS_Host *host)
+ * Function: int acornscsi_reconect_finish(AS_Host *host)
  * Purpose : finish reconnecting a command
  * Params  : host - host to complete
  * Returns : 0 if failed
  */
 static
-int acornscsi_reconnect_finish (AS_Host *host)
+int acornscsi_reconnect_finish(AS_Host *host)
 {
     if (host->scsi.disconnectable && host->SCpnt) {
 	host->scsi.disconnectable = 0;
@@ -1797,45 +1888,44 @@
 	    host->SCpnt->lun	== host->scsi.reconnected.lun &&
 	    host->SCpnt->tag	== host->scsi.reconnected.tag) {
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	    DBG(host->SCpnt, printk ("scsi%d.%c: reconnected",
-		    host->host->host_no, acornscsi_target (host)));
+	    DBG(host->SCpnt, printk("scsi%d.%c: reconnected",
+		    host->host->host_no, acornscsi_target(host)));
 #endif
 	} else {
-	    queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+	    queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	    DBG(host->SCpnt, printk ("scsi%d.%c: had to move command "
+	    DBG(host->SCpnt, printk("scsi%d.%c: had to move command "
 		    "to disconnected queue\n",
-		    host->host->host_no, acornscsi_target (host)));
+		    host->host->host_no, acornscsi_target(host)));
 #endif
 	    host->SCpnt = NULL;
 	}
     }
     if (!host->SCpnt) {
-	host->SCpnt = queue_remove_tgtluntag (&host->queues.disconnected,
+	host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected,
 				host->scsi.reconnected.target,
 				host->scsi.reconnected.lun,
 				host->scsi.reconnected.tag);
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	DBG(host->SCpnt, printk ("scsi%d.%c: had to get command",
-		host->host->host_no, acornscsi_target (host)));
+	DBG(host->SCpnt, printk("scsi%d.%c: had to get command",
+		host->host->host_no, acornscsi_target(host)));
 #endif
     }
 
-    if (!host->SCpnt) {
-	acornscsi_abortcmd (host, host->scsi.reconnected.tag);
-	host->scsi.phase = PHASE_ABORTED;
-    } else {
+    if (!host->SCpnt)
+	acornscsi_abortcmd(host, host->scsi.reconnected.tag);
+    else {
 	/*
 	 * Restore data pointer from SAVED pointers.
 	 */
 	host->scsi.SCp = host->SCpnt->SCp;
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	printk (", data pointers: [%p, %X]",
+	printk(", data pointers: [%p, %X]",
 		host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
 #endif
     }
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-    printk ("\n");
+    printk("\n");
 #endif
 
     host->dma.transferred = host->scsi.SCp.scsi_xferred;
@@ -1844,47 +1934,48 @@
 }
 
 /*
- * Function: void acornscsi_disconnect_unexpected (AS_Host *host)
+ * Function: void acornscsi_disconnect_unexpected(AS_Host *host)
  * Purpose : handle an unexpected disconnect
  * Params  : host - host on which disconnect occurred
  */
 static
-void acornscsi_disconnect_unexpected (AS_Host *host)
+void acornscsi_disconnect_unexpected(AS_Host *host)
 {
-    printk (KERN_ERR "scsi%d.%c: unexpected disconnect\n",
-	    host->host->host_no, acornscsi_target (host));
+    printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n",
+	    host->host->host_no, acornscsi_target(host));
 #if (DEBUG & DEBUG_ABORT)
-    acornscsi_dumplog (host, 8);
+    acornscsi_dumplog(host, 8);
 #endif
 
-    acornscsi_done (host, &host->SCpnt, DID_ABORT);
+    acornscsi_done(host, &host->SCpnt, DID_ERROR);
 }
 
 /*
- * Function: void acornscsi_abortcmd (AS_host *host, unsigned char tag)
+ * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)
  * Purpose : abort a currently executing command
  * Params  : host - host with connected command to abort
  *	     tag  - tag to abort
  */
 static
-void acornscsi_abortcmd (AS_Host *host, unsigned char tag)
+void acornscsi_abortcmd(AS_Host *host, unsigned char tag)
 {
-    sbic_arm_write (host->scsi.io_port, CMND, CMND_ASSERTATN);
+    host->scsi.phase = PHASE_ABORTED;
+    sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN);
 
-    msgqueue_flush (&host->scsi.msgs);
+    msgqueue_flush(&host->scsi.msgs);
 #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
     if (tag)
-	msgqueue_addmsg (&host->scsi.msgs, 2, ABORT_TAG, tag);
+	msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag);
     else
 #endif
-	msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+	msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
 }
 
 /* ==========================================================================================
  * Interrupt routines.
  */
 /*
- * Function: int acornscsi_sbicintr (AS_Host *host)
+ * Function: int acornscsi_sbicintr(AS_Host *host)
  * Purpose : handle interrupts from SCSI device
  * Params  : host - host to process
  * Returns : INTR_PROCESS if expecting another SBIC interrupt
@@ -1892,15 +1983,15 @@
  *	     INTR_NEXT_COMMAND if we have finished processing the command
  */
 static
-intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq)
+intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
 {
     unsigned int asr, ssr;
 
-    asr = sbic_arm_read (host->scsi.io_port, ASR);
+    asr = sbic_arm_read(host->scsi.io_port, ASR);
     if (!(asr & ASR_INT))
 	return INTR_IDLE;
 
-    ssr = sbic_arm_read (host->scsi.io_port, SSR);
+    ssr = sbic_arm_read(host->scsi.io_port, SSR);
 
 #if (DEBUG & DEBUG_PHASES)
     print_sbic_status(asr, ssr, host->scsi.phase);
@@ -1913,23 +2004,23 @@
 
     switch (ssr) {
     case 0x00:				/* reset state - not advanced			*/
-	printk (KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
+	printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
 		host->host->host_no);
 	/* setup sbic - WD33C93A */
-	sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
-	sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+	sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+	sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET);
 	return INTR_IDLE;
 
     case 0x01:				/* reset state - advanced			*/
-	sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
-	sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
-	sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
-	sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
-	msgqueue_flush (&host->scsi.msgs);
+	sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+	sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+	sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+	sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+	msgqueue_flush(&host->scsi.msgs);
 	return INTR_IDLE;
 
     case 0x41:				/* unexpected disconnect aborted command	*/
-	acornscsi_disconnect_unexpected (host);
+	acornscsi_disconnect_unexpected(host);
 	return INTR_NEXT_COMMAND;
     }
 
@@ -1939,35 +2030,35 @@
 	case 0x11:			/* -> PHASE_CONNECTED				*/
 	    /* BUS FREE -> SELECTION */
 	    host->scsi.phase = PHASE_CONNECTED;
-	    msgqueue_flush (&host->scsi.msgs);
+	    msgqueue_flush(&host->scsi.msgs);
 	    host->dma.transferred = host->scsi.SCp.scsi_xferred;
 	    /* 33C93 gives next interrupt indicating bus phase */
-	    asr = sbic_arm_read (host->scsi.io_port, ASR);
+	    asr = sbic_arm_read(host->scsi.io_port, ASR);
 	    if (!(asr & ASR_INT))
 		break;
-	    ssr = sbic_arm_read (host->scsi.io_port, SSR);
+	    ssr = sbic_arm_read(host->scsi.io_port, SSR);
 	    ADD_STATUS(8, ssr, host->scsi.phase, 1);
 	    ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, 1);
 	    goto connected;
 	    
 	case 0x42:			/* select timed out				*/
 					/* -> PHASE_IDLE				*/
-	    acornscsi_done (host, &host->SCpnt, DID_NO_CONNECT);
+	    acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);
 	    return INTR_NEXT_COMMAND;
 
 	case 0x81:			/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
 	    /* BUS FREE -> RESELECTION */
 	    host->origSCpnt = host->SCpnt;
 	    host->SCpnt = NULL;
-	    msgqueue_flush (&host->scsi.msgs);
-	    acornscsi_reconnect (host);
+	    msgqueue_flush(&host->scsi.msgs);
+	    acornscsi_reconnect(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	}
 	return INTR_PROCESSING;
 
@@ -1977,12 +2068,12 @@
 #ifdef NONSTANDARD
 	case 0x8a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	    /* SELECTION -> COMMAND */
-	    acornscsi_sendcommand (host);
+	    acornscsi_sendcommand(host);
 	    break;
 
 	case 0x8b:			/* -> PHASE_STATUS				*/
 	    /* SELECTION -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 #endif
@@ -1990,55 +2081,57 @@
 	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* SELECTION ->MESSAGE OUT */
 	    host->scsi.phase = PHASE_MSGOUT;
-	    acornscsi_buildmessages (host);
-	    acornscsi_sendmessage (host);
+	    acornscsi_buildmessages(host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	/* these should not happen */
 	case 0x85:			/* target disconnected				*/
-	    acornscsi_done (host, &host->SCpnt, DID_ERROR);
+	    acornscsi_done(host, &host->SCpnt, DID_ERROR);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_MSGOUT:			/* STATE: connected & sent IDENTIFY message	*/
 	/*
-	 * SCSI standard says th at a MESSAGE OUT phases can be followed by a DATA phase
+	 * SCSI standard says that MESSAGE OUT phases can be followed by a
+	 * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase
 	 */
 	switch (ssr) {
-	case 0x8a:
+	case 0x8a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	case 0x1a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	    /* MESSAGE OUT -> COMMAND */
-	    acornscsi_sendcommand (host);
+	    acornscsi_sendcommand(host);
 	    break;
 
+	case 0x8b:			/* -> PHASE_STATUS				*/
 	case 0x1b:			/* -> PHASE_STATUS				*/
 	    /* MESSAGE OUT -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
-        case 0x4f:
+	case 0x4f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    /* MESSAGE OUT -> MESSAGE IN */
-	    acornscsi_message (host);
+	    acornscsi_message(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2047,43 +2140,43 @@
 	case 0x18:			/* -> PHASE_DATAOUT				*/
 	    /* COMMAND -> DATA OUT */
 	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
-		acornscsi_abortcmd (host, host->SCpnt->tag);
-	    acornscsi_dma_setup (host, DMA_OUT);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+		acornscsi_abortcmd(host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_OUT);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAOUT;
 	    return INTR_IDLE;
 
 	case 0x19:			/* -> PHASE_DATAIN				*/
 	    /* COMMAND -> DATA IN */
 	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
-		acornscsi_abortcmd (host, host->SCpnt->tag);
-	    acornscsi_dma_setup (host, DMA_IN);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+		acornscsi_abortcmd(host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_IN);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAIN;
 	    return INTR_IDLE;
 
 	case 0x1b:			/* -> PHASE_STATUS				*/
 	    /* COMMAND -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	    /* COMMAND -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    /* COMMAND -> MESSAGE IN */
-	    acornscsi_message (host);
+	    acornscsi_message(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2094,19 +2187,19 @@
 	    host->scsi.phase = PHASE_IDLE;
 	    host->stats.disconnects += 1;
 	} else {
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_NEXT_COMMAND;
 
     case PHASE_IDLE:			/* STATE: disconnected				*/
 	if (ssr == 0x81)		/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
-	    acornscsi_reconnect (host);
+	    acornscsi_reconnect(host);
 	else {
-	    printk (KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2119,54 +2212,54 @@
 	 * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
 	 * reconnect I_T_L command
 	 */
-	if (ssr != 0x8f && !acornscsi_reconnect_finish (host))
+	if (ssr != 0x8f && !acornscsi_reconnect_finish(host))
 	    return INTR_IDLE;
 	ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, in_irq);
 	switch (ssr) {
 	case 0x88:			/* data out phase				*/
 					/* -> PHASE_DATAOUT				*/
 	    /* MESSAGE IN -> DATA OUT */
-	    acornscsi_dma_setup (host, DMA_OUT);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_OUT);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAOUT;
 	    return INTR_IDLE;
 
 	case 0x89:			/* data in phase				*/
 					/* -> PHASE_DATAIN				*/
 	    /* MESSAGE IN -> DATA IN */
-	    acornscsi_dma_setup (host, DMA_IN);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_IN);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAIN;
 	    return INTR_IDLE;
 
 	case 0x8a:			/* command out					*/
 	    /* MESSAGE IN -> COMMAND */
-	    acornscsi_sendcommand (host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
+	    acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	    break;
 
 	case 0x8b:			/* status in					*/
 					/* -> PHASE_STATUSIN				*/
 	    /* MESSAGE IN -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x8e:			/* message out					*/
 					/* -> PHASE_MSGOUT				*/
 	    /* MESSAGE IN -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x8f:			/* message in					*/
-	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2177,41 +2270,45 @@
 	 */
 	switch (ssr) {
 	case 0x19:			/* -> PHASE_DATAIN				*/
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	case 0x89:			/* -> PHASE_DATAIN				*/
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	    return INTR_IDLE;
 
-	case 0x4b:			/* -> PHASE_STATUSIN				*/
 	case 0x1b:			/* -> PHASE_STATUSIN				*/
+	case 0x4b:			/* -> PHASE_STATUSIN				*/
+	case 0x8b:			/* -> PHASE_STATUSIN				*/
 	    /* DATA IN -> STATUS */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_readstatusbyte (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* DATA IN -> MESSAGE OUT */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_sendmessage (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* message in					*/
 	case 0x4f:			/* message in					*/
+	case 0x8f:			/* message in					*/
 	    /* DATA IN -> MESSAGE IN */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2222,58 +2319,69 @@
 	 */
 	switch (ssr) {
 	case 0x18:			/* -> PHASE_DATAOUT				*/
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	case 0x88:			/* -> PHASE_DATAOUT				*/
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	    return INTR_IDLE;
 
-	case 0x4b:			/* -> PHASE_STATUSIN				*/
 	case 0x1b:			/* -> PHASE_STATUSIN				*/
+	case 0x4b:			/* -> PHASE_STATUSIN				*/
+	case 0x8b:			/* -> PHASE_STATUSIN				*/
 	    /* DATA OUT -> STATUS */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_dma_adjust (host);
-	    acornscsi_readstatusbyte (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_dma_adjust(host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* DATA OUT -> MESSAGE OUT */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_dma_adjust (host);
-	    acornscsi_sendmessage (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_dma_adjust(host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* message in					*/
 	case 0x4f:			/* message in					*/
+	case 0x8f:			/* message in					*/
 	    /* DATA OUT -> MESSAGE IN */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_dma_adjust (host);
-	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_dma_adjust(host);
+	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_STATUSIN:		/* STATE: status in complete			*/
-	if (ssr == 0x1f)		/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+	switch (ssr) {
+	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+	case 0x8f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
 	    /* STATUS -> MESSAGE IN */
-	    acornscsi_message (host);
-	else if (ssr == 0x1e)		/* -> PHASE_MSGOUT				*/
+	    acornscsi_message(host);
+	    break;
+
+	case 0x1e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* STATUS -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
-	else {
-	    printk (KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_sendmessage(host);
+	    break;
+
+	default:
+	    printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2281,78 +2389,93 @@
 	switch (ssr) {
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* MESSAGE IN -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
 	case 0x2f:
 	case 0x4f:
 	case 0x8f:
-	    acornscsi_message (host);
+	    acornscsi_message(host);
+	    break;
+
+	case 0x85:
+	    printk("scsi%d.%c: strange message in disconnection\n",
+		host->host->host_no, acornscsi_target(host));
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_done(host, &host->SCpnt, DID_ERROR);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_DONE:			/* STATE: received status & message		*/
 	switch (ssr) {
 	case 0x85:			/* -> PHASE_IDLE				*/
-	    acornscsi_done (host, &host->SCpnt, DID_OK);
+	    acornscsi_done(host, &host->SCpnt, DID_OK);
 	    return INTR_NEXT_COMMAND;
 
+	case 0x1e:
 	case 0x8e:
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_ABORTED:
 	switch (ssr) {
 	case 0x85:
-	    acornscsi_done (host, &host->SCpnt, DID_ABORT);
+	    if (host->SCpnt)
+		acornscsi_done(host, &host->SCpnt, DID_ABORT);
+	    else {
+		clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,
+			  host->busyluns);
+		host->scsi.phase = PHASE_IDLE;
+	    }
 	    return INTR_NEXT_COMMAND;
 
 	case 0x1e:
 	case 0x2e:
 	case 0x4e:
 	case 0x8e:
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     default:
-	printk (KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
-		host->host->host_no, acornscsi_target (host), ssr);
-	acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
+		host->host->host_no, acornscsi_target(host), ssr);
+	acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
     }
     return INTR_PROCESSING;
 }
 
 /*
- * Prototype: void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+ * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
  * Purpose  : handle interrupts from Acorn SCSI card
  * Params   : irq    - interrupt number
  *	      dev_id - device specific data (AS_Host structure)
  *	      regs   - processor registers when interrupt occurred
  */
 static
-void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
     AS_Host *host = (AS_Host *)dev_id;
     intr_ret_t ret;
@@ -2360,21 +2483,21 @@
     int in_irq = 0;
 
     if (host->scsi.interrupt)
-	printk ("scsi%d: interrupt re-entered\n", host->host->host_no);
+	printk("scsi%d: interrupt re-entered\n", host->host->host_no);
     host->scsi.interrupt = 1;
 
     do {
 	ret = INTR_IDLE;
 
-	iostatus = inb (host->card.io_intr);
+	iostatus = inb(host->card.io_intr);
 
 	if (iostatus & 2) {
-	    acornscsi_dma_intr (host);
-	    iostatus = inb (host->card.io_intr);
+	    acornscsi_dma_intr(host);
+	    iostatus = inb(host->card.io_intr);
 	}
 
 	if (iostatus & 8)
-	    ret = acornscsi_sbicintr (host, in_irq);
+	    ret = acornscsi_sbicintr(host, in_irq);
 
 	/*
 	 * If we have a transfer pending, start it.
@@ -2382,10 +2505,10 @@
 	 * it's data
 	 */
 	if (host->dma.xfer_required)
-	    acornscsi_dma_xfer (host);
+	    acornscsi_dma_xfer(host);
 
 	if (ret == INTR_NEXT_COMMAND)
-	    ret = acornscsi_kick (host);
+	    ret = acornscsi_kick(host);
 
 	in_irq = 1;
     } while (ret != INTR_IDLE);
@@ -2398,29 +2521,29 @@
  */
 
 /*
- * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+ * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
  * Purpose  : queues a SCSI command
  * Params   : cmd  - SCSI command
  *	      done - function called on completion, with pointer to command descriptor
  * Returns  : 0, or < 0 on error.
  */
-int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
     AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
 
     if (!done) {
 	/* there should be some way of rejecting errors like this without panicing... */
-	panic ("scsi%d: queuecommand called with NULL done function [cmd=%p]",
+	panic("scsi%d: queuecommand called with NULL done function [cmd=%p]",
 		SCpnt->host->host_no, SCpnt);
 	return -EINVAL;
     }
 
 #if (DEBUG & DEBUG_NO_WRITE)
-    if (acornscsi_cmdtype (SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) {
-	printk (KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
+    if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) {
+	printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
 	    SCpnt->host->host_no, '0' + SCpnt->target);
 	SCpnt->result = DID_NO_CONNECT << 16;
-	done (SCpnt);
+	done(SCpnt);
 	return 0;
     }
 #endif
@@ -2429,7 +2552,7 @@
     SCpnt->host_scribble = NULL;
     SCpnt->result = 0;
     SCpnt->tag = 0;
-    SCpnt->SCp.phase = (int)acornscsi_datadirection (SCpnt->cmnd[0]);
+    SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
     SCpnt->SCp.sent_command = 0;
     SCpnt->SCp.scsi_xferred = 0;
     SCpnt->SCp.Status = 0;
@@ -2452,21 +2575,21 @@
     {
 	unsigned long flags;
 
-	if (!queue_add_cmd_ordered (&host->queues.issue, SCpnt)) {
+	if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {
 	    SCpnt->result = DID_ERROR << 16;
-	    done (SCpnt);
+	    done(SCpnt);
 	    return 0;
 	}
-	save_flags_cli (flags);
+	save_flags_cli(flags);
 	if (host->scsi.phase == PHASE_IDLE)
-	    acornscsi_kick (host);
-	restore_flags (flags);
+	    acornscsi_kick(host);
+	restore_flags(flags);
     }
     return 0;
 }
 
 /*
- * Prototype: void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+ * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
  * Purpose  : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
  * Params   : SCpntp1 - pointer to command to return
  *	      SCpntp2 - pointer to command to check
@@ -2474,7 +2597,7 @@
  * Returns  : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
  */
 static inline
-void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
 {
     Scsi_Cmnd *SCpnt = *SCpntp1;
 
@@ -2482,80 +2605,203 @@
 	*SCpntp1 = NULL;
 
 	SCpnt->result = result;
-	SCpnt->scsi_done (SCpnt);
+	SCpnt->scsi_done(SCpnt);
     }
 
     if (SCpnt == *SCpntp2)
 	*SCpntp2 = NULL;
 }
 
+enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
+
+/*
+ * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt)
+ * Purpose  : abort a command on this host
+ * Params   : SCpnt - command to abort
+ * Returns  : our abort status
+ */
+static enum res_abort
+acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt)
+{
+	enum res_abort res = res_not_running;
+
+	if (queue_removecmd(&host->queues.issue, SCpnt)) {
+		/*
+		 * The command was on the issue queue, and has not been
+		 * issued yet.  We can remove the command from the queue,
+		 * and acknowledge the abort.  Neither the devices nor the
+		 * interface know about the command.
+		 */
+//#if (DEBUG & DEBUG_ABORT)
+		printk("on issue queue ");
+//#endif
+		res = res_success;
+	} else if (queue_removecmd(&host->queues.disconnected, SCpnt)) {
+		/*
+		 * The command was on the disconnected queue.  Simply
+		 * acknowledge the abort condition, and when the target
+		 * reconnects, we will give it an ABORT message.  The
+		 * target should then disconnect, and we will clear
+		 * the busylun bit.
+		 */
+//#if (DEBUG & DEBUG_ABORT)
+		printk("on disconnected queue ");
+//#endif
+		res = res_success;
+	} else if (host->SCpnt == SCpnt) {
+		unsigned long flags;
+
+//#if (DEBUG & DEBUG_ABORT)
+		printk("executing ");
+//#endif
+
+		save_flags(flags);
+		cli();
+		switch (host->scsi.phase) {
+		/*
+		 * If the interface is idle, and the command is 'disconnectable',
+		 * then it is the same as on the disconnected queue.  We simply
+		 * remove all traces of the command.  When the target reconnects,
+		 * we will give it an ABORT message since the command could not
+		 * be found.  When the target finally disconnects, we will clear
+		 * the busylun bit.
+		 */
+		case PHASE_IDLE:
+			if (host->scsi.disconnectable) {
+				host->scsi.disconnectable = 0;
+				host->SCpnt = NULL;
+				res = res_success;
+			}
+			break;
+
+		/*
+		 * If the command has connected and done nothing further,
+		 * simply force a disconnect.  We also need to clear the
+		 * busylun bit.
+		 */
+		case PHASE_CONNECTED:
+			sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT);
+			host->SCpnt = NULL;
+			res = res_success_clear;
+			break;
+
+		default:
+			acornscsi_abortcmd(host, host->SCpnt->tag);
+			res = res_snooze;
+		}
+		restore_flags(flags);
+	} else if (host->origSCpnt == SCpnt) {
+		/*
+		 * The command will be executed next, but a command
+		 * is currently using the interface.  This is similar to
+		 * being on the issue queue, except the busylun bit has
+		 * been set.
+		 */
+		host->origSCpnt = NULL;
+//#if (DEBUG & DEBUG_ABORT)
+		printk("waiting for execution ");
+//#endif
+		res = res_success_clear;
+	} else
+		printk("unknown ");
+
+	return res;
+}
+
 /*
- * Prototype: int acornscsi_abort (Scsi_Cmnd *SCpnt)
+ * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt)
  * Purpose  : abort a command on this host
  * Params   : SCpnt - command to abort
  * Returns  : one of SCSI_ABORT_ macros
  */
-int acornscsi_abort (Scsi_Cmnd *SCpnt)
+int acornscsi_abort(Scsi_Cmnd *SCpnt)
 {
-    AS_Host *host = (AS_Host *) SCpnt->host->hostdata;
-    int result = SCSI_ABORT_NOT_RUNNING;
+	AS_Host *host = (AS_Host *) SCpnt->host->hostdata;
+	int result;
 
-    host->stats.aborts += 1;
+	host->stats.aborts += 1;
 
 #if (DEBUG & DEBUG_ABORT)
-    {
-	int asr, ssr;
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-	ssr = sbic_arm_read (host->scsi.io_port, SSR);
+	{
+		int asr, ssr;
+		asr = sbic_arm_read(host->scsi.io_port, ASR);
+		ssr = sbic_arm_read(host->scsi.io_port, SSR);
 
-	printk (KERN_WARNING "acornscsi_abort: ");
-	print_sbic_status(asr, ssr, host->scsi.phase);
-	acornscsi_dumplog (host, SCpnt->target);
-    }
+		printk(KERN_WARNING "acornscsi_abort: ");
+		print_sbic_status(asr, ssr, host->scsi.phase);
+		acornscsi_dumplog(host, SCpnt->target);
+	}
 #endif
 
-    if (queue_removecmd (&host->queues.issue, SCpnt)) {
-	SCpnt->result = DID_ABORT << 16;
-	SCpnt->scsi_done (SCpnt);
-#if (DEBUG & DEBUG_ABORT)
-	printk ("scsi%d: command on issue queue\n", host->host->host_no);
-#endif
-	result = SCSI_ABORT_SUCCESS;
-    } else if (queue_cmdonqueue (&host->queues.disconnected, SCpnt)) {
-	printk ("scsi%d: command on disconnected queue\n", host->host->host_no);
-	result = SCSI_ABORT_SNOOZE;
-    } else if (host->SCpnt == SCpnt) {
-	acornscsi_abortcmd (host, host->SCpnt->tag);
-	printk ("scsi%d: command executing\n", host->host->host_no);
-	result = SCSI_ABORT_SNOOZE;
-    } else if (host->origSCpnt == SCpnt) {
-	host->origSCpnt = NULL;
-	SCpnt->result = DID_ABORT << 16;
-	SCpnt->scsi_done (SCpnt);
-#if (DEBUG & DEBUG_ABORT)
-	printk ("scsi%d: command waiting for execution\n", host->host->host_no);
-#endif
-	result = SCSI_ABORT_SUCCESS;
-    }
+	printk("scsi%d: ", host->host->host_no);
+
+	switch (acornscsi_do_abort(host, SCpnt)) {
+	/*
+	 * We managed to find the command and cleared it out.
+	 * We do not expect the command to be executing on the
+	 * target, but we have set the busylun bit.
+	 */
+	case res_success_clear:
+//#if (DEBUG & DEBUG_ABORT)
+		printk("clear ");
+//#endif
+		clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+
+	/*
+	 * We found the command, and cleared it out.  Either
+	 * the command is still known to be executing on the
+	 * target, or the busylun bit is not set.
+	 */
+	case res_success:
+//#if (DEBUG & DEBUG_ABORT)
+		printk("success\n");
+//#endif
+		SCpnt->result = DID_ABORT << 16;
+		SCpnt->scsi_done(SCpnt);
+		result = SCSI_ABORT_SUCCESS;
+		break;
 
-    if (result == SCSI_ABORT_NOT_RUNNING) {
-	printk ("scsi%d: abort(): command not running\n", host->host->host_no);
-	acornscsi_dumplog (host, SCpnt->target);
+	/*
+	 * We did find the command, but unfortunately we couldn't
+	 * unhook it from ourselves.  Wait some more, and if it
+	 * still doesn't complete, reset the interface.
+	 */
+	case res_snooze:
+//#if (DEBUG & DEBUG_ABORT)
+		printk("snooze\n");
+//#endif
+		result = SCSI_ABORT_SNOOZE;
+		break;
+
+	/*
+	 * The command could not be found (either because it completed,
+	 * or it got dropped.
+	 */
+	default:
+	case res_not_running:
+		acornscsi_dumplog(host, SCpnt->target);
 #if (DEBUG & DEBUG_ABORT)
-	result = SCSI_ABORT_SNOOZE;
+		result = SCSI_ABORT_SNOOZE;
+#else
+		result = SCSI_ABORT_NOT_RUNNING;
 #endif
-    }
-    return result;
+//#if (DEBUG & DEBUG_ABORT)
+		printk("not running\n");
+//#endif
+		break;
+	}
+
+	return result;
 }
 
 /*
- * Prototype: int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
  * Purpose  : reset a command on this host/reset this host
  * Params   : SCpnt  - command causing reset
  *	      result - what type of reset to perform
  * Returns  : one of SCSI_RESET_ macros
  */
-int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
 {
     AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
     Scsi_Cmnd *SCptr;
@@ -2566,16 +2812,16 @@
     {
 	int asr, ssr;
 
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-	ssr = sbic_arm_read (host->scsi.io_port, SSR);
+	asr = sbic_arm_read(host->scsi.io_port, ASR);
+	ssr = sbic_arm_read(host->scsi.io_port, SSR);
 
-	printk (KERN_WARNING "acornscsi_reset: ");
+	printk(KERN_WARNING "acornscsi_reset: ");
 	print_sbic_status(asr, ssr, host->scsi.phase);
-	acornscsi_dumplog (host, SCpnt->target);
+	acornscsi_dumplog(host, SCpnt->target);
     }
 #endif
 
-    acornscsi_dma_stop (host);
+    acornscsi_dma_stop(host);
 
     SCptr = host->SCpnt;
 
@@ -2583,19 +2829,19 @@
      * do hard reset.  This resets all devices on this host, and so we
      * must set the reset status on all commands.
      */
-    acornscsi_resetcard (host);
+    acornscsi_resetcard(host);
 
     /*
      * report reset on commands current connected/disconnected
      */
-    acornscsi_reportstatus (&host->SCpnt, &SCptr, DID_RESET);
+    acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET);
 
-    while ((SCptr = queue_remove (&host->queues.disconnected)) != NULL)
-	acornscsi_reportstatus (&SCptr, &SCpnt, DID_RESET);
+    while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL)
+	acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET);
 
     if (SCpnt) {
 	SCpnt->result = DID_RESET << 16;
-	SCpnt->scsi_done (SCpnt);
+	SCpnt->scsi_done(SCpnt);
     }
 
     return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS;
@@ -2607,19 +2853,19 @@
 static struct expansion_card *ecs[MAX_ECARDS];
 
 /*
- * Prototype: void acornscsi_init (AS_Host *host)
+ * Prototype: void acornscsi_init(AS_Host *host)
  * Purpose  : initialise the AS_Host structure for one interface & setup hardware
  * Params   : host - host to setup
  */
 static
-void acornscsi_init (AS_Host *host)
+void acornscsi_init(AS_Host *host)
 {
-    memset (&host->stats, 0, sizeof (host->stats));
-    queue_initialise (&host->queues.issue);
-    queue_initialise (&host->queues.disconnected);
-    msgqueue_initialise (&host->scsi.msgs);
+    memset(&host->stats, 0, sizeof (host->stats));
+    queue_initialise(&host->queues.issue);
+    queue_initialise(&host->queues.disconnected);
+    msgqueue_initialise(&host->scsi.msgs);
 
-    acornscsi_resetcard (host);
+    acornscsi_resetcard(host);
 }
 
 int acornscsi_detect(Scsi_Host_Template * tpnt)
@@ -2634,7 +2880,7 @@
     for (i = 0; i < MAX_ECARDS; i++)
 	ecs[i] = NULL;
 
-    ecard_startfind ();
+    ecard_startfind();
 
     while(1) {
 	ecs[count] = ecard_find(0, acornscsi_cids);
@@ -2642,37 +2888,37 @@
 	    break;
 
 	if (ecs[count]->irq == 0xff) {
-	    printk ("scsi: WD33C93 does not have IRQ enabled - ignoring\n");
+	    printk("scsi: WD33C93 does not have IRQ enabled - ignoring\n");
 	    continue;
 	}
 
 	ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */
 
-	instance = scsi_register (tpnt, sizeof(AS_Host));
+	instance = scsi_register(tpnt, sizeof(AS_Host));
 	host = (AS_Host *)instance->hostdata;
 
-	instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0);
+	instance->io_port = ecard_address(ecs[count], ECARD_MEMC, 0);
 	instance->irq = ecs[count]->irq;
 
 	host->host		= instance;
-	host->scsi.io_port	= ioaddr (instance->io_port + 0x800);
+	host->scsi.io_port	= ioaddr(instance->io_port + 0x800);
 	host->scsi.irq		= instance->irq;
 	host->card.io_intr	= POD_SPACE(instance->io_port) + 0x800;
 	host->card.io_page	= POD_SPACE(instance->io_port) + 0xc00;
-	host->card.io_ram	= ioaddr (instance->io_port);
+	host->card.io_ram	= ioaddr(instance->io_port);
 	host->dma.io_port	= instance->io_port + 0xc00;
 	host->dma.io_intr_clear = POD_SPACE(instance->io_port) + 0x800;
 
 	ecs[count]->irqaddr	= (char *)ioaddr(host->card.io_intr);
 	ecs[count]->irqmask	= 0x0a;
 
-	request_region (instance->io_port + 0x800,  2, "acornscsi(sbic)");
-	request_region (host->card.io_intr,  1, "acornscsi(intr)");
-	request_region (host->card.io_page,  1, "acornscsi(page)");
+	request_region(instance->io_port + 0x800,  2, "acornscsi(sbic)");
+	request_region(host->card.io_intr,  1, "acornscsi(intr)");
+	request_region(host->card.io_page,  1, "acornscsi(page)");
 #ifdef USE_DMAC
-	request_region (host->dma.io_port, 256, "acornscsi(dmac)");
+	request_region(host->dma.io_port, 256, "acornscsi(dmac)");
 #endif
-	request_region (instance->io_port, 2048, "acornscsi(ram)");
+	request_region(instance->io_port, 2048, "acornscsi(ram)");
 
 	if (request_irq(host->scsi.irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", host)) {
 	    printk(KERN_CRIT "scsi%d: IRQ%d not free, interrupts disabled\n",
@@ -2680,7 +2926,7 @@
 	    host->scsi.irq = NO_IRQ;
 	}
 
-	acornscsi_init (host);
+	acornscsi_init(host);
 
 	++count;
     }
@@ -2688,12 +2934,12 @@
 }
 
 /*
- * Function: int acornscsi_release (struct Scsi_Host *host)
+ * Function: int acornscsi_release(struct Scsi_Host *host)
  * Purpose : release all resources used by this adapter
  * Params  : host - driver structure to release
  * Returns : nothing of any consequence
  */
-int acornscsi_release (struct Scsi_Host *instance)
+int acornscsi_release(struct Scsi_Host *instance)
 {
     AS_Host *host = (AS_Host *)instance->hostdata;
     int i;
@@ -2701,30 +2947,30 @@
     /*
      * Put card into RESET state
      */
-    outb (0x80, host->card.io_page);
+    outb(0x80, host->card.io_page);
 
     if (host->scsi.irq != NO_IRQ)
-	free_irq (host->scsi.irq, host);
+	free_irq(host->scsi.irq, host);
 
-    release_region (instance->io_port + 0x800, 2);
-    release_region (host->card.io_intr, 1);
-    release_region (host->card.io_page, 1);
-    release_region (host->dma.io_port, 256);
-    release_region (instance->io_port, 2048);
+    release_region(instance->io_port + 0x800, 2);
+    release_region(host->card.io_intr, 1);
+    release_region(host->card.io_page, 1);
+    release_region(host->dma.io_port, 256);
+    release_region(instance->io_port, 2048);
 
     for (i = 0; i < MAX_ECARDS; i++)
-	if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
-	    ecard_release (ecs[i]);
+	if (ecs[i] && instance->io_port == ecard_address(ecs[i], ECARD_MEMC, 0))
+	    ecard_release(ecs[i]);
 
-    msgqueue_free (&host->scsi.msgs);
-    queue_free (&host->queues.disconnected);
-    queue_free (&host->queues.issue);
+    msgqueue_free(&host->scsi.msgs);
+    queue_free(&host->queues.disconnected);
+    queue_free(&host->queues.issue);
 
     return 0;
 }
 
 /*
- * Function: char *acornscsi_info (struct Scsi_Host *host)
+ * Function: char *acornscsi_info(struct Scsi_Host *host)
  * Purpose : return a string describing this interface
  * Params  : host - host to give information on
  * Returns : a constant string
@@ -2736,7 +2982,7 @@
 
     p = string;
     
-    p += sprintf (string, "%s at port %lX irq %d v%d.%d.%d"
+    p += sprintf(string, "%s at port %lX irq %d v%d.%d.%d"
 #ifdef CONFIG_SCSI_ACORNSCSI_SYNC
     " SYNC"
 #endif
@@ -2772,7 +3018,7 @@
 
     host  = (AS_Host *)instance->hostdata;
     
-    p += sprintf (p, "AcornSCSI driver v%d.%d.%d"
+    p += sprintf(p, "AcornSCSI driver v%d.%d.%d"
 #ifdef CONFIG_SCSI_ACORNSCSI_SYNC
     " SYNC"
 #endif
@@ -2787,14 +3033,14 @@
 #endif
 		"\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
 
-    p += sprintf (p,	"SBIC: WD33C93A  Address: %08X  IRQ : %d\n",
+    p += sprintf(p,	"SBIC: WD33C93A  Address: %08X  IRQ : %d\n",
 			host->scsi.io_port, host->scsi.irq);
 #ifdef USE_DMAC
-    p += sprintf (p,	"DMAC: uPC71071  Address: %08X  IRQ : %d\n\n",
+    p += sprintf(p,	"DMAC: uPC71071  Address: %08X  IRQ : %d\n\n",
 			host->dma.io_port, host->scsi.irq);
 #endif
 
-    p += sprintf (p,	"Statistics:\n"
+    p += sprintf(p,	"Statistics:\n"
 			"Queued commands: %-10u    Issued commands: %-10u\n"
 			"Done commands  : %-10u    Reads          : %-10u\n"
 			"Writes         : %-10u    Others         : %-10u\n"
@@ -2809,47 +3055,47 @@
     for (devidx = 0; devidx < 9; devidx ++) {
 	unsigned int statptr, prev;
 
-	p += sprintf (p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
-	statptr = status_ptr[devidx] - 10;
+	p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
+	statptr = host->status_ptr[devidx] - 10;
 
 	if ((signed int)statptr < 0)
-	    statptr += 16;
+	    statptr += STATUS_BUFFER_SIZE;
 
-	prev = status[devidx][statptr].when;
+	prev = host->status[devidx][statptr].when;
 
-	for (; statptr != status_ptr[devidx]; statptr = (statptr + 1) & 15) {
-	    if (status[devidx][statptr].when) {
-		p += sprintf (p, "%c%02X:%02X+%2ld",
-			status[devidx][statptr].irq ? '-' : ' ',
-			status[devidx][statptr].ph,
-			status[devidx][statptr].ssr,
-			(status[devidx][statptr].when - prev) < 100 ?
-				(status[devidx][statptr].when - prev) : 99);
-		prev = status[devidx][statptr].when;
+	for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+	    if (host->status[devidx][statptr].when) {
+		p += sprintf(p, "%c%02X:%02X+%2ld",
+			host->status[devidx][statptr].irq ? '-' : ' ',
+			host->status[devidx][statptr].ph,
+			host->status[devidx][statptr].ssr,
+			(host->status[devidx][statptr].when - prev) < 100 ?
+				(host->status[devidx][statptr].when - prev) : 99);
+		prev = host->status[devidx][statptr].when;
 	    }
 	}
     }
 
-    p += sprintf (p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none");
+    p += sprintf(p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none");
 
     for (scd = instance->host_queue; scd; scd = scd->next) {
 	int len;
 
-	proc_print_scsidevice (scd, p, &len, 0);
+	proc_print_scsidevice(scd, p, &len, 0);
 	p += len;
 
-	p += sprintf (p, "Extensions: ");
+	p += sprintf(p, "Extensions: ");
 
 	if (scd->tagged_supported)
-	    p += sprintf (p, "TAG %sabled [%d] ",
+	    p += sprintf(p, "TAG %sabled [%d] ",
 			  scd->tagged_queue ? "en" : "dis", scd->current_tag);
-	p += sprintf (p, "\nTransfers: ");
+	p += sprintf(p, "\nTransfers: ");
 	if (host->device[scd->id].sync_xfer & 15)
-	    p += sprintf (p, "sync, offset %d, %d ns\n",
+	    p += sprintf(p, "sync, offset %d, %d ns\n",
 			  host->device[scd->id].sync_xfer & 15,
-			  acornscsi_getperiod (host->device[scd->id].sync_xfer));
+			  acornscsi_getperiod(host->device[scd->id].sync_xfer));
 	else
-	    p += sprintf (p, "async\n");
+	    p += sprintf(p, "async\n");
 
 	pos = p - buffer;
 	if (pos + begin < offset) {
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/acornscsi.h linux/drivers/acorn/scsi/acornscsi.h
--- linux-2.2.18-pre8/drivers/acorn/scsi/acornscsi.h	Sun Dec 28 09:06:25 1997
+++ linux/drivers/acorn/scsi/acornscsi.h	Mon Dec  3 20:25:14 2001
@@ -291,6 +291,27 @@
 #include "queue.h"
 #include "msgqueue.h"
 
+#define STATUS_BUFFER_SIZE	32
+/*
+ * This is used to dump the previous states of the SBIC
+ */
+struct status_entry {
+	unsigned long	when;
+	unsigned char	ssr;
+	unsigned char	ph;
+	unsigned char	irq;
+	unsigned char	unused;
+};
+
+#define ADD_STATUS(_q,_ssr,_ph,_irq) \
+({									\
+	host->status[(_q)][host->status_ptr[(_q)]].when = jiffies;	\
+	host->status[(_q)][host->status_ptr[(_q)]].ssr  = (_ssr);	\
+	host->status[(_q)][host->status_ptr[(_q)]].ph   = (_ph);	\
+	host->status[(_q)][host->status_ptr[(_q)]].irq  = (_irq);	\
+	host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \
+})
+
 /*
  * AcornSCSI host specific data
  */
@@ -303,7 +324,7 @@
     /* driver information */
     struct {
 	unsigned int	io_port;		/* base address of WD33C93		*/
-	unsigned char	irq;			/* interrupt				*/
+	unsigned int	irq;			/* interrupt				*/
 	phase_t		phase;			/* current phase			*/
 
 	struct {
@@ -361,6 +382,7 @@
 	char		*xfer_ptr;		/* pointer to area			*/
 	unsigned char	xfer_required:1;	/* set if we need to transfer something	*/
 	unsigned char	xfer_setup:1;		/* set if DMA is setup			*/
+	unsigned char	xfer_done:1;		/* set if DMA reached end of BH list	*/
     } dma;
 
     /* card info */
@@ -370,6 +392,9 @@
 	unsigned int	io_ram;			/* base address of RAM access		*/
 	unsigned char	page_reg;		/* current setting of page reg		*/
     } card;
+
+    unsigned char status_ptr[9];
+    struct status_entry status[9][STATUS_BUFFER_SIZE];
 } AS_Host;
 
 #endif /* ndef HOSTS_C */
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/arxescsi.c linux/drivers/acorn/scsi/arxescsi.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/arxescsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/arxescsi.c	Mon Dec  3 20:25:14 2001
@@ -0,0 +1,395 @@
+/*
+ * linux/arch/arm/drivers/scsi/cumana_2.c
+ *
+ * Copyright (C) 1997,1998 Russell King
+ *
+ * This driver is based on experimentation.  Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ *  30-08-1997	RMK	0.0.0	Created, READONLY version as cumana_2.c
+ *  22-01-1998	RMK	0.0.1	Updated to 2.1.80
+ *  15-04-1998	RMK	0.0.1	Only do PIO if FAS216 will allow it.
+ *  11-06-1998 		0.0.2   Changed to support ARXE 16-bit SCSI card, enabled writing
+ *  				by Stefan Hanske
+ */
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+
+#include <asm/delay.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#include "../../scsi/sd.h"
+#include "../../scsi/hosts.h"
+#include "arxescsi.h"
+#include "fas216.h"
+
+/* Hmm - this should go somewhere else */
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+
+/* Configuration */
+#define ARXESCSI_XTALFREQ		24
+#define ARXESCSI_ASYNC_PERIOD		200
+#define ARXESCSI_SYNC_DEPTH		0
+
+/*
+ * List of devices that the driver will recognise
+ */
+#define ARXESCSI_LIST		{ MANU_ARXE, PROD_ARXE_SCSI }
+
+/*
+ * Version
+ */
+#define VER_MAJOR	0
+#define VER_MINOR	0
+#define VER_PATCH	2
+
+static struct expansion_card *ecs[MAX_ECARDS];
+
+static struct proc_dir_entry proc_scsi_arxescsi = {
+	PROC_SCSI_QLOGICFAS, 6, "arxescsi",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*
+ * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params  : host      - host
+ *	     SCpnt     - command
+ *	     direction - DMA on to/off of card
+ *	     min_type  - minimum DMA support that we must have for this transfer
+ * Returns : 0 if we should not set CMD_WITHDMA for transfer info command
+ */
+static fasdmatype_t
+arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+		       fasdmadir_t direction, fasdmatype_t min_type)
+{
+	/*
+	 * We don't do real DMA
+	 */
+	return fasdma_pseudo;
+}
+
+
+
+/* Faster transfer routines, written by SH to speed up the loops */
+
+static __inline__ unsigned char getb(unsigned int address, unsigned int reg)
+{
+	unsigned char value;
+
+	__asm__ __volatile__(
+	"ldrb	%0, [%1, %2, lsl #5]"
+	: "=r" (value)
+	: "r" (address), "r" (reg) );
+	return value;
+}
+
+static __inline__ unsigned int getw(unsigned int address, unsigned int reg)
+{
+	unsigned int value;
+	
+	__asm__ __volatile__(
+	"ldr	%0, [%1, %2, lsl #5]\n\t"
+	"mov	%0, %0, lsl #16\n\t"
+	"mov	%0, %0, lsr #16"
+	: "=r" (value)
+	: "r" (address), "r" (reg) );
+	return value;
+}
+
+static __inline__ void putw(unsigned int address, unsigned int reg, unsigned long value)
+{
+	__asm__ __volatile__(
+	"mov	%0, %0, lsl #16\n\t"
+	"str	%0, [%1, %2, lsl #5]"
+	:
+	: "r" (value), "r" (address), "r" (reg) );
+}
+
+
+/*
+ * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer)
+ * Purpose : handles pseudo DMA
+ * Params  : host      - host
+ *	     SCpnt     - command
+ *	     direction - DMA on to/off of card
+ *	     transfer  - minimum number of bytes we expect to transfer
+ */
+void arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+			fasdmadir_t direction, int transfer)
+{
+	ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata;
+	unsigned int length, io, error=0;
+	unsigned char *addr;
+
+	length = SCp->this_residual;
+	addr = SCp->ptr;
+	io = __ioaddr(host->io_port);
+
+	if (direction == DMA_OUT) {
+		while (length > 0) {
+			unsigned long word;
+
+
+			word = *addr | *(addr + 1) << 8;
+			if (getb(io, 4) & STAT_INT)
+				break;
+
+			if (!(getb(io, 48) & CSTATUS_IRQ))
+				continue;
+
+			putw(io, 16, word);
+			if (length > 1) {
+				addr += 2;
+				length -= 2;
+			} else {
+				addr += 1;
+				length -= 1;
+			}
+		}
+	}
+	else {
+		if (transfer && (transfer & 255)) {
+			while (length >= 256) {
+				if (getb(io, 4) & STAT_INT) {
+					error=1;
+					break;
+				}
+	    
+				if (!(getb(io, 48) & CSTATUS_IRQ))
+					continue;
+
+				insw(info->dmaarea, addr, 256 >> 1);
+				addr += 256;
+				length -= 256;
+			}
+		}
+
+		if (!(error))
+			while (length > 0) {
+				unsigned long word;
+
+				if (getb(io, 4) & STAT_INT)
+					break;
+
+				if (!(getb(io, 48) & CSTATUS_IRQ))
+					continue;
+
+				word = getw(io, 16);
+				*addr++ = word;
+				if (--length > 0) {
+					*addr++ = word >> 8;
+					length --;
+				}
+			}
+	}
+}
+
+/*
+ * Function: int arxescsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params  : host  - host
+ *	     SCpnt - command
+ */
+static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+	/*
+	 * no DMA to stop
+	 */
+}
+
+/*
+ * Function: int arxescsi_detect(Scsi_Host_Template * tpnt)
+ * Purpose : initialises ARXE SCSI driver
+ * Params  : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
+ */
+int arxescsi_detect(Scsi_Host_Template *tpnt)
+{
+	static const card_ids arxescsi_cids[] = { ARXESCSI_LIST, { 0xffff, 0xffff} };
+	int count = 0;
+	struct Scsi_Host *host;
+  
+	tpnt->proc_dir = &proc_scsi_arxescsi;
+	memset(ecs, 0, sizeof (ecs));
+
+	ecard_startfind();
+
+	while (1) {
+	    	ARXEScsi_Info *info;
+
+		ecs[count] = ecard_find(0, arxescsi_cids);
+		if (!ecs[count])
+			break;
+
+		ecard_claim(ecs[count]);
+		
+		host = scsi_register(tpnt, sizeof (ARXEScsi_Info));
+		if (!host) {
+			ecard_release(ecs[count]);
+			break;
+		}
+
+		host->io_port = ecard_address(ecs[count], ECARD_MEMC, 0) + 0x0800;
+		host->irq = NO_IRQ;
+		host->dma_channel = NO_DMA;
+		host->can_queue = 0; /* no command queueing */
+		info = (ARXEScsi_Info *)host->hostdata;
+
+		info->info.scsi.io_port		= host->io_port;
+		info->info.scsi.irq		= host->irq;
+		info->info.scsi.io_shift	= 3;
+		info->info.ifcfg.clockrate	= ARXESCSI_XTALFREQ;
+		info->info.ifcfg.select_timeout = 255;
+		info->info.ifcfg.asyncperiod	= ARXESCSI_ASYNC_PERIOD;
+		info->info.ifcfg.sync_max_depth	= ARXESCSI_SYNC_DEPTH;
+		info->info.ifcfg.cntl3		= CNTL3_FASTSCSI | CNTL3_FASTCLK;
+		info->info.ifcfg.disconnect_ok	= 0;
+		info->info.ifcfg.wide_max_size	= 0;
+		info->info.dma.setup		= arxescsi_dma_setup;
+		info->info.dma.pseudo		= arxescsi_dma_pseudo;
+		info->info.dma.stop		= arxescsi_dma_stop;
+		info->dmaarea			= host->io_port + 128;
+		info->cstatus			= host->io_port + 384;
+		
+		ecs[count]->irqaddr = (unsigned char *)BUS_ADDR(host->io_port);
+		ecs[count]->irqmask = CSTATUS_IRQ;
+
+		request_region(host->io_port      , 120, "arxescsi-fas");
+		request_region(host->io_port + 128, 384, "arxescsi-dma");
+
+		printk("scsi%d: Has no interrupts - using polling mode\n",
+		       host->host_no);
+
+		fas216_init(host);
+		++count;
+	}
+	return count;
+}
+
+/*
+ * Function: int arxescsi_release(struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params  : host - driver host structure to return info for.
+ * Returns : nothing
+ */
+int arxescsi_release(struct Scsi_Host *host)
+{
+	int i;
+
+	fas216_release(host);
+
+	release_region(host->io_port, 120);
+	release_region(host->io_port + 128, 384);
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ecs[i] && host->io_port == (ecard_address(ecs[i], ECARD_MEMC, 0) + 0x0800))
+			ecard_release(ecs[i]);
+	return 0;
+}
+
+/*
+ * Function: const char *arxescsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params  : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *arxescsi_info(struct Scsi_Host *host)
+{
+	ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata;
+	static char string[100], *p;
+
+	p = string;
+	p += sprintf(string, "%s at port %lX irq %d v%d.%d.%d scsi %s",
+		     host->hostt->name, host->io_port, host->irq,
+		     VER_MAJOR, VER_MINOR, VER_PATCH,
+		     info->info.scsi.type);
+
+	return string;
+}
+
+/*
+ * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset,
+ *					 int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ *	     the /proc filesystem.
+ * Params  : buffer - a buffer to write information to
+ *	     start  - a pointer into this buffer set by this routine to the start
+ *		      of the required information.
+ *	     offset - offset into information that we have read upto.
+ *	     length - length of buffer
+ *	     host_no - host number to return information for
+ *	     inout  - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int arxescsi_proc_info(char *buffer, char **start, off_t offset,
+			    int length, int host_no, int inout)
+{
+	int pos, begin;
+	struct Scsi_Host *host = scsi_hostlist;
+	ARXEScsi_Info *info;
+	Scsi_Device *scd;
+
+	while (host) {
+		if (host->host_no == host_no)
+			break;
+		host = host->next;
+	}
+	if (!host)
+		return 0;
+
+	info = (ARXEScsi_Info *)host->hostdata;
+	if (inout == 1)
+		return -EINVAL;
+
+	begin = 0;
+	pos = sprintf(buffer,
+			"ARXE 16-bit SCSI driver version %d.%d.%d\n",
+			VER_MAJOR, VER_MINOR, VER_PATCH);
+	pos += sprintf(buffer + pos,
+			"Address: %08lX          IRQ : %d\n"
+			"FAS    : %s\n\n"
+			"Statistics:\n",
+			host->io_port, host->irq, info->info.scsi.type);
+
+	pos += fas216_print_stats(&info->info, buffer + pos);
+
+	pos += sprintf (buffer+pos, "\nAttached devices:\n");
+
+	for (scd = host->host_queue; scd; scd = scd->next) {
+		pos += fas216_print_device(&info->info, scd, buffer + pos);
+
+		if (pos + begin < offset) {
+			begin += pos;
+			pos = 0;
+		}
+		if (pos + begin > offset + length)
+			break;
+	}
+
+	*start = buffer + (offset - begin);
+	pos -= offset - begin;
+	if (pos > length)
+		pos = length;
+
+	return pos;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = ARXEScsi;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/arxescsi.h linux/drivers/acorn/scsi/arxescsi.h
--- linux-2.2.18-pre8/drivers/acorn/scsi/arxescsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/arxescsi.h	Mon Dec  3 20:25:14 2001
@@ -0,0 +1,80 @@
+/*
+ * ARXE SCSI card driver
+ *
+ * Copyright (C) 1997 Russell King
+ * Changes to support ARXE 16-bit SCSI card by Stefan Hanske
+ */
+#ifndef ARXE_SCSI_H
+#define ARXE_SCSI_H
+
+#define MANU_ARXE 	0x0041
+#define PROD_ARXE_SCSI	0x00be
+
+extern int arxescsi_detect (Scsi_Host_Template *);
+extern int arxescsi_release (struct Scsi_Host *);
+extern const char *arxescsi_info (struct Scsi_Host *);
+extern int arxescsi_proc_info (char *buffer, char **start, off_t offset,
+				int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#ifndef CAN_QUEUE
+/*
+ * Default queue size
+ */
+#define CAN_QUEUE	1
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN	1
+#endif
+
+#ifndef SCSI_ID
+/*
+ * Default SCSI host ID
+ */
+#define SCSI_ID		7
+#endif
+
+#include <scsi/scsicam.h>
+
+#ifndef HOSTS_C
+#include "fas216.h"
+#endif
+
+#define ARXEScsi {							\
+proc_info:	arxescsi_proc_info,						\
+name:		"ARXE SCSI card",						\
+detect:		arxescsi_detect,		/* detect		*/	\
+release:	arxescsi_release,		/* release		*/	\
+info:		arxescsi_info,			/* info			*/	\
+command:	fas216_command,			/* command		*/	\
+queuecommand:	fas216_queue_command,		/* queuecommand		*/	\
+abort:		fas216_abort,			/* abort		*/	\
+reset:		fas216_reset,			/* reset		*/	\
+bios_param:	scsicam_bios_param,		/* biosparam		*/	\
+can_queue:	CAN_QUEUE,			/* can queue		*/	\
+this_id:	SCSI_ID,			/* scsi host id		*/	\
+sg_tablesize:	SG_ALL,				/* sg_tablesize		*/	\
+cmd_per_lun:	CMD_PER_LUN,			/* cmd per lun		*/	\
+use_clustering:	DISABLE_CLUSTERING						\
+	}
+
+#ifndef HOSTS_C
+
+typedef struct {
+    FAS216_Info info;
+
+    /* other info... */
+    unsigned int	cstatus;	/* card status register	*/
+    unsigned int	dmaarea;	/* Pseudo DMA area	*/
+} ARXEScsi_Info;
+
+#define CSTATUS_IRQ	(1 << 0)
+#define CSTATUS_DRQ	(1 << 0)
+
+#endif /* HOSTS_C */
+
+#endif /* ARXE_SCSI_H */
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/cumana_2.c linux/drivers/acorn/scsi/cumana_2.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/cumana_2.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/scsi/cumana_2.c	Mon Dec  3 20:25:14 2001
@@ -4,12 +4,12 @@
  * Copyright (C) 1997-1998 Russell King
  *
  * Changelog:
- *  30-08-1997	RMK	0.0.0	Created, READONLY version
- *  22-01-1998	RMK	0.0.1	Updated to 2.1.80
+ *  30-08-1997	RMK	0.0.0	Created, READONLY version.
+ *  22-01-1998	RMK	0.0.1	Updated to 2.1.80.
  *  15-04-1998	RMK	0.0.1	Only do PIO if FAS216 will allow it.
- *  02-05-1998	RMK	0.0.2	Updated & added DMA support
+ *  02-05-1998	RMK	0.0.2	Updated & added DMA support.
  *  27-06-1998	RMK		Changed asm/delay.h to linux/delay.h
- *  18-08-1998	RMK	0.0.3	Fixed synchronous transfer depth
+ *  18-08-1998	RMK	0.0.3	Fixed synchronous transfer depth.
  */
 
 #include <linux/module.h>
@@ -117,6 +117,8 @@
 	cumanascsi_2_irqenable,
 	cumanascsi_2_irqdisable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -364,6 +366,7 @@
 		info->info.ifcfg.sync_max_depth	= CUMANASCSI2_SYNC_DEPTH;
 		info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
 		info->info.ifcfg.disconnect_ok	= 1;
+		info->info.ifcfg.wide_max_size	= 0;
 		info->info.dma.setup		= cumanascsi_2_dma_setup;
 		info->info.dma.pseudo		= cumanascsi_2_dma_pseudo;
 		info->info.dma.stop		= cumanascsi_2_dma_stop;
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/eesox.c linux/drivers/acorn/scsi/eesox.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/eesox.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/scsi/eesox.c	Mon Dec  3 20:25:14 2001
@@ -38,9 +38,6 @@
 #include "../../scsi/hosts.h"
 #include "eesox.h"
 
-#define NO_IRQ	255
-#define NO_DMA	255
-
 /* Configuration */
 #define EESOX_XTALFREQ		40
 #define EESOX_ASYNC_PERIOD	200
@@ -123,6 +120,8 @@
 	eesoxscsi_irqenable,
 	eesoxscsi_irqdisable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -379,6 +378,7 @@
 		info->info.ifcfg.sync_max_depth	= EESOX_SYNC_DEPTH;
 		info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
 		info->info.ifcfg.disconnect_ok	= 1;
+		info->info.ifcfg.wide_max_size	= 0;
 		info->info.dma.setup		= eesoxscsi_dma_setup;
 		info->info.dma.pseudo		= eesoxscsi_dma_pseudo;
 		info->info.dma.stop		= eesoxscsi_dma_stop;
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/fas216.c linux/drivers/acorn/scsi/fas216.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/fas216.c	Wed Dec 23 09:44:41 1998
+++ linux/drivers/acorn/scsi/fas216.c	Mon Dec  3 20:25:14 2001
@@ -24,9 +24,10 @@
  *  02-05-1998	RMK	Added extra checks in fas216_reset
  *  24-05-1998	RMK	Fixed synchronous transfers with period >= 200ns
  *  27-06-1998	RMK	Changed asm/delay.h to linux/delay.h
+ *  26-08-1998	RMK	Improved message support wrt MESSAGE_REJECT
+ *  17-01-2000	RMK	Added Command->Message In transition
  *
  * Todo:
- *  - tighten up the MESSAGE_REJECT support.
  *  - allow individual devices to enable sync xfers.
  */
 
@@ -57,7 +58,7 @@
 
 #define VER_MAJOR	0
 #define VER_MINOR	0
-#define VER_PATCH	4
+#define VER_PATCH	6
 
 #define SCSI2_TAG
 
@@ -86,6 +87,8 @@
  */
 #define SCSI2_SYNC
 
+#define SCSI2_WIDE
+
 #undef DEBUG_CONNECT
 #undef DEBUG_BUSSERVICE
 #undef DEBUG_FUNCTIONDONE
@@ -98,11 +101,16 @@
 
 static void fas216_dumpstate(FAS216_Info *info)
 {
+	unsigned char is, stat, inst;
+
+	is   = inb(REG_IS(info));
+	stat = inb(REG_STAT(info));
+	inst = inb(REG_INST(info));
+	
 	printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X"
 	       " INST=%02X IS=%02X CFIS=%02X",
 		inb(REG_CTCL(info)), inb(REG_CTCM(info)),
-		inb(REG_CMD(info)),  inb(REG_STAT(info)),
-		inb(REG_INST(info)), inb(REG_IS(info)),
+		inb(REG_CMD(info)),  stat, inst, is,
 		inb(REG_CFIS(info)));
 	printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n",
 		inb(REG_CNTL1(info)), inb(REG_CNTL2(info)),
@@ -117,43 +125,27 @@
 	if (used++)
 		return;
 
-	printk("FAS216_Info=\n");
-	printk("  { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n",
-		info->magic_start, info->host, info->SCpnt,
-		info->origSCpnt);
-	printk("    scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n",
-		info->scsi.io_port, info->scsi.io_shift, info->scsi.irq,
-		info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2],
-		info->scsi.cfg[3]);
-	printk("           type=%p phase=%X reconnected={ target=%d lun=%d tag=%d }\n",
-		info->scsi.type, info->scsi.phase,
-		info->scsi.reconnected.target,
+	printk("FAS216 Info (%s) =\n", info->scsi.type);
+	printk("  SCpnt=%p origSCpnt=%p\n", info->SCpnt, info->origSCpnt);
+	printk("  phase=%X reconnected={ target=%d lun=%d tag=%d }\n",
+		info->scsi.phase, info->scsi.reconnected.target,
 		info->scsi.reconnected.lun, info->scsi.reconnected.tag);
-	printk("           SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n",
+	printk("  SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n",
 		info->scsi.SCp.ptr, info->scsi.SCp.this_residual,
 		info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual);
-	printk("      msgs async_stp=%X last_message=%X disconnectable=%d aborting=%d }\n",
-		info->scsi.async_stp, info->scsi.last_message,
+	printk("  async_stp=%X disconnectable=%d aborting=%d\n",
+		info->scsi.async_stp,
 		info->scsi.disconnectable, info->scsi.aborting);
-	printk("    stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n"
-	       "            disconnects=%X aborts=%X resets=%X }\n",
-		info->stats.queues, info->stats.removes, info->stats.fins,
-		info->stats.reads, info->stats.writes, info->stats.miscs,
-		info->stats.disconnects, info->stats.aborts, info->stats.resets);
-	printk("    ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n",
-		info->ifcfg.clockrate, info->ifcfg.select_timeout,
-		info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth);
 	for (i = 0; i < 8; i++) {
-		printk("    busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X negstate=%X }\n",
-			i, info->busyluns[i], i,
+		printk("  dev[%d]={ busy=%d disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n",
+			i, info->busyluns[i],
 			info->device[i].disconnect_ok, info->device[i].stp,
-			info->device[i].sof, info->device[i].negstate);
+			info->device[i].sof, info->device[i].sync_state);
 	}
-	printk("    dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
+	printk("  dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
 		info->dma.transfer_type, info->dma.setup,
 		info->dma.pseudo, info->dma.stop);
-	printk("    internal_done=%X magic_end=%lX }\n",
-		info->internal_done, info->magic_end);
+	printk("  internal_done=%X\n", info->internal_done);
 }
 
 #ifdef CHECK_STRUCTURE
@@ -192,19 +184,19 @@
 static const char *fas216_drv_phase(FAS216_Info *info)
 {
 	switch (info->scsi.phase) {
-	case PHASE_IDLE:	return "idle";
-	case PHASE_SELECTION:	return "selection";
-	case PHASE_MESSAGESENT:	return "message sent";
-	case PHASE_RECONNECTED:	return "reconnected";
-	case PHASE_DATAOUT:	return "data out";
-	case PHASE_DATAIN:	return "data in";
-	case PHASE_MSGOUT:	return "message out";
-	case PHASE_MSGIN:	return "message in";
-	case PHASE_AFTERMSGOUT:	return "after message out";
-	case PHASE_STATUS:	return "status";
-	case PHASE_DISCONNECT:	return "disconnect";
-	case PHASE_DONE:	return "done";
-	default:		return "???";
+	case PHASE_IDLE:		return "idle";
+	case PHASE_SELECTION:		return "selection";
+	case PHASE_COMMAND:		return "command";
+	case PHASE_RECONNECTED:		return "reconnected";
+	case PHASE_DATAOUT:		return "data out";
+	case PHASE_DATAIN:		return "data in";
+	case PHASE_MSGIN:		return "message in";
+	case PHASE_MSGIN_DISCONNECT:	return "disconnect";
+	case PHASE_MSGOUT_EXPECT:	return "expect message out";
+	case PHASE_MSGOUT:		return "message out";
+	case PHASE_STATUS:		return "status";
+	case PHASE_DONE:		return "done";
+	default:			return "???";
 	}
 }
 
@@ -262,6 +254,37 @@
 	return clock;
 }
 
+/* Function: unsigned short fas216_get_last_msg(FAS216_Info *info, int pos)
+ * Purpose : retrieve a last message from the list, using position in fifo
+ * Params  : info - interface to search
+ *         : pos  - current fifo position
+ */
+static inline unsigned short
+fas216_get_last_msg(FAS216_Info *info, int pos)
+{
+	unsigned short packed_msg = NOP;
+	struct message *msg;
+	int msgnr = 0;
+
+	while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+		if (pos >= msg->fifo)
+			break;
+	}
+
+	if (msg) {
+		if (msg->msg[0] == EXTENDED_MESSAGE)
+			packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8;
+		else
+			packed_msg = msg->msg[0];
+	}
+
+#ifdef DEBUG_MESSAGES
+	printk("Message: %04X found at position %02X\n",
+		packed_msg, pos);
+#endif
+	return packed_msg;
+}
+
 /* Function: int fas216_syncperiod(FAS216_Info *info, int ns)
  * Purpose : Calculate value to be loaded into the STP register
  *           for a given period in ns
@@ -303,6 +326,252 @@
 		outb(info->scsi.cfg[2], REG_CNTL3(info));
 }
 
+typedef enum {
+	mstat_accept,
+	mstat_reject,
+	mstat_unrecognised
+} msgin_stat_t;
+
+/* Synchronous transfer support
+ *
+ * Note: The SCSI II r10 spec says (5.6.12):
+ *
+ *  (2)  Due to historical problems with early host adapters that could
+ *  not accept an SDTR message, some targets may not initiate synchronous
+ *  negotiation after a power cycle as required by this standard.  Host
+ *  adapters that support synchronous mode may avoid the ensuing failure
+ *  modes when the target is independently power cycled by initiating a
+ *  synchronous negotiation on each REQUEST SENSE and INQUIRY command.
+ *  This approach increases the SCSI bus overhead and is not recommended
+ *  for new implementations.  The correct method is to respond to an
+ *  SDTR message with a MESSAGE REJECT message if the either the
+ *  initiator or target devices does not support synchronous transfers
+ *  or does not want to negotiate for synchronous transfers at the time.
+ *  Using the correct method assures compatibility with wide data
+ *  transfers and future enhancements.
+ *
+ * We will always initiate a synchronous transfer negociation request on
+ * every INQUIRY or REQUEST SENSE message, unless the target itself has
+ * at some point performed a synchronous transfer negociation request, or
+ * we have synchronous transfers disabled for this device.
+ */
+
+/* Function: void fas216_handlesync(FAS216_Info *info, char *msg)
+ * Purpose : Handle a synchronous transfer message from the target
+ * Params  : info - state structure for interface
+ *         : msg  - message from target
+ */
+static msgin_stat_t
+fas216_handlesync(FAS216_Info *info, char *msg)
+{
+	struct fas216_device *dev = &info->device[info->SCpnt->target];
+	enum { sync, async, none, reject } res = none;
+	msgin_stat_t mstat;
+
+#ifdef SCSI2_SYNC
+	switch (msg[0]) {
+	case MESSAGE_REJECT:
+		/* Synchronous transfer request failed.
+		 * Note: SCSI II r10:
+		 *
+		 *  SCSI devices that are capable of synchronous
+		 *  data transfers shall not respond to an SDTR
+		 *  message with a MESSAGE REJECT message.
+		 *
+		 * Hence, if we get this condition, we disable
+		 * negociation for this device.
+		 */
+		if (dev->sync_state == neg_inprogress) {
+			dev->sync_state = neg_invalid;
+			res = async;
+		}
+		break;
+
+	case EXTENDED_MESSAGE:
+		switch (dev->sync_state) {
+		/* We don't accept synchronous transfer requests.
+		 * Respond with a MESSAGE_REJECT to prevent a
+		 * synchronous transfer agreement from being reached.
+		 */
+		case neg_invalid:
+			res = reject;
+			break;
+
+		/* We were not negociating a synchronous transfer,
+		 * but the device sent us a negociation request.
+		 * Honour the request by sending back a SDTR
+		 * message containing our capability, limited by
+		 * the targets capability.
+		 */
+		default:
+			outb(CMD_SETATN, REG_CMD(info));
+			if (msg[4] > info->ifcfg.sync_max_depth)
+				msg[4] = info->ifcfg.sync_max_depth;
+			if (msg[3] < 1000 / info->ifcfg.clockrate)
+				msg[3] = 1000 / info->ifcfg.clockrate;
+
+			msgqueue_flush(&info->scsi.msgs);
+			msgqueue_addmsg(&info->scsi.msgs, 5,
+					EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+					msg[3], msg[4]);
+			info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+			/* This is wrong.  The agreement is not in effect
+			 * until this message is accepted by the device
+			 */
+			dev->sync_state = neg_targcomplete;
+			res = sync;
+			break;
+
+		/* We initiated the synchronous transfer negociation,
+		 * and have successfully received a response from the
+		 * target.  The synchronous transfer agreement has been
+		 * reached.  Note: if the values returned are out of our
+		 * bounds, we must reject the message.
+		 */
+		case neg_inprogress:
+			res = reject;
+			if (msg[4] <= info->ifcfg.sync_max_depth &&
+			    msg[3] >= 1000 / info->ifcfg.clockrate) {
+				dev->sync_state = neg_complete;
+				res = sync;
+			}
+			break;
+		}
+	}
+#else
+	res = reject;
+#endif
+
+	mstat = mstat_accept;
+
+	switch (res) {
+	case sync:
+		dev->period = msg[3];
+		dev->sof    = msg[4];
+		dev->stp    = fas216_syncperiod(info, msg[3] * 4);
+		fas216_set_sync(info, info->SCpnt->target);
+		break;
+
+	case reject:
+		mstat = mstat_reject;
+
+	case async:
+		dev->period = info->ifcfg.asyncperiod / 4;
+		dev->sof    = 0;
+		dev->stp    = info->scsi.async_stp;
+		fas216_set_sync(info, info->SCpnt->target);
+		break;
+
+	case none:
+		mstat = mstat_unrecognised;
+		break;
+	}
+
+	return mstat;
+}
+
+/* Function: void fas216_handlewide(FAS216_Info *info, char *msg)
+ * Purpose : Handle a wide transfer message from the target
+ * Params  : info - state structure for interface
+ *         : msg  - message from target
+ */
+static msgin_stat_t
+fas216_handlewide(FAS216_Info *info, char *msg)
+{
+	struct fas216_device *dev = &info->device[info->SCpnt->target];
+	enum { wide, bit8, none, reject } res = none;
+	msgin_stat_t mstat;
+
+#ifdef SCSI2_WIDE
+	switch (msg[0]) {
+	case MESSAGE_REJECT:
+		/* Wide transfer request failed.
+		 * Note: SCSI II r10:
+		 *
+		 *  SCSI devices that are capable of wide
+		 *  data transfers shall not respond to a
+		 *  WDTR message with a MESSAGE REJECT message.
+		 *
+		 * Hence, if we get this condition, we never
+		 * reattempt negociation for this device.
+		 */
+		if (dev->wide_state == neg_inprogress) {
+			dev->wide_state = neg_invalid;
+			res = bit8;
+		}
+		break;
+
+	case EXTENDED_MESSAGE:
+		switch (dev->wide_state) {
+		/* We don't accept wide data transfer requests.
+		 * Respond with a MESSAGE REJECT to prevent a
+		 * wide data transfer agreement from being reached.
+		 */
+		case neg_invalid:
+			res = reject;
+			break;
+
+		/* We were not negociating a wide data transfer,
+		 * but the device sent is a negociation request.
+		 * Honour the request by sending back a WDTR
+		 * message containing our capability, limited by
+		 * the targets capability.
+		 */
+		default:
+			outb(CMD_SETATN, REG_CMD(info));
+			if (msg[3] > info->ifcfg.wide_max_size)
+				msg[3] = info->ifcfg.wide_max_size;
+
+			msgqueue_flush(&info->scsi.msgs);
+			msgqueue_addmsg(&info->scsi.msgs, 4,
+					EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
+					msg[3]);
+			info->scsi.phase = PHASE_MSGOUT_EXPECT;
+			res = wide;
+			break;
+
+		/* We initiated the wide data transfer negociation,
+		 * and have successfully received a response from the
+		 * target.  The synchronous transfer agreement has been
+		 * reached.  Note: if the values returned are out of our
+		 * bounds, we must reject the message.
+		 */
+		case neg_inprogress:
+			res = reject;
+			if (msg[3] <= info->ifcfg.wide_max_size) {
+				dev->wide_state = neg_complete;
+				res = wide;
+			}
+			break;
+		}
+	}
+#else
+	res = reject;
+#endif
+
+	mstat = mstat_accept;
+
+	switch (res) {
+	case wide:
+		dev->wide_xfer = msg[3];
+		break;
+
+	case reject:
+		mstat = mstat_reject;
+
+	case bit8:
+		dev->wide_xfer = 0;
+		break;
+
+	case none:
+		mstat = mstat_unrecognised;
+		break;
+	}
+
+	return mstat;
+}
+
 /* Function: void fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
  * Purpose : update data pointers after transfer suspended/paused
  * Params  : info              - interface's local pointer to update
@@ -338,6 +607,9 @@
 	residual -= bytes_transferred;
 	ptr += bytes_transferred;
 
+	if (residual == 0)
+		ptr = NULL;
+
 	info->scsi.SCp.ptr = ptr;
 	info->scsi.SCp.this_residual = residual;
 }
@@ -353,35 +625,18 @@
 {
 	unsigned int residual;
 	char *ptr;
-	int correction;
 
 	fas216_checkmagic(info, "fas216_pio");
 
 	residual = info->scsi.SCp.this_residual;
 	ptr = info->scsi.SCp.ptr;
 
-	if (direction == DMA_OUT) {
-	    	while (residual > 0) {
-			if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
-				outb(*ptr++, REG_FF(info));
-				residual -= 1;
-			} else if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		}
-		correction = inb(REG_CFIS(info)) & CFIS_CF;
-	} else {
-	    	while (residual > 0) {
-			if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) {
-				*ptr++ = inb(REG_FF(info));
-				residual -= 1;
-			} else if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		}
-		correction = 0;
-	}
+	if (direction == DMA_OUT)
+		outb(*ptr++, REG_FF(info));
+	else
+		*ptr++ = inb(REG_FF(info));
 
-	ptr -= correction;
-	residual += correction;
+	residual -= 1;
 
 	if (residual == 0) {
 		if (info->scsi.SCp.buffers_residual) {
@@ -549,10 +804,11 @@
 
 	switch (info->scsi.phase) {
 	case PHASE_SELECTION:			/* while selecting - no target		*/
+	case PHASE_SELSTEPS:
 		fas216_done(info, DID_NO_CONNECT);
 		break;
 
-	case PHASE_DISCONNECT:			/* message in - disconnecting		*/
+	case PHASE_MSGIN_DISCONNECT:		/* message in - disconnecting		*/
 		outb(CMD_ENABLESEL, REG_CMD(info));
 		info->scsi.disconnectable = 1;
 		info->scsi.reconnected.tag = 0;
@@ -564,8 +820,8 @@
 		fas216_done(info, DID_OK);
 		break;
 
-	case PHASE_AFTERMSGOUT:			/* message out - possible ABORT message	*/
-		if (info->scsi.last_message == ABORT) {
+	case PHASE_MSGOUT:			/* message out - possible ABORT message	*/
+		if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) {
 			info->scsi.aborting = 0;
 			fas216_done(info, DID_ABORT);
 			break;
@@ -592,14 +848,17 @@
 
 	fas216_checkmagic(info, "fas216_reselected_intr");
 
-	if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) {
+	if ((info->scsi.phase == PHASE_SELECTION ||
+	     info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) {
 		Scsi_Cmnd *SCpnt = info->SCpnt;
 
 		info->origSCpnt = SCpnt;
 		info->SCpnt = NULL;
 
-		if (info->device[SCpnt->target].negstate == syncneg_sent)
-			info->device[SCpnt->target].negstate = syncneg_start;
+		if (info->device[SCpnt->target].wide_state == neg_inprogress)
+			info->device[SCpnt->target].wide_state = neg_wait;
+		if (info->device[SCpnt->target].sync_state == neg_inprogress)
+			info->device[SCpnt->target].sync_state = neg_wait;
 	}
 
 #ifdef DEBUG_CONNECT
@@ -607,15 +866,14 @@
 		fas216_target(info), info->scsi.phase);
 #endif
 
-	msgqueue_flush(&info->scsi.msgs);
-
 	if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
 		printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
 			info->host->host_no);
 		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-		info->scsi.phase = PHASE_MSGOUT;
 		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		return;
 	}
 
@@ -636,13 +894,14 @@
 
 	if (!ok) {
 		/*
-		 * Something went wrong - abort the command on
-		 * the target.  Should this be INITIATOR_ERROR ?
+		 * Something went wrong - send an initiator error to
+		 * the target.
 		 */
 		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT;
 		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		return;
 	}
 
@@ -672,17 +931,20 @@
 	if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg))
 		ok = 1;
 
+	msgqueue_flush(&info->scsi.msgs);
 	if (ok) {
 		info->scsi.phase = PHASE_RECONNECTED;
 		outb(target, REG_SDID(info));
 	} else {
 		/*
-		 * Our command structure not found - abort the command on the target
-		 * Should this be INITIATOR_ERROR ?
+		 * Our command structure not found - abort the
+		 * command on the target.  Since we have no
+		 * record of this command, we can't send
+		 * an INITIATOR DETECTED ERROR message.
 		 */
 		outb(CMD_SETATN, REG_CMD(info));
 		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT;
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 	}
 	outb(CMD_MSGACCEPTED, REG_CMD(info));
 }
@@ -733,8 +995,14 @@
 	}
 	if (!info->SCpnt) {
 		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT;
+		msgqueue_flush(&info->scsi.msgs);
+#if 0
+		if (info->scsi.reconnected.tag)
+			msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag);
+		else
+#endif
+			msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		info->scsi.aborting = 1;
 	} else {
 		/*
@@ -751,50 +1019,91 @@
 #endif
 }
 
-/* Function: void fas216_message(FAS216_Info *info)
- * Purpose : handle a function done interrupt from FAS216 chip
- * Params  : info - interface which caused function done interrupt
- */
-static void fas216_message(FAS216_Info *info)
+static int fas216_wait_cmd(FAS216_Info *info, int cmd)
 {
-	unsigned char message[16];
+	int tout;
+	int stat;
+
+	outb(cmd, REG_CMD(info));
+
+	for (tout = 1000; tout; tout -= 1) {
+		stat = inb(REG_STAT(info));
+		if (stat & STAT_INT)
+			break;
+		udelay(1);
+	}
+
+	return stat;
+}
+
+static int fas216_get_msg_byte(FAS216_Info *info)
+{
+	int stat;
+
+	stat = fas216_wait_cmd(info, CMD_MSGACCEPTED);
+
+	if ((stat & STAT_INT) == 0)
+		goto timedout;
+
+	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+		goto unexpected_phase_change;
+
+	inb(REG_INST(info));
+
+	stat = fas216_wait_cmd(info, CMD_TRANSFERINFO);
+
+	if ((stat & STAT_INT) == 0)
+		goto timedout;
+
+	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+		goto unexpected_phase_change;
+
+	inb(REG_INST(info));
+
+	return inb(REG_FF(info));
+
+timedout:
+	printk("scsi%d.%c: timed out waiting for message byte\n",
+		info->host->host_no, fas216_target(info));
+	return -1;
+
+unexpected_phase_change:
+	printk("scsi%d.%c: unexpected phase change: status = %02X\n",
+		info->host->host_no, fas216_target(info), stat);
+
+	return -2;
+}
+
+static msgin_stat_t fas216___message(FAS216_Info *info)
+{
+	unsigned char *message = info->scsi.message;
 	unsigned int msglen = 1;
+	msgin_stat_t mstat = mstat_unrecognised;
+	signed int msgbyte = 0;
 
 	fas216_checkmagic(info, "fas216_message");
 
 	message[0] = inb(REG_FF(info));
 
 	if (message[0] == EXTENDED_MESSAGE) {
-		int tout;
-		outb(CMD_MSGACCEPTED, REG_CMD(info));
-		for (tout = 1000000; tout; tout--)
-			if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		inb(REG_INST(info));
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		for (tout = 1000000; tout; tout--)
-			if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		inb(REG_INST(info));
+		msgbyte = fas216_get_msg_byte(info);
 
-		message[1] = inb(REG_FF(info));
+		if (msgbyte >= 0) {
+			message[1] = msgbyte;
 
-		for (msglen = 2; msglen < message[1] + 2; msglen++) {
-			outb(CMD_MSGACCEPTED, REG_CMD(info));
-			for (tout = 1000000; tout; tout--)
-				if (inb(REG_STAT(info)) & STAT_INT)
-					break;
-			inb(REG_INST(info));
-			outb(CMD_TRANSFERINFO, REG_CMD(info));
-			for (tout = 1000000; tout; tout--)
-				if (inb(REG_STAT(info)) & STAT_INT)
-					break;
-			inb(REG_INST(info));
+			for (msglen = 2; msglen < message[1] + 2; msglen++) {
+				msgbyte = fas216_get_msg_byte(info);
 
-			message[msglen] = inb(REG_FF(info));
+				if (msgbyte >= 0)
+					message[msglen] = msgbyte;
+				else
+					break;
+			}
 		}
 	}
 
+	info->scsi.msglen = msglen;
+
 #ifdef DEBUG_MESSAGES
 	{
 		int i;
@@ -806,6 +1115,19 @@
 		printk("\n");
 	}
 #endif
+
+	/*
+	 * check the length of the message.
+	 * messages 0x00, 0x02-0x1f, 0x80-0xff are one-byte
+	 * messages 0x20-0x30 are two-byte
+	 * check extended messages below...
+	 */
+	if ((((message[0] < 0x20 && message[0] != EXTENDED_MESSAGE) ||
+	      (message[0] >= 0x80)) && msglen != 1) ||
+	    ((message[0] >= 0x20 && message[0] < 0x30) && msglen != 2))
+		return mstat_unrecognised;
+
+
 	if (info->scsi.phase == PHASE_RECONNECTED) {
 		if (message[0] == SIMPLE_QUEUE_TAG)
 			info->scsi.reconnected.tag = message[1];
@@ -815,19 +1137,29 @@
 
 	switch (message[0]) {
 	case COMMAND_COMPLETE:
-		printk("fas216: command complete with no status in MESSAGE_IN?\n");
+		printk(KERN_ERR "scsi%d.%c: command complete with no "
+			"status in MESSAGE_IN?\n",
+			info->host->host_no, fas216_target(info));
+		mstat = mstat_accept;
 		break;
 
 	case SAVE_POINTERS:
 		/*
 		 * Save current data pointer to SAVED data pointer
+		 * SCSI II standard says that we must not acknowledge
+		 * this until we have really saved pointers.
+		 * NOTE: we DO NOT save the command nor status pointers
+		 * as required by the SCSI II standard.  These always
+		 * point to the start of their respective areas.
 		 */
 		info->SCpnt->SCp = info->scsi.SCp;
+		info->SCpnt->SCp.sent_command = 0;
 #if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
 		printk("scsi%d.%c: save data pointers: [%p, %X]\n",
 			info->host->host_no, fas216_target(info),
 			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 #endif
+		mstat = mstat_accept;
 		break;
 
 	case RESTORE_POINTERS:
@@ -840,85 +1172,103 @@
 			info->host->host_no, fas216_target(info),
 			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 #endif
+		mstat = mstat_accept;
 		break;
 
 	case DISCONNECT:
-		info->scsi.phase = PHASE_DISCONNECT;
+		info->scsi.phase = PHASE_MSGIN_DISCONNECT;
+		mstat = mstat_accept;
 		break;
 
 	case MESSAGE_REJECT:
-		printk("scsi%d.%c: reject, last message %04X\n",
-			info->host->host_no, fas216_target(info),
-			info->scsi.last_message);
+		mstat = mstat_accept;
+		switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) {
+		case EXTENDED_MESSAGE | EXTENDED_SDTR << 8:
+			mstat = fas216_handlesync(info, message);
+			break;
+
+		case EXTENDED_MESSAGE | EXTENDED_WDTR << 8:
+			mstat = fas216_handlewide(info, message);
+			break;
+
+		default:
+			printk("scsi%d.%c: reject, last message %04X\n",
+				info->host->host_no, fas216_target(info),
+				fas216_get_last_msg(info, info->scsi.msgin_fifo));
+		}
+		break;
+
+	case NOP:
+		mstat = mstat_accept;
 		break;
 
 	case SIMPLE_QUEUE_TAG:
-		/* handled above */
+		/* handled above - print a warning since this is untested */
 		printk("scsi%d.%c: reconnect queue tag %02X\n",
 			info->host->host_no, fas216_target(info),
 			message[1]);
+		mstat = mstat_accept;
 		break;
 
 	case EXTENDED_MESSAGE:
-		switch (message[2]) {
-		case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
-			switch (info->device[info->SCpnt->target].negstate) {
-			case syncneg_invalid:
-				msgqueue_flush(&info->scsi.msgs);
-				outb(CMD_SETATN, REG_CMD(info));
-				msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-				info->scsi.phase = PHASE_MSGOUT;
+		if (msglen >= 3) {
+			switch (message[2]) {
+			case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
+				mstat = fas216_handlesync(info, message);
+				break;
+
+			case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
+				mstat = fas216_handlewide(info, message);
 				break;
 
 			default:
-				if (message[4] > info->ifcfg.sync_max_depth)
-					message[4] = info->ifcfg.sync_max_depth;
-				if (message[3] < 1000 / info->ifcfg.clockrate)
-					message[3] = 1000 / info->ifcfg.clockrate;
-
-				outb(CMD_SETATN, REG_CMD(info));
-				msgqueue_addmsg(&info->scsi.msgs, 5,
-						EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
-						message[3], message[4]);
-				info->scsi.phase = PHASE_MSGOUT;
-			case syncneg_sent:
-				info->device[info->SCpnt->target].negstate = syncneg_complete;
-				info->device[info->SCpnt->target].period = message[3];
-				info->device[info->SCpnt->target].sof = message[4];
-				info->device[info->SCpnt->target].stp =
-					fas216_syncperiod(info, message[3] * 4);
-				printk(KERN_NOTICE "scsi%d.%c: using synchronous transfer, offset %d, %d ns\n",
-					info->host->host_no, fas216_target(info), message[4], message[3] * 4);
-				fas216_set_sync(info, info->SCpnt->target);
 				break;
 			}
-			break;
-
-		case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
-			/* We don't do wide transfers - reject message */
-		default:
-			printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
-				info->host->host_no, fas216_target(info),
-				message[2]);
-			msgqueue_flush(&info->scsi.msgs);
-			outb(CMD_SETATN, REG_CMD(info));
-			msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-			info->scsi.phase = PHASE_MSGOUT;
-			break;
 		}
 		break;
 
 	default:
-		printk("scsi%d.%c: unrecognised message %02X, rejecting\n",
-			info->host->host_no, fas216_target(info),
-			message[0]);
-		msgqueue_flush(&info->scsi.msgs);
+		break;
+	}
+	return mstat;
+}
+
+/* Function: void fas216_message(FAS216_Info *info)
+ * Purpose : handle a function done interrupt from FAS216 chip
+ * Params  : info - interface which caused function done interrupt
+ */
+static void fas216_message(FAS216_Info *info)
+{
+	int i;
+
+	switch (fas216___message(info)) {
+	case mstat_unrecognised:
+		printk("scsi%d.%c: unrecognised message, rejecting\n",
+			info->host->host_no, fas216_target(info));
+		printk("scsi%d.%c: message was", info->host->host_no,
+			fas216_target(info));
+		for (i = 0; i < info->scsi.msglen; i++)
+			printk("%s%02X", i & 31 ? " " : "\n  ",
+				info->scsi.message[i]);
+		printk("\n");
+
+	case mstat_reject:
+		/*
+		 * Something strange seems to be happening here -
+		 * I can't use SETATN since the chip gives me an
+		 * invalid command interrupt when I do.  Weird.
+		 */
+outb(CMD_NOP, REG_CMD(info));
+fas216_dumpstate(info);
 		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
 		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-		info->scsi.phase = PHASE_MSGOUT;
-		break;
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+fas216_dumpstate(info);
+
+	case mstat_accept:
+		outb(CMD_MSGACCEPTED, REG_CMD(info));
 	}
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
 }
 
 /* Function: void fas216_send_command(FAS216_Info *info)
@@ -935,308 +1285,266 @@
 	outb(CMD_FLUSHFIFO, REG_CMD(info));
 
 	/* load command */
-	for (i = 0; i < info->SCpnt->cmd_len; i++)
+	for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++)
 		outb(info->SCpnt->cmnd[i], REG_FF(info));
 
 	outb(CMD_TRANSFERINFO, REG_CMD(info));
+
+	info->scsi.phase = PHASE_COMMAND;
 }
 
-/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in selection phase
+/* Function: void fas216_send_messageout(FAS216_Info *info, int start)
+ * Purpose : handle bus service to send a message
  * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
+ * Note    : We do not allow the device to change the data direction!
  */
-static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
+static void fas216_send_messageout(FAS216_Info *info, int start)
 {
-	fas216_checkmagic(info, "fas216_busservice_selection");
-
-	switch (stat & STAT_BUSMASK) {
-	case STAT_DATAOUT:	/* data out phase			*/
-		fas216_starttransfer(info, DMA_OUT, 1);
-		return 1;
-
-	case STAT_DATAIN:		/* data in phase		*/
-		fas216_starttransfer(info, DMA_IN, 0);
-		return 1;
+	unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs);
 
-	case STAT_STATUS:		/* status phase			*/
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
+	fas216_checkmagic(info, "fas216_send_messageout");
 
-	case STAT_MESGIN:		/* message in phase		*/
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
-
-	case STAT_MESGOUT:{		/* message out phase		*/
-		char *msg;
-		int start = 1, msglen;
+	outb(CMD_FLUSHFIFO, REG_CMD(info));
 
-		/* load message bytes, but don't forget to miss the first
-		 * byte!
-		 */
-		while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
+	if (tot_msglen) {
+		struct message *msg;
+		int msgnr = 0;
+#ifdef DEBUG_MESSAGES
+		printk("scsi%d.%c: message out: ",
+		       info->host->host_no, fas216_target(info));
+#endif
+		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
 			int i;
+#ifdef DEBUG_MESSAGES
+			printk("{ ");
+#endif
+			for (i = start; i < msg->length; i++) {
+#ifdef DEBUG_MESSAGES
+				printk("%02X ", msg->msg[i]);
+#endif
+				outb(msg->msg[i], REG_FF(info));
+			}
+#ifdef DEBUG_MESSAGES
+			printk("} ");
+#endif
 
-			for (i = start; i < msglen; i++)
-				outb(msg[i], REG_FF(info));
+			msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
 			start = 0;
 		}
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		info->scsi.phase = PHASE_MESSAGESENT;
-		return 1;
-	}
-	default:
-		return 0;
-	}
-}
-
-/* Function: int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service after the IDENTIFY message has been sent
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- */
-static int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
-{
-	fas216_checkmagic(info, "fas216_busservice_messagesent");
-
-	switch (stat & STAT_BUSMASK) {
-	case STAT_MESGIN:		/* message in phase		*/
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
+#ifdef DEBUG_MESSAGES
+		printk("\n");
+#endif
+	} else
+		outb(NOP, REG_FF(info));
 
-	case STAT_COMMAND:		/* command phase		*/
-		fas216_send_command(info);
-		return 1;
+	outb(CMD_TRANSFERINFO, REG_CMD(info));
 
-	default:
-		return 0;
-	}
+	info->scsi.phase = PHASE_MSGOUT;
 }
 
-/* Function: int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in a data in/out phase.
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note    : We do not allow the device to change the data direction!
+/* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
+ * Purpose : handle a bus service interrupt from FAS216 chip
+ * Params  : info - interface which caused bus service interrupt
+ *           stat - Status register contents
+ *           ssr  - SCSI Status register contents
  */
-static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
+static void
+fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
 {
-	fas216_checkmagic(info, "fas216_busservice_dataphase");
+	fas216_checkmagic(info, "fas216_busservice_intr");
 
-	switch (stat & STAT_BUSMASK) {
-	case STAT_DATAIN:		/* continue data in phase	*/
-		if (info->scsi.phase == PHASE_DATAIN) {
-			fas216_starttransfer(info, DMA_IN, 0);
-			return 1;
-		} else
-			return 0;
+#ifdef DEBUG_BUSSERVICE
+	printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
+		info->host->host_no, fas216_target(info), stat, ssr,
+		info->scsi.phase);
+#endif
 
-	case STAT_DATAOUT:		/* continue data out phase	*/
-		if (info->scsi.phase == PHASE_DATAOUT) {
-			fas216_starttransfer(info, DMA_OUT, 0);
-			return 1;
-		} else
-			return 0;
+	switch (info->scsi.phase) {
+	case PHASE_SELECTION:
+		if ((ssr & IS_BITS) != 1)
+			goto bad_is;
+		break;
 
-	case STAT_STATUS:		/* status in phase		*/
-		fas216_stoptransfer(info);
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
+	case PHASE_SELSTEPS:
+		switch (ssr & IS_BITS) {
+		case IS_SELARB:
+		case IS_MSGBYTESENT:
+			goto bad_is;
+
+		case IS_NOTCOMMAND:
+		case IS_EARLYPHASE:
+			if ((stat & STAT_BUSMASK) == STAT_MESGIN)
+				break;
+			goto bad_is;
 
-	case STAT_MESGIN:		/* message in phase		*/
-		fas216_stoptransfer(info);
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
+		case IS_COMPLETE:
+			break;
+		}
 
 	default:
-		return 0;
+		break;
 	}
-}
 
-/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in after a reconnection
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note    : We do not allow the device to change the data direction!
- */
-static int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
-{
-	fas216_checkmagic(info, "fas216_busservice_reconnected");
+	outb(CMD_NOP, REG_CMD(info));
 
-	switch (stat & STAT_BUSMASK) {
-	case STAT_MESGIN:
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
+	/*
+	 * s2r10 5.1.9.2 para 5
+	 * If the target accepts the messages sent during a message out
+	 * phase, it will change to a new phase.
+	 */
+	if (info->scsi.phase == PHASE_MSGOUT &&
+	    (stat & STAT_BUSMASK) != STAT_MESGOUT)
+		msgqueue_flush(&info->scsi.msgs);
 
-	case STAT_STATUS:
+#define STATE(st,ph) ((ph) << 3 | (st))
+	/*
+	 * This table describes the legal SCSI state transitions,
+	 * as described by the SCSI II spec.
+	 */
+	switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) {
+						/* Reselmsgin   -> Data In	*/
+	case STATE(STAT_DATAIN, PHASE_RECONNECTED):
 		fas216_finish_reconnect(info);
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
+	case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_DATAIN):  /* Data In      -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_MSGOUT):  /* Message Out  -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command      -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_MSGIN):   /* Message In   -> Data In      */
+		fas216_starttransfer(info, DMA_IN, 0);
+		return;
+
+	case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out     -> Data Out     */
+		fas216_starttransfer(info, DMA_OUT, 0);
+		return;
 
-	case STAT_DATAOUT:		/* data out phase		*/
+						/* Reselmsgin   -> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_RECONNECTED):
 		fas216_finish_reconnect(info);
+	case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out  -> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command      -> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_MSGIN):  /* Message In   -> Data Out     */
 		fas216_starttransfer(info, DMA_OUT, 1);
-		return 1;
+		return;
 
-	case STAT_DATAIN:		/* data in phase		*/
+						/* Reselmsgin   -> Status       */
+	case STATE(STAT_STATUS, PHASE_RECONNECTED):
 		fas216_finish_reconnect(info);
-		fas216_starttransfer(info, DMA_IN, 0);
-		return 1;
-
-	default:
-		return 0;
-	}
-}
-
-/* Function: int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service to send a message
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note    : We do not allow the device to change the data direction!
- */
-static int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
-{
-	fas216_checkmagic(info, "fas216_busservice_messageout");
-
-	if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
-		printk("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
-		       info->host->host_no, fas216_target(info));
-		return 0;
-	} else {
-		unsigned int msglen = msgqueue_msglength(&info->scsi.msgs);
+		goto status;
+	case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out     -> Status       */
+	case STATE(STAT_STATUS, PHASE_DATAIN):  /* Data In      -> Status       */
+		fas216_stoptransfer(info);
+	case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status       */
+	case STATE(STAT_STATUS, PHASE_MSGOUT):  /* Message Out  -> Status       */
+	case STATE(STAT_STATUS, PHASE_COMMAND): /* Command      -> Status       */
+	case STATE(STAT_STATUS, PHASE_MSGIN):   /* Message In   -> Status       */
+	status:
+		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+		info->scsi.phase = PHASE_STATUS;
+		return;
 
+	case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out     -> Message In   */
+	case STATE(STAT_MESGIN, PHASE_DATAIN):  /* Data In      -> Message In   */
+		fas216_stoptransfer(info);
+	case STATE(STAT_MESGIN, PHASE_COMMAND):	/* Command	-> Message In	*/
+	case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In   */
+	case STATE(STAT_MESGIN, PHASE_MSGOUT):  /* Message Out  -> Message In   */
+		info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
 		outb(CMD_FLUSHFIFO, REG_CMD(info));
-
-		if (msglen == 0)
-			outb(NOP, REG_FF(info));
-		else {
-			char *msg;
-
-			while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
-				int i;
-
-				for (i = 0; i < msglen; i++)
-					outb(msg[i], REG_FF(info));
-			}
-		}
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		info->scsi.phase = PHASE_AFTERMSGOUT;
-		return 1;
-	}
-}
-
-/* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
- * Purpose : handle a bus service interrupt from FAS216 chip
- * Params  : info - interface which caused bus service interrupt
- *           stat - Status register contents
- *           ssr  - SCSI Status register contents
- */
-static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
-{
-	fas216_checkmagic(info, "fas216_busservice_intr");
-
-#ifdef DEBUG_BUSSERVICE
-	printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
-		info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
-#endif
-	switch (ssr & IS_BITS) {
-	case IS_MSGBYTESENT:		/* select with ATN and stop steps completed	*/
-	case IS_COMPLETE:			/* last action completed		*/
-		outb(CMD_NOP, REG_CMD(info));
-
-		switch (info->scsi.phase) {
-		case PHASE_SELECTION:		/* while selecting - selected target	*/
-			if (!fas216_busservice_selection(info, stat))
-				printk("scsi%d.%c: bus phase %s after connect?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
-
-		case PHASE_MESSAGESENT:
-			if (!fas216_busservice_messagesent(info, stat))
-				printk("scsi%d.%c: bus phase %s after message sent?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
-
-		case PHASE_DATAIN:		/* while transfering data in		*/
-		case PHASE_DATAOUT:		/* while transfering data out		*/
-			if (!fas216_busservice_dataphase(info, stat))
-				printk("scsi%d.%c: bus phase %s after %s?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat), fas216_drv_phase(info));
-			break;
-
-		case PHASE_RECONNECTED:		/* newly reconnected device		*/
-			/*
-			 * Command reconnected - if MESGIN, get message - it may be
-			 * the tag.  If not, get command out of the disconnected queue
-			 */
-			if (!fas216_busservice_reconnected(info, stat))
-				printk("scsi%d.%c: bus phase %s after reconnect?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
-
-		case PHASE_MSGIN:
-		case PHASE_AFTERMSGOUT:
-			switch (stat & STAT_BUSMASK) {
-			case STAT_MESGIN:
-				info->scsi.phase = PHASE_MSGIN;
-				outb(CMD_TRANSFERINFO, REG_CMD(info));
-				break;
+		info->scsi.phase = PHASE_MSGIN;
+		return;
 
-			case STAT_COMMAND:	/* command phase			*/
-				fas216_send_command(info);
-				info->scsi.phase = PHASE_SELECTION;
-				break;
+						/* Reselmsgin   -> Message In   */
+	case STATE(STAT_MESGIN, PHASE_RECONNECTED):
+	case STATE(STAT_MESGIN, PHASE_MSGIN):
+		info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		return;
 
-			default:
-				printk("scsi%d.%c: bus phase %s after %s?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat),
-					fas216_drv_phase(info));
-			}
-			break;
+						/* Reselmsgin   -> Command      */
+	case STATE(STAT_COMMAND, PHASE_RECONNECTED):
+		fas216_finish_reconnect(info);
+	case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out  -> Command      */
+	case STATE(STAT_COMMAND, PHASE_MSGIN):  /* Message In   -> Command      */
+		fas216_send_command(info);
+		info->scsi.phase = PHASE_COMMAND;
+		return;
+						/* Selection    -> Message Out  */
+	case STATE(STAT_MESGOUT, PHASE_SELECTION):
+		fas216_send_messageout(info, 1);
+		return;
+						/* Any          -> Message Out  */
+	case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT):
+		fas216_send_messageout(info, 0);
+		return;
 
-		case PHASE_MSGOUT:
-			if (!fas216_busservice_messageout(info, stat))
-				printk("scsi%d.%c: bus phase %s instead of message out?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
+	/* Error recovery rules.
+	 *   These either attempt to abort or retry the operation.
+	 * TODO: we need more of these
+	 */
+	case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command      -> Command      */
+		/* error - we've sent out all the command bytes
+		 * we have.
+		 * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS
+		 * to include the command bytes sent for this to work
+		 * correctly.
+		 */
+		printk(KERN_ERR "scsi%d.%c: "
+			"target trying to receive more command bytes\n",
+			info->host->host_no, fas216_target(info));
+		outb(CMD_SETATN, REG_CMD(info));
+		outb(15, REG_STCL(info));
+		outb(0, REG_STCM(info));
+		outb(0, REG_STCH(info));
+		outb(CMD_PADBYTES | CMD_WITHDMA, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+		return;
 
-		case PHASE_DISCONNECT:
-			printk("scsi%d.%c: disconnect message received, but bus service %s?\n",
-				info->host->host_no, fas216_target(info),
-				fas216_bus_phase(stat));
+						/* Selection    -> Message Out  */
+	case STATE(STAT_MESGOUT, PHASE_SELSTEPS):
+	case STATE(STAT_MESGOUT, PHASE_MSGOUT): /* Message Out  -> Message Out  */
+		/* If we get another message out phase, this
+		 * usually means some parity error occurred.
+		 * Resend complete set of messages.  If we have
+		 * more than 1 byte to send, we need to assert
+		 * ATN again.
+		 */
+		if (msgqueue_msglength(&info->scsi.msgs) > 1)
 			outb(CMD_SETATN, REG_CMD(info));
-			msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
-			info->scsi.phase = PHASE_MSGOUT;
-			info->scsi.aborting = 1;
-			outb(CMD_TRANSFERINFO, REG_CMD(info));
-			break;
 
-		default:
-			printk("scsi%d.%c: internal phase %s for bus service?"
-				"  What do I do with this?\n",
-				info->host->host_no, fas216_target(info),
-				fas216_drv_phase(info));
-		}
-		break;
+		fas216_send_messageout(info, 0);
+		return;
+	}
 
-	default:
-		printk("scsi%d.%c: bus service at step %d?\n",
+	if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) {
+		printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n",
 			info->host->host_no, fas216_target(info),
-			ssr & IS_BITS);
+			fas216_bus_phase(stat));
+		msgqueue_flush(&info->scsi.msgs);
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+		info->scsi.aborting = 1;
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		return;
 	}
+	printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n",
+		info->host->host_no, fas216_target(info),
+		fas216_bus_phase(stat),
+		fas216_drv_phase(info));
+	print_debug_list();
+	return;
+
+bad_is:
+	printk("scsi%d.%c: bus service at step %d?\n",
+		info->host->host_no, fas216_target(info),
+		ssr & IS_BITS);
+	print_debug_list();
+
+	fas216_done(info, DID_ERROR);
 }
 
 /* Function: void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
@@ -1269,6 +1577,7 @@
 	case PHASE_MSGIN:			/* message in phase			*/
 	case PHASE_RECONNECTED:			/* reconnected command			*/
 		if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
+			info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
 			fas216_message(info);
 			break;
 		}
@@ -1300,10 +1609,11 @@
 
 	if (stat & STAT_INT) {
 		if (isr & INST_BUSRESET)
-			printk("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
-		else if (isr & INST_ILLEGALCMD)
+			printk(KERN_DEBUG "scsi%d.H: bus reset detected\n", instance->host_no);
+		else if (isr & INST_ILLEGALCMD) {
 			printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
-		else if (isr & INST_DISCONNECT)
+			fas216_dumpstate(info);
+		} else if (isr & INST_DISCONNECT)
 			fas216_disconnect_intr(info);
 		else if (isr & INST_RESELECTED)		/* reselected			*/
 			fas216_reselected_intr(info);
@@ -1327,7 +1637,7 @@
 static void fas216_kick(FAS216_Info *info)
 {
 	Scsi_Cmnd *SCpnt;
-	int i, msglen, from_queue = 0;
+	int tot_msglen, from_queue = 0;
 
 	fas216_checkmagic(info, "fas216_kick");
 
@@ -1380,7 +1690,8 @@
 
 	if (from_queue) {
 #ifdef SCSI2_TAG
-		if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) {
+		if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE &&
+		    SCpnt->cmnd[0] != INQUIRY) {
 		    SCpnt->device->current_tag += 1;
 			if (SCpnt->device->current_tag == 0)
 			    SCpnt->device->current_tag = 1;
@@ -1409,6 +1720,7 @@
 
 	/* build outgoing message bytes */
 	msgqueue_flush(&info->scsi.msgs);
+
 	if (info->device[SCpnt->target].disconnect_ok)
 		msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun));
 	else
@@ -1418,15 +1730,29 @@
 	if (SCpnt->tag)
 		msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
 
-	/* add synchronous negociation */
-	if (SCpnt->cmnd[0] == REQUEST_SENSE &&
-	    info->device[SCpnt->target].negstate == syncneg_start) {
-		info->device[SCpnt->target].negstate = syncneg_sent;
+#ifdef SCSI2_WIDE
+	if (info->device[SCpnt->target].wide_state == neg_wait) {
+		info->device[SCpnt->target].wide_state = neg_inprogress;
+		msgqueue_addmsg(&info->scsi.msgs, 4,
+				EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
+				info->ifcfg.wide_max_size);
+	}
+#ifdef SCSI2_SYNC
+	else
+#endif
+#endif
+#ifdef SCSI2_SYNC
+	if ((info->device[SCpnt->target].sync_state == neg_wait ||
+	     info->device[SCpnt->target].sync_state == neg_complete) &&
+	    (SCpnt->cmnd[0] == REQUEST_SENSE ||
+	     SCpnt->cmnd[0] == INQUIRY)) {
+		info->device[SCpnt->target].sync_state = neg_inprogress;
 		msgqueue_addmsg(&info->scsi.msgs, 5,
 				EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
 				1000 / info->ifcfg.clockrate,
 				info->ifcfg.sync_max_depth);
 	}
+#endif
 
 	/* following what the ESP driver says */
 	outb(0, REG_STCL(info));
@@ -1444,25 +1770,29 @@
 	/* synchronous transfers */
 	fas216_set_sync(info, SCpnt->target);
 
-	msglen = msgqueue_msglength(&info->scsi.msgs);
+	tot_msglen = msgqueue_msglength(&info->scsi.msgs);
 
-	if (msglen == 1 || msglen == 3) {
+	if (tot_msglen == 1 || tot_msglen == 3) {
 		/*
 		 * We have an easy message length to send...
 		 */
-		char *msg;
+		struct message *msg;
+		int msgnr = 0, i;
+
+		info->scsi.phase = PHASE_SELSTEPS;
 
 		/* load message bytes */
-		while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
-			for (i = 0; i < msglen; i++)
-				outb(msg[i], REG_FF(info));
+		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+			for (i = 0; i < msg->length; i++)
+				outb(msg->msg[i], REG_FF(info));
+			msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
 		}
 
 		/* load command */
 		for (i = 0; i < SCpnt->cmd_len; i++)
 			outb(SCpnt->cmnd[i], REG_FF(info));
 
-		if (msglen == 1)
+		if (tot_msglen == 1)
 			outb(CMD_SELECTATN, REG_CMD(info));
 		else
 			outb(CMD_SELECTATN3, REG_CMD(info));
@@ -1471,17 +1801,11 @@
 		 * We have an unusual number of message bytes to send.
 		 *  Load first byte into fifo, and issue SELECT with ATN and
 		 *  stop steps.
-		 * Note: we only peek at t his message - we need the rest
-		 * later on!
 		 */
-		int thismsg;
-		char *msg = msgqueue_peeknextmsg(&info->scsi.msgs, &thismsg);
+		struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0);
 
-		if (!msg || thismsg < 1)
-			printk(KERN_CRIT "scsi%d.%c: no message to send, but %d bytes\n",
-				info->host->host_no, fas216_target(info), msglen);
-		else
-			outb(msg[0], REG_FF(info));
+		outb(msg->msg[0], REG_FF(info));
+		msg->fifo = 1;
 
 		outb(CMD_SELECTATNSTOP, REG_CMD(info));
 	}
@@ -1525,11 +1849,15 @@
 		/*
 		 * In theory, this should not happen, but just in case it does.
 		 */
-		if (info->scsi.SCp.ptr && result == DID_OK) {
+		if (info->scsi.SCp.ptr &&
+		    info->scsi.SCp.this_residual &&
+		    result == DID_OK) {
 			switch (SCpnt->cmnd[0]) {
 			case INQUIRY:
 			case START_STOP:
 			case READ_CAPACITY:
+			case TEST_UNIT_READY:
+			case MODE_SENSE:
 				break;
 
 			default:
@@ -1579,6 +1907,7 @@
 int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+	unsigned long flags;
 
 	fas216_checkmagic(info, "fas216_queue_command");
 
@@ -1618,27 +1947,18 @@
 	info->stats.queues += 1;
 	SCpnt->tag = 0;
 
-	if (info->scsi.irq != NO_IRQ) {
-		unsigned long flags;
-
-		/* add command into execute queue and let it complete under
-		 * the drivers interrupts.
-		 */
-		if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
-			SCpnt->result = DID_ERROR << 16;
-			done(SCpnt);
-		}
-		save_flags_cli(flags);
-		if (!info->SCpnt || info->scsi.disconnectable)
-			fas216_kick(info);
-		restore_flags(flags);
-	} else {
-		/* no interrupts to rely on - we'll have to handle the
-		 * command ourselves.  For now, we give up.
-		 */
+	/* add command into execute queue and let it complete under
+	 * whatever scheme we're using.
+	 */
+	if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
 		SCpnt->result = DID_ERROR << 16;
 		done(SCpnt);
 	}
+	save_flags_cli(flags);
+	if (!info->SCpnt || info->scsi.disconnectable)
+		fas216_kick(info);
+	restore_flags(flags);
+
 	return 0;
 }
 
@@ -1672,15 +1992,28 @@
 
 	/*
 	 * This wastes time, since we can't return until the command is
-	 * complete. We can't seep either since we may get re-entered!
+	 * complete. We can't sleep either since we may get re-entered!
 	 * However, we must re-enable interrupts, or else we'll be
 	 * waiting forever.
 	 */
 	save_flags(flags);
 	sti();
 
-	while (!info->internal_done)
-		barrier();
+	while (!info->internal_done) {
+		/*
+		 * If we don't have an IRQ, then we must
+		 * poll the card for it's interrupt, and
+		 * use that to call this driver's interrupt
+		 * routine.  That way, we keep the command
+		 * progressing.
+		 */
+		if (info->scsi.irq == NO_IRQ) {
+			sti();
+			while (!(inb(REG_STAT(info)) & STAT_INT));
+			cli();
+			fas216_intr(info->host);
+		}
+	}
 
 	restore_flags(flags);
 
@@ -1688,7 +2021,7 @@
 }
 
 /* Prototype: void fas216_reportstatus(Scsi_Cmnd **SCpntp1,
- *				       Scsi_Cmnd **SCpntp2, int result)
+ *				       Scsi_Cmnd **SCpntp2, int result, int no_report)
  * Purpose  : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
  * Params   : SCpntp1 - pointer to command to return
  *	      SCpntp2 - pointer to command to check
@@ -1697,7 +2030,7 @@
  *	      structure as *SCpntp2.
  */
 static void fas216_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2,
-				int result)
+				int result, int no_report)
 {
 	Scsi_Cmnd *SCpnt = *SCpntp1;
 
@@ -1705,7 +2038,8 @@
 		*SCpntp1 = NULL;
 
 		SCpnt->result = result;
-		SCpnt->scsi_done(SCpnt);
+		if (!no_report || SCpnt != *SCpntp2)
+			SCpnt->scsi_done(SCpnt);
 	}
 
 	if (SCpnt == *SCpntp2)
@@ -1752,6 +2086,95 @@
 	return FAILED;
 }
 
+enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
+
+/*
+ * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+ * Purpose  : abort a command on this host
+ * Params   : SCpnt - command to abort
+ * Returns  : abort status
+ */
+static enum res_abort
+fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+	enum res_abort res = res_not_running;
+
+	if (queue_removecmd(&info->queues.issue, SCpnt)) {
+		/*
+		 * The command was on the issue queue, and has not been
+		 * issued yet.  We can remove the command from the queue,
+		 * and acknowledge the abort.  Neither the devices nor the
+		 * interface know about the command.
+		 */
+		printk("on issue queue ");
+
+		res = res_success;
+	} else if (queue_removecmd(&info->queues.disconnected, SCpnt)) {
+		/*
+		 * The command was on the disconnected queue.  Simply
+		 * acknowledge the abort condition, and when the target
+		 * reconnects, we will give it an ABORT message.  The
+		 * target should then disconnect, and we will clear
+		 * the busylun bit.
+		 */
+		printk("on disconnected queue ");
+
+		res = res_success;
+	} else if (info->SCpnt == SCpnt) {
+		unsigned long flags;
+
+		printk("executing ");
+
+		save_flags(flags);
+		cli();
+		switch (info->scsi.phase) {
+		/*
+		 * If the interface is idle, and the command is 'disconnectable',
+		 * then it is the same as on the disconnected queue.  We simply
+		 * remove all traces of the command.  When the target reconnects,
+		 * we will give it an ABORT message since the command could not
+		 * be found.  When the target finally disconnects, we will clear
+		 * the busylun bit.
+		 */
+		case PHASE_IDLE:
+			if (info->scsi.disconnectable) {
+				info->scsi.disconnectable = 0;
+				info->SCpnt = NULL;
+				res = res_success;
+			}
+			break;
+
+		/*
+		 * If the command has connected and done nothing futher,
+		 * simply force a disconnect.  We also need to clear the
+		 * busylun bit.
+		 */
+		case PHASE_SELECTION:
+//			info->SCpnt = NULL;
+//			res = res_success_clear;
+//			break;
+
+		default:
+			res = res_snooze;
+			break;
+		}
+		restore_flags(flags);
+	} else if (info->origSCpnt == SCpnt) {
+		/*
+		 * The command will be executed next, but a command
+		 * is currently using the interface.  This is similar to
+		 * being on the issue queue, except the busylun bit has
+		 * been set.
+		 */
+		info->origSCpnt = NULL;
+		printk("waiting for execution ");
+		res = res_success_clear;
+	} else
+		printk("unknown ");
+
+	return res;
+}
+
 /* Function: int fas216_abort(Scsi_Cmnd *SCpnt)
  * Purpose : abort a command if something horrible happens.
  * Params  : SCpnt - Command that is believed to be causing a problem.
@@ -1769,46 +2192,51 @@
 	print_debug_list();
 	fas216_dumpstate(info);
 	fas216_dumpinfo(info);
-	printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no);
 
-	do {
-		/* If command is waiting in the issue queue, then we can
-		 * simply remove the command and return abort status
-		 */
-		if (queue_removecmd(&info->queues.issue, SCpnt)) {
-			SCpnt->result = DID_ABORT << 16;
-			SCpnt->scsi_done(SCpnt);
-			printk("command on issue queue");
-			result = SCSI_ABORT_SUCCESS;
-			break;
-		}
+	printk(KERN_WARNING "scsi%d: abort ", info->host->host_no);
 
-		/* If the command is on the disconencted queue, we need to
-		 * reconnect to the device
-		 */
-		if (queue_cmdonqueue(&info->queues.disconnected, SCpnt))
-			printk("command on disconnected queue");
+	switch (fas216_do_abort(info, SCpnt)) {
+	/*
+	 * We managed to find the command and cleared it out.
+	 * We do not expect the command to be executing on the
+	 * target, but we have set the busylun bit.
+	 */
+	case res_success_clear:
+		printk("clear ");
+		clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
 
-		/* If the command is connected, we need to flag that the
-		 * command needs to be aborted
-		 */
-		if (info->SCpnt == SCpnt)
-			printk("command executing");
+	/*
+	 * We found the command, and cleared it out.  Either
+	 * the command is still known to be executing on the
+	 * target, or the busylun bit is not set.
+	 */
+	case res_success:
+		printk("success\n");
+		SCpnt->result = DID_ABORT << 16;
+		SCpnt->scsi_done(SCpnt);
+		result = SCSI_ABORT_SUCCESS;
+		break;
 
-		/* If the command is pending for execution, then again
-		 * this is simple - we remove it and report abort status
-		 */
-		if (info->origSCpnt == SCpnt) {
-			info->origSCpnt = NULL;
-			SCpnt->result = DID_ABORT << 16;
-			SCpnt->scsi_done(SCpnt);
-			printk("command waiting for execution");
-			result = SCSI_ABORT_SUCCESS;
-			break;
-		}
-	} while (0);
+	/*
+	 * We did find the command, but unfortunately we couldn't
+	 * unhook it from ourselves.  Wait some more, and if it
+	 * still doesn't complete, reset the interface.
+	 */
+	case res_snooze:
+		printk("snooze\n");
+		result = SCSI_ABORT_SNOOZE;
+		break;
 
-	printk("\n");
+	/*
+	 * The command could not be found (either because it completed,
+	 * or it got dropped.
+	 */
+	default:
+	case res_not_running:
+		result = SCSI_ABORT_SNOOZE;
+		printk("not running\n");
+		break;
+	}
 
 	return result;
 }
@@ -1819,7 +2247,7 @@
  */
 static void fas216_reset_state(FAS216_Info *info)
 {
-	syncneg_t negstate;
+	neg_t sync_state, wide_state;
 	int i;
 
 	fas216_checkmagic(info, "fas216_reset_state");
@@ -1833,26 +2261,37 @@
 	info->scsi.reconnected.lun = 0;
 	info->scsi.reconnected.tag = 0;
 	info->scsi.disconnectable = 0;
-	info->scsi.last_message = 0;
 	info->scsi.aborting = 0;
 	info->scsi.phase = PHASE_IDLE;
-	info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+	info->scsi.async_stp =
+			fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+	if (info->ifcfg.wide_max_size == 0)
+		wide_state = neg_invalid;
+	else
+#ifdef SCSI2_WIDE
+		wide_state = neg_wait;
+#else
+		wide_state = neg_invalid;
+#endif
 
 	if (info->host->dma_channel == NO_DMA || !info->dma.setup)
-		negstate = syncneg_invalid;
+		sync_state = neg_invalid;
 	else
 #ifdef SCSI2_SYNC
-		negstate = syncneg_start;
+		sync_state = neg_wait;
 #else
-		negstate = syncneg_invalid;
+		sync_state = neg_invalid;
 #endif
 
 	for (i = 0; i < 8; i++) {
-		info->device[i].disconnect_ok = info->ifcfg.disconnect_ok;
-		info->device[i].negstate = negstate;
-		info->device[i].period = info->ifcfg.asyncperiod / 4;
-		info->device[i].stp = info->scsi.async_stp;
-		info->device[i].sof = 0;
+		info->device[i].disconnect_ok	= info->ifcfg.disconnect_ok;
+		info->device[i].sync_state	= sync_state;
+		info->device[i].wide_state	= wide_state;
+		info->device[i].period		= info->ifcfg.asyncperiod / 4;
+		info->device[i].stp		= info->scsi.async_stp;
+		info->device[i].sof		= 0;
+		info->device[i].wide_xfer	= 0;
 	}
 }
 
@@ -1887,6 +2326,7 @@
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
 	Scsi_Cmnd *SCptr;
 	int result = 0;
+	int synchronous = reset_flags & SCSI_RESET_SYNCHRONOUS;
 
 	fas216_checkmagic(info, "fas216_reset");
 
@@ -1908,9 +2348,11 @@
 	info->stats.resets += 1;
 
 	print_debug_list();
-	printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no);
+	printk(KERN_WARNING "scsi%d: reset ", info->host->host_no);
 	if (SCpnt)
-		printk(" for target %d ", SCpnt->target);
+		printk("for target %d ", SCpnt->target);
+
+	printk("\n");
 
 	outb(info->scsi.cfg[3], REG_CNTL3(info));
 
@@ -1944,10 +2386,10 @@
 	/*
 	 * Signal all commands in progress have been reset
 	 */
-	fas216_reportstatus(&info->SCpnt, &SCpnt, DID_RESET << 16);
+	fas216_reportstatus(&info->SCpnt, &SCpnt, DID_RESET << 16, synchronous);
 
 	while ((SCptr = queue_remove(&info->queues.disconnected)) != NULL)
-		fas216_reportstatus(&SCptr, &SCpnt, DID_RESET << 16);
+		fas216_reportstatus(&SCptr, &SCpnt, DID_RESET << 16, synchronous);
 
 	if (SCpnt) {
 		/*
@@ -1960,11 +2402,10 @@
 		queue_removecmd(&info->queues.issue, SCpnt);
 
 		SCpnt->result = DID_RESET << 16;
-		SCpnt->scsi_done(SCpnt);
+		if (!synchronous)
+			SCpnt->scsi_done(SCpnt);
 	}
 
-	printk("\n");
-
 	return result | SCSI_RESET_SUCCESS;
 }
 
@@ -2083,6 +2524,49 @@
 	return 0;
 }
 
+int fas216_print_stats(FAS216_Info *info, char *buffer)
+{
+	return sprintf(buffer,
+			"Queued commands: %-10u   Issued commands: %-10u\n"
+			"Done commands  : %-10u   Reads          : %-10u\n"
+			"Writes         : %-10u   Others         : %-10u\n"
+			"Disconnects    : %-10u   Aborts         : %-10u\n"
+			"Resets         : %-10u\n",
+			info->stats.queues,	 info->stats.removes,
+			info->stats.fins,	 info->stats.reads,
+			info->stats.writes,	 info->stats.miscs,
+			info->stats.disconnects, info->stats.aborts,
+			info->stats.resets);
+}
+
+int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer)
+{
+	struct fas216_device *dev = &info->device[scd->id];
+	int len = 0;
+	char *p;
+
+	proc_print_scsidevice(scd, buffer, &len, 0);
+	p = buffer + len;
+
+	p += sprintf(p, "  Extensions: ");
+
+	if (scd->tagged_supported)
+		p += sprintf(p, "TAG %sabled [%d] ",
+			     scd->tagged_queue ? "en" : "dis",
+			     scd->current_tag);
+
+	p += sprintf(p, "\n  Transfers : %d-bit ",
+		     8 << dev->wide_xfer);
+
+	if (dev->sof)
+		p += sprintf(p, "sync offset %d, %d ns\n",
+				dev->sof, dev->period * 4);
+	else
+		p += sprintf(p, "async\n");
+
+	return p - buffer;
+}
+
 EXPORT_SYMBOL(fas216_init);
 EXPORT_SYMBOL(fas216_abort);
 EXPORT_SYMBOL(fas216_reset);
@@ -2094,7 +2578,8 @@
 EXPORT_SYMBOL(fas216_eh_device_reset);
 EXPORT_SYMBOL(fas216_eh_bus_reset);
 EXPORT_SYMBOL(fas216_eh_host_reset);
-
+EXPORT_SYMBOL(fas216_print_stats);
+EXPORT_SYMBOL(fas216_print_device);
 
 #ifdef MODULE
 int init_module(void)
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/fas216.h linux/drivers/acorn/scsi/fas216.h
--- linux-2.2.18-pre8/drivers/acorn/scsi/fas216.h	Sat Jul 18 11:55:24 1998
+++ linux/drivers/acorn/scsi/fas216.h	Mon Dec  3 20:25:14 2001
@@ -40,6 +40,7 @@
 #define CMD_TRANSFERINFO	0x10
 #define CMD_INITCMDCOMPLETE	0x11
 #define CMD_MSGACCEPTED		0x12
+#define CMD_PADBYTES		0x18
 #define CMD_SETATN		0x1a
 #define CMD_RSETATN		0x1b
 
@@ -171,15 +172,17 @@
 typedef enum {
 	PHASE_IDLE,					/* we're not planning on doing anything	*/
 	PHASE_SELECTION,				/* selecting a device			*/
+	PHASE_SELSTEPS,					/* selection with command steps		*/
+	PHASE_COMMAND,					/* command sent				*/
 	PHASE_MESSAGESENT,				/* selected, and we're sending cmd	*/
 	PHASE_RECONNECTED,				/* reconnected				*/
 	PHASE_DATAOUT,					/* data out to device			*/
 	PHASE_DATAIN,					/* data in from device			*/
 	PHASE_MSGIN,					/* message in from device		*/
-	PHASE_MSGOUT,					/* message out to device		*/
-	PHASE_AFTERMSGOUT,				/* after message out phase		*/
+	PHASE_MSGIN_DISCONNECT,				/* disconnecting from bus		*/
+	PHASE_MSGOUT,					/* after message out phase		*/
+	PHASE_MSGOUT_EXPECT,				/* expecting message out		*/
 	PHASE_STATUS,					/* status from device			*/
-	PHASE_DISCONNECT,				/* disconnecting from bus		*/
 	PHASE_DONE					/* Command complete			*/
 } phase_t;
 
@@ -197,13 +200,15 @@
 } fasdmatype_t;
 
 typedef enum {
-	syncneg_start,					/* Negociate with device for Sync xfers	*/
-	syncneg_sent,					/* Sync Xfer negociation sent		*/
-	syncneg_complete,				/* Sync Xfer complete			*/
-	syncneg_invalid					/* Sync Xfer not supported		*/
-} syncneg_t;
+	neg_wait,					/* Negociate with device		*/
+	neg_inprogress,					/* Negociation sent			*/
+	neg_complete,					/* Negociation complete			*/
+	neg_targcomplete,				/* Target completed negociation		*/
+	neg_invalid					/* Negociation not supported		*/
+} neg_t;
 
 #define MAGIC	0x441296bdUL
+#define NR_MSGS	8
 
 typedef struct {
 	unsigned long		magic_start;
@@ -231,7 +236,9 @@
 		MsgQueue_t	msgs;			/* message queue for connected device	*/
 
 		unsigned int	async_stp;		/* Async transfer STP value		*/
-		unsigned short	last_message;		/* last message to be sent		*/
+		unsigned char	msgin_fifo;		/* bytes in fifo at time of message in	*/
+		unsigned char	message[256];		/* last message received from device	*/
+		unsigned int	msglen;			/* length of last message received	*/
 
 		unsigned char	disconnectable:1;	/* this command can be disconnected	*/
 		unsigned char	aborting:1;		/* aborting command			*/
@@ -255,6 +262,7 @@
 		unsigned char	clockrate;		/* clock rate of FAS device (MHz)	*/
 		unsigned char	select_timeout;		/* timeout (R5)				*/
 		unsigned char	sync_max_depth;		/* Synchronous xfer max fifo depth	*/
+		unsigned char	wide_max_size;		/* Maximum wide transfer size		*/
 		unsigned char	cntl3;			/* Control Reg 3			*/
 		unsigned int	asyncperiod;		/* Async transfer period (ns)		*/
 		unsigned int	disconnect_ok:1;	/* Disconnects allowed?			*/
@@ -267,12 +275,14 @@
 	} queues;
 
 	/* per-device info */
-	struct {
+	struct fas216_device {
 		unsigned char	disconnect_ok:1;	/* device can disconnect		*/
-		unsigned int	period;			/* sync xfer period (*4ns)		*/
+		unsigned char	period;			/* sync xfer period in (*4ns)		*/
 		unsigned char	stp;			/* synchronous transfer period		*/
 		unsigned char	sof;			/* synchronous offset register		*/
-		syncneg_t	negstate;		/* synchronous transfer mode		*/
+		unsigned char	wide_xfer;		/* currently negociated wide transfer	*/
+		neg_t		sync_state;		/* synchronous transfer mode		*/
+		neg_t		wide_state;		/* wide transfer mode			*/
 	} device[8];
 	unsigned char	busyluns[8];			/* array of bits indicating LUNs busy	*/
 
@@ -339,6 +349,9 @@
  * Returns : 0 on success
  */
 extern int fas216_release (struct Scsi_Host *instance);
+
+extern int fas216_print_stats(FAS216_Info *info, char *buffer);
+extern int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer);
 
 /* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt)
  * Purpose : abort this command
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/msgqueue.c linux/drivers/acorn/scsi/msgqueue.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/msgqueue.c	Sat Jul 18 11:55:24 1998
+++ linux/drivers/acorn/scsi/msgqueue.c	Mon Dec  3 20:25:14 2001
@@ -83,45 +83,25 @@
 	int length = 0;
 
 	for (mq = msgq->qe; mq; mq = mq->next)
-		length += mq->length;
+		length += mq->msg.length;
 
 	return length;
 }
 
 /*
- * Function: char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return a message & its length
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
+ * Purpose : return a message
  * Params  : msgq   - queue to obtain message from
- *	     length - pointer to int for message length
+ *	   : msgno  - message number
  * Returns : pointer to message string, or NULL
  */
-char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
+struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
 {
 	struct msgqueue_entry *mq;
 
-	if ((mq = msgq->qe) != NULL) {
-		msgq->qe = mq->next;
-		mqe_free(msgq, mq);
-		*length = mq->length;
-	}
-
-	return mq ? mq->msg : NULL;
-}
-
-/*
- * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return next message & length without removing it from the list
- * Params  : msgq   - queue to obtain message from
- *         : length - pointer to int for message length
- * Returns : pointer to message string, or NULL
- */
-char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
-{
-	struct msgqueue_entry *mq = msgq->qe;
-
-	*length = mq ? mq->length : 0;
+	for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--);
 
-	return mq ? mq->msg : NULL;
+	return mq ? &mq->msg : NULL;
 }
 
 /*
@@ -143,10 +123,11 @@
 
 		va_start(ap, length);
 		for (i = 0; i < length; i++)
-			mq->msg[i] = va_arg(ap, unsigned char);
+			mq->msg.msg[i] = va_arg(ap, unsigned char);
 		va_end(ap);
 
-		mq->length = length;
+		mq->msg.length = length;
+		mq->msg.fifo = 0;
 		mq->next = NULL;
 
 		mqp = &msgq->qe;
@@ -178,8 +159,7 @@
 EXPORT_SYMBOL(msgqueue_initialise);
 EXPORT_SYMBOL(msgqueue_free);
 EXPORT_SYMBOL(msgqueue_msglength);
-EXPORT_SYMBOL(msgqueue_getnextmsg);
-EXPORT_SYMBOL(msgqueue_peeknextmsg);
+EXPORT_SYMBOL(msgqueue_getmsg);
 EXPORT_SYMBOL(msgqueue_addmsg);
 EXPORT_SYMBOL(msgqueue_flush);
 
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/msgqueue.h linux/drivers/acorn/scsi/msgqueue.h
--- linux-2.2.18-pre8/drivers/acorn/scsi/msgqueue.h	Fri May  8 00:42:39 1998
+++ linux/drivers/acorn/scsi/msgqueue.h	Mon Dec  3 20:25:14 2001
@@ -6,9 +6,14 @@
 #ifndef MSGQUEUE_H
 #define MSGQUEUE_H
 
-struct msgqueue_entry {
+struct message {
     char msg[8];
     int length;
+    int fifo;
+};
+
+struct msgqueue_entry {
+    struct message msg;
     struct msgqueue_entry *next;
 };
 
@@ -21,60 +26,51 @@
 } MsgQueue_t;
 
 /*
- * Function: void msgqueue_initialise (MsgQueue_t *msgq)
+ * Function: void msgqueue_initialise(MsgQueue_t *msgq)
  * Purpose : initialise a message queue
  * Params  : msgq - queue to initialise
  */
-extern void msgqueue_initialise (MsgQueue_t *msgq);
+extern void msgqueue_initialise(MsgQueue_t *msgq);
 
 /*
- * Function: void msgqueue_free (MsgQueue_t *msgq)
+ * Function: void msgqueue_free(MsgQueue_t *msgq)
  * Purpose : free a queue
  * Params  : msgq - queue to free
  */
-extern void msgqueue_free (MsgQueue_t *msgq);
+extern void msgqueue_free(MsgQueue_t *msgq);
 
 /*
- * Function: int msgqueue_msglength (MsgQueue_t *msgq)
+ * Function: int msgqueue_msglength(MsgQueue_t *msgq)
  * Purpose : calculate the total length of all messages on the message queue
  * Params  : msgq - queue to examine
  * Returns : number of bytes of messages in queue
  */
-extern int msgqueue_msglength (MsgQueue_t *msgq);
+extern int msgqueue_msglength(MsgQueue_t *msgq);
 
 /*
- * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
  * Purpose : return a message & its length
  * Params  : msgq   - queue to obtain message from
- *	     length - pointer to int for message length
- * Returns : pointer to message string, or NULL
- */
-extern char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length);
-
-/*
- * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return next message & length without removing it from the list
- * Params  : msgq   - queue to obtain message from
- *         : length - pointer to int for message length
+ *         : msgno  - message number
  * Returns : pointer to message string, or NULL
  */
-extern char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length);
+extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno);
 
 /*
- * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+ * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
  * Purpose : add a message onto a message queue
  * Params  : msgq   - queue to add message on
  *	     length - length of message
  *	     ...    - message bytes
  * Returns : != 0 if successful
  */
-extern int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...);
+extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...);
 
 /*
- * Function: void msgqueue_flush (MsgQueue_t *msgq)
+ * Function: void msgqueue_flush(MsgQueue_t *msgq)
  * Purpose : flush all messages from message queue
  * Params  : msgq - queue to flush
  */
-extern void msgqueue_flush (MsgQueue_t *msgq);
+extern void msgqueue_flush(MsgQueue_t *msgq);
 
 #endif
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/powertec.c linux/drivers/acorn/scsi/powertec.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/powertec.c	Thu Dec 17 09:07:45 1998
+++ linux/drivers/acorn/scsi/powertec.c	Mon Dec  3 20:25:14 2001
@@ -114,6 +114,8 @@
 	powertecscsi_irqenable,
 	powertecscsi_irqdisable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -271,8 +273,9 @@
 		info->info.ifcfg.select_timeout	= 255;
 		info->info.ifcfg.asyncperiod	= POWERTEC_ASYNC_PERIOD;
 		info->info.ifcfg.sync_max_depth	= POWERTEC_SYNC_DEPTH;
-		info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+		info->info.ifcfg.cntl3		= /*CNTL3_BS8 |*/ CNTL3_FASTSCSI | CNTL3_FASTCLK;
 		info->info.ifcfg.disconnect_ok	= 1;
+		info->info.ifcfg.wide_max_size	= 0;
 		info->info.dma.setup		= powertecscsi_dma_setup;
 		info->info.dma.pseudo		= NULL;
 		info->info.dma.stop		= powertecscsi_dma_stop;
@@ -443,31 +446,12 @@
 			host->io_port, host->irq, host->dma_channel,
 			info->info.scsi.type, info->control.terms ? "on" : "off");
 
-	pos += sprintf(buffer+pos,
-			"Queued commands: %-10u   Issued commands: %-10u\n"
-			"Done commands  : %-10u   Reads          : %-10u\n"
-			"Writes         : %-10u   Others         : %-10u\n"
-			"Disconnects    : %-10u   Aborts         : %-10u\n"
-			"Resets         : %-10u\n",
-			info->info.stats.queues,      info->info.stats.removes,
-			info->info.stats.fins,        info->info.stats.reads,
-			info->info.stats.writes,      info->info.stats.miscs,
-			info->info.stats.disconnects, info->info.stats.aborts,
-			info->info.stats.resets);
+	pos += fas216_print_stats(&info->info, buffer + pos);
 
-	pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
+	pos += sprintf (buffer+pos, "\nAttached devices:\n");
 
 	for (scd = host->host_queue; scd; scd = scd->next) {
-		int len;
-
-		proc_print_scsidevice (scd, buffer, &len, pos);
-		pos += len;
-		pos += sprintf (buffer+pos, "Extensions: ");
-		if (scd->tagged_supported)
-			pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
-				    scd->tagged_queue ? "en" : "dis",
-				    scd->current_tag);
-		pos += sprintf (buffer+pos, "\n");
+		pos += fas216_print_device(&info->info, scd, buffer + pos);
 
 		if (pos + begin < offset) {
 			begin += pos;
diff -urN linux-2.2.18-pre8/drivers/acorn/scsi/queue.c linux/drivers/acorn/scsi/queue.c
--- linux-2.2.18-pre8/drivers/acorn/scsi/queue.c	Sat Jul 18 11:55:25 1998
+++ linux/drivers/acorn/scsi/queue.c	Mon Dec  3 20:25:14 2001
@@ -55,6 +55,7 @@
 			q->magic = QUEUE_MAGIC_FREE;
 			q->SCpnt = NULL;
 		}
+		q -= 1;
 		q->next = NULL;
 	}
 
diff -urN linux-2.2.18-pre8/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- linux-2.2.18-pre8/drivers/scsi/Config.in	Mon Dec  3 20:24:43 2001
+++ linux/drivers/scsi/Config.in	Mon Dec  3 20:25:15 2001
@@ -43,6 +43,9 @@
 if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
     bool '  Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
 fi
+if [ "$CONFIG_PCI" = "y" ]; then
+  dep_tristate 'Compaq Fibre Channel 64-bit/66Mhz HBA support' CONFIG_SCSI_CPQFCTS $CONFIG_SCSI
+fi
 dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
 dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI
   if [ "$CONFIG_SCSI_EATA" != "n" ]; then
diff -urN linux-2.2.18-pre8/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux-2.2.18-pre8/drivers/scsi/Makefile	Mon Dec  3 20:24:43 2001
+++ linux/drivers/scsi/Makefile	Mon Dec  3 20:25:15 2001
@@ -277,6 +277,7 @@
   endif
 endif
 
+
 ifeq ($(CONFIG_SCSI_QLOGIC_FAS),y)
 L_OBJS += qlogicfas.o
 else
@@ -326,6 +327,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_CPQFCTS),y)
+L_OBJS += cpqfc.o 
+else
+  ifeq ($(CONFIG_SCSI_CPQFCTS),m)   
+  M_OBJS += cpqfc.o 
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_AHA152X),y)
 L_OBJS += aha152x.o
 else
@@ -685,6 +694,28 @@
 sim710_u.h: sim710_d.h
 
 sim710.o : sim710_d.h
+
+# if we have a triggerable HBA (extra circuit addition for
+# TTL level output on GPIO line), build in the trigger file
+
+#CPQTRIGGER_FILE = cpqfcTStrigger.c
+
+CPQTRIG = cpqfcTStrigger.o
+
+cpqfcTStrigger.o: cpqfcTStrigger.c
+
+cpqfcTSinit.o: cpqfcTSinit.c cpqfcTSstructs.h cpqfcTSchip.h cpqfcTSioctl.h
+
+cpqfcTScontrol.o: cpqfcTScontrol.c cpqfcTSstructs.h cpqfcTSchip.h 
+
+cpqfcTSi2c.o: cpqfcTSi2c.c cpqfcTSchip.h 
+
+cpqfcTSworker.o: cpqfcTSworker.c cpqfcTSchip.h cpqfcTSstructs.h cpqfcTSioctl.h
+
+cpqfc.o: cpqfcTSinit.o cpqfcTScontrol.o cpqfcTSi2c.o cpqfcTSworker.o $(CPQTRIG)
+	$(LD) -r -o cpqfc.o cpqfcTSinit.o cpqfcTScontrol.o \
+	cpqfcTSi2c.o cpqfcTSworker.o $(CPQTRIG)
+
 
 initio.o: ini9100u.c i91uscsi.c
 	$(CC) $(CFLAGS) -c ini9100u.c -o ini9100u.o
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfc.Readme linux/drivers/scsi/cpqfc.Readme
--- linux-2.2.18-pre8/drivers/scsi/cpqfc.Readme	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfc.Readme	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,216 @@
+Notes for CPQFCTS driver for Compaq Tachyon TS
+Fibre Channel Host Bus Adapter, PCI 64-bit, 66MHz
+for Linux (RH 6.1, 6.2 kernel 2.2.12-32, 2.2.14-5)
+SMP tested
+Tested in single and dual HBA configuration, 32 and 64bit busses,
+33 and 66MHz.  Only supports FC-AL.
+SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc() 
+	max of 128k bytes contiguous.
+Ver 1.3.4  Sep   7, 2000
+  Added Modinfo information
+  Fixed problem with statically linking the driver
+
+Ver 1.3.3, Aug  23, 2000
+  Fixed device/function number in ioctl
+
+Ver 1.3.2, July 27, 2000
+  Add include for Alpha compile on 2.2.14 kernel (cpq*i2c.c)
+  Change logic for different FCP-RSP sense_buffer location for HSG80 target
+  And search for Agilent Tachyon XL2 HBAs (not finished! - in test)
+
+Tested with 
+(storage):
+           Compaq RA-4x000, RAID firmware ver 2.40 - 2.54
+           Seagate FC drives model ST39102FC, rev 0006
+           Hitachi DK31CJ-72FC rev J8A8
+           IBM DDYF-T18350R rev F60K
+           Compaq FC-SCSI bridge w/ DLT 35/70 Gb DLT (tape)
+(servers):
+           Compaq PL-1850R
+           Compaq PL-6500 Xeon (400MHz)
+	   Compaq PL-8500 (500MHz, 66MHz, 64bit PCI)
+           Compaq Alpha DS20 (RH 6.1)
+(hubs):
+           Vixel Rapport 1000 (7-port "dumb")
+	   Gadzoox Gibralter (12-port "dumb")
+	   Gadzoox Capellix 2000, 3000 
+(switches):
+           Brocade 2010, 2400, 2800, rev 2.0.3a (& later)
+           Gadzoox 3210 (Fabric blade beta)
+           Vixel 7100 (Fabric beta firmare - known hot plug issues)
+using "qa_test" (esp. io_test script) suite modified from Unix tests.
+	
+Installation:
+copy file cpqfcTS.patch to /usr/src/linux
+patch -p1 < cpqfcTS.patch
+make menuconfig
+  (select SCSI low-level, Compaq FC HBA)
+make dep
+make modules
+make modules_install
+
+e.g. insmod -f cpqfc
+
+Due to Fabric/switch delays, driver requires 4 seconds 
+to initialize.  If adapters are found, there will be a entries at
+/proc/scsi/cpqfcTS/*
+
+sample contents of startup messages
+
+*************************
+ scsi_register allocating 3596 bytes for CPQFCHBA
+ ioremap'd Membase: c887e600
+  HBA Tachyon RevId 1.2
+Allocating 119808 for 576 Exchanges @ c0dc0000
+Allocating 112904 for LinkQ @ c0c20000 (576 elements)
+Allocating 110600 for TachSEST for 512 Exchanges
+  cpqfcTS: writing IMQ BASE 7C0000h    PI 7C4000h
+  cpqfcTS: SEST c0e40000(virt): Wrote base E40000h @ c887e740
+cpqfcTS: New FC port 0000E8h WWN: 500507650642499D SCSI Chan/Trgt 0/0
+cpqfcTS: New FC port 0000EFh WWN: 50000E100000D5A6 SCSI Chan/Trgt 0/1
+cpqfcTS: New FC port 0000E4h WWN: 21000020370097BB SCSI Chan/Trgt 0/2
+cpqfcTS: New FC port 0000E2h WWN: 2100002037009946 SCSI Chan/Trgt 0/3
+cpqfcTS: New FC port 0000E1h WWN: 21000020370098FE SCSI Chan/Trgt 0/4
+cpqfcTS: New FC port 0000E0h WWN: 21000020370097B2 SCSI Chan/Trgt 0/5
+cpqfcTS: New FC port 0000DCh WWN: 2100002037006CC1 SCSI Chan/Trgt 0/6
+cpqfcTS: New FC port 0000DAh WWN: 21000020370059F6 SCSI Chan/Trgt 0/7
+cpqfcTS: New FC port 00000Fh WWN: 500805F1FADB0E20 SCSI Chan/Trgt 0/8
+cpqfcTS: New FC port 000008h WWN: 500805F1FADB0EBA SCSI Chan/Trgt 0/9
+cpqfcTS: New FC port 000004h WWN: 500805F1FADB1EB9 SCSI Chan/Trgt 0/10
+cpqfcTS: New FC port 000002h WWN: 500805F1FADB1ADE SCSI Chan/Trgt 0/11
+cpqfcTS: New FC port 000001h WWN: 500805F1FADBA2CA SCSI Chan/Trgt 0/12
+scsi4 : Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2: WWN 500508B200193F50
+ on PCI bus 0 device 0xa0fc irq 5 IObaseL 0x3400, MEMBASE 0xc6ef8600
+PCI bus width 32 bits, bus speed 33 MHz
+FCP-SCSI Driver v1.3.0
+GBIC detected: Short-wave.  LPSM 0h Monitor
+scsi : 5 hosts.
+  Vendor: IBM       Model: DDYF-T18350R      Rev: F60K
+  Type:   Direct-Access                      ANSI SCSI revision: 03
+Detected scsi disk sdb at scsi4, channel 0, id 0, lun 0
+  Vendor: HITACHI   Model: DK31CJ-72FC       Rev: J8A8
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdc at scsi4, channel 0, id 1, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdd at scsi4, channel 0, id 2, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sde at scsi4, channel 0, id 3, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdf at scsi4, channel 0, id 4, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdg at scsi4, channel 0, id 5, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdh at scsi4, channel 0, id 6, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdi at scsi4, channel 0, id 7, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.48
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdj at scsi4, channel 0, id 8, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.48
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdk at scsi4, channel 0, id 8, lun 1
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.40
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdl at scsi4, channel 0, id 9, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.40
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdm at scsi4, channel 0, id 9, lun 1
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdn at scsi4, channel 0, id 10, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdo at scsi4, channel 0, id 11, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdp at scsi4, channel 0, id 11, lun 1
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdq at scsi4, channel 0, id 12, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdr at scsi4, channel 0, id 12, lun 1
+resize_dma_pool: unknown device type 12
+resize_dma_pool: unknown device type 12
+SCSI device sdb: hdwr sector= 512 bytes. Sectors= 35843670 [17501 MB] [17.5 GB]
+ sdb: sdb1
+SCSI device sdc: hdwr sector= 512 bytes. Sectors= 144410880 [70513 MB] [70.5 GB]
+ sdc: sdc1
+SCSI device sdd: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdd: sdd1
+SCSI device sde: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sde: sde1
+SCSI device sdf: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdf: sdf1
+SCSI device sdg: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdg: sdg1
+SCSI device sdh: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdh: sdh1
+SCSI device sdi: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdi: sdi1
+SCSI device sdj: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB]
+ sdj: sdj1
+SCSI device sdk: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB]
+ sdk: sdk1
+SCSI device sdl: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdl: sdl1
+SCSI device sdm: hdwr sector= 512 bytes. Sectors= 8380320 [4091 MB] [4.1 GB]
+ sdm: sdm1
+SCSI device sdn: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdn: sdn1
+SCSI device sdo: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdo: sdo1
+SCSI device sdp: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdp: sdp1
+SCSI device sdq: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB]
+ sdq: sdq1
+SCSI device sdr: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB]
+ sdr: sdr1
+
+*************************
+
+If a GBIC of type Short-wave, Long-wave, or Copper is detected, it will
+print out; otherwise, "none" is displayed.  If the cabling is correct
+and a loop circuit is completed, you should see "Monitor"; otherwise, 
+"LoopFail" (on open circuit) or some LPSM number/state with bit 3 set.
+
+
+ERRATA:
+1. Normally, Linux Scsi queries FC devices with INQUIRY strings.  All LUNs
+found according to INQUIRY should get READ commands at sector 0 to find
+partition table, etc.  Older kernels only query the first 4 devices.  Some
+Linux kernels only look for one LUN per target (i.e. FC device).
+
+2. Physically removing a device, or a malfunctioning system which hides a
+device, leads to a 30-second timeout and subsequent _abort call.  
+In some process contexts, this will hang the kernel (crashing the system).
+Single bit errors in frames and virtually all hot plugging events are 
+gracefully handled with internal driver timer and Abort processing.
+
+3. Some SCSI drives with error conditions will not handle the 7 second timeout
+in this software driver, leading to infinite retries on timed out SCSI commands.
+The 7 secs balances the need to quickly recover from lost frames (esp. on sequence
+initiatives) and time needed by older/slower/error-state drives in responding.
+This can be easily changed in "Exchanges[].timeOut".
+
+4. Due to the nature of FC soft addressing, there is no assurance that the 
+same LUNs (drives) will have the same path (e.g. /dev/sdb1) from one boot to
+next.  Dynamic soft address changes (i.e. 24-bit FC port_id) are
+supported during run time (e.g. due to hot plug event) by the use of WWN to
+SCSI Nexus (channel/target/LUN) mapping.
+
+5. Compaq RA4x00 firmware version 2.54 and later supports SSP (Selective 
+Storage Presentation), which maps LUNs to a WWN.  If RA4x00 firmware prior
+2.54 (e.g. older controller) is used, or the FC HBA is replaced (another WWN
+is used), logical volumes on the RA4x00 will no longer be visible.
+
+
+Send questions/comments to:
+donald.zimmerman@compaq.com
+dszimmerman@yahoo.com
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTS.h linux/drivers/scsi/cpqfcTS.h
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTS.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTS.h	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,39 @@
+#ifndef CPQFCTS_H
+#define CPQFCTS_H
+#include "cpqfcTSstructs.h"
+
+// These functions are required by the Linux SCSI layers
+extern int cpqfcTS_detect(Scsi_Host_Template *);
+extern int cpqfcTS_release(struct Scsi_Host *);
+const char * cpqfcTS_info(struct Scsi_Host *);
+extern int cpqfcTS_proc_info(char *, char **, off_t, int, int, int);
+extern int cpqfcTS_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+extern int cpqfcTS_abort(Scsi_Cmnd *);
+extern int cpqfcTS_reset(Scsi_Cmnd *, unsigned int);
+extern int cpqfcTS_biosparam(Disk *, kdev_t, int[]);
+extern int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg);
+
+// note: since Tachyon TS supports an extended scatter/gather
+// linked list of infinite length (with linked Ext S/G pages,
+// limited only by available physical memory) we use SG_ALL.
+
+#define CPQFCTS {                                \
+ detect:                 cpqfcTS_detect,         \
+ release:                cpqfcTS_release,        \
+ info:                   cpqfcTS_info,           \
+ proc_info:              cpqfcTS_proc_info,      \
+ ioctl:                  cpqfcTS_ioctl,          \
+ queuecommand:           cpqfcTS_queuecommand,   \
+ eh_abort_handler:       cpqfcTS_abort,          \
+ reset:                  cpqfcTS_reset,          \
+ bios_param:             cpqfcTS_biosparam,      \
+ can_queue:              CPQFCTS_REQ_QUEUE_LEN,  \
+ this_id:                -1,                     \
+ sg_tablesize:           SG_ALL,                 \
+ cmd_per_lun:            CPQFCTS_CMD_PER_LUN,    \
+ present:                0,                      \
+ unchecked_isa_dma:      0,                      \
+ use_clustering:         ENABLE_CLUSTERING       \
+}
+
+#endif /* CPQFCTS_H */ 
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTSchip.h linux/drivers/scsi/cpqfcTSchip.h
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTSchip.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTSchip.h	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,238 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) 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.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSCHIP_H
+#define CPQFCTSCHIP_H
+#ifndef TACHYON_CHIP_INC
+
+// FC-PH (Physical) specification levels for Login payloads
+// NOTE: These are NOT strictly complied with by any FC vendors
+
+#define FC_PH42			0x08
+#define FC_PH43			0x09
+#define FC_PH3			0x20
+
+#define TACHLITE_TS_RX_SIZE     1024  // max inbound frame size
+// "I" prefix is for Include
+
+#define IVENDID    0x00  // word
+#define IDEVID     0x02
+#define ITLCFGCMD 0x04
+#define IMEMBASE   0x18    // Tachyon
+#define ITLMEMBASE   0x1C  // Tachlite
+#define IIOBASEL   0x10    // Tachyon I/O base address, lower 256 bytes
+#define IIOBASEU   0x14    // Tachyon I/O base address, upper 256 bytes
+#define ITLIOBASEL   0x14  // TachLite I/O base address, lower 256 bytes
+#define ITLIOBASEU   0x18  // TachLite I/O base address, upper 256 bytes
+#define ITLRAMBASE   0x20  // TL on-board RAM start
+#define ISROMBASE  0x24
+#define IROMBASE   0x30
+
+#define ICFGCMD    0x04    // PCI config - PCI config access (word)
+#define ICFGSTAT   0x06    // PCI status (R - word)
+#define IRCTR_WCTR 0x1F2   // ROM control / pre-fetch wait counter
+#define IPCIMCTR   0x1F3   // PCI master control register
+#define IINTPEND   0x1FD   // Interrupt pending (I/O Upper - Tachyon & TL)
+#define IINTEN     0x1FE   // Interrupt enable  (I/O Upper - Tachyon & TL)
+#define IINTSTAT   0x1FF   // Interrupt status  (I/O Upper - Tachyon & TL)
+
+#define IMQ_BASE            0x80
+#define IMQ_LENGTH          0x84
+#define IMQ_CONSUMER_INDEX  0x88
+#define IMQ_PRODUCER_INDEX  0x8C   // Tach copies its INDX to bits 0-7 of value
+
+/*
+// IOBASE UPPER
+#define SFSBQ_BASE            0x00   // single-frame sequences
+#define SFSBQ_LENGTH          0x04
+#define SFSBQ_PRODUCER_INDEX  0x08
+#define SFSBQ_CONSUMER_INDEX  0x0C   // (R)
+#define SFS_BUFFER_LENGTH     0X10
+                              // SCSI-FCP hardware assists
+#define SEST_BASE             0x40   // SSCI Exchange State Table
+#define SEST_LENGTH           0x44
+#define SCSI_BUFFER_LENGTH    0x48
+#define SEST_LINKED_LIST      0x4C
+
+#define TACHYON_My_ID         0x6C
+#define TACHYON_CONFIGURATION 0x84   // (R/W) reset val 2
+#define TACHYON_CONTROL       0x88
+#define TACHYON_STATUS        0x8C   // (R)
+#define TACHYON_FLUSH_SEST    0x90   // (R/W)
+#define TACHYON_EE_CREDIT_TMR 0x94   // (R)
+#define TACHYON_BB_CREDIT_TMR 0x98   // (R)
+#define TACHYON_RCV_FRAME_ERR 0x9C   // (R)
+#define FRAME_MANAGER_CONFIG  0xC0   // (R/W)
+#define FRAME_MANAGER_CONTROL 0xC4
+#define FRAME_MANAGER_STATUS  0xC8   // (R)
+#define FRAME_MANAGER_ED_TOV  0xCC
+#define FRAME_MANAGER_LINK_ERR1 0xD0   // (R)
+#define FRAME_MANAGER_LINK_ERR2 0xD4   // (R)
+#define FRAME_MANAGER_TIMEOUT2  0xD8   // (W)
+#define FRAME_MANAGER_BB_CREDIT 0xDC   // (R)
+#define FRAME_MANAGER_WWN_HI    0xE0   // (R/W)
+#define FRAME_MANAGER_WWN_LO    0xE4   // (R/W)
+#define FRAME_MANAGER_RCV_AL_PA 0xE8   // (R)
+#define FRAME_MANAGER_PRIMITIVE 0xEC   // {K28.5} byte1 byte2 byte3
+*/
+		    
+#define TL_MEM_ERQ_BASE                    0x0 //ERQ Base
+#define TL_IO_ERQ_BASE                     0x0 //ERQ base
+
+#define TL_MEM_ERQ_LENGTH                  0x4 //ERQ Length
+#define TL_IO_ERQ_LENGTH                   0x4 //ERQ Length
+
+#define TL_MEM_ERQ_PRODUCER_INDEX          0x8 //ERQ Producer Index  register
+#define TL_IO_ERQ_PRODUCER_INDEX           0x8 //ERQ Producer Index  register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register
+#define TL_IO_ERQ_CONSUMER_INDEX_ADR  0xC //ERQ Consumer Index address register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX     0xC //ERQ Consumer Index 
+#define TL_IO_ERQ_CONSUMER_INDEX      0xC //ERQ Consumer Index 
+
+#define TL_MEM_SFQ_BASE               0x50 //SFQ Base
+#define TL_IO_SFQ_BASE                0x50 //SFQ base
+
+#define TL_MEM_SFQ_LENGTH             0x54 //SFQ Length
+#define TL_IO_SFQ_LENGTH              0x54 //SFQ Length
+
+#define TL_MEM_SFQ_CONSUMER_INDEX     0x58 //SFQ Consumer Index
+#define TL_IO_SFQ_CONSUMER_INDEX      0x58 //SFQ Consumer Index
+
+#define TL_MEM_IMQ_BASE               0x80 //IMQ Base
+#define TL_IO_IMQ_BASE                0x80 //IMQ base
+
+#define TL_MEM_IMQ_LENGTH             0x84 //IMQ Length
+#define TL_IO_IMQ_LENGTH              0x84 //IMQ Length
+
+#define TL_MEM_IMQ_CONSUMER_INDEX     0x88 //IMQ Consumer Index
+#define TL_IO_IMQ_CONSUMER_INDEX      0x88 //IMQ Consumer Index
+
+#define TL_MEM_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register
+#define TL_IO_IMQ_PRODUCER_INDEX_ADR  0x8C //IMQ Producer Index address register
+
+#define TL_MEM_SEST_BASE              0x140 //SFQ Base
+#define TL_IO_SEST_BASE               0x40 //SFQ base
+
+#define TL_MEM_SEST_LENGTH            0x144 //SFQ Length
+#define TL_IO_SEST_LENGTH             0x44 //SFQ Length
+
+#define TL_MEM_SEST_LINKED_LIST       0x14C
+
+#define TL_MEM_SEST_SG_PAGE           0x168  // Extended Scatter/Gather page size
+
+#define TL_MEM_TACH_My_ID             0x16C
+#define TL_IO_TACH_My_ID              0x6C //My AL_PA ID
+
+#define TL_MEM_TACH_CONFIG            0x184 //Tachlite Configuration register
+#define TL_IO_CONFIG                  0x84 //Tachlite Configuration register
+
+#define TL_MEM_TACH_CONTROL           0x188 //Tachlite Control register
+#define TL_IO_CTR                     0x88 //Tachlite Control register
+
+#define TL_MEM_TACH_STATUS            0x18C //Tachlite Status register
+#define TL_IO_STAT                    0x8C //Tachlite Status register
+
+#define TL_MEM_FM_CONFIG        0x1C0 //Frame Manager Configuration register
+#define TL_IO_FM_CONFIG         0xC0 //Frame Manager Configuration register
+
+#define TL_MEM_FM_CONTROL       0x1C4 //Frame Manager Control
+#define TL_IO_FM_CTL            0xC4 //Frame Manager Control
+
+#define TL_MEM_FM_STATUS        0x1C8 //Frame Manager Status
+#define TL_IO_FM_STAT           0xC8 //Frame Manager Status
+
+#define TL_MEM_FM_LINK_STAT1    0x1D0 //Frame Manager Link Status 1
+#define TL_IO_FM_LINK_STAT1     0xD0 //Frame Manager Link Status 1
+
+#define TL_MEM_FM_LINK_STAT2    0x1D4 //Frame Manager Link Status 2
+#define TL_IO_FM_LINK_STAT2     0xD4 //Frame Manager Link Status 2
+
+#define TL_MEM_FM_TIMEOUT2      0x1D8   // (W)
+
+#define TL_MEM_FM_BB_CREDIT0    0x1DC
+
+#define TL_MEM_FM_WWN_HI        0x1E0 //Frame Manager World Wide Name High
+#define TL_IO_FM_WWN_HI         0xE0 //Frame Manager World Wide Name High
+
+#define TL_MEM_FM_WWN_LO        0x1E4 //Frame Manager World Wide Name LOW
+#define TL_IO_FM_WWN_LO         0xE4 //Frame Manager World Wide Name Low
+
+#define TL_MEM_FM_RCV_AL_PA     0x1E8 //Frame Manager AL_PA Received register
+#define TL_IO_FM_ALPA           0xE8 //Frame Manager AL_PA Received register
+
+#define TL_MEM_FM_ED_TOV           0x1CC
+
+#define TL_IO_ROMCTR            0xFA //TL PCI ROM Control Register
+#define TL_IO_PCIMCTR           0xFB //TL PCI Master Control Register
+#define TL_IO_SOFTRST           0xFC //Tachlite Configuration register
+#define TL_MEM_SOFTRST          0x1FC //Tachlite Configuration register
+
+// completion message types (bit 8 set means Interrupt generated)
+// CM_Type
+#define OUTBOUND_COMPLETION        0
+#define ERROR_IDLE_COMPLETION   0x01
+#define OUT_HI_PRI_COMPLETION   0x01
+#define INBOUND_MFS_COMPLETION  0x02
+#define INBOUND_000_COMPLETION  0x03
+#define INBOUND_SFS_COMPLETION  0x04  // Tachyon & TachLite
+#define ERQ_FROZEN_COMPLETION   0x06  // TachLite
+#define INBOUND_C1_TIMEOUT      0x05
+#define INBOUND_BUSIED_FRAME    0x06
+#define SFS_BUF_WARN            0x07
+#define FCP_FROZEN_COMPLETION   0x07  // TachLite
+#define MFS_BUF_WARN            0x08
+#define IMQ_BUF_WARN            0x09
+#define FRAME_MGR_INTERRUPT     0x0A
+#define READ_STATUS             0x0B
+#define INBOUND_SCSI_DATA_COMPLETION  0x0C
+#define INBOUND_FCP_XCHG_COMPLETION   0x0C  // TachLite
+#define INBOUND_SCSI_DATA_COMMAND     0x0D
+#define BAD_SCSI_FRAME                0x0E
+#define INB_SCSI_STATUS_COMPLETION    0x0F
+#define BUFFER_PROCESSED_COMPLETION   0x11
+
+// FC-AL (Tachyon) Loop Port State Machine defs
+// (loop "Up" states)
+#define MONITORING 0x0
+#define ARBITRATING 0x1
+#define ARBITRAT_WON 0x2
+#define OPEN 0x3
+#define OPENED 0x4
+#define XMITTD_CLOSE 0x5
+#define RCVD_CLOSE 0x6
+#define TRANSFER 0x7
+
+// (loop "Down" states)
+#define INITIALIZING 0x8
+#define O_I_INIT 0x9
+#define O_I_PROTOCOL 0xa
+#define O_I_LIP_RCVD 0xb
+#define HOST_CONTROL 0xc
+#define LOOP_FAIL 0xd
+// (no 0xe)
+#define OLD_PORT 0xf
+
+
+
+#define TACHYON_CHIP_INC
+#endif
+#endif /* CPQFCTSCHIP_H */
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTScontrol.c linux/drivers/scsi/cpqfcTScontrol.c
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTScontrol.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTScontrol.c	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,2200 @@
+/* Copyright 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) 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.
+ * Written by Don Zimmerman
+*/
+/* These functions control the host bus adapter (HBA) hardware.  The main chip
+   control takes place in the interrupt handler where we process the IMQ 
+   (Inbound Message Queue).  The IMQ is Tachyon's way of communicating FC link
+   events and state information to the driver.  The Single Frame Queue (SFQ)
+   buffers incoming FC frames for processing by the driver.  References to 
+   "TL/TS UG" are for:
+   "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed.
+   Hewlitt Packard Manual Part Number 5968-1083E.
+*/
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>  // request_region() prototype
+#include <linux/sched.h>
+#include <linux/malloc.h>  // need "kfree" for ext. S/G pages
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <asm/io.h>  // struct pt_regs for IRQ handler & Port I/O
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+
+#include "sd.h"
+#include "hosts.h"   // Scsi_Host definition for INT handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+//#define IMQ_DEBUG 1
+
+static void fcParseLinkStatusCounters(TACHYON * fcChip);
+static void CpqTsGetSFQEntry(TACHYON * fcChip, 
+	      USHORT pi, ULONG * buffr, BOOLEAN UpdateChip); 
+
+
+// Note special requirements for Q alignment!  (TL/TS UG pg. 190)
+// We place critical index pointers at end of QUE elements to assist
+// in non-symbolic (i.e. memory dump) debugging
+// opcode defines placement of Queues (e.g. local/external RAM)
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+  int iStatus=0;
+  unsigned long ulAddr;
+
+
+  // NOTE! fcMemManager() will return system virtual addresses.
+  // System (kernel) virtual addresses, though non-paged, still
+  // aren't physical addresses.  Convert to PHYSICAL_ADDRESS for Tachyon's
+  // DMA use.
+  ENTER("CreateTachLiteQues");
+
+
+  // Allocate primary EXCHANGES array...
+  
+  printk("Allocating %u for %u Exchanges ", 
+	  (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID);
+  fcChip->Exchanges = kmalloc( sizeof( FC_EXCHANGES), GFP_KERNEL );
+  printk("@ %p\n", fcChip->Exchanges);
+
+  if( fcChip->Exchanges == NULL ) // fatal error!!
+  {
+    printk("kmalloc failure on Exchanges: fatal error\n");
+    return -1;
+  }
+  // zero out the entire EXCHANGE space
+  memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES));  
+
+
+  printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE));
+  cpqfcHBAdata->fcLQ = kmalloc( sizeof( FC_LINK_QUE), GFP_KERNEL );
+  printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH);
+  memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE));
+
+  if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!!
+  {
+    printk("kmalloc failure on fc Link Que: fatal error\n");
+    return -1;
+  }
+  // zero out the entire EXCHANGE space
+  memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE));  
+
+
+
+  
+  // Verify that basic Tach I/O registers are not NULL  
+  
+  if( !fcChip->Registers.ReMapMemBase )
+  {
+    printk("HBA base address NULL: fatal error\n");
+    return -1;
+  }
+
+
+  // Initialize the fcMemManager memory pairs (stores allocated/aligned
+  // pairs for future freeing)
+  memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem));
+  
+
+  // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes)
+  
+  fcChip->ERQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L );
+  if( !fcChip->ERQ )
+  {
+    printk("kmalloc/alignment failure on ERQ: fatal error\n");
+    return -1;
+  }
+  fcChip->ERQ->length = ERQ_LEN-1;
+  ulAddr = virt_to_bus( fcChip->ERQ);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->ERQ->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+  // Allocate Tach's Inbound Message Queue (32 bytes per entry)
+  
+  fcChip->IMQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L );
+  if( !fcChip->IMQ )
+  {
+    printk("kmalloc/alignment failure on IMQ: fatal error\n");
+    return -1;
+  }
+  fcChip->IMQ->length = IMQ_LEN-1;
+
+  ulAddr = virt_to_bus( fcChip->IMQ);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->IMQ->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+  // Allocate Tach's  Single Frame Queue (64 bytes per entry)
+  fcChip->SFQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L );
+  if( !fcChip->SFQ )
+  {
+    printk("kmalloc/alignment failure on SFQ: fatal error\n");
+    return -1;
+  }
+  fcChip->SFQ->length = SFQ_LEN-1;      // i.e. Que length [# entries -
+                                       // min. 32; max.  4096 (0xffff)]
+  
+  ulAddr = virt_to_bus( fcChip->SFQ);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->SFQ->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+  // Allocate SCSI Exchange State Table; aligned nearest @sizeof
+  // power-of-2 boundary
+  // LIVE DANGEROUSLY!  Assume the boundary for SEST mem will
+  // be on physical page (e.g. 4k) boundary.
+  printk("Allocating %u for TachSEST for %u Exchanges\n", 
+		 (ULONG)sizeof(TachSEST), TACH_SEST_LEN);
+  fcChip->SEST = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof(TachSEST),  4, 0L );
+//		  sizeof(TachSEST),  64*TACH_SEST_LEN, 0L );
+  if( !fcChip->SEST )
+  {
+    printk("kmalloc/alignment failure on SEST: fatal error\n");
+    return -1;
+  }
+
+  fcChip->SEST->length = TACH_SEST_LEN;  // e.g. DON'T subtract one 
+                                       // (TL/TS UG, pg 153)
+
+  ulAddr = virt_to_bus( fcChip->SEST);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->SEST->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+			      // Now that structures are defined,
+			      // fill in Tachyon chip registers...
+
+			      // EEEEEEEE  EXCHANGE REQUEST QUEUE
+
+  writel( fcChip->ERQ->base, 
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+      
+  writel( fcChip->ERQ->length,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH));
+     
+
+  fcChip->ERQ->producerIndex = 0L;
+  writel( fcChip->ERQ->producerIndex,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX));
+      
+
+		// NOTE! write consumer index last, since the write
+		// causes Tachyon to process the other registers
+
+  ulAddr = virt_to_bus( &fcChip->ERQ->consumerIndex);
+
+  // NOTE! Tachyon DMAs to the ERQ consumer Index host
+		// address; must be correctly aligned
+  writel( (ULONG)ulAddr,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR));
+
+
+
+				 // IIIIIIIIIIIII  INBOUND MESSAGE QUEUE
+				 // Tell Tachyon where the Que starts
+
+  // set the Host's pointer for Tachyon to access
+
+  printk("  cpqfcTS: writing IMQ BASE %Xh  ", fcChip->IMQ->base );
+  writel( fcChip->IMQ->base, 
+    (fcChip->Registers.ReMapMemBase + IMQ_BASE));
+
+  writel( fcChip->IMQ->length,
+    (fcChip->Registers.ReMapMemBase + IMQ_LENGTH));
+
+  writel( fcChip->IMQ->consumerIndex,
+    (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+
+
+		// NOTE: TachLite DMAs to the producerIndex host address
+		// must be correctly aligned with address bits 1-0 cleared
+    // Writing the BASE register clears the PI register, so write it last
+  ulAddr = virt_to_bus( &fcChip->IMQ->producerIndex);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+//#if DBG
+  printk("  PI %Xh\n", (ULONG)ulAddr );
+//#endif
+  writel( (ULONG)ulAddr, 
+    (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX));
+
+
+
+				 // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE
+				 // Tell TachLite where the Que starts
+
+  writel( fcChip->SFQ->base, 
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE));
+
+  writel( fcChip->SFQ->length,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH));
+
+
+         // tell TachLite where SEST table is & how long
+  writel( fcChip->SEST->base,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE));
+
+  printk("  cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n",
+    fcChip->SEST, fcChip->SEST->base, 
+    fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE);
+
+  writel( fcChip->SEST->length,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH));
+      
+  writel( (TL_EXT_SG_PAGE_COUNT-1),
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE));
+
+
+  LEAVE("CreateTachLiteQues");
+
+  return iStatus;
+}
+
+
+
+// function to return TachLite to Power On state
+// 1st - reset tachyon ('SOFT' reset)
+// others - future
+
+int CpqTsResetTachLite(void *pHBA, int type)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  ULONG ulBuff, i;
+  int ret_status=0; // def. success
+
+  ENTER("ResetTach");
+  
+  switch(type)
+  {
+
+    case CLEAR_FCPORTS:
+
+      // in case he was running previously, mask Tach's interrupt
+      writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+      
+     // de-allocate mem for any Logged in ports
+      // (e.g., our module is unloading)
+      // search the forward linked list, de-allocating
+      // the memory we allocated when the port was initially logged in
+      {
+        PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort;
+        PFC_LOGGEDIN_PORT ptr;
+//        printk("checking for allocated LoggedInPorts...\n");
+			
+        while( pLoggedInPort )
+        {
+          ptr = pLoggedInPort;
+          pLoggedInPort = ptr->pNextPort;
+//	  printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n",
+//			  ptr, ptr->port_id);
+          kfree( ptr );
+        }
+      }
+      // (continue resetting hardware...)
+
+    case 1:                   // RESTART Tachyon (power-up state)
+
+      // in case he was running previously, mask Tach's interrupt
+      writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+			      // turn OFF laser (NOTE: laser is turned
+                              // off during reset, because GPIO4 is cleared
+                              // to 0 by reset action - see TLUM, sec 7.22)
+                              // However, CPQ 64-bit HBAs have a "health
+                              // circuit" which keeps laser ON for a brief
+                              // period after it is turned off ( < 1s)
+      
+      fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0);
+  
+
+
+            // soft reset timing constraints require:
+            //   1. set RST to 1
+            //   2. read SOFTRST register 
+            //      (128 times per R. Callison code)
+            //   3. clear PCI ints
+            //   4. clear RST to 0
+      writel( 0xff000001L,
+        (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+        
+      for( i=0; i<128; i++)
+        ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST);
+
+        // clear the soft reset
+      for( i=0; i<8; i++)
+  	writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+
+               
+
+			       // clear out our copy of Tach regs,
+			       // because they must be invalid now,
+			       // since TachLite reset all his regs.
+      CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs
+      cpqfcTSClearLinkStatusCounters(fcChip);  // clear our s/w accumulators
+                               // lower bits give GBIC info
+      fcChip->Registers.TYstatus.value = 
+	              readl( fcChip->Registers.TYstatus.address );
+      break;
+
+/*
+    case 2:                   // freeze SCSI
+    case 3:                   // reset Outbound command que (ERQ)
+    case 4:                   // unfreeze OSM (Outbound Seq. Man.) 'er'
+    case 5:                   // report status
+
+    break;
+*/
+    default:
+      ret_status = -1;  // invalid option passed to RESET function
+      break;
+  }
+  LEAVE("ResetTach");
+  return ret_status;
+}
+
+
+
+
+
+
+// 'addrBase' is IOBaseU for both TachLite and (older) Tachyon
+int CpqTsLaserControl( void* addrBase, int opcode )
+{
+  ULONG dwBuff;
+
+  dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg
+                                                    // (change only bit 4)
+  if( opcode == 1)
+    dwBuff |= ~0xffffffefL; // set - ON
+  else
+    dwBuff &= 0xffffffefL;  // clear - OFF
+  writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg
+  return 0;
+}
+
+
+
+
+
+// Use controller's "Options" field to determine loopback mode (if any)
+//   internal loopback (silicon - no GBIC)
+//   external loopback (GBIC - no FC loop)
+//   no loopback: L_PORT, external cable from GBIC required
+
+int CpqTsInitializeFrameManager( void *pChip, int opcode)
+{
+  PTACHYON fcChip;
+  int iStatus;
+  ULONG wwnLo, wwnHi; // for readback verification
+
+  ENTER("InitializeFrameManager");
+  fcChip = (PTACHYON)pChip;
+  if( !fcChip->Registers.ReMapMemBase )   // undefined controller?
+    return -1;
+
+  // TL/TS UG, pg. 184
+  // 0x0065 = 100ms for RT_TOV
+  // 0x01f5 = 500ms for ED_TOV
+  // 0x07D1 = 2000ms 
+  fcChip->Registers.ed_tov.value = 0x006507D1; 
+  writel( fcChip->Registers.ed_tov.value,
+    (fcChip->Registers.ed_tov.address));
+      
+
+  // Set LP_TOV to the FC-AL2 specified 2 secs.
+  // TL/TS UG, pg. 185
+  writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+
+
+  // Now try to read the WWN from the adapter's NVRAM
+  iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ
+
+  if( iStatus )   // NVRAM read failed?
+  {
+    printk(" WARNING! HBA NVRAM WWN read failed - make alias\n");
+    // make up a WWN.  If NULL or duplicated on loop, FC loop may hang!
+
+
+    fcChip->Registers.wwn_hi = (__u32)jiffies;
+    fcChip->Registers.wwn_hi |= 0x50000000L;
+    fcChip->Registers.wwn_lo = 0x44556677L;
+  }
+
+  
+  writel( fcChip->Registers.wwn_hi, 
+	  fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI);
+  
+  writel( fcChip->Registers.wwn_lo, 
+	  fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+	  
+
+  // readback for verification:
+  wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI ); 
+          
+  wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+  // test for correct chip register WRITE/READ
+  DEBUG_PCI( printk("  WWN %08X%08X\n",
+    fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) );
+    
+  if( wwnHi != fcChip->Registers.wwn_hi ||
+      wwnLo != fcChip->Registers.wwn_lo )
+  {
+    printk( "cpqfcTS: WorldWideName register load failed\n");
+    return -1; // FAILED!
+  }
+
+
+
+			// set Frame Manager Initialize command
+  fcChip->Registers.FMcontrol.value = 0x06;
+
+  // Note: for test/debug purposes, we may use "Hard" address,
+  // but we completely support "soft" addressing, including
+  // dynamically changing our address.
+  if( fcChip->Options.intLoopback == 1 )            // internal loopback
+    fcChip->Registers.FMconfig.value = 0x0f002080L;
+  else if( fcChip->Options.extLoopback == 1 )            // internal loopback
+    fcChip->Registers.FMconfig.value = 0x0f004080L;
+  else                  // L_Port
+    fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+//    fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick)
+//    fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+		
+  // write config to FM
+
+  if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback )
+                               // (also need LASER for real LOOP)
+    fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER
+
+  writel( fcChip->Registers.FMconfig.value,
+    fcChip->Registers.FMconfig.address);
+    
+
+			       // issue INITIALIZE command to FM - ACTION!
+  writel( fcChip->Registers.FMcontrol.value,
+    fcChip->Registers.FMcontrol.address);
+    
+  LEAVE("InitializeFrameManager");
+  
+  return 0;
+}
+
+
+
+
+
+// This "look ahead" function examines the IMQ for occurence of
+// "type".  Returns 1 if found, 0 if not.
+static int PeekIMQEntry( PTACHYON fcChip, ULONG type)
+{
+  ULONG CI = fcChip->IMQ->consumerIndex;
+  ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes
+  
+  while( CI != PI )
+  {                             // proceed with search
+    if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check
+    
+    switch( type )
+    {
+      case ELS_LILP_FRAME:
+      {
+      // first, we need to find an Inbound Completion message,
+      // If we find it, check the incoming frame payload (1st word)
+      // for LILP frame
+        if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 )
+        { 
+          TachFCHDR_GCMND* fchs;
+          ULONG ulFibreFrame[2048/4];  // max DWORDS in incoming FC Frame
+	  USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL);
+
+	  CpqTsGetSFQEntry( fcChip,
+            SFQpi,        // SFQ producer ndx         
+	    ulFibreFrame, // contiguous dest. buffer
+	    FALSE);       // DON'T update chip--this is a "lookahead"
+          
+	  fchs = (TachFCHDR_GCMND*)&ulFibreFrame;
+          if( fchs->pl[0] == ELS_LILP_FRAME)
+	  {
+            return 1; // found the LILP frame!
+	  }
+	  else
+	  {
+	    // keep looking...
+	  }
+	}  
+      }
+      break;
+
+      case OUTBOUND_COMPLETION:
+        if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 )
+	{
+
+          // any OCM errors?
+          if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L )
+            return 1;   	    // found OCM error
+	}
+      break;
+
+
+      
+      default:
+      break;
+    }
+  }
+  return 0; // failed to find "type"
+}
+
+			
+static void SetTachTOV( CPQFCHBA* cpqfcHBAdata)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip; 
+  
+  // TL/TS UG, pg. 184
+  // 0x0065 = 100ms for RT_TOV
+  // 0x01f5 = 500ms for ED_TOV
+  // 0x07d1 = 2000ms for ED_TOV
+
+  // SANMark Level 1 requires an "initialization backoff"
+  // (See "SANMark Test Suite Level 1":
+  // initialization_timeout.fcal.SANMark-1.fc)
+  // We have to use 2sec, 24sec, then 128sec when login/
+  // port discovery processes fail to complete.
+  
+  // when port discovery completes (logins done), we set
+  // ED_TOV to 500ms -- this is the normal operational case
+  // On the first Link Down, we'll move to 2 secs (7D1 ms)
+  if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5)
+    fcChip->Registers.ed_tov.value = 0x006507D1; 
+  
+  // If we get another LST after we moved TOV to 2 sec,
+  // increase to 24 seconds (5DC1 ms) per SANMark!
+  else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1)
+    fcChip->Registers.ed_tov.value = 0x00655DC1; 
+
+  // If we get still another LST, set the max TOV (Tachyon
+  // has only 16 bits for ms timer, so the max is 65.5 sec)
+  else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1)
+    fcChip->Registers.ed_tov.value = 0x0065FFFF; 
+
+  writel( fcChip->Registers.ed_tov.value,
+    (fcChip->Registers.ed_tov.address));
+  // keep the same 2sec LP_TOV 
+  writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+}	
+
+
+// The IMQ is an array with IMQ_LEN length, each element (QEntry)
+// with eight 32-bit words.  Tachyon PRODUCES a QEntry with each
+// message it wants to send to the host.  The host CONSUMES IMQ entries
+
+// This function copies the current
+// (or oldest not-yet-processed) QEntry to
+// the caller, clears/ re-enables the interrupt, and updates the
+// (Host) Consumer Index.
+// Return value:
+//  0   message processed, none remain (producer and consumer
+//        indexes match)
+//  1   message processed, more messages remain
+// -1   no message processed - none were available to process
+// Remarks:
+//   TL/TS UG specifices that the following actions for
+//   INTA_L handling:
+//   1. read PCI Interrupt Status register (0xff)
+//   2. all IMQ messages should be processed before writing the
+//      IMQ consumer index.
+
+
+int CpqTsProcessIMQEntry(void *host)
+{
+  struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip; 
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  int iStatus;
+  USHORT i, RPCset, DPCset;
+  ULONG x_ID;
+  ULONG ulBuff, dwStatus;
+  TachFCHDR_GCMND* fchs;
+  ULONG ulFibreFrame[2048/4];  // max number of DWORDS in incoming Fibre Frame
+  UCHAR ucInboundMessageType;  // Inbound CM, dword 3 "type" field
+
+  ENTER("ProcessIMQEntry");
+   
+
+				// check TachLite's IMQ producer index -
+				// is a new message waiting for us?
+				// equal indexes means empty que
+
+  if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex )
+  {                             // need to process message
+
+
+#ifdef IMQ_DEBUG
+    printk("PI %X, CI %X  type: %X\n", 
+      fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex,
+      fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type);
+#endif                                
+    // Examine Completion Messages in IMQ
+    // what CM_Type?
+    switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type
+                    & 0xffL) )
+    {
+    case OUTBOUND_COMPLETION:
+
+      // Remarks:
+      // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries
+      // (starting at 0), and SFS entries (starting at
+      // SEST_LEN -- outside the SEST space).
+      // Psuedo code:
+      // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index
+      // range check - x_ID
+      //   if x_ID outside 'Transactions' length, error - exit
+      // if any OCM error, copy error status to Exchange slot
+      // if FCP ASSIST transaction (x_ID within SEST),
+      //   call fcComplete (to App)
+      // ...
+
+
+      ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1];
+      x_ID = ulBuff & 0x7fffL;     // lower 14 bits SEST_Index/Trans_ID
+                                     // Range check CM OX/RX_ID value...
+      if( x_ID < TACH_MAX_XID )   // don't go beyond array space
+      {
+
+
+	if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete?
+          RPCset = 1;              // (SEST transactions only)
+        else
+          RPCset = 0;
+
+        if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete?
+          DPCset = 1;              // (SEST transactions only)
+        else
+          DPCset = 0;
+                // set the status for this Outbound transaction's ID
+        dwStatus = 0L;
+        if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error)
+            dwStatus |= SESTPROG_ERR;
+
+        ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2];
+        if( ulBuff & 0x7a000000L ) // any other errs?
+        {
+          if( ulBuff & 0x40000000L )
+            dwStatus |= INV_ENTRY;
+          if( ulBuff & 0x20000000L )
+            dwStatus |= FRAME_TO;        // FTO
+          if( ulBuff & 0x10000000L )
+            dwStatus |= HOSTPROG_ERR;
+          if( ulBuff & 0x08000000L )
+            dwStatus |= LINKFAIL_TX;
+          if( ulBuff & 0x02000000L )
+            dwStatus |= ABORTSEQ_NOTIFY;  // ASN
+        }
+
+	  
+	if( dwStatus )          // any errors?
+        {
+                  // set the Outbound Completion status
+          Exchanges->fcExchange[ x_ID ].status |= dwStatus;
+
+          // if this Outbound frame was for a SEST entry, automatically
+          // reque it in the case of LINKFAIL (it will restart on PDISC)
+          if( x_ID < TACH_SEST_LEN )
+          {
+
+            printk(" #OCM error %Xh x_ID %X# ", 
+		    dwStatus, x_ID);
+
+	    Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default
+                                                 
+
+	    // We Q ABTS for each exchange.
+	    // NOTE: We can get FRAME_TO on bad alpa (device gone).  Since
+	    // bad alpa is reported before FRAME_TO, examine the status
+	    // flags to see if the device is removed.  If so, DON'T
+	    // post an ABTS, since it will be terminated by the bad alpa
+	    // message.
+	    if( dwStatus & FRAME_TO ) // check for device removed...
+	    {
+	      if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) )
+	      { 
+		// presumes device is still there: send ABTS.
+  
+                cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+	      }
+	    }
+	    else  // Abort all other errors
+	    {
+              cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+	    }
+
+            // if the HPE bit is set, we have to CLose the LOOP
+            // (see TL/TS UG, pg. 239)
+
+            if( dwStatus &= HOSTPROG_ERR )
+            // set CL bit (see TL/TS UG, pg. 172)
+              writel( 4, fcChip->Registers.FMcontrol.address);
+          }
+        }
+          // NOTE: we don't necessarily care about ALL completion messages...
+                                      // SCSI resp. complete OR
+        if( ((x_ID < TACH_SEST_LEN) && RPCset)|| 
+             (x_ID >= TACH_SEST_LEN) )  // non-SCSI command
+        {
+              // exchange done; complete to upper levels with status
+              // (if necessary) and free the exchange slot
+            
+
+          if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+                                    // A Request or Reply has been sent
+          {                         // signal waiting WorkerThread
+
+            up( cpqfcHBAdata->TYOBcomplete);   // frame is OUT of Tach
+
+                                    // WorkerThread will complete Xchng
+          }
+          else  // X_ID is for FCP assist (SEST)
+          {
+              // TBD (target mode)
+//            fcCompleteExchange( fcChip, x_ID); // TRE completed
+          }
+        }
+      }
+      else  // ERROR CONDITION!  bogus x_ID in completion message
+      {
+
+        printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID);
+
+      }
+
+
+
+          // Load the Frame Manager's error counters.  We check them here
+          // because presumably the link is up and healthy enough for the
+          // counters to be meaningful (i.e., don't check them while loop
+          // is initializing).
+      fcChip->Registers.FMLinkStatus1.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus1.address);
+                  
+      fcChip->Registers.FMLinkStatus2.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus2.address);
+            
+
+      fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+    break;
+
+
+
+    case ERROR_IDLE_COMPLETION:  // TachLite Error Idle...
+    
+    // We usually get this when the link goes down during heavy traffic.
+    // For now, presume that if SEST Exchanges are open, we will
+    // get this as our cue to INVALIDATE all SEST entries
+    // (and we OWN all the SEST entries).
+    // See TL/TS UG, pg. 53
+    
+      for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+      {
+
+        // Does this VALid SEST entry need to be invalidated for Abort?
+        fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; 
+      }
+      
+      CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK
+
+    break;
+
+
+    case INBOUND_SFS_COMPLETION:  //0x04
+          // NOTE! we must process this SFQ message to avoid SFQ filling
+          // up and stopping TachLite.  Incoming commands are placed here,
+          // as well as 'unknown' frames (e.g. LIP loop position data)
+          // write this CM's producer index to global...
+          // TL/TS UG, pg 234:
+          // Type: 0 - reserved
+          //       1 - Unassisted FCP
+          //       2 - BAD FCP
+          //       3 - Unkown Frame
+          //       4-F reserved
+
+
+      fcChip->SFQ->producerIndex = (USHORT)
+        (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL);
+
+
+      ucInboundMessageType = 0;  // default to useless frame
+
+        // we can only process two Types: 1, Unassisted FCP, and 3, Unknown
+        // Also, we aren't interested in processing frame fragments
+        // so don't Que anything with 'LKF' bit set
+      if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] 
+        & 0x40000000) )  // 'LKF' link failure bit clear?
+      {
+        ucInboundMessageType = (UCHAR)  // ICM DWord3, "Type"
+        (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL);
+      }
+      else
+      {
+	fcChip->fcStats.linkFailRX++;
+//        printk("LKF (link failure) bit set on inbound message\n");
+      }
+
+          // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff
+      CpqTsGetSFQEntry(
+        fcChip,                  // i.e. this Device Object
+        (USHORT)fcChip->SFQ->producerIndex,  // SFQ producer ndx         
+        ulFibreFrame, TRUE);    // contiguous destination buffer, update chip
+                     
+        // analyze the incoming frame outside the INT handler...
+        // (i.e., Worker)
+
+      if( ucInboundMessageType == 1 )
+      {
+        fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame
+        // don't fill up our Q with garbage - only accept FCP-CMND  
+        // or XRDY frames
+        if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND
+        {
+	  // someone sent us a SCSI command
+	  
+//          fcPutScsiQue( cpqfcHBAdata, 
+//                        SFQ_UNASSISTED_FCP, ulFibreFrame); 
+	}
+	else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status)
+            (fchs->d_id & 0xFF000000) == 0x05000000 )  // XRDY  
+	{
+	  ULONG x_ID;
+	  // Unfortunately, ABTS requires a Freeze on the chip so
+	  // we can modify the shared memory SEST.  When frozen,
+	  // any received Exchange frames cannot be processed by
+	  // Tachyon, so they will be dumped in here.  It is too
+	  // complex to attempt the reconstruct these frames in
+	  // the correct Exchange context, so we simply seek to
+	  // find status or transfer ready frames, and cause the
+	  // exchange to complete with errors before the timeout
+	  // expires.  We use a Linux Scsi Cmnd result code that
+	  // causes immediate retry.
+	  
+
+	  // Do we have an open exchange that matches this s_id
+	  // and ox_id?
+	  for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+	  {
+            if( (fchs->s_id & 0xFFFFFF) == 
+                 (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF) 
+		       &&
+                (fchs->ox_rx_id & 0xFFFF0000) == 
+                 (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) )
+	    {
+    //          printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id );
+              // simulate the anticipated error - since the
+	      // SEST was frozen, frames were lost...
+              Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME;
+              
+	      // presumes device is still there: send ABTS.
+              cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+	      break;  // done
+	    }
+	  }
+	}
+	  
+      }
+          
+      else if( ucInboundMessageType == 3)
+      {
+        // FC Link Service frames (e.g. PLOGI, ACC) come in here.  
+        cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame); 
+                          
+      }
+
+      else if( ucInboundMessageType == 2 ) // "bad FCP"?
+      {
+#ifdef IMQ_DEBUG
+        printk("Bad FCP incoming frame discarded\n");
+#endif
+      }
+
+      else // don't know this type
+      {
+#ifdef IMQ_DEBUG 
+        printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType);
+#endif
+      }
+        
+        // Check the Frame Manager's error counters.  We check them here
+        // because presumably the link is up and healthy enough for the
+        // counters to be meaningful (i.e., don't check them while loop
+        // is initializing).
+      fcChip->Registers.FMLinkStatus1.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus1.address);
+                  
+
+      fcChip->Registers.FMLinkStatus2.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus2.address);
+                
+
+      break;
+
+
+
+
+                    // We get this CM because we issued a freeze
+                    // command to stop outbound frames.  We issue the
+                    // freeze command at Link Up time; when this message
+                    // is received, the ERQ base can be switched and PDISC
+                    // frames can be sent.
+
+      
+    case ERQ_FROZEN_COMPLETION:  // note: expect ERQ followed immediately
+                                 // by FCP when freezing TL
+      fcChip->Registers.TYstatus.value =         // read what's frozen
+        readl(fcChip->Registers.TYstatus.address);
+      // (do nothing; wait for FCP frozen message)
+      break;
+    case FCP_FROZEN_COMPLETION:
+      
+      fcChip->Registers.TYstatus.value =         // read what's frozen
+        readl(fcChip->Registers.TYstatus.address);
+      
+      // Signal the kernel thread to proceed with SEST modification
+      up( cpqfcHBAdata->TachFrozen);
+
+      break;
+
+
+
+    case INBOUND_C1_TIMEOUT:
+    case MFS_BUF_WARN:
+    case IMQ_BUF_WARN:
+    break;
+
+
+
+
+
+        // In older Tachyons, we 'clear' the internal 'core' interrupt state
+        // by reading the FMstatus register.  In newer TachLite (Tachyon),
+        // we must WRITE the register
+        // to clear the condition (TL/TS UG, pg 179)
+    case FRAME_MGR_INTERRUPT:
+    {
+      PFC_LOGGEDIN_PORT pLoggedInPort; 
+
+      fcChip->Registers.FMstatus.value = 
+        readl( fcChip->Registers.FMstatus.address );
+                
+      // PROBLEM: It is possible, especially with "dumb" hubs that
+      // don't automatically LIP on by-pass of ports that are going
+      // away, for the hub by-pass process to destroy critical 
+      // ordered sets of a frame.  The result of this is a hung LPSM
+      // (Loop Port State Machine), which on Tachyon results in a
+      // (default 2 sec) Loop State Timeout (LST) FM message.  We 
+      // want to avoid this relatively huge timeout by detecting
+      // likely scenarios which will result in LST.
+      // To do this, we could examine FMstatus for Loss of Synchronization
+      // and/or Elastic Store (ES) errors.  Of these, Elastic Store is better
+      // because we get this indication more quickly than the LOS.
+      // Not all ES errors are harmfull, so we don't want to LIP on every
+      // ES.  Instead, on every ES, detect whether our LPSM in in one
+      // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE,
+      // or RECEIVED CLOSE.  (See TL/TS UG, pg. 181)
+      // If any of these LPSM states are detected
+      // in combination with the LIP while LDn is not set, 
+      // send an FM init (LIP F7,F7 for loops)!
+      // It is critical to the physical link stability NOT to reset (LIP)
+      // more than absolutely necessary; this is a basic premise of the
+      // SANMark level 1 spec.
+      {
+	ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4;
+	
+	if( (fcChip->Registers.FMstatus.value & 0x400)  // ElasticStore?
+                      &&
+            !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn
+	              &&
+            !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF
+	{
+	  if( (Lpsm != 0) || // not MONITORING? or
+	      !(Lpsm & 0x8) )// not already offline?
+	  {
+	  // now check the particular LST states...
+            if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) ||
+	      (Lpsm == OPENED)      || (Lpsm == XMITTD_CLOSE) ||
+	      (Lpsm == RCVD_CLOSE) )
+	    {
+	      // re-init the loop before it hangs itself!
+              printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm);
+
+
+	      fcChip->fcStats.FMinits++;
+              writel( 6, fcChip->Registers.FMcontrol.address); // LIP
+	    }
+	  }
+	}
+	else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST?
+	{
+          printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm);
+	 
+          fcChip->fcStats.FMinits++;
+          writel( 6, fcChip->Registers.FMcontrol.address);  // LIP
+	}  
+      }
+
+
+      // clear only the 'interrupting' type bits for this REG read
+      writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L),
+        fcChip->Registers.FMstatus.address);
+                          
+
+               // copy frame manager status to unused ULONG slot
+      fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] =
+          fcChip->Registers.FMstatus.value; // (for debugging)
+
+
+          // Load the Frame Manager's error counters.  We check them here
+          // because presumably the link is up and healthy enough for the
+          // counters to be meaningful (i.e., don't check them while loop
+          // is initializing).
+      fcChip->Registers.FMLinkStatus1.value =   // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus1.address);
+            
+      fcChip->Registers.FMLinkStatus2.value =   // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus2.address);
+          
+          // Get FM BB_Credit Zero Reg - does not clear on READ
+      fcChip->Registers.FMBB_CreditZero.value =   // get TL's counter
+        readl(fcChip->Registers.FMBB_CreditZero.address);
+            
+
+
+      fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+
+
+               // LINK DOWN
+
+      if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit
+      {                                 
+	
+#ifdef IMQ_DEBUG
+        printk("LinkDn\n");
+#endif
+        printk(" #LDn# ");
+        
+        fcChip->fcStats.linkDown++;
+        
+	SetTachTOV( cpqfcHBAdata);  // must set according to SANMark
+
+	// Check the ERQ - force it to be "empty" to prevent Tach
+	// from sending out frames before we do logins.
+
+
+  	if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex)
+	{
+//	  printk("#ERQ PI != CI#");
+          CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only	  
+	  fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0;
+ 	  writel( fcChip->ERQ->base, 
+	    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+          // re-writing base forces ERQ PI to equal CI
+  
+	}
+		
+	// link down transition occurred -- port_ids can change
+        // on next LinkUp, so we must invalidate current logins
+        // (and any I/O in progress) until PDISC or PLOGI/PRLI
+        // completes
+        {
+          pLoggedInPort = &fcChip->fcPorts; 
+          while( pLoggedInPort ) // for all ports which are expecting
+                                 // PDISC after the next LIP, set the
+                                 // logoutTimer
+          {
+
+	    if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+            {
+              pLoggedInPort->LOGO_timer = 3;  // we want 2 seconds
+                                              // but Timer granularity
+                                              // is 1 second
+            }
+                                // suspend any I/O in progress until
+                                // PDISC received...
+            pLoggedInPort->prli = FALSE;   // block FCP-SCSI commands
+	    
+            pLoggedInPort = pLoggedInPort->pNextPort;
+          }  // ... all Previously known ports checked
+        }
+        
+	// since any hot plugging device may NOT support LILP frames
+	// (such as early Tachyon chips), clear this flag indicating
+	// we shouldn't use (our copy of) a LILP map.
+	// If we receive an LILP frame, we'll set it again.
+	fcChip->Options.LILPin = 0; // our LILPmap is invalid
+        cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports!
+
+          // also, we want to invalidate (i.e. INITIATOR_ABORT) any
+          // open Login exchanges, in case the LinkDown happened in the
+          // middle of logins.  It's possible that some ports already
+          // ACCepted login commands which we have not processed before
+          // another LinkDown occured.  Any accepted Login exhanges are
+          // invalidated by LinkDown, even before they are acknowledged.
+          // It's also possible for a port to have a Queued Reply or Request
+          // for login which was interrupted by LinkDown; it may come later,
+          // but it will be unacceptable to us.
+
+          // we must scan the entire exchange space, find every Login type
+          // originated by us, and abort it. This is NOT an abort due to
+          // timeout, so we don't actually send abort to the other port -
+          // we just complete it to free up the fcExchange slot.
+
+        for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++)
+        {                     // looking for Extended Link Serv.Exchanges
+          if( Exchanges->fcExchange[i].type == ELS_PDISC ||
+              Exchanges->fcExchange[i].type == ELS_PLOGI ||
+              Exchanges->fcExchange[i].type == ELS_PRLI ) 
+          {
+              // ABORT the exchange!
+#ifdef IMQ_DEBUG
+            printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n",
+              i, Exchanges->fcExchange[i].type,
+            Exchanges->fcExchange[i].fchs.d_id);
+#endif
+
+            Exchanges->fcExchange[i].status |= INITIATOR_ABORT;
+            cpqfcTSCompleteExchange( fcChip, i); // abort on LDn
+          }
+        }
+
+      }
+
+             // ################   LINK UP   ##################
+      if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit
+      {                                 // AL_PA could have changed
+
+          // We need the following code, duplicated from LinkDn condition,
+          // because it's possible for the Tachyon to re-initialize (hard
+          // reset) without ever getting a LinkDn indication.
+        pLoggedInPort = &fcChip->fcPorts; 
+        while( pLoggedInPort )   // for all ports which are expecting
+                                 // PDISC after the next LIP, set the
+                                 // logoutTimer
+        {
+          if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+          {
+            pLoggedInPort->LOGO_timer = 3;  // we want 2 seconds
+                                              // but Timer granularity
+                                              // is 1 second
+             
+                                  // suspend any I/O in progress until
+                                  // PDISC received...
+
+          }
+          pLoggedInPort = pLoggedInPort->pNextPort;
+        }  // ... all Previously known ports checked
+ 
+          // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA)
+        fcChip->Registers.rcv_al_pa.value = 
+          readl(fcChip->Registers.rcv_al_pa.address);
+ 
+	// Now, if our acquired address is DIFFERENT from our
+        // previous one, we are not allow to do PDISC - we
+        // must go back to PLOGI, which will terminate I/O in
+        // progress for ALL logged in FC devices...
+	// (This is highly unlikely).
+
+	if( (fcChip->Registers.my_al_pa & 0xFF) != 
+	    ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) )
+	{
+
+//	  printk(" #our HBA port_id changed!# "); // FC port_id changed!!	
+
+	  pLoggedInPort = &fcChip->fcPorts; 
+          while( pLoggedInPort ) // for all ports which are expecting
+                                 // PDISC after the next LIP, set the
+                                 // logoutTimer
+          {
+	    pLoggedInPort->pdisc  = FALSE;
+            pLoggedInPort->prli = FALSE;
+            pLoggedInPort = pLoggedInPort->pNextPort;
+          }  // ... all Previously known ports checked
+
+	  // when the port_id changes, we must terminate
+	  // all open exchanges.
+          cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED);
+
+	}
+	               
+	// Replace the entire 24-bit port_id.  We only know the
+	// lower 8 bits (alpa) from Tachyon; if a FLOGI is done,
+	// we'll get the upper 16-bits from the FLOGI ACC frame.
+	// If someone plugs into Fabric switch, we'll do FLOGI and
+	// get full 24-bit port_id; someone could then remove and
+	// hot-plug us into a dumb hub.  If we send a 24-bit PLOGI
+	// to a "private" loop device, it might blow up.
+	// Consequently, we force the upper 16-bits of port_id to
+	// be re-set on every LinkUp transition
+        fcChip->Registers.my_al_pa =
+          (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF;
+
+              
+              // copy frame manager status to unused ULONG slot
+        fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+          fcChip->Registers.my_al_pa; // (for debugging)
+
+              // for TachLite, we need to write the acquired al_pa
+              // back into the FMconfig register, because after
+              // first initialization, the AQ (prev. acq.) bit gets
+              // set, causing TL FM to use the AL_PA field in FMconfig.
+              // (In Tachyon, FM writes the acquired AL_PA for us.)
+        ulBuff = readl( fcChip->Registers.FMconfig.address);
+        ulBuff &= 0x00ffffffL;  // mask out current al_pa
+        ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa
+        fcChip->Registers.FMconfig.value = ulBuff; // copy it back
+        writel( fcChip->Registers.FMconfig.value,  // put in TachLite
+          fcChip->Registers.FMconfig.address);
+            
+
+#ifdef IMQ_DEBUG
+        printk("#LUp %Xh, FMstat 0x%08X#", 
+		fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value);
+#endif
+
+              // also set the WRITE-ONLY My_ID Register (for Fabric
+              // initialization)
+        writel( fcChip->Registers.my_al_pa,
+          fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID);
+          
+
+        fcChip->fcStats.linkUp++;
+
+                                     // reset TL statistics counters
+                                     // (we ignore these error counters
+                                     // while link is down)
+        ulBuff =                     // just reset TL's counter
+                 readl( fcChip->Registers.FMLinkStatus1.address);
+          
+        ulBuff =                     // just reset TL's counter
+                 readl( fcChip->Registers.FMLinkStatus2.address);
+
+          // for initiator, need to start verifying ports (e.g. PDISC)
+
+
+
+         
+      
+      
+	CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK
+	
+	// Tachyon creates an interesting problem for us on LILP frames.
+	// Instead of writing the incoming LILP frame into the SFQ before
+	// indicating LINK UP (the actual order of events), Tachyon tells
+	// us LINK UP, and later us the LILP.  So we delay, then examine the
+	// IMQ for an Inbound CM (x04); if found, we can set
+	// LINKACTIVE after processing the LILP.  Otherwise, just proceed.
+	// Since Tachyon imposes this time delay (and doesn't tell us
+	// what it is), we have to impose a delay before "Peeking" the IMQ
+	// for Tach hardware (DMA) delivery.
+	// Processing LILP is required by SANMark
+	udelay( 1000);  // microsec delay waiting for LILP (if it comes)
+        if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) )
+	{  // found SFQ LILP, which will post LINKACTIVE	  
+//	  printk("skipping LINKACTIVE post\n");
+
+	}
+	else
+          cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame);  
+      }
+
+
+
+      // ******* Set Fabric Login indication ********
+      if( fcChip->Registers.FMstatus.value & 0x2000 )
+      {
+	printk(" #Fabric# ");
+        fcChip->Options.fabric = 1;
+      }
+      else
+        fcChip->Options.fabric = 0;
+
+      
+      
+                             // ******* LIP(F8,x) or BAD AL_PA? ********
+      if( fcChip->Registers.FMstatus.value & 0x30000L )
+      {
+                        // copy the error AL_PAs
+        fcChip->Registers.rcv_al_pa.value = 
+          readl(fcChip->Registers.rcv_al_pa.address);
+            
+                        // Bad AL_PA?
+        if( fcChip->Registers.FMstatus.value & 0x10000L )
+        {
+          PFC_LOGGEDIN_PORT pLoggedInPort;
+        
+                       // copy "BAD" al_pa field
+          fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+              (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8;
+
+	  pLoggedInPort = fcFindLoggedInPort( fcChip,
+            NULL,     // DON'T search Scsi Nexus
+            fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id
+            NULL,     // DON'T search linked list for FC WWN
+            NULL);    // DON'T care about end of list
+ 
+	  if( pLoggedInPort )
+	  {
+            // Just in case we got this BAD_ALPA because a device
+	    // quietly disappeared (can happen on non-managed hubs such 
+	    // as the Vixel Rapport 1000),
+	    // do an Implicit Logout.  We never expect this on a Logged
+	    // in port (but do expect it on port discovery).
+	    // (As a reasonable alternative, this could be changed to 
+	    // simply start the implicit logout timer, giving the device
+	    // several seconds to "come back".)
+	    // 
+	    printk(" #BAD alpa %Xh# ",
+		   fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]);
+            cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+	  }
+        }
+                        // LIP(f8,x)?
+        if( fcChip->Registers.FMstatus.value & 0x20000L )
+        {
+                        // for debugging, copy al_pa field
+          fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] =
+              (fcChip->Registers.rcv_al_pa.value & 0xffL);
+                        // get the other port's al_pa
+                        // (one that sent LIP(F8,?) )
+        }
+      }
+
+                             // Elastic store err
+      if( fcChip->Registers.FMstatus.value & 0x400L )
+      {
+            // don't count e-s if loop is down!
+        if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) )
+          fcChip->fcStats.e_stores++;
+          
+      }
+    }
+    break;
+
+
+    case INBOUND_FCP_XCHG_COMPLETION:  // 0x0C
+
+    // Remarks:
+    // On Tachlite TL/TS, we get this message when the data phase
+    // of a SEST inbound transfer is complete.  For example, if a WRITE command
+    // was received with OX_ID 0, we might respond with XFER_RDY with
+    // RX_ID 8001.  This would start the SEST controlled data phases.  When
+    // all data frames are received, we get this inbound completion. This means
+    // we should send a status frame to complete the status phase of the 
+    // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data
+    // frames.
+    // See Outbound CM discussion of x_IDs
+    // Psuedo Code
+    //   Get SEST index (x_ID)
+    //     x_ID out of range, return (err condition)
+    //   set status bits from 2nd dword
+    //   free transactionID & SEST entry
+    //   call fcComplete with transactionID & status
+
+      ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0];
+      x_ID = ulBuff & 0x7fffL;  // lower 14 bits SEST_Index/Trans_ID
+                                // (mask out MSB "direction" bit)
+                                // Range check CM OX/RX_ID value...
+      if( x_ID < TACH_SEST_LEN )  // don't go beyond SEST array space
+      {
+
+//#define FCP_COMPLETION_DBG 1
+#ifdef FCP_COMPLETION_DBG
+        printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n", 
+          x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd);
+#endif
+        if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or -
+                                   // time to send response frame?
+          RPCset = 1;             // (SEST transaction)
+        else
+          RPCset = 0;
+                // set the status for this Inbound SCSI transaction's ID
+        dwStatus = 0L;
+        if( ulBuff & 0x70000000L ) // any errs?
+        {
+          
+          if( ulBuff & 0x40000000L )
+            dwStatus |= LINKFAIL_RX;
+          
+	  if( ulBuff & 0x20000000L )
+            dwStatus |= COUNT_ERROR;
+          
+          if( ulBuff & 0x10000000L )
+            dwStatus |= OVERFLOW;
+        }
+      
+	
+	  // FCP transaction done - copy status
+        Exchanges->fcExchange[ x_ID ].status = dwStatus;
+
+
+        // Did the exchange get an FCP-RSP response frame?
+        // (Note the little endian/big endian FC payload difference)
+
+        if( RPCset )             // SEST transaction Response frame rec'd
+        {
+    	  // complete the command in our driver...
+          cpqfcTSCompleteExchange( fcChip, x_ID);
+
+        }  // end "RPCset"
+	
+        else  // ("target" logic)
+        {
+            // Tachlite says all data frames have been received - now it's time
+            // to analyze data transfer (successful?), then send a response 
+            // frame for this exchange
+
+          ulFibreFrame[0] = x_ID; // copy for later reference
+
+          // if this was a TWE, we have to send satus response
+          if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE )
+	  {
+//            fcPutScsiQue( cpqfcHBAdata, 
+//                NEED_FCP_RSP, ulFibreFrame);  // (ulFibreFrame not used here)
+	  }
+        }
+      }
+      else  // ERROR CONDITION!  bogus x_ID in completion message
+      {
+        printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID);
+      }
+
+    break;
+
+
+
+
+    case INBOUND_SCSI_DATA_COMMAND:
+    case BAD_SCSI_FRAME:
+    case INB_SCSI_STATUS_COMPLETION:
+    case BUFFER_PROCESSED_COMPLETION:
+    break;
+    }
+
+					   // Tachyon is producing;
+					   // we are consuming
+    fcChip->IMQ->consumerIndex++;             // increment OUR consumerIndex
+    if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover
+      fcChip->IMQ->consumerIndex = 0L;        // reset it
+
+
+    if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex )
+    {                           // all Messages are processed -
+      iStatus = 0;              // no more messages to process
+
+    }
+    else
+      iStatus = 1;              // more messages to process
+
+    // update TachLite's ConsumerIndex... (clears INTA_L)
+    // NOTE: according to TL/TS UG, the 
+    // "host must return completion messages in sequential order".
+    // Does this mean one at a time, in the order received?  We
+    // presume so.
+
+    writel( fcChip->IMQ->consumerIndex,
+      (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+		    
+#if IMQ_DEBUG
+    printk("Process IMQ: writing consumer ndx %d\n ", 
+      fcChip->IMQ->consumerIndex);
+    printk("PI %X, CI %X\n", 
+    fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex );
+#endif
+  
+
+
+  }
+  else
+  {
+   // hmmm... why did we get interrupted/called with no message?
+    iStatus = -1;               // nothing to process
+#if IMQ_DEBUG
+    printk("Process IMQ: no message PI %Xh  CI %Xh", 
+      fcChip->IMQ->producerIndex,
+      fcChip->IMQ->consumerIndex);
+#endif
+  }
+
+  LEAVE("ProcessIMQEntry");
+  
+  return iStatus;
+}
+
+
+
+
+
+// This routine initializes Tachyon according to the following
+// options (opcode1):
+// 1 - RESTART Tachyon, simulate power on condition by shutting
+//     down laser, resetting the hardware, de-allocating all buffers;
+//     continue
+// 2 - Config Tachyon / PCI registers;
+//     continue
+// 3 - Allocating memory and setting Tachyon queues (write Tachyon regs);
+//     continue
+// 4 - Config frame manager registers, initialize, turn on laser
+//
+// Returns:
+//  -1 on fatal error
+//   0 on success
+
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  ULONG ulBuff;
+  UCHAR bBuff;
+  int iStatus=-1;  // assume failure
+
+  ENTER("InitializeTachLite");
+
+  // verify board's base address (sanity check)
+
+  if( !fcChip->Registers.ReMapMemBase)                // NULL address for card?
+    return -1;                         // FATAL error!
+
+
+
+  switch( opcode1 )
+  {
+    case 1:       // restore hardware to power-on (hard) restart
+
+
+      iStatus = fcChip->ResetTachyon( 
+		  cpqfcHBAdata, opcode2); // laser off, reset hardware
+				      // de-allocate aligned buffers
+
+
+/* TBD      // reset FC link Q (producer and consumer = 0)
+      fcLinkQReset(cpqfcHBAdata); 
+
+*/
+
+      if( iStatus )
+        break;
+
+    case 2:       // Config PCI/Tachyon registers
+      // NOTE: For Tach TL/TS, bit 31 must be set to 1.  For TS chips, a read
+      // of bit 31 indicates state of M66EN signal; if 1, chip may run at 
+      // 33-66MHz  (see TL/TS UG, pg 159)
+
+      ulBuff = 0x80000000;  // TachLite Configuration Register
+
+      writel( ulBuff, fcChip->Registers.TYconfig.address);
+//      ulBuff = 0x0147L;  // CpqTs PCI CFGCMD register
+//      WritePCIConfiguration( fcChip->Backplane.bus,
+//                           fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4);
+//      ulBuff = 0x0L;  // test!
+//      ReadPCIConfiguration( fcChip->Backplane.bus,
+//                           fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4);
+
+      // read back for reference...
+      fcChip->Registers.TYconfig.value = 
+         readl( fcChip->Registers.TYconfig.address );
+
+      // what is the PCI bus width?
+      pci_read_config_byte( cpqfcHBAdata->PciDev,
+                                0x43, // PCIMCTR offset
+                                &bBuff);
+      
+      fcChip->Registers.PCIMCTR = bBuff;
+
+      // set string identifying the chip on the circuit board
+
+      fcChip->Registers.TYstatus.value =
+        readl( fcChip->Registers.TYstatus.address);
+      
+      {
+// Now that we are supporting multiple boards, we need to change
+// this logic to check for PCI vendor/device IDs...
+// for now, quick & dirty is simply checking Chip rev
+	
+	ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5;
+	UCHAR Minor = (UCHAR)(RevId & 0x3);
+	UCHAR Major = (UCHAR)((RevId & 0x1C) >>2);
+  
+        printk("  HBA Tachyon RevId %d.%d\n", Major, Minor);
+  	if( (Major == 1) && (Minor == 2) )
+        {
+	  sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12);
+
+	}
+	else if( (Major == 1) && (Minor == 3) )
+        {
+	  sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13);
+	}
+	else if( (Major == 2) && (Minor == 1) )
+        {
+	  sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21);
+	}
+	else
+	  sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN);
+      }
+
+
+
+    case 3:       // allocate mem, set Tachyon Que registers
+      iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2);
+
+      // now that the Queues exist, Tach can DMA to them, so
+      // we can begin processing INTs
+      // INTEN register - enable INT (TachLite interrupt)
+      writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN);
+
+
+      if( iStatus )
+        break;
+
+
+    case 4:       // Config Fame Manager, Init Loop Command, laser on
+
+                 // L_PORT or loopback
+                 // depending on Options
+      iStatus = CpqTsInitializeFrameManager( fcChip,0 );
+      if( iStatus )
+      {
+           // failed to initialize Frame Manager
+	      break;
+      }
+
+    default:
+      break;
+  }
+  LEAVE("InitializeTachLite");
+  
+  return iStatus;
+}
+
+
+
+
+// Depending on the type of platform memory allocation (e.g. dynamic),
+// it's probably best to free memory in opposite order as it was allocated.
+// Order of allocation: see other function
+
+
+int CpqTsDestroyTachLiteQues( void *pHBA, int opcode)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  USHORT i, j, iStatus=0;
+  void* vPtr;  // mem Align manager sets this to the freed address on success
+  unsigned long ulPtr;  // for 64-bit pointer cast (e.g. Alpa machine)
+
+  ENTER("DestroyTachLiteQues");
+
+  if( fcChip->SEST )
+  {
+                // search out and free Pool for Extended S/G list pages
+
+    for( i=0, j=0; i < TACH_SEST_LEN; i++, j=0)  // for each exchange
+    {
+      // It's possible that extended S/G pages were allocated and
+      // not cleared due to error conditions or O/S driver termination.
+      // Make sure they're all gone.
+      while( fcChip->SEST->sgPages[i].PoolPage[j] &&
+        (j < TL_MAX_SGPAGES))
+        kfree( fcChip->SEST->sgPages[i].PoolPage[j++]);
+
+    }
+    ulPtr = (unsigned long)fcChip->SEST;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->SEST = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("SEST mem not freed\n");
+      iStatus = -1;
+    }
+  }
+
+  if( fcChip->SFQ )
+  {
+
+    ulPtr = (unsigned long)fcChip->SFQ;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->SFQ = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("SFQ mem not freed\n");
+      iStatus = -2;
+    }
+  }
+
+
+  if( fcChip->IMQ )
+  {
+      // clear Indexes to show empty Queue
+    fcChip->IMQ->producerIndex = 0;
+    fcChip->IMQ->consumerIndex = 0;
+
+    ulPtr = (unsigned long)fcChip->IMQ;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->IMQ = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("IMQ mem not freed\n");
+      iStatus = -3;
+    }
+  }
+
+  if( fcChip->ERQ )         // release memory blocks used by the queues
+  {
+    ulPtr = (unsigned long)fcChip->ERQ;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->ERQ = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("ERQ mem not freed\n");
+      iStatus = -4;
+    }
+  }
+    
+  // free up the primary EXCHANGES struct
+  if( fcChip->Exchanges != NULL)
+  {
+//    printk("kfree() on Exchanges @%p\n", fcChip->Exchanges);
+    kfree( fcChip->Exchanges);
+  }
+
+  // free up Link Q
+  if( cpqfcHBAdata->fcLQ != NULL )
+  {
+//    printk("kfree() on LinkQ @%p\n", fcChip->fcLQ);
+    kfree( cpqfcHBAdata->fcLQ);
+  }
+  
+  LEAVE("DestroyTachLiteQues");
+  
+  return iStatus;     // non-zero (failed) if any memory not freed
+}
+
+
+
+
+
+// The SFQ is an array with SFQ_LEN length, each element (QEntry)
+// with eight 32-bit words.  TachLite places incoming FC frames (i.e.
+// a valid FC frame with our AL_PA ) in contiguous SFQ entries
+// and sends a completion message telling the host where the frame is
+// in the que.
+// This function copies the current (or oldest not-yet-processed) QEntry to
+// a caller's contiguous buffer and updates the Tachyon chip's consumer index
+//
+// NOTE:
+//   An FC frame may consume one or many SFQ entries.  We know the total
+//   length from the completion message.  The caller passes a buffer large
+//   enough for the complete message (max 2k).
+
+static void CpqTsGetSFQEntry(
+         PTACHYON fcChip,
+         USHORT producerNdx,
+         ULONG *ulDestPtr,            // contiguous destination buffer
+	 BOOLEAN UpdateChip)
+{
+  ULONG total_bytes=0;
+  ULONG consumerIndex = fcChip->SFQ->consumerIndex;
+  
+				// check passed copy of SFQ producer index -
+				// is a new message waiting for us?
+				// equal indexes means SFS is copied
+
+  while( producerNdx != consumerIndex )
+  {                             // need to process message
+    total_bytes += 64;   // maintain count to prevent writing past buffer
+                   // don't allow copies over Fibre Channel defined length!
+    if( total_bytes <= 2048 )
+    {
+      memcpy( ulDestPtr, 
+              &fcChip->SFQ->QEntry[consumerIndex],
+              64 );  // each SFQ entry is 64 bytes
+      ulDestPtr += 16;   // advance pointer to next 64 byte block
+    }
+		         // Tachyon is producing,
+                         // and we are consuming
+
+    if( ++consumerIndex >= SFQ_LEN)// check for rollover
+      consumerIndex = 0L;        // reset it
+  }
+
+  // if specified, update the Tachlite chip ConsumerIndex...
+  if( UpdateChip )
+  {
+    fcChip->SFQ->consumerIndex = consumerIndex;
+    writel( fcChip->SFQ->consumerIndex,
+      fcChip->Registers.SFQconsumerIndex.address);
+  }
+}
+
+
+
+// TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO,
+// and Exchange Request Queue (ERQ) on error recover - 
+// (e.g. whenever a LIP occurs).  Here
+// we routinely RESUME by clearing these bits, but only if the loop is up
+// to avoid ERROR IDLE messages forever.
+
+void CpqTsUnFreezeTachlite( void *pChip, int type )
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+  fcChip->Registers.TYcontrol.value = 
+    readl(fcChip->Registers.TYcontrol.address);
+            
+  // (bit 4 of value is GBIC LASER)
+  // if we 'unfreeze' the core machines before the loop is healthy
+  // (i.e. FLT, OS, LS failure bits set in FMstatus)
+  // we can get 'error idle' messages forever.  Verify that
+  // FMstatus (Link Status) is OK before unfreezing.
+
+  if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear?
+      !(fcChip->Registers.FMstatus.value & 0x80  ))  // Active LPSM?
+  {
+    fcChip->Registers.TYcontrol.value &=  ~0x300L; // clear FEQ, FFA
+    if( type == 1 )  // unfreeze ERQ only
+    {
+//      printk("Unfreezing ERQ\n");
+      fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ
+    }
+    else             // unfreeze both ERQ and FCP-ASSIST (SEST)
+    {
+//      printk("Unfreezing ERQ & FCP-ASSIST\n");
+
+                     // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ
+      fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ
+    }
+
+    writel( fcChip->Registers.TYcontrol.value,
+      fcChip->Registers.TYcontrol.address);
+              
+  }
+          // readback for verify (TachLite still frozen?)
+  fcChip->Registers.TYstatus.value = 
+    readl(fcChip->Registers.TYstatus.address);
+}
+
+
+// Whenever an FC Exchange Abort is required, we must manipulate the
+// Host/Tachyon shared memory SEST table.  Before doing this, we
+// must freeze Tachyon, which flushes certain buffers and ensure we
+// can manipulate the SEST without contention.
+// This freeze function will result in FCP & ERQ FROZEN completion
+// messages (per argument "type").
+
+void CpqTsFreezeTachlite( void *pChip, int type )
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+  fcChip->Registers.TYcontrol.value = 
+    readl(fcChip->Registers.TYcontrol.address);
+    
+                     //set FFA, FEQ - freezes SCSI assist and ERQ
+  if( type == 1)    // freeze ERQ only
+    fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser)
+  else              // freeze both FCP assists (SEST) and ERQ
+    fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser)
+  
+  writel( fcChip->Registers.TYcontrol.value,
+    fcChip->Registers.TYcontrol.address);
+              
+}
+
+
+
+
+// TL has two Frame Manager Link Status Registers, with three 8-bit
+// fields each. These eight bit counters are cleared after each read,
+// so we define six 32-bit accumulators for these TL counters. This
+// function breaks out each 8-bit field and adds the value to the existing
+// sum.  (s/w counters cleared independently)
+
+void fcParseLinkStatusCounters(PTACHYON fcChip)
+{
+  UCHAR bBuff;
+  ULONG ulBuff;
+
+
+// The BB0 timer usually increments when TL is initialized, resulting
+// in an initially bogus count.  If our own counter is ZERO, it means we
+// are reading this thing for the first time, so we ignore the first count.
+// Also, reading the register does not clear it, so we have to keep an
+// additional static counter to detect rollover (yuk).
+
+  if( fcChip->fcStats.lastBB0timer == 0L)  // TL was reset? (ignore 1st values)
+  {
+                           // get TL's register counter - the "last" count
+    fcChip->fcStats.lastBB0timer = 
+      fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+  }
+  else  // subsequent pass - check for rollover
+  {
+                              // "this" count
+    ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+    if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened
+    {
+                                // counter advanced to max...
+      fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer);
+      fcChip->fcStats.BB0_Timer += ulBuff;  // plus some more
+
+
+    }
+    else // no rollover -- more counts or no change
+    {
+      fcChip->fcStats.BB0_Timer +=  (ulBuff - fcChip->fcStats.lastBB0timer);
+
+    }
+
+    fcChip->fcStats.lastBB0timer = ulBuff;
+  }
+
+
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24);
+  fcChip->fcStats.LossofSignal += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16);
+  fcChip->fcStats.BadRXChar += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8);
+  fcChip->fcStats.LossofSync += bBuff;
+
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24);
+  fcChip->fcStats.Rx_EOFa += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16);
+  fcChip->fcStats.Dis_Frm += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8);
+  fcChip->fcStats.Bad_CRC += bBuff;
+}
+
+
+void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip)
+{
+  ENTER("ClearLinkStatusCounters");
+  memset( &fcChip->fcStats, 0, sizeof( FCSTATS));
+  LEAVE("ClearLinkStatusCounters");
+
+}
+
+
+
+
+// The following function reads the I2C hardware to get the adapter's
+// World Wide Name (WWN).
+// If the WWN is "500805f1fadb43e8" (as printed on the card), the
+// Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register
+// is fadb43e8.
+// In the NVRAM, the bytes appear as:
+// [2d] ..
+// [2e] .. 
+// [2f] 50
+// [30] 08
+// [31] 05
+// [32] f1
+// [33] fa
+// [34] db
+// [35] 43
+// [36] e8
+//
+// In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will
+// be correctly loaded by Tachyon silicon.  In the login payload, bytes
+// must be correctly swapped for Big Endian format.
+
+int CpqTsReadWriteWWN( PVOID pChip, int Read)
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+  unsigned short i, count = NVRAM_SIZE;
+  UCHAR nvRam[NVRAM_SIZE], WWNbuf[8];
+  ULONG ulBuff;
+  int iStatus=-1;  // assume failure
+  int WWNoffset;
+
+  ENTER("ReadWriteWWN");
+  // Now try to read the WWN from the adapter's NVRAM
+
+  if( Read )  // READing NVRAM WWN?
+  {
+    ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address,
+                              fcChip->Registers.TYcontrol.address,
+                              count, &nvRam[0] );
+
+    if( ulBuff )   // NVRAM read successful?
+    {
+      iStatus = 0; // success!
+      
+                   // for engineering/ prototype boards, the data may be
+                   // invalid (GIGO, usually all "FF"); this prevents the
+                   // parse routine from working correctly, which means
+                   // nothing will be written to our passed buffer.
+
+      WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam );
+
+      if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly
+      {
+        printk( "CAUTION: Copying NVRAM data on fcChip\n");
+        for( i= 0; i < 8; i++)
+          WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work
+      }
+      
+      fcChip->Registers.wwn_hi = 0L;
+      fcChip->Registers.wwn_lo = 0L;
+      for( i=0; i<4; i++)  // WWN bytes are big endian in NVRAM
+      {
+        ulBuff = 0L;
+        ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i));
+        fcChip->Registers.wwn_hi |= ulBuff;
+      }
+      for( i=0; i<4; i++)  // WWN bytes are big endian in NVRAM
+      {
+        ulBuff = 0L;
+        ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i));
+        fcChip->Registers.wwn_lo |= ulBuff;
+      }
+    }  // done reading
+    else
+    {
+
+      printk( "cpqfcTS: NVRAM read failed\n");
+
+    }
+  }
+
+  else  // WRITE
+  {
+
+    // NOTE: WRITE not supported & not used in released driver.
+
+   
+    printk("ReadWriteNRAM: can't write NVRAM; aborting write\n");
+  }
+  
+  LEAVE("ReadWriteWWN");
+  return iStatus;
+}
+
+
+
+
+
+// The following function reads or writes the entire "NVRAM" contents of 
+// the I2C hardware (i.e. the NM24C03).  Note that HP's 5121A (TS 66Mhz)
+// adapter does not use the NM24C03 chip, so this function only works on
+// Compaq's adapters.
+
+int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read)
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+  ULONG ulBuff;
+  UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array
+  int iStatus=-1;  // assume failure
+
+     
+  if( Read )  // READing NVRAM?
+  {
+    ulBuff = cpqfcTS_ReadNVRAM(   // TRUE on success
+                fcChip->Registers.TYstatus.address,
+                fcChip->Registers.TYcontrol.address,
+                256,            // bytes to write
+                ucPtr );        // source ptr
+
+
+    if( ulBuff )
+      iStatus = 0; // success
+    else
+    {
+#ifdef DBG
+      printk( "CAUTION: NVRAM read failed\n");
+#endif
+    }
+  }  // done reading
+
+  else  // WRITING NVRAM 
+  {
+
+    printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n");
+  }
+    
+  return iStatus;
+}
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTSi2c.c linux/drivers/scsi/cpqfcTSi2c.c
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTSi2c.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTSi2c.c	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,494 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) 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.
+ * Written by Don Zimmerman
+*/
+// These functions control the NVRAM I2C hardware on 
+// non-intelligent Fibre Host Adapters.
+// The primary purpose is to read the HBA's NVRAM to get adapter's 
+// manufactured WWN to copy into Tachyon chip registers
+// Orignal source author unknown
+
+#include <linux/types.h>
+enum boolean { FALSE, TRUE } ;
+
+
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h>  // struct pt_regs for IRQ handler & Port I/O
+
+#include "cpqfcTSchip.h"
+
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data );
+/*static BOOLEAN tl_write_i2c_page_portion( void* GPIOin, void* GPIOout,
+  USHORT startOffset,  // e.g. 0x2f for WWN start
+  USHORT count,
+  UCHAR *buf );
+*/
+
+//
+// Tachlite GPIO2, GPIO3 (I2C) DEFINES
+// The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data)
+// GPIO2 drives SDA, and GPIO3 drives SCL
+// 
+// Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0
+// and clear writes 1. The input lines (read in TL status) is NOT inverted
+// This really helps confuse the code and debugging.
+
+#define SET_DATA_HI  0x0
+#define SET_DATA_LO  0x8
+#define SET_CLOCK_HI 0x0
+#define SET_CLOCK_LO 0x4
+
+#define SENSE_DATA_HI  0x8
+#define SENSE_DATA_LO  0x0
+#define SENSE_CLOCK_HI 0x4
+#define SENSE_CLOCK_LO 0x0
+
+#define SLAVE_READ_ADDRESS    0xA1
+#define SLAVE_WRITE_ADDRESS   0xA0
+					      
+
+static void i2c_delay(ULONG mstime);
+static void tl_i2c_clock_pulse( UCHAR , void* GPIOout);
+static UCHAR tl_read_i2c_data( void* );
+
+
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_RX_ACK
+//
+//      This routine receives an acknowledge over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_rx_ack( void* GPIOin, void* GPIOout )
+{
+  unsigned long value;
+
+	// do clock pulse, let data line float high
+  tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+	// slave must drive data low for acknowledge
+  value = tl_read_i2c_data( GPIOin);
+  if (value & SENSE_DATA_HI )
+    return( FALSE );
+
+  return( TRUE );
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   READ_I2C_REG
+//
+//      This routine reads the I2C control register using the global
+//      IO address stored in gpioreg.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_read_i2c_data( void* gpioreg )
+{
+  return( (UCHAR)(readl( gpioreg ) & 0x08L) ); // GPIO3
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   WRITE_I2C_REG
+//
+//      This routine writes the I2C control register using the global
+//      IO address stored in gpioreg.
+//      In Tachlite, we don't want to modify other bits in TL Control reg.
+//
+//-----------------------------------------------------------------------------
+static void tl_write_i2c_reg( void* gpioregOUT, UCHAR value )
+{
+  ULONG  temp;
+
+	// First read the register and clear out the old bits
+  temp = readl( gpioregOUT ) & 0xfffffff3L;
+
+	// Now or in the new data and send it back out
+  writel( temp | value, gpioregOUT);
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_TX_START
+//
+//      This routine transmits a start condition over the I2C bus.
+//      1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH,
+//      wait 5us to stabilize.
+//      2. With SCL still HIGH, drive SDA low.  The low transition marks
+//         the start condition to NM24Cxx (the chip)
+//      NOTE! In TL control reg., output 1 means chip sees LOW
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_tx_start( void* GPIOin, void* GPIOout )
+{
+  unsigned short i;
+  ULONG value;
+
+  if ( !(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI))
+  {
+    // start with clock high, let data float high
+    tl_write_i2c_reg(  GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+    // keep sending clock pulses if slave is driving data line
+    for (i = 0; i < 10; i++)
+    {
+      tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+      if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+	break;
+    }
+
+		// if he's still driving data low after 10 clocks, abort
+    value = tl_read_i2c_data( GPIOin ); // read status
+    if (!(value & 0x08) )
+      return( FALSE );
+  }
+
+
+	// To START, bring data low while clock high
+  tl_write_i2c_reg(  GPIOout, SET_CLOCK_HI | SET_DATA_LO );
+
+  i2c_delay(0);
+
+  return( TRUE );                           // TX start successful
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_TX_STOP
+//
+//      This routine transmits a stop condition over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+
+static unsigned short tl_i2c_tx_stop( void* GPIOin, void* GPIOout )
+{
+  int i;
+
+  for (i = 0; i < 10; i++) 
+  {
+  // Send clock pulse, drive data line low
+    tl_i2c_clock_pulse( SET_DATA_LO, GPIOout );
+
+  // To STOP, bring data high while clock high
+    tl_write_i2c_reg(  GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+  // Give the data line time to float high
+    i2c_delay(0);
+
+  // If slave is driving data line low, there's a problem; retry
+    if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+      return( TRUE );  // TX STOP successful!
+  }
+
+  return( FALSE );                      // error
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_TX_uchar
+//
+//      This routine transmits a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data )
+{
+  UCHAR bit;
+
+  for (bit = 0x80; bit; bit >>= 1)
+  {
+    if( data & bit )
+      tl_i2c_clock_pulse( (UCHAR)SET_DATA_HI, GPIOout);
+    else
+      tl_i2c_clock_pulse( (UCHAR)SET_DATA_LO, GPIOout);
+  }  
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_RX_uchar
+//
+//      This routine receives a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_i2c_rx_byte( void* GPIOin, void* GPIOout )
+{
+  UCHAR bit;
+  UCHAR data = 0;
+
+
+  for (bit = 0x80; bit; bit >>= 1) {
+    // do clock pulse, let data line float high
+    tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+    // read data line
+    if ( tl_read_i2c_data( GPIOin) & 0x08 )
+      data |= bit;
+  }
+
+  return (data);
+}
+//*****************************************************************************
+//*****************************************************************************
+// Function:   read_i2c_nvram
+// Arguments:  UCHAR count     number of bytes to read
+//             UCHAR *buf      area to store the bytes read
+// Returns:    0 - failed
+//             1 - success
+//*****************************************************************************
+//*****************************************************************************
+unsigned long cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+	UCHAR *buf )
+{
+  unsigned short i;
+
+  if( !( tl_i2c_tx_start(GPIOin, GPIOout) ))
+    return FALSE;
+
+  // Select the NVRAM for "dummy" write, to set the address
+  tl_i2c_tx_byte( GPIOout , SLAVE_WRITE_ADDRESS );
+  if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+    return( FALSE );
+
+  // Now send the address where we want to start reading  
+  tl_i2c_tx_byte( GPIOout , 0 );
+  if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+    return( FALSE );
+
+  // Send a repeated start condition and select the
+  //  slave for reading now.
+  if( tl_i2c_tx_start(GPIOin, GPIOout) )
+    tl_i2c_tx_byte( GPIOout, SLAVE_READ_ADDRESS );
+
+  if ( !tl_i2c_rx_ack(GPIOin, GPIOout) )
+    return( FALSE );
+
+  // this loop will now read out the data and store it
+  //  in the buffer pointed to by buf
+  for ( i=0; i<count; i++) 
+  {
+    *buf++ = tl_i2c_rx_byte(GPIOin, GPIOout);
+
+    // Send ACK by holding data line low for 1 clock
+    if ( i < (count-1) )
+      tl_i2c_clock_pulse( 0x08, GPIOout );
+    else {
+	// Don't send ack for final byte
+      tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+    }
+  }
+
+  tl_i2c_tx_stop(GPIOin, GPIOout);
+
+  return( TRUE );
+}
+
+//****************************************************************
+//
+//
+//
+// routines to set and clear the data and clock bits
+//
+//
+//
+//****************************************************************
+
+static void tl_set_clock(void* gpioreg)
+{
+  ULONG ret_val;
+
+  ret_val = readl( gpioreg );
+  ret_val &= 0xffffffFBL;  // clear GPIO2 (SCL)
+  writel( ret_val, gpioreg);
+}
+
+static void tl_clr_clock(void* gpioreg)
+{
+  ULONG ret_val;
+
+  ret_val = readl( gpioreg );
+  ret_val |= SET_CLOCK_LO;
+  writel( ret_val, gpioreg);
+}
+
+//*****************************************************************
+//
+//
+// This routine will advance the clock by one period
+//
+//
+//*****************************************************************
+static void tl_i2c_clock_pulse( UCHAR value, void* GPIOout  )
+{
+  ULONG ret_val;
+
+  // clear the clock bit
+  tl_clr_clock( GPIOout );
+
+  i2c_delay(0);
+
+
+  // read the port to preserve non-I2C bits
+  ret_val = readl( GPIOout );
+
+  // clear the data & clock bits
+  ret_val &= 0xFFFFFFf3;
+
+  // write the value passed in...
+  // data can only change while clock is LOW!
+  ret_val |= value;           // the data
+  ret_val |= SET_CLOCK_LO;    // the clock
+  writel( ret_val, GPIOout );
+
+  i2c_delay(0);
+
+
+  //set clock bit
+  tl_set_clock( GPIOout);
+}
+
+
+
+
+//*****************************************************************
+//
+//
+// This routine returns the 64-bit WWN
+//
+//
+//*****************************************************************
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf )
+{
+  ULONG len;
+  ULONG sub_len;
+  ULONG ptr_inc;
+  ULONG i;
+  ULONG j;
+  UCHAR *data_ptr;
+  UCHAR  z;
+  UCHAR  name;
+  UCHAR  sub_name;
+  UCHAR  done;
+  int iReturn=0;  // def. 0 offset is failure to find WWN field
+  
+
+	  
+  data_ptr = (UCHAR *)buf;
+
+  done = FALSE;
+  i = 0;
+
+  while ( (i < 128) && (!done) ) 
+  {
+    z = data_ptr[i];\
+    if ( !(z & 0x80) )  
+    {	
+      len  = 1 + (z & 0x07);
+
+      name = (z & 0x78) >> 3;
+      if (name == 0x0F)
+        done = TRUE;
+    }
+    else 
+    {
+      name = z & 0x7F;
+      len  = 3 + data_ptr[i+1] + (data_ptr[i+2] << 8);
+           
+      switch (name) 
+      {
+      case 0x0D:
+	//
+	  j = i + 3;
+	  //
+	  if ( data_ptr[j] == 0x3b ) {
+	    len = 6;
+	    break;
+	  }
+
+	  while ( j<(i+len) ) {
+	    sub_name = (data_ptr[j] & 0x3f);
+	    sub_len  = data_ptr[j+1] + 
+	               (data_ptr[j+2] << 8);
+            ptr_inc  = sub_len + 3; 
+	    switch (sub_name) 
+	    {
+	    case 0x3C:
+              memcpy( wwnbuf, &data_ptr[j+3], 8);
+              iReturn = j+3;
+              break;
+            default:
+              break;
+	    }
+	    j += ptr_inc;
+          }
+	  break;
+        default:
+	  break;
+      }  
+    }  
+  //
+    i += len;
+  }  // end while 
+  return iReturn;
+}
+
+
+
+
+
+// define a short 5 micro sec delay, and longer (ms) delay
+
+static void i2c_delay(ULONG mstime)
+{
+  ULONG i;
+  
+// NOTE: we only expect to use these delays when reading
+// our adapter's NVRAM, which happens only during adapter reset.
+// Delay technique from "Linux Device Drivers", A. Rubini 
+// (1st Ed.) pg 137.
+
+//  printk(" delay %lx  ", mstime);
+  if( mstime ) // ms delay?
+  {
+    // delay technique
+    for( i=0; i < mstime; i++)
+      udelay(1000); // 1ms per loop
+	
+  }
+  else  // 5 micro sec delay
+  
+    udelay( 5 ); // micro secs
+  
+//  printk("done\n");
+}
+
+
+
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTSinit.c linux/drivers/scsi/cpqfcTSinit.c
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTSinit.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTSinit.c	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,1815 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) 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.
+ * Written by Don Zimmerman
+ * IOCTL and procfs added by Jouke Numan
+ * SMP testing by Chel Van Gennip
+ *
+ * portions copied from:
+ * QLogic CPQFCTS SCSI-FCP
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ * Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu> 
+ * Chris Loveland <cwl@iol.unh.edu> to support the isp2100 and isp2200
+*/
+
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>  // request_region() prototype
+#include <linux/vmalloc.h> // ioremap()
+#ifdef __alpha__
+#define __KERNEL_SYSCALLS__
+#endif
+#include <asm/unistd.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>   // ioctl related
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+#include "hosts.h"
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+#include "cpqfcTS.h"
+
+#include <linux/module.h>
+/* Embedded module documentation macros - see module.h */
+MODULE_AUTHOR("Compaq Computer Corporation");
+MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA");
+
+// This struct was originally defined in 
+// /usr/src/linux/include/linux/proc_fs.h
+// since it's only partially implemented, we only use first
+// few fields...
+// NOTE: proc_fs changes in 2.4 kernel
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_cpqfcTS =
+{
+  PROC_SCSI_CPQFCTS,           // ushort low_ino (enumerated list)
+  7,                           // ushort namelen
+  DEV_NAME,                    // const char* name
+  S_IFDIR | S_IRUGO | S_IXUGO, // mode_t mode
+  2                            // nlink_t nlink
+	                       // etc. ...
+};
+
+
+#endif
+
+
+
+/* local function to load our per-HBA (local) data for chip
+   registers, FC link state, all FC exchanges, etc.
+
+   We allocate space and compute address offsets for the
+   most frequently accessed addresses; others (like World Wide
+   Name) are not necessary.
+   
+*/
+static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
+{
+             
+  cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr
+
+  // since x86 port space is 64k, we only need the lower 16 bits
+  cpqfcHBAdata->fcChip.Registers.IOBaseL = 
+    PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+  
+  cpqfcHBAdata->fcChip.Registers.IOBaseU = 
+    PciDev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
+  
+  // 32-bit memory addresses
+  cpqfcHBAdata->fcChip.Registers.MemBase = 
+    PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK;
+
+  cpqfcHBAdata->fcChip.Registers.ReMapMemBase = 
+    ioremap( PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK,
+             0x200);
+  
+  cpqfcHBAdata->fcChip.Registers.RAMBase = 
+    PciDev->base_address[4];
+  
+  cpqfcHBAdata->fcChip.Registers.SROMBase =  // NULL for HP TS adapter
+    PciDev->base_address[5];
+  
+  // now the Tachlite chip registers
+  // the REGISTER struct holds both the physical address & last
+  // written value (some TL registers are WRITE ONLY)
+
+  cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_SFQ_CONSUMER_INDEX;
+
+  cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX;
+      
+  // TL Frame Manager
+  cpqfcHBAdata->fcChip.Registers.FMconfig.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONFIG;
+  cpqfcHBAdata->fcChip.Registers.FMcontrol.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONTROL;
+  cpqfcHBAdata->fcChip.Registers.FMstatus.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_STATUS;
+  cpqfcHBAdata->fcChip.Registers.FMLinkStatus1.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT1;
+  cpqfcHBAdata->fcChip.Registers.FMLinkStatus2.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT2;
+  cpqfcHBAdata->fcChip.Registers.FMBB_CreditZero.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_BB_CREDIT0;
+      
+      // TL Control Regs
+  cpqfcHBAdata->fcChip.Registers.TYconfig.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONFIG;
+  cpqfcHBAdata->fcChip.Registers.TYcontrol.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONTROL;
+  cpqfcHBAdata->fcChip.Registers.TYstatus.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_STATUS;
+  cpqfcHBAdata->fcChip.Registers.rcv_al_pa.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_RCV_AL_PA;
+  cpqfcHBAdata->fcChip.Registers.ed_tov.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_ED_TOV;
+
+
+  cpqfcHBAdata->fcChip.Registers.INTEN.address = 
+	        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTEN;
+  cpqfcHBAdata->fcChip.Registers.INTPEND.address = 
+	        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTPEND;
+  cpqfcHBAdata->fcChip.Registers.INTSTAT.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTSTAT;
+
+  DEBUG_PCI(printk("  cpqfcHBAdata->fcChip.Registers. :\n"));
+  DEBUG_PCI(printk("    IOBaseL = %x\n", 
+    cpqfcHBAdata->fcChip.Registers.IOBaseL));
+  DEBUG_PCI(printk("    IOBaseU = %x\n", 
+    cpqfcHBAdata->fcChip.Registers.IOBaseU));
+  
+  printk(" ioremap'd Membase: %p\n", cpqfcHBAdata->fcChip.Registers.ReMapMemBase);
+  
+  DEBUG_PCI(printk("    SFQconsumerIndex.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address));
+  DEBUG_PCI(printk("    ERQproducerIndex.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address));
+  DEBUG_PCI(printk("    TYconfig.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.TYconfig.address));
+  DEBUG_PCI(printk("    FMconfig.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.FMconfig.address));
+  DEBUG_PCI(printk("    FMcontrol.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.FMcontrol.address));
+
+  // set default options for FC controller (chip)
+  cpqfcHBAdata->fcChip.Options.initiator = 1;  // default: SCSI initiator
+  cpqfcHBAdata->fcChip.Options.target = 0;     // default: SCSI target
+  cpqfcHBAdata->fcChip.Options.extLoopback = 0;// default: no loopback @GBIC
+  cpqfcHBAdata->fcChip.Options.intLoopback = 0;// default: no loopback inside chip
+
+  // set highest and lowest FC-PH version the adapter/driver supports
+  // (NOT strict compliance)
+  cpqfcHBAdata->fcChip.highest_FCPH_ver = FC_PH3;
+  cpqfcHBAdata->fcChip.lowest_FCPH_ver = FC_PH43;
+
+  // set function points for this controller / adapter
+  cpqfcHBAdata->fcChip.ResetTachyon = CpqTsResetTachLite;
+  cpqfcHBAdata->fcChip.FreezeTachyon = CpqTsFreezeTachlite;
+  cpqfcHBAdata->fcChip.UnFreezeTachyon = CpqTsUnFreezeTachlite;
+  cpqfcHBAdata->fcChip.CreateTachyonQues = CpqTsCreateTachLiteQues;
+  cpqfcHBAdata->fcChip.DestroyTachyonQues = CpqTsDestroyTachLiteQues;
+  cpqfcHBAdata->fcChip.InitializeTachyon = CpqTsInitializeTachLite;  
+  cpqfcHBAdata->fcChip.LaserControl = CpqTsLaserControl;  
+  cpqfcHBAdata->fcChip.ProcessIMQEntry = CpqTsProcessIMQEntry;
+  cpqfcHBAdata->fcChip.InitializeFrameManager = CpqTsInitializeFrameManager;;  
+  cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN;
+  cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM;
+
+ 
+
+}
+
+
+/* (borrowed from linux/drivers/scsi/hosts.c) */
+static void launch_FCworker_thread(struct Scsi_Host *HostAdapter)
+{
+  DECLARE_MUTEX_LOCKED(sem);
+
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+  ENTER("launch_FC_worker_thread");
+             
+  cpqfcHBAdata->notify_wt = &sem;
+
+  kernel_thread((int (*)(void *))cpqfcTSWorkerThread, 
+                          (void *) HostAdapter, 0);
+  /*
+   * Now wait for the kernel error thread to initialize itself
+
+   */
+  down (&sem);
+  cpqfcHBAdata->notify_wt = NULL;
+
+  LEAVE("launch_FC_worker_thread");
+ 
+}
+
+
+/* "Entry" point to discover if any supported PCI 
+   bus adapter can be found
+*/
+// We're supporting:
+// Compaq 64-bit, 66MHz HBA with Tachyon TS
+// Agilent XL2 
+#define HBA_TYPES 2
+
+
+int cpqfcTS_detect(Scsi_Host_Template *ScsiHostTemplate)
+{
+  int NumberOfAdapters=0; // how many of our PCI adapters are found?
+  struct pci_dev *PciDev = NULL;
+  struct Scsi_Host *HostAdapter = NULL;
+  CPQFCHBA *cpqfcHBAdata = NULL; 
+  struct timer_list *cpqfcTStimer = NULL;
+  SupportedPCIcards PCIids[HBA_TYPES];
+  int i;
+  
+  ENTER("cpqfcTS_detect");
+  
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+  ScsiHostTemplate->proc_dir = &proc_scsi_cpqfcTS;
+#else
+  ScsiHostTemplate->proc_name = "cpqfcTS";
+#endif
+  
+  if( pci_present() == 0) // no PCI busses?
+  {
+    printk( "  no PCI bus?@#!\n");
+    return NumberOfAdapters;
+  }
+
+  // what HBA adapters are we supporting?
+  PCIids[0].vendor_id = PCI_VENDOR_ID_COMPAQ;
+  PCIids[0].device_id = CPQ_DEVICE_ID;
+  PCIids[1].vendor_id = PCI_VENDOR_ID_HP; // i.e. 103Ch (Agilent == HP for now)
+  PCIids[1].device_id = AGILENT_XL2_ID;   // i.e. 1029h
+
+  for( i=0; i < HBA_TYPES; i++)
+  {
+    // look for all HBAs of each type
+
+    while( (PciDev =
+      pci_find_device( PCIids[i].vendor_id, PCIids[i].device_id, PciDev) ))
+    {
+      // NOTE: (kernel 2.2.12-32) limits allocation to 128k bytes...
+      printk(" scsi_register allocating %d bytes for FC HBA\n",
+		      (ULONG)sizeof(CPQFCHBA));
+
+      HostAdapter = scsi_register( ScsiHostTemplate, sizeof( CPQFCHBA ) );
+      DEBUG_PCI( printk("  HBA found!\n"));
+      DEBUG_PCI( printk("  HostAdapter->PciDev->irq = %u\n", PciDev->irq) );
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[0]));
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[1]));
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[2]));
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[3]));
+
+      
+      HostAdapter->irq = PciDev->irq;  // copy for Scsi layers
+      
+      // HP Tachlite uses two (255-byte) ranges of Port I/O (lower & upper),
+      // for a total I/O port address space of 512 bytes.
+      // mask out the I/O port address (lower) & record
+      HostAdapter->io_port = (unsigned int)
+	     PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+      HostAdapter->n_io_port = 0xff;
+      
+      // i.e., expect 128 targets (arbitrary number), while the
+      //  RA-4000 supports 32 LUNs
+      HostAdapter->max_id =  0;   // incremented as devices log in    
+      HostAdapter->max_lun = CPQFCTS_MAX_LUN;         // LUNs per FC device
+      HostAdapter->max_channel = CPQFCTS_MAX_CHANNEL; // multiple busses?
+      HostAdapter->hostt->use_new_eh_code = 1; // new error handling
+      
+      // get the pointer to our HBA specific data... (one for
+      // each HBA on the PCI bus(ses)).
+      cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+      
+      // make certain our data struct is clear
+      memset( cpqfcHBAdata, 0, sizeof( CPQFCHBA ) );
+
+
+      // initialize our HBA info
+      cpqfcHBAdata->HBAnum = NumberOfAdapters;
+
+      cpqfcHBAdata->HostAdapter = HostAdapter; // back ptr
+      Cpqfc_initHBAdata( cpqfcHBAdata, PciDev ); // fill MOST fields
+     
+      cpqfcHBAdata->HBAnum = NumberOfAdapters;
+
+
+      // request necessary resources and check for conflicts
+      if( request_irq( HostAdapter->irq,
+		       cpqfcTS_intr_handler,
+	               SA_INTERRUPT | SA_SHIRQ,
+	               DEV_NAME,
+		       HostAdapter) )
+      {
+	printk(" IRQ %u already used\n", HostAdapter->irq);
+        scsi_unregister( HostAdapter);
+	continue;
+      }
+
+      // Since we have two 256-byte I/O port ranges (upper
+      // and lower), check them both
+      if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff) )
+      {
+	printk("  cpqfcTS address in use: %x\n", 
+			cpqfcHBAdata->fcChip.Registers.IOBaseU);
+	free_irq( HostAdapter->irq, HostAdapter);
+        scsi_unregister( HostAdapter);
+	continue;
+      }	
+      
+      if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff) )
+      {
+  	printk("  cpqfcTS address in use: %x\n", 
+	      			cpqfcHBAdata->fcChip.Registers.IOBaseL);
+	free_irq( HostAdapter->irq, HostAdapter);
+        scsi_unregister( HostAdapter);
+	continue;
+      }	
+      
+      // OK, we should be able to grab everything we need now.
+      request_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff, DEV_NAME);
+      request_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff, DEV_NAME);
+      DEBUG_PCI(printk("  Requesting 255 I/O addresses @ %x\n",
+        cpqfcHBAdata->fcChip.Registers.IOBaseL ));
+      DEBUG_PCI(printk("  Requesting 255 I/O addresses @ %x\n",
+        cpqfcHBAdata->fcChip.Registers.IOBaseU ));
+
+      
+      // start our kernel worker thread
+
+      launch_FCworker_thread(HostAdapter);
+
+
+      // start our TimerTask...
+
+      cpqfcTStimer = &cpqfcHBAdata->cpqfcTStimer;
+
+      init_timer( cpqfcTStimer); // Linux clears next/prev values
+      cpqfcTStimer->expires = jiffies + HZ; // one second
+      cpqfcTStimer->data = (unsigned long)cpqfcHBAdata; // this adapter
+      cpqfcTStimer->function = cpqfcTSheartbeat; // handles timeouts, housekeeping
+
+      add_timer( cpqfcTStimer);  // give it to Linux
+
+
+      // now initialize our hardware...
+
+      cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1);
+
+      cpqfcHBAdata->fcStatsTime = jiffies;  // (for FC Statistics delta)
+      
+      // give our HBA time to initialize and login current devices...
+      {
+	// The Brocade switch (e.g. 2400, 2010, etc.) as of March 2000,
+	// has the following algorithm for FL_Port startup:
+	// Time(sec) Action
+	// 0:        Device Plugin and LIP(F7,F7) transmission
+	// 1.0       LIP incoming
+        // 1.027     LISA incoming, no CLS! (link not up)
+	// 1.028     NOS incoming (switch test for N_Port)
+        // 1.577     ED_TOV expired, transmit LIPs again	
+	// 3.0       LIP(F8,F7) incoming (switch passes Tach Prim.Sig)
+	// 3.028     LILP received, link up, FLOGI starts
+	// slowest(worst) case, measured on 1Gb Finisar GT analyzer
+	
+	int wait_time;
+        for( wait_time = jiffies + 4*HZ; wait_time > jiffies; )
+	  schedule();  // (our worker task needs to run)
+
+      }
+      
+      NumberOfAdapters++; 
+    } // end of while()
+  }
+
+  LEAVE("cpqfcTS_detect");
+ 
+  return NumberOfAdapters;
+}
+
+
+static void my_ioctl_done (Scsi_Cmnd * SCpnt)
+{
+    struct request * req;
+    
+    req = &SCpnt->request;
+    req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+  
+    if (req->sem != NULL) {
+	up(req->sem);
+    }
+}   
+
+
+
+int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
+{
+  int result = 0;
+  struct Scsi_Host *HostAdapter = ScsiDev->host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  Scsi_Cmnd DumCmnd;
+  int i, j;
+  VENDOR_IOCTL_REQ ioc;
+  cpqfc_passthru_t *vendor_cmd;
+  Scsi_Device *SDpnt;
+  Scsi_Cmnd *ScsiPassThruCmnd;
+  unsigned long flags;
+
+  ENTER("cpqfcTS_ioctl");
+  
+  // can we find an FC device mapping to this SCSI target?
+  DumCmnd.channel = ScsiDev->channel;		// For searching
+  DumCmnd.target  = ScsiDev->id;
+  pLoggedInPort = fcFindLoggedInPort( fcChip,
+    &DumCmnd, // search Scsi Nexus
+    0,        // DON'T search linked list for FC port id
+    NULL,     // DON'T search linked list for FC WWN
+    NULL);    // DON'T care about end of list
+ 
+  if( pLoggedInPort == NULL )      // not found!
+  {
+    result = -ENXIO;
+  }
+ 
+  else  // we know what FC device to operate on...
+  {
+    switch (Cmnd) 
+    {
+      // Passthrough provides a mechanism to bypass the RAID
+      // or other controller and talk directly to the devices
+      // (e.g. physical disk drive)
+      // Passthrough commands, unfortunately, tend to be vendor
+      // specific; this is tailored to COMPAQ's RAID (RA4x00)
+      case CPQFCTS_SCSI_PASSTHRU:
+      {
+	void *buf = NULL; // for kernel space buffer for user data
+	
+	if( !arg)
+	  return -EINVAL;
+
+	// must be super user to send stuff directly to the
+	// controller and/or physical drives...
+	if( !suser() )
+	  return -EPERM;
+
+	// copy the caller's struct to our space.
+        copy_from_user_ret( &ioc, arg, 
+			  sizeof( VENDOR_IOCTL_REQ), -EFAULT);
+
+	vendor_cmd = ioc.argp;  // i.e., CPQ specific command struct
+
+	// If necessary, grab a kernel/DMA buffer
+	if( vendor_cmd->len)
+	{
+  	  buf = kmalloc( vendor_cmd->len, GFP_KERNEL);
+	  if( !buf)
+	    return -ENOMEM;
+	}
+
+        // Now build a SCSI_CMND to pass down...
+	// This function allocates and sets Scsi_Cmnd ptrs such as
+	//  ->channel, ->target, ->host
+        ScsiPassThruCmnd = scsi_allocate_device(NULL, ScsiDev, 1);
+
+        // Need data from user?
+	// make sure caller's buffer is in kernel space.
+	if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) &&
+	    vendor_cmd->len)
+          copy_from_user_ret( buf, vendor_cmd->bufp, vendor_cmd->len, -EFAULT);
+	    
+	// copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below)
+        memcpy( &ScsiPassThruCmnd->cmnd[0], 
+		&vendor_cmd->cdb[0], 
+		MAX_COMMAND_SIZE);  
+        // we want to copy all 16 bytes into the FCP-SCSI CDB,
+	// although the actual passthru only uses up to the
+	// first 12.
+	
+	ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB
+
+	// Unfortunately, the SCSI command cmnd[] field has only
+	// 12 bytes.  Ideally the MAX_COMMAND_SIZE should be increased
+	// to 16 for newer Fibre Channel and SCSI-3 larger CDBs.
+	// However, to avoid a mandatory kernel rebuild, we use the SCp
+	// spare field to store the extra 4 bytes ( ugly :-(
+
+	if( MAX_COMMAND_SIZE < 16)
+	{
+          memcpy( &ScsiPassThruCmnd->SCp.buffers_residual,
+		  &vendor_cmd->cdb[12], 4);
+	}	  
+                  
+	
+        ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU!
+	                                        // suppress LUN masking
+	                                        // and VSA logic
+
+	// Use spare fields to copy FCP-SCSI LUN address info...
+        ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus;
+	ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive;
+
+
+
+        // We copy the scheme used by scsi.c to submit commands
+	// to our own HBA.  We do this in order to stall the
+	// thread calling the IOCTL until it completes, and use
+	// the same "_quecommand" function for synchronizing
+	// FC Link events with our "worker thread".
+	
+        spin_lock_irqsave(&io_request_lock, flags);
+        {
+          DECLARE_MUTEX_LOCKED(sem);
+          ScsiPassThruCmnd->request.sem = &sem;
+          // eventually gets us to our own _quecommand routine
+          scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0], 
+	       buf, 
+	       vendor_cmd->len, 
+	       my_ioctl_done, 
+	       10*HZ, 1);// timeout,retries
+          spin_unlock_irqrestore(&io_request_lock, flags);
+          // Other I/Os can now resume; we wait for our ioctl
+	  // command to complete
+	  down(&sem);
+          spin_lock_irqsave(&io_request_lock, flags);
+          ScsiPassThruCmnd->request.sem = NULL;
+        }
+	
+        result = ScsiPassThruCmnd->result;
+
+        // copy any sense data back to caller
+        if( result != 0 )
+	{
+	  memcpy( vendor_cmd->sense_data, // see struct def - size=40
+		  ScsiPassThruCmnd->sense_buffer, 
+		  sizeof(ScsiPassThruCmnd->sense_buffer)); 
+	}
+        SDpnt = ScsiPassThruCmnd->device;
+        scsi_release_command(ScsiPassThruCmnd); // "de-allocate"
+        ScsiPassThruCmnd = NULL;
+
+        if (!SDpnt->was_reset && SDpnt->scsi_request_fn)
+          (*SDpnt->scsi_request_fn)();
+
+        wake_up(&SDpnt->device_wait);
+        spin_unlock_irqrestore(&io_request_lock, flags);
+
+	// need to pass data back to user (space)?
+	if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) &&
+	     vendor_cmd->len )
+          copy_to_user_ret( vendor_cmd->bufp, buf, vendor_cmd->len, -EFAULT);
+
+        if( buf) 
+	  kfree( buf);
+	
+        return result;
+      }
+      
+      case CPQFCTS_GETPCIINFO:
+      {
+	cpqfc_pci_info_struct pciinfo;
+	
+	if( !arg)
+	  return -EINVAL;
+
+         	
+	
+        pciinfo.bus = cpqfcHBAdata->PciDev->bus->number;
+        pciinfo.dev_fn = cpqfcHBAdata->PciDev->devfn;  
+	pciinfo.board_id = cpqfcHBAdata->PciDev->device |
+			  (cpqfcHBAdata->PciDev->vendor <<16); 
+	      
+        copy_to_user_ret( arg, &pciinfo, 
+			  sizeof(cpqfc_pci_info_struct), -EFAULT);
+        return 0;
+      }
+
+      case CPQFCTS_GETDRIVVER:
+      {
+	DriverVer_type DriverVer = 
+		CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR);
+	
+	if( !arg)
+	  return -EINVAL;
+
+        copy_to_user_ret( arg, &DriverVer, 
+			  sizeof(DriverVer), -EFAULT);
+        return 0;
+      }
+
+
+
+      case SCSI_IOCTL_FC_TARGET_ADDRESS:
+      result = 
+        verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress));
+      if (result) 
+	break;
+ 
+      put_user(pLoggedInPort->port_id,
+		&((Scsi_FCTargAddress *) arg)->host_port_id);
+ 
+      for( i=3,j=0; i>=0; i--)   	// copy the LOGIN port's WWN
+        put_user(pLoggedInPort->u.ucWWN[i], 
+		&((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+      for( i=7; i>3; i--)		// copy the LOGIN port's WWN
+        put_user(pLoggedInPort->u.ucWWN[i], 
+		&((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+        break;
+    default:
+      result = -EINVAL;
+      break;
+    }
+  }
+
+  LEAVE("cpqfcTS_ioctl");
+  return result;
+}
+
+
+/* "Release" the Host Bus Adapter...
+   disable interrupts, stop the HBA, release the interrupt,
+   and free all resources */
+
+int cpqfcTS_release(struct Scsi_Host *HostAdapter)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; 
+
+
+  ENTER("cpqfcTS_release");
+	
+  DEBUG_PCI( printk(" cpqfcTS: delete timer...\n"));
+  del_timer( &cpqfcHBAdata->cpqfcTStimer);  
+    
+  // disable the hardware...
+  DEBUG_PCI( printk(" disable hardware, destroy queues, free mem\n"));
+  cpqfcHBAdata->fcChip.ResetTachyon( cpqfcHBAdata, CLEAR_FCPORTS);
+
+  // kill kernel thread
+  if( cpqfcHBAdata->worker_thread ) // (only if exists)
+  {
+    DECLARE_MUTEX_LOCKED(sem);  // synchronize thread kill
+
+    cpqfcHBAdata->notify_wt = &sem;
+    DEBUG_PCI( printk(" killing kernel thread\n"));
+    send_sig( SIGKILL, cpqfcHBAdata->worker_thread, 1);
+    down( &sem);
+    cpqfcHBAdata->notify_wt = NULL;
+    
+  }
+
+  // free Linux resources
+  DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n"));
+  free_irq( HostAdapter->irq, HostAdapter);
+  scsi_unregister( HostAdapter);
+  release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff);
+  release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff);
+ /* we get "vfree: bad address" executing this - need to investigate... 
+  if( (void*)((unsigned long)cpqfcHBAdata->fcChip.Registers.MemBase) !=
+      cpqfcHBAdata->fcChip.Registers.ReMapMemBase)
+    vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase);
+*/
+
+  LEAVE("cpqfcTS_release");
+  return 0;
+}
+
+
+const char * cpqfcTS_info(struct Scsi_Host *HostAdapter)
+{
+  static char buf[300];
+  CPQFCHBA *cpqfcHBA;
+  int BusSpeed, BusWidth;
+  
+  // get the pointer to our Scsi layer HBA buffer  
+  cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+
+  BusWidth = (cpqfcHBA->fcChip.Registers.PCIMCTR &0x4) > 0 ?
+               64 : 32;
+
+  if( cpqfcHBA->fcChip.Registers.TYconfig.value & 0x80000000)
+    BusSpeed = 66;
+  else
+    BusSpeed = 33;
+
+  sprintf(buf, 
+"%s: WWN %08X%08X\n on PCI bus %d device 0x%02x irq %d IObaseL 0x%x, MEMBASE 0x%x\nPCI bus width %d bits, bus speed %d MHz\nFCP-SCSI Driver v%d.%d.%d",
+      cpqfcHBA->fcChip.Name, 
+      cpqfcHBA->fcChip.Registers.wwn_hi,
+      cpqfcHBA->fcChip.Registers.wwn_lo,
+      cpqfcHBA->PciDev->bus->number,
+      cpqfcHBA->PciDev->device,  
+      HostAdapter->irq,
+      cpqfcHBA->fcChip.Registers.IOBaseL,
+      cpqfcHBA->fcChip.Registers.MemBase,
+      BusWidth,
+      BusSpeed,
+      VER_MAJOR, VER_MINOR, VER_SUBMINOR
+);
+
+  
+  cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+  cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+  return buf;
+}
+
+//
+// /proc/scsi support. The following routines allow us to do 'normal'
+// sprintf like calls to return the currently requested piece (buflenght
+// chars, starting at bufoffset) of the file. Although procfs allows for
+// a 1 Kb bytes overflow after te supplied buffer, I consider it bad 
+// programming to use it to make programming a little simpler. This piece
+// of coding is borrowed from ncr53c8xx.c with some modifications 
+//
+struct info_str
+{
+        char *buffer;			// Pointer to output buffer
+        int buflength;			// It's length
+        int bufoffset;			// File offset corresponding with buf[0]
+	int buffillen;			// Current filled length 
+        int filpos;			// Current file offset
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int datalen)
+{
+
+  if (info->filpos < info->bufoffset) {	// Current offset before buffer offset
+    if (info->filpos + datalen <= info->bufoffset) {
+      info->filpos += datalen; 		// Discard if completely before buffer
+      return;
+    } else {				// Partial copy, set to begin
+      data += (info->bufoffset - info->filpos);
+      datalen  -= (info->bufoffset - info->filpos);
+      info->filpos = info->bufoffset;
+    }
+  }
+
+  info->filpos += datalen;		// Update current offset
+
+  if (info->buffillen == info->buflength) // Buffer full, discard
+    return;
+
+  if (info->buflength - info->buffillen < datalen)  // Overflows buffer ?
+    datalen = info->buflength - info->buffillen;
+
+  memcpy(info->buffer + info->buffillen, data, datalen);
+  info->buffillen += datalen;
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+        va_list args;
+        char buf[400];
+        int len;
+
+        va_start(args, fmt);
+        len = vsprintf(buf, fmt, args);
+        va_end(args);
+
+        copy_mem_info(info, buf, len);
+        return len;
+}
+
+
+// Routine to get data for /proc RAM filesystem
+//
+int cpqfcTS_proc_info (char *buffer, char **start, off_t offset, int length, 
+		       int hostno, int inout)
+{
+  struct Scsi_Host *host;
+  Scsi_Cmnd DumCmnd;
+  int Chan, Targ, i;
+  struct info_str info;
+  CPQFCHBA *cpqfcHBA;
+  PTACHYON fcChip;
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  char buf[81];
+
+  // Search the Scsi host list for our controller
+  for (host=scsi_hostlist; host; host=host->next)
+    if (host->host_no == hostno)
+      break;
+
+  if (!host) return -ESRCH;
+
+  if (inout) return -EINVAL;
+
+  // get the pointer to our Scsi layer HBA buffer  
+  cpqfcHBA = (CPQFCHBA *)host->hostdata;
+  fcChip = &cpqfcHBA->fcChip;
+  
+  *start 	  = buffer;
+
+  info.buffer     = buffer;
+  info.buflength  = length;
+  info.bufoffset  = offset;
+  info.filpos     = 0;
+  info.buffillen  = 0;
+  copy_info(&info, "Driver version = %d.%d.%d", VER_MAJOR, VER_MINOR, VER_SUBMINOR); 
+  cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[0]);
+  cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+  copy_info(&info, "%s\n", buf); 
+		  
+
+#define DISPLAY_WWN_INFO
+#ifdef DISPLAY_WWN_INFO
+  copy_info(&info, "WWN database: (\"port_id: 000000\" means disconnected)\n");
+  for ( Chan=0; Chan <= host->max_channel; Chan++) {
+    DumCmnd.channel = Chan;
+    for (Targ=0; Targ <= host->max_id; Targ++) {
+      DumCmnd.target = Targ;
+      if ((pLoggedInPort = fcFindLoggedInPort( fcChip,
+	    			&DumCmnd, // search Scsi Nexus
+    				0,        // DON'T search list for FC port id
+    				NULL,     // DON'T search list for FC WWN
+    				NULL))){   // DON'T care about end of list
+	copy_info(&info, "Host: scsi%d Channel: %02d TargetId: %02d -> WWN: ",
+			   hostno, Chan, Targ);
+        for( i=3; i>=0; i--)        // copy the LOGIN port's WWN
+          copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+        for( i=7; i>3; i--)             // copy the LOGIN port's WWN
+          copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+	copy_info(&info, " port_id: %06X\n", pLoggedInPort->port_id); 
+      }
+    }
+  }
+#endif
+  
+  
+// Unfortunately, the proc_info buffer isn't big enough
+// for everything we would like...
+// For FC stats, compile this and turn off WWN stuff above  
+//#define DISPLAY_FC_STATS
+#ifdef DISPLAY_FC_STATS
+// get the Fibre Channel statistics
+  {
+    int DeltaSecs = (jiffies - cpqfcHBA->fcStatsTime) / HZ;
+    int days,hours,minutes,secs;
+    
+    days = DeltaSecs / (3600*24); // days
+    hours = (DeltaSecs% (3600*24)) / 3600; // hours
+    minutes = (DeltaSecs%3600 /60); // minutes
+    secs =  DeltaSecs%60;  // secs
+copy_info( &info, "Fibre Channel Stats (time dd:hh:mm:ss %02u:%02u:%02u:%02u\n",
+      days, hours, minutes, secs);
+  }
+    
+  cpqfcHBA->fcStatsTime = jiffies;  // (for next delta)
+
+  copy_info( &info, "  LinkUp           %9u     LinkDown      %u\n",
+        fcChip->fcStats.linkUp, fcChip->fcStats.linkDown);
+        
+  copy_info( &info, "  Loss of Signal   %9u     Loss of Sync  %u\n",
+    fcChip->fcStats.LossofSignal, fcChip->fcStats.LossofSync);
+		  
+  copy_info( &info, "  Discarded Frames %9u     Bad CRC Frame %u\n",
+    fcChip->fcStats.Dis_Frm, fcChip->fcStats.Bad_CRC);
+
+  copy_info( &info, "  TACH LinkFailTX  %9u     TACH LinkFailRX     %u\n",
+    fcChip->fcStats.linkFailTX, fcChip->fcStats.linkFailRX);
+  
+  copy_info( &info, "  TACH RxEOFa      %9u     TACH Elastic Store  %u\n",
+    fcChip->fcStats.Rx_EOFa, fcChip->fcStats.e_stores);
+
+  copy_info( &info, "  BufferCreditWait %9uus   TACH FM Inits %u\n",
+    fcChip->fcStats.BB0_Timer*10, fcChip->fcStats.FMinits );
+	
+  copy_info( &info, "  FC-2 Timeouts    %9u     FC-2 Logouts  %u\n",
+    fcChip->fcStats.timeouts, fcChip->fcStats.logouts); 
+        
+  copy_info( &info, "  FC-2 Aborts      %9u     FC-4 Aborts   %u\n",
+    fcChip->fcStats.FC2aborted, fcChip->fcStats.FC4aborted);
+   
+  // clear the counters
+  cpqfcTSClearLinkStatusCounters( fcChip);
+#endif
+	
+  return info.buffillen;
+}
+
+
+#if DEBUG_CMND
+
+UCHAR *ScsiToAscii( UCHAR ScsiCommand)
+{
+
+/*++
+
+Routine Description:
+
+   Converts a SCSI command to a text string for debugging purposes.
+
+
+Arguments:
+
+   ScsiCommand -- hex value SCSI Command
+
+
+Return Value:
+
+   An ASCII, null-terminated string if found, else returns NULL.
+
+Original code from M. McGowen, Compaq
+--*/
+
+
+   switch (ScsiCommand)
+   {
+      case 0x00:
+         return( "Test Unit Ready" );
+
+      case 0x01:
+         return( "Rezero Unit or Rewind" );
+
+      case 0x02:
+         return( "Request Block Address" );
+
+      case 0x03:
+         return( "Requese Sense" );
+
+      case 0x04:
+         return( "Format Unit" );
+
+      case 0x05:
+         return( "Read Block Limits" );
+
+      case 0x07:
+         return( "Reassign Blocks" );
+
+      case 0x08:
+         return( "Read (6)" );
+
+      case 0x0a:
+         return( "Write (6)" );
+
+      case 0x0b:
+         return( "Seek (6)" );
+
+      case 0x12:
+         return( "Inquiry" );
+
+      case 0x15:
+         return( "Mode Select (6)" );
+
+      case 0x16:
+         return( "Reserve" );
+
+      case 0x17:
+         return( "Release" );
+
+      case 0x1a:
+         return( "ModeSen(6)" );
+
+      case 0x1b:
+         return( "Start/Stop Unit" );
+
+      case 0x1c:
+         return( "Receive Diagnostic Results" );
+
+      case 0x1d:
+         return( "Send Diagnostic" );
+
+      case 0x25:
+         return( "Read Capacity" );
+
+      case 0x28:
+         return( "Read (10)" );
+
+      case 0x2a:
+         return( "Write (10)" );
+
+      case 0x2b:
+         return( "Seek (10)" );
+
+      case 0x2e:
+         return( "Write and Verify" );
+
+      case 0x2f:
+         return( "Verify" );
+
+      case 0x34:
+         return( "Pre-Fetch" );
+
+      case 0x35:
+         return( "Synchronize Cache" );
+
+      case 0x37:
+         return( "Read Defect Data (10)" );
+
+      case 0x3b:
+         return( "Write Buffer" );
+
+      case 0x3c:
+         return( "Read Buffer" );
+
+      case 0x3e:
+         return( "Read Long" );
+
+      case 0x3f:
+         return( "Write Long" );
+
+      case 0x41:
+         return( "Write Same" );
+
+      case 0x4c:
+         return( "Log Select" );
+
+      case 0x4d:
+         return( "Log Sense" );
+
+      case 0x56:
+         return( "Reserve (10)" );
+
+      case 0x57:
+         return( "Release (10)" );
+
+      case 0xa0:
+         return( "ReportLuns" );
+
+      case 0xb7:
+         return( "Read Defect Data (12)" );
+
+      case 0xca:
+         return( "Peripheral Device Addressing SCSI Passthrough" );
+
+      case 0xcb:
+         return( "Compaq Array Firmware Passthrough" );
+
+      default:
+         return( NULL );
+   }
+
+} // end ScsiToAscii()
+
+void cpqfcTS_print_scsi_cmd(Scsi_Cmnd * cmd)
+{
+
+printk("cpqfcTS: (%s) chnl 0x%02x, trgt = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n", 
+    ScsiToAscii( cmd->cmnd[0]), cmd->channel, cmd->target, cmd->lun, cmd->cmd_len);
+
+if( cmd->cmnd[0] == 0)   // Test Unit Ready?
+{
+  int i;
+
+  printk("Cmnd->request_bufflen = 0x%X, ->use_sg = %d, ->bufflen = %d\n",
+    cmd->request_bufflen, cmd->use_sg, cmd->bufflen);
+  printk("Cmnd->request_buffer = %p, ->sglist_len = %d, ->buffer = %p\n",
+    cmd->request_buffer, cmd->sglist_len, cmd->buffer);
+  for (i = 0; i < cmd->cmd_len; i++)
+    printk("0x%02x ", cmd->cmnd[i]);
+  printk("\n");
+}
+
+}
+
+#endif				/* DEBUG_CMND */
+
+
+
+
+static void QueCmndOnBoardLock( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+  int i;
+
+  for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+  {    // find spare slot
+    if( cpqfcHBAdata->BoardLockCmnd[i] == NULL )
+    {
+      cpqfcHBAdata->BoardLockCmnd[i] = Cmnd;
+//      printk(" BoardLockCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+//        i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+      break;
+    }
+  }
+  if( i >= CPQFCTS_REQ_QUEUE_LEN)
+  {
+    printk(" cpqfcTS WARNING: Lost Cmnd %p on BoardLock Q full!", Cmnd);
+  }
+
+}
+
+
+static void QueLinkDownCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+  int indx;
+
+  // Remember the command ptr so we can return; we'll complete when
+  // the device comes back, causing immediate retry
+  for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++)//, SCptr++)
+  {
+    if( cpqfcHBAdata->LinkDnCmnd[indx] == NULL ) // available?
+    {
+#ifdef DUMMYCMND_DBG
+      printk(" @add Cmnd %p to LnkDnCmnd[%d]@ ", Cmnd,indx);
+#endif
+      cpqfcHBAdata->LinkDnCmnd[indx] = Cmnd;
+      break;
+    }
+  }
+
+  if( indx >= CPQFCTS_REQ_QUEUE_LEN ) // no space for Cmnd??
+  {
+    // this will result in an _abort call later (with possible trouble)
+    printk("no buffer for LinkDnCmnd!! %p\n", Cmnd);
+  }
+}
+
+
+
+
+
+// The file "hosts.h" says not to call scsi_done from
+// inside _queuecommand, so we'll do it from the heartbeat timer
+
+static void QueBadTargetCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+  int i;
+    //    printk(" can't find target %d\n", Cmnd->target);
+
+  for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+  {    // find spare slot
+    if( cpqfcHBAdata->BadTargetCmnd[i] == NULL )
+    {
+      cpqfcHBAdata->BadTargetCmnd[i] = Cmnd;
+//      printk(" BadTargetCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+//          i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+      break;
+    }
+  }
+}
+
+
+// This is the "main" entry point for Linux Scsi commands --
+// it all starts here.
+
+int cpqfcTS_queuecommand(Scsi_Cmnd *Cmnd, void (* done)(Scsi_Cmnd *))
+{
+  struct Scsi_Host *HostAdapter = Cmnd->host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  TachFCHDR_GCMND fchs;  // only use for FC destination id field  
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  ULONG ulStatus, SESTtype;
+  LONG ExchangeID;
+
+
+
+
+  ENTER("cpqfcTS_queuecommand");
+      
+  PCI_TRACEO( (ULONG)Cmnd, 0x98)
+      
+  
+  Cmnd->scsi_done = done;
+#ifdef DEBUG_CMND  
+  cpqfcTS_print_scsi_cmd( Cmnd);
+#endif
+
+  // prevent board contention with kernel thread...  
+  
+   if( cpqfcHBAdata->BoardLock )
+  {
+//    printk(" @BrdLck Hld@ ");
+    QueCmndOnBoardLock( cpqfcHBAdata, Cmnd);
+  }
+  
+  else
+  {
+
+    // in the current system (2.2.12), this routine is called
+    // after spin_lock_irqsave(), so INTs are disabled. However,
+    // we might have something pending in the LinkQ, which
+    // might cause the WorkerTask to run.  In case that
+    // happens, make sure we lock it out.
+    
+    
+    
+    PCI_TRACE( 0x98) 
+    CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+    PCI_TRACE( 0x98) 
+	    
+  // can we find an FC device mapping to this SCSI target?
+    pLoggedInPort = fcFindLoggedInPort( fcChip,
+      Cmnd,     // search Scsi Nexus
+      0,        // DON'T search linked list for FC port id
+      NULL,     // DON'T search linked list for FC WWN
+      NULL);    // DON'T care about end of list
+ 
+    if( pLoggedInPort == NULL )      // not found!
+    {
+//    printk(" @Q bad targ cmnd %p@ ", Cmnd);
+      QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+    }
+
+    else  // we know what FC device to send to...
+    {
+
+      // does this device support FCP target functions?
+      // (determined by PRLI field)
+
+      if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+      {
+        printk(" Doesn't support TARGET functions port_id %Xh\n",
+          pLoggedInPort->port_id );
+        QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+      }
+
+    // In this case (previous login OK), the device is temporarily
+    // unavailable waiting for re-login, in which case we expect it
+    // to be back in between 25 - 500ms.  
+    // If the FC port doesn't log back in within several seconds
+    // (i.e. implicit "logout"), or we get an explicit logout,
+    // we set "device_blocked" in Scsi_Device struct; in this
+    // case 30 seconds will elapse before Linux/Scsi sends another
+    // command to the device.
+      else if( pLoggedInPort->prli != TRUE )
+      {
+//      printk("Device (Chnl/Target %d/%d) invalid PRLI, port_id %06lXh\n",
+//        Cmnd->channel, Cmnd->target, pLoggedInPort->port_id);
+        QueLinkDownCmnd( cpqfcHBAdata, Cmnd);
+//    Need to use "blocked" flag??   	
+//	Cmnd->device->device_blocked = TRUE; // just let it timeout
+      }
+      else  // device supports TARGET functions, and is logged in...
+      {
+      // (context of fchs is to "reply" to...)
+        fchs.s_id = pLoggedInPort->port_id; // destination FC address
+
+      // what is the data direction?  For data TO the device,
+      // we need IWE (Intiator Write Entry).  Otherwise, IRE.
+
+        if( Cmnd->cmnd[0] == WRITE_10 ||
+  	  Cmnd->cmnd[0] == WRITE_6 ||
+	  Cmnd->cmnd[0] == WRITE_BUFFER ||      
+	  Cmnd->cmnd[0] == VENDOR_WRITE_OPCODE ||  // CPQ specific 
+	  Cmnd->cmnd[0] == MODE_SELECT )
+        {
+          SESTtype = SCSI_IWE; // data from HBA to Device
+        }
+        else
+          SESTtype = SCSI_IRE; // data from Device to HBA
+    	  
+        ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          SESTtype,     // e.g. Initiator Read Entry (IRE)
+          &fchs,        // we are originator; only use d_id
+          Cmnd,         // Linux SCSI command (with scatter/gather list)
+          &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+        if( !ulStatus ) // Exchange setup?
+   
+        {
+          if( cpqfcHBAdata->BoardLock )
+          {
+    TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+	    printk(" @bl! %d, xID %Xh@ ", current->pid, ExchangeID);
+          }
+
+	  ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+	  if( !ulStatus )
+          {
+            PCI_TRACEO( ExchangeID, 0xB8) 
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+	  }
+          else
+            // check reason for Exchange not being started - we might
+            // want to Queue and start later, or fail with error
+          {
+            printk("quecommand: cpqfcTSStartExchange failed: %Xh\n", ulStatus );
+          }
+        }            // end good BuildExchange status
+        
+        else  // SEST table probably full  -- why? hardware hang?
+        {
+	  printk("quecommand: cpqfcTSBuildExchange faild: %Xh\n", ulStatus);
+        }
+      }  // end can't do FCP-SCSI target functions
+    } // end can't find target (FC device)
+
+    CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+  }
+	
+  PCI_TRACEO( (ULONG)Cmnd, 0x9C) 
+  LEAVE("cpqfcTS_queuecommand");
+  return 0;
+}    
+
+
+// Entry point for upper Scsi layer intiated abort.  Typically
+// this is called if the command (for hard disk) fails to complete
+// in 30 seconds.  This driver intends to complete all disk commands
+// within Exchange ".timeOut" seconds (now 7) with target status, or
+// in case of ".timeOut" expiration, a DID_SOFT_ERROR which causes
+// immediate retry.
+// If any disk commands get the _abort call, except for the case that
+// the physical device was removed or unavailable due to hardware
+// errors, it should be considered a driver error and reported to
+// the author.
+
+int cpqfcTS_abort(Scsi_Cmnd *Cmnd)
+{
+  struct Scsi_Host *HostAdapter = Cmnd->host;
+  // get the pointer to our Scsi layer HBA buffer  
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  int i;
+  ENTER("cpqfcTS_abort");
+
+  Cmnd->result = DID_ABORT <<16;  // assume we'll find it
+
+  printk(" @Linux _abort Scsi_Cmnd %p ", Cmnd);
+  // See if we can find a Cmnd pointer that matches...
+  // The most likely case is we accepted the command
+  // from Linux Scsi (e.g. ceated a SEST entry) and it
+  // got lost somehow.  If we can't find any reference
+  // to the passed pointer, we can only presume it
+  // got completed as far as our driver is concerned.
+  // If we found it, we will try to abort it through
+  // common mechanism.  If FC ABTS is successful (ACC)
+  // or is rejected (RJT) by target, we will call
+  // Scsi "done" quickly.  Otherwise, the ABTS will timeout
+  // and we'll call "done" later.
+
+  // Search the SEST exchanges for a matching Cmnd ptr.
+  for( i=0; i< TACH_SEST_LEN; i++)
+  {
+    if( Exchanges->fcExchange[i].Cmnd == Cmnd )
+    {
+      
+      // found it!
+      printk(" x_ID %Xh, type %Xh\n", i, Exchanges->fcExchange[i].type);
+
+      Exchanges->fcExchange[i].status = INITIATOR_ABORT; // seconds default
+      Exchanges->fcExchange[i].timeOut = 10; // seconds default (changed later)
+
+      // Since we need to immediately return the aborted Cmnd to Scsi 
+      // upper layers, we can't make future reference to any of it's 
+      // fields (e.g the Nexus).
+
+      cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i);
+
+      break;
+    }
+  }
+
+  if( i >= TACH_SEST_LEN ) // didn't find Cmnd ptr in chip's SEST?
+  {
+    // now search our non-SEST buffers (i.e. Cmnd waiting to
+    // start on the HBA or waiting to complete with error for retry).
+    
+    // first check BadTargetCmnd
+    for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+    { 
+      if( cpqfcHBAdata->BadTargetCmnd[i] == Cmnd )
+      {
+        cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+	printk("in BadTargetCmnd Q\n");
+	goto Done; // exit
+      }
+    }
+
+    // if not found above...
+
+    for( i=0; i < CPQFCTS_REQ_QUEUE_LEN; i++)
+    {
+      if( cpqfcHBAdata->LinkDnCmnd[i] == Cmnd ) 
+      {
+	cpqfcHBAdata->LinkDnCmnd[i] = NULL;
+	printk("in LinkDnCmnd Q\n");
+	goto Done;
+      }
+    }
+
+
+    for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+    {    // find spare slot
+      if( cpqfcHBAdata->BoardLockCmnd[i] == Cmnd )
+      {
+        cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+	printk("in BoardLockCmnd Q\n");
+	goto Done;
+      }
+    }
+    
+    Cmnd->result = DID_ERROR <<16;  // Hmmm...
+    printk("Not found! ");
+//    panic("_abort");
+  }
+  
+Done:
+  
+//    panic("_abort");
+  LEAVE("cpqfcTS_abort");
+  return 0;  // (see scsi.h)
+}    
+
+
+
+
+// To be done...	
+int cpqfcTS_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags)
+{
+  int return_status = SUCCESS;
+
+  ENTER("cpqfcTS_reset");
+
+
+            
+
+  LEAVE("cpqfcTS_reset");
+  return return_status;
+}    
+
+
+
+/* This function determines the bios parameters for a given
+   harddisk. These tend to be numbers that are made up by the
+   host adapter.  Parameters:
+   size, device number, list (heads, sectors,cylinders).
+   (from hosts.h)
+*/
+
+int cpqfcTS_biosparam(Disk *disk, kdev_t n, int ip[])
+{
+  int size = disk->capacity;
+  
+  ENTER("cpqfcTS_biosparam");
+  ip[0] = 64;
+  ip[1] = 32;
+  ip[2] = size >> 11;
+  
+  if( ip[2] > 1024 )
+  {
+    ip[0] = 255;
+    ip[1] = 63;
+    ip[2] = size / (ip[0] * ip[1]);
+  }
+
+  LEAVE("cpqfcTS_biosparam");
+  return 0;
+}    
+
+
+
+void cpqfcTS_intr_handler( int irq, 
+		void *dev_id, 
+		struct pt_regs *regs)
+{
+
+  unsigned long flags, InfLoopBrk=0;
+  struct Scsi_Host *HostAdapter = dev_id;
+  CPQFCHBA *cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+  int MoreMessages = 1; // assume we have something to do
+  UCHAR IntPending;
+  
+  ENTER("intr_handler");
+
+  spin_lock_irqsave( &io_request_lock, flags);
+  // is this our INT?
+  IntPending = readb( cpqfcHBA->fcChip.Registers.INTPEND.address);
+
+  // broken boards can generate messages forever, so
+  // prevent the infinite loop
+#define INFINITE_IMQ_BREAK 10000
+  if( IntPending )
+  {
+    
+    // mask our HBA interrupts until we handle it...
+    writeb( 0, cpqfcHBA->fcChip.Registers.INTEN.address);
+
+    if( IntPending & 0x4) // "INT" - Tach wrote to IMQ
+    {
+      while( (++InfLoopBrk < INFINITE_IMQ_BREAK) && (MoreMessages ==1) ) 
+      {
+        MoreMessages = CpqTsProcessIMQEntry( HostAdapter); // ret 0 when done
+      }
+      if( InfLoopBrk >= INFINITE_IMQ_BREAK )
+      {
+        printk("WARNING: Compaq FC adapter generating excessive INTs -REPLACE\n");
+        printk("or investigate alternate causes (e.g. physical FC layer)\n");
+      }
+
+      else  // working normally - re-enable INTs and continue
+        writeb( 0x1F, cpqfcHBA->fcChip.Registers.INTEN.address);
+    
+    }  // (...ProcessIMQEntry() clears INT by writing IMQ consumer)
+    else  // indications of errors or problems...
+          // these usually indicate critical system hardware problems.
+    {
+      if( IntPending & 0x10 )
+  	printk(" cpqfcTS adapter external memory parity error detected\n");
+      if( IntPending & 0x8 )
+  	printk(" cpqfcTS adapter PCI master address crossed 45-bit boundary\n");
+      if( IntPending & 0x2 )
+	printk(" cpqfcTS adapter DMA error detected\n");
+      if( IntPending & 0x1 )
+  	printk(" cpqfcTS adapter PCI error detected\n");
+    }      
+  }
+  spin_unlock_irqrestore( &io_request_lock, flags);
+  LEAVE("intr_handler");
+}
+
+
+
+
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[])
+{
+        // Verify GBIC type (if any) and correct Tachyon Port State Machine
+        // (GBIC) module definition is:
+        // GPIO1, GPIO0, GPIO4 for MD2, MD1, MD0.  The input states appear
+        // to be inverted -- i.e., a setting of 111 is read when there is NO
+        // GBIC present.  The Module Def (MD) spec says 000 is "no GBIC"
+        // Hard code the bit states to detect Copper, 
+        // Long wave (single mode), Short wave (multi-mode), and absent GBIC
+
+  ULONG ulBuff;
+
+  sprintf( cErrorString, "\nGBIC detected: ");
+
+  ulBuff = fcChip->Registers.TYstatus.value & 0x13; 
+  switch( ulBuff )
+  {
+  case 0x13:  // GPIO4, GPIO1, GPIO0 = 111; no GBIC!
+    sprintf( &cErrorString[ strlen( cErrorString)],
+            "NONE! ");
+    return FALSE;          
+          
+       
+  case 0x11:   // Copper GBIC detected
+    sprintf( &cErrorString[ strlen( cErrorString)],
+            "Copper. ");
+    break;
+
+  case 0x10:   // Long-wave (single mode) GBIC detected
+    sprintf( &cErrorString[ strlen( cErrorString)],
+        "Long-wave. ");
+    break;
+  case 0x1:    // Short-wave (multi mode) GBIC detected
+    sprintf( &cErrorString[ strlen( cErrorString)],
+        "Short-wave. ");
+    break;
+  default:     // unknown GBIC - presumably it will work (?)
+    sprintf( &cErrorString[ strlen( cErrorString)],
+            "Unknown. ");
+          
+    break;
+  }  // end switch GBIC detection
+
+  return TRUE;
+}
+
+
+
+
+
+
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[])
+{
+  // Tachyon's Frame Manager LPSM in LinkDown state?
+  // (For non-loop port, check PSM instead.)
+  // return string with state and FALSE is Link Down
+
+  int LinkUp;
+
+  if( fcChip->Registers.FMstatus.value & 0x80 ) 
+    LinkUp = FALSE;
+  else
+    LinkUp = TRUE;
+
+  sprintf( &cErrorString[ strlen( cErrorString)],
+    " LPSM %Xh ", 
+     (fcChip->Registers.FMstatus.value >>4) & 0xf );
+
+
+  switch( fcChip->Registers.FMstatus.value & 0xF0)
+  {
+                    // bits set in LPSM
+    case 0x10:
+      sprintf( &cErrorString[ strlen( cErrorString)], "ARB");
+      break;
+    case 0x20:
+      sprintf( &cErrorString[ strlen( cErrorString)], "ARBwon");
+      break;
+    case 0x30:
+      sprintf( &cErrorString[ strlen( cErrorString)], "OPEN");
+      break;
+    case 0x40:
+      sprintf( &cErrorString[ strlen( cErrorString)], "OPENed");
+      break;
+    case 0x50:
+      sprintf( &cErrorString[ strlen( cErrorString)], "XmitCLS");
+      break;
+    case 0x60:
+      sprintf( &cErrorString[ strlen( cErrorString)], "RxCLS");
+      break;
+    case 0x70:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Xfer");
+      break;
+    case 0x80:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Init");
+      break;
+    case 0x90:
+      sprintf( &cErrorString[ strlen( cErrorString)], "O-IInitFin");
+      break;
+    case 0xa0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "O-IProtocol");
+      break;
+    case 0xb0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "O-ILipRcvd");
+      break;
+    case 0xc0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "HostControl");
+      break;
+    case 0xd0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "LoopFail");
+      break;
+    case 0xe0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Offline");
+      break;
+    case 0xf0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "OldPort");
+      break;
+    case 0:
+    default:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Monitor");
+      break;
+
+  }
+
+  return LinkUp;
+}
+
+
+
+
+#include "linux/malloc.h"
+
+// Dynamic memory allocation alignment routines
+// HP's Tachyon Fibre Channel Controller chips require
+// certain memory queues and register pointers to be aligned
+// on various boundaries, usually the size of the Queue in question.
+// Alignment might be on 2, 4, 8, ... or even 512 byte boundaries.
+// Since most O/Ss don't allow this (usually only Cache aligned -
+// 32-byte boundary), these routines provide generic alignment (after
+// O/S allocation) at any boundary, and store the original allocated
+// pointer for deletion (O/S free function).  Typically, we expect
+// these functions to only be called at HBA initialization and
+// removal time (load and unload times)
+// ALGORITHM notes:
+// Memory allocation varies by compiler and platform.  In the worst case,
+// we are only assured BYTE allignment, but in the best case, we can
+// request allocation on any desired boundary.  Our strategy: pad the
+// allocation request size (i.e. waste memory) so that we are assured
+// of passing desired boundary near beginning of contiguous space, then
+// mask out lower address bits.
+// We define the following algorithm:
+//   allocBoundary - compiler/platform specific address alignment
+//                   in number of bytes (default is single byte; i.e. 1)
+//   n_alloc       - number of bytes application wants @ aligned address
+//   ab            - alignment boundary, in bytes (e.g. 4, 32, ...)
+//   t_alloc       - total allocation needed to ensure desired boundary
+//   mask          - to clear least significant address bits for boundary
+//   Compute:
+//   t_alloc = n_alloc + (ab - allocBoundary)
+//   allocate t_alloc bytes @ alloc_address
+//   mask =  NOT (ab - 1)
+//       (e.g. if ab=32  _0001 1111  -> _1110 0000
+//   aligned_address = alloc_address & mask
+//   set n_alloc bytes to 0
+//   return aligned_address (NULL if failed)
+//
+// If u32_AlignedAddress is non-zero, then search for BaseAddress (stored
+// from previous allocation).  If found, invoke call to FREE the memory.
+// Return NULL if BaseAddress not found
+
+// we need about 8 allocations per HBA.  Figuring at most 10 HBAs per server
+// size the dynamic_mem array at 80.
+
+void* fcMemManager( ALIGNED_MEM *dynamic_mem, ULONG n_alloc, ULONG ab,
+                   ULONG u32_AlignedAddress)
+{
+  USHORT allocBoundary=1;   // compiler specific - worst case 1
+                                  // best case - replace malloc() call
+                                  // with function that allocates exactly
+                                  // at desired boundary
+
+  unsigned long ulAddress;
+  ULONG t_alloc, i;
+  void *alloc_address = 0;  // def. error code / address not found
+  LONG mask;                // must be 32-bits wide!
+
+  ENTER("fcMemManager");
+  if( u32_AlignedAddress )          // are we freeing existing memory?
+  {
+//    printk(" freeing AlignedAddress %Xh\n", u32_AlignedAddress);
+    for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for the base address
+    {
+//    printk("dynamic_mem[%u].AlignedAddress %lX\n", i, dynamic_mem[i].AlignedAddress);
+      if( dynamic_mem[i].AlignedAddress == u32_AlignedAddress )
+      {
+        alloc_address = dynamic_mem[i].BaseAllocated; // 'success' status
+        kfree( dynamic_mem[i].BaseAllocated);  // return pages to kernel
+        dynamic_mem[i].BaseAllocated = 0;   // clear for next use
+        dynamic_mem[i].AlignedAddress = 0;
+        break;                        // quit for loop; done
+      }
+    }
+  }
+  else if( n_alloc )                   // want new memory?
+  {
+    t_alloc = n_alloc + (ab - allocBoundary); // pad bytes for alignment
+//    printk("kmalloc() for Tach alignment: %ld bytes\n", t_alloc);
+
+    alloc_address =                  // total bytes (NumberOfBytes)
+      kmalloc( t_alloc, GFP_KERNEL); // allow thread block to free pages 
+
+
+                                  // now mask off least sig. bits of address
+    if( alloc_address )           // (only if non-NULL)
+    {
+                                  // find place to store ptr, so we
+                                  // can free it later...
+      for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for free slot
+      {
+        if( dynamic_mem[i].BaseAllocated == 0) // take 1st available
+        {
+          dynamic_mem[i].BaseAllocated = alloc_address;// address from O/S
+          break;
+        }
+      }
+      mask = (LONG)(ab - 1);            // mask all low-order bits
+      mask = ~mask;                            // invert bits
+
+      ulAddress = (unsigned long)alloc_address;
+      
+      ulAddress += (ab - allocBoundary);    // add the alignment bytes-
+                                            // then truncate address...
+      alloc_address = (void*)(ulAddress & mask);
+      
+      dynamic_mem[i].AlignedAddress = 
+	(ULONG)(ulAddress & mask); // 32bit Tach address
+      memset( alloc_address, 0, n_alloc );  // clear new memory
+    }
+    else  // O/S dynamic mem alloc failed!
+      alloc_address = 0;  // (for debugging breakpt)
+
+  }
+
+  LEAVE("fcMemManager");
+  return alloc_address;  // good (or NULL) address
+}
+
+
+
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = CPQFCTS;
+
+#include "scsi_module.c"
+
+
+#endif
+
+
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTSioctl.h linux/drivers/scsi/cpqfcTSioctl.h
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTSioctl.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTSioctl.h	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,84 @@
+// for user apps, make sure data size types are defined
+// with 
+
+
+#define CCPQFCTS_IOC_MAGIC 'Z'
+
+typedef struct 
+{
+  __u8 bus;
+  __u8 dev_fn;
+  __u32 board_id;
+} cpqfc_pci_info_struct;
+
+typedef __u32 DriverVer_type;
+/*
+typedef union
+{
+  struct  // Peripheral Unit Device
+  { 
+    __u8 Bus:6;
+    __u8 Mode:2;  // b00
+    __u8 Dev;
+  } PeripDev;
+  struct  // Volume Set Address
+  { 
+    __u8 DevMSB:6;
+    __u8 Mode:2;  // b01
+    __u8 DevLSB;
+  } LogDev;
+  struct  // Logical Unit Device (SCSI-3, SCC-2 defined)
+  { 
+    __u8 Targ:6;
+    __u8 Mode:2;  // b10
+    __u8 Dev:5;
+    __u8 Bus:3;
+
+  } LogUnit;
+} SCSI3Addr_struct;
+
+
+typedef struct
+{
+  SCSI3Addr_struct FCP_Nexus;
+  __u8 cdb[16];
+} PassThru_Command_struct;
+*/
+
+/* this is nearly duplicated in idashare.h */
+typedef struct {
+  int	lc;    		/* Controller number */
+  int	node;		/* Node (box) number */
+  int	ld;		/* Logical Drive on this box, if required */
+  __u32 nexus;          /* SCSI Nexus */
+  void	*argp;		/* Argument pointer */
+} VENDOR_IOCTL_REQ;
+
+
+typedef struct {
+  char	cdb[16];	/* SCSI CDB for the pass-through */
+  ushort bus;		/* Target bus on the box */
+  ushort pdrive;	/* Physical drive on the box */
+  int len;              /* Length of the data area of the CDB */
+  int sense_len;	/* Length of the sense data */
+  char sense_data[40];  /* Sense data */
+  void *bufp;		/* Data area for the CDB */
+  char rw_flag;		/* Read CDB or Write CDB */
+} cpqfc_passthru_t;
+
+
+
+
+/*
+** Defines for the IOCTLS.
+*/
+
+#define VENDOR_READ_OPCODE			0x26
+#define VENDOR_WRITE_OPCODE			0x27
+
+#define CPQFCTS_GETPCIINFO _IOR( CCPQFCTS_IOC_MAGIC, 1, cpqfc_pci_info_struct)
+#define CPQFCTS_GETDRIVVER _IOR( CCPQFCTS_IOC_MAGIC, 9, DriverVer_type)
+
+#define CPQFCTS_SCSI_PASSTHRU _IOWR( CCPQFCTS_IOC_MAGIC,11, VENDOR_IOCTL_REQ)
+
+
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTSstructs.h linux/drivers/scsi/cpqfcTSstructs.h
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTSstructs.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTSstructs.h	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,1494 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *  
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) 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.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSSTRUCTS_H
+#define CPQFCTSSTRUCTS_H
+
+#include <linux/timer.h>  // timer declaration in our host data
+#include <linux/tqueue.h> // task queue sched
+#include <asm/atomic.h>
+#include "cpqfcTSioctl.h"
+
+#define DbgDelay(secs) { int wait_time; printk( " DbgDelay %ds ", secs); \
+                         for( wait_time=jiffies + (secs*HZ); \
+		         wait_time > jiffies ;) ; }
+#define CPQFCTS_DRIVER_VER(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
+#define VER_MAJOR 1
+#define VER_MINOR 3
+#define VER_SUBMINOR 4
+
+// Macros for kernel (esp. SMP) tracing using a PCI analyzer
+// (e.g. x86).
+//#define PCI_KERNEL_TRACE
+#ifdef PCI_KERNEL_TRACE
+#define PCI_TRACE(x) inl( fcChip->Registers.IOBaseL +x);
+#define PCI_TRACEO(x,y) outl( x, (fcChip->Registers.IOBaseL +y));
+#else
+
+#define PCI_TRACE(x) 
+#define PCI_TRACEO(x,y)
+#endif
+
+			 
+//#define DEBUG_CMND 1   // debug output for Linux Scsi CDBs
+//#define DUMMYCMND_DBG 1
+
+//#define DEBUG_CPQFCTS 1
+//#undef DEBUG_CPQFCTS 
+#ifdef DEBUG_CPQFCTS
+#define ENTER(x)	printk("cpqfcts : entering %s()\n", x);
+#define LEAVE(x)	printk("cpqfcts : leaving %s()\n", x);
+#define DEBUG(x)	x
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#define DEBUG(x)
+#endif				/* DEBUG_CPQFCTS */
+
+//#define DEBUG_CPQFCTS_PCI 1
+//#undef DEBUG_CPQFCTS_PCI
+#if DEBUG_CPQFCTS_PCI
+#define DEBUG_PCI(x)	x
+#else
+#define DEBUG_PCI(x)
+#endif				/* DEBUG_CPQFCTS_PCI */
+
+#define STACHLITE66_TS12  "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2"
+#define STACHLITE66_TS13  "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.3"
+#define STACHLITE_UNKNOWN "Compaq FibreChannel HBA Tachyon Chip/Board Ver??"
+#define SAGILENT_XL2_21   "Agilent FC HBA, Tachyon XL2 HPFC-5200B/2.1"
+
+// PDA is Peripheral Device Address, VSA is Volume Set Addressing
+// Linux SCSI parameters
+#define CPQFCTS_MAX_TARGET_ID 64
+#define CPQFCTS_MAX_LUN 8    // The RA-4x00 supports 32 (Linux SCSI supports 8)
+#define CPQFCTS_MAX_CHANNEL 0 // One FC port on cpqfcTS HBA
+
+#define CPQFCTS_CMD_PER_LUN 15 // power of 2 -1, must be >0 
+#define CPQFCTS_REQ_QUEUE_LEN (TACH_SEST_LEN/2) // must be < TACH_SEST_LEN
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#ifndef DECLARE_MUTEX_LOCKED
+#define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED
+#endif
+
+#define DEV_NAME "cpqfcTS"
+
+#define CPQ_DEVICE_ID     0xA0FC
+#define AGILENT_XL2_ID    0x1029
+
+typedef struct
+{
+  __u16 vendor_id;
+  __u16 device_id;
+} SupportedPCIcards;
+			 
+// nn:nn denotes bit field
+                            // TachyonHeader struct def.
+                            // the fields shared with ODB
+                            // need to have same value
+
+
+
+
+#ifndef BYTE
+//typedef UCHAR BYTE;
+typedef __u8 BYTE;
+#endif
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef LONG
+typedef __s32 LONG;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+#ifndef PVOID
+typedef void * PVOID;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+
+
+// macro for FC-PH reject codes
+// payload format for LS_RJT (FC payloads are big endian):
+//     byte  0         1         2         3  (MSB)
+// DWORD 0   01        00        00        00
+// DWORD 1   resvd     code      expl.     vendor
+
+#define LS_RJT_REASON( code, expl) (( code<<8) | (expl <<16))
+
+
+#define TachLiteSTATUS 0x12
+
+// Fibre Channel EXCHANGE status codes for Tachyon chips/ driver software
+// 32-bit ERROR word defines
+#define INVALID_ARGS 0x1
+#define LNKDWN_OSLS  0x2
+#define LNKDWN_LASER 0x4
+#define OUTQUE_FULL  0x8
+#define DRIVERQ_FULL 0x10
+#define SEST_FULL    0x20
+#define BAD_ALPA     0x40
+#define OVERFLOW     0x80  // inbound CM
+#define COUNT_ERROR     0x100  // inbound CM
+#define LINKFAIL_RX     0x200  // inbound CM
+#define ABORTSEQ_NOTIFY 0x400  // outbound CM
+#define LINKFAIL_TX     0x800  // outbound CM
+#define HOSTPROG_ERR     0x1000  // outbound CM
+#define FRAME_TO         0x2000  // outbound CM
+#define INV_ENTRY        0x4000  // outbound CM
+#define SESTPROG_ERR     0x8000  // outbound CM
+#define OUTBOUND_TIMEOUT 0x10000L // timeout waiting for Tachyon outbound CM
+#define INITIATOR_ABORT  0x20000L // initiator exchange timeout or O/S ABORT
+#define MEMPOOL_FAIL     0x40000L // O/S memory pool allocation failed
+#define FC2_TIMEOUT      0x80000L // driver timeout for lost frames
+#define TARGET_ABORT     0x100000L // ABTS received from FC port
+#define EXCHANGE_QUEUED  0x200000L // e.g. Link State was LDn on fcStart
+#define PORTID_CHANGED   0x400000L // fc Port address changed
+#define DEVICE_REMOVED   0x800000L // fc Port address changed
+// Several error scenarios result in SEST Exchange frames 
+// unexpectedly arriving in the SFQ
+#define SFQ_FRAME        0x1000000L // SFQ frames from open Exchange
+
+// Maximum number of Host Bus Adapters (HBA) / controllers supported
+// only important for mem allocation dimensions - increase as necessary
+
+#define MAX_ADAPTERS 8
+#define MAX_RX_PAYLOAD 1024  // hardware dependent max frame payload
+// Tach header struc defines
+#define SOFi3 0x7
+#define SOFf  0x8
+#define SOFn3 0xB
+#define EOFn  0x5
+#define EOFt  0x6
+
+// FCP R_CTL defines
+#define FCP_CMND 0x6
+#define FCP_XFER_RDY 0x5
+#define FCP_RSP 0x7
+#define FCP_RESPONSE 0x777 // (arbitrary #)
+#define NEED_FCP_RSP 0x77  // (arbitrary #)
+#define FCP_DATA 0x1
+
+#define RESET_TACH 0x100 // Reset Tachyon/TachLite
+#define SCSI_IWE 0x2000  // initiator write entry (for SEST)
+#define SCSI_IRE 0x3000  // initiator read entry (for SEST)
+#define SCSI_TRE 0x400  // target read entry (for SEST)
+#define SCSI_TWE 0x500  // target write entry (for SEST)
+#define TOGGLE_LASER 0x800
+#define LIP 0x900
+#define CLEAR_FCPORTS 99 // (arbitrary #) free mem for Logged in ports
+#define FMINIT 0x707     // (arbitrary) for Frame Manager Init command
+
+// BLS == Basic Link Service
+// ELS == Extended Link Service
+#define BLS_NOP 4
+#define BLS_ABTS 0x10   // FC-PH Basic Link Service Abort Sequence
+#define BLS_ABTS_ACC 0x100  // FC-PH Basic Link Service Abort Sequence Accept
+#define BLS_ABTS_RJT 0x101  // FC-PH Basic Link Service Abort Sequence Reject
+#define ELS_PLOGI 0x03  // FC-PH Port Login (arbitrary assign)
+#define ELS_SCR   0x70  // (arb assign) State Change Registration (Fabric)
+#define FCS_NSR   0x72  // (arb assign) Name Service Request (Fabric)
+#define ELS_FLOGI 0x44  // (arb assign) Fabric Login
+#define ELS_FDISC 0x41  // (arb assign) Fabric Discovery (Login)
+#define ELS_PDISC 0x50  // FC-PH2 Port Discovery
+#define ELS_ABTX  0x06  // FC-PH Abort Exchange 
+#define ELS_LOGO 0x05   // FC-PH Port Logout
+#define ELS_PRLI 0x20   // FCP-SCSI Process Login
+#define ELS_PRLO 0x21   // FCP-SCSI Process Logout
+#define ELS_LOGO_ACC 0x07   // {FC-PH} Port Logout Accept
+#define ELS_PLOGI_ACC 0x08  // {FC-PH} Port Login Accept
+#define ELS_ACC 0x18        // {FC-PH} (generic) ACCept
+#define ELS_PRLI_ACC 0x22  // {FCP-SCSI} Process Login Accept
+#define ELS_RJT 0x1000000
+#define SCSI_REPORT_LUNS 0x0A0
+#define REPORT_LUNS 0xA0 // SCSI-3 command op-code
+
+#define ELS_LILP_FRAME 0x00000711 // 1st payload word of LILP frame
+
+#define SFQ_UNASSISTED_FCP  1  // ICM, DWord3, "Type" unassisted FCP
+#define SFQ_UNKNOWN         0x31 // (arbitrary) ICM, DWord3, "Type" unknown
+
+// these "LINK" bits refer to loop or non-loop
+#define LINKACTIVE 0x2    // fcLinkQ type - LINK UP Tachyon FM 'Lup' bit set
+#define LINKDOWN 0xf2     // fcLinkQ type - LINK DOWN Tachyon FM 'Ldn' bit set
+
+//#define VOLUME_SET_ADDRESSING 1 // "channel" or "bus" 1
+
+typedef struct      // 32 bytes hdr ONLY (e.g. FCP_DATA buffer for SEST)
+{
+  ULONG reserved;   // dword 0 (don't use)
+  ULONG sof_eof;
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;         // dword 7 - relative offset
+} TachFCHDR;
+
+                    // NOTE!! the following struct MUST be 64 bytes.
+typedef struct      // 32 bytes hdr + 32 bytes payload
+{
+  ULONG reserved;   // dword 0 (don't use - must clear to 0)
+  ULONG sof_eof;    // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;  // dword 7 - relative offset
+//---------
+  __u32 pl[8];              // dwords 8-15 frame data payload
+} TachFCHDR_CMND;
+
+
+typedef struct      // 32 bytes hdr + 120 bytes payload
+{
+  ULONG reserved;   // dword 0 (don't use - must clear to 0)
+  ULONG sof_eof;    // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;  // dword 7 - relative offset
+//---------
+  __u32 pl[30];              // largest necessary payload (for LOGIN cmnds)
+} TachFCHDR_GCMND;
+
+typedef struct      // 32 bytes hdr + 64 bytes payload
+{
+  ULONG reserved;   // dword 0 (don't use)
+  ULONG sof_eof;
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;  // dword 7 - relative offset
+//---------
+  __u32 pl[18]; // payload for FCP-RSP (response buffer) RA-4x00 is 72bytes
+} TachFCHDR_RSP;
+
+
+
+
+
+
+// Inbound Message Queue structures...
+typedef struct              // each entry 8 words (32 bytes)
+{
+  ULONG type;               // IMQ completion message types
+  ULONG word[7];            // remainder of structure
+                            // interpreted by IMQ type
+} TachyonIMQE;
+
+
+// Queues for TachLite not in original Tachyon
+// ERQ       - Exchange Request Queue (for outbound commands)
+// SFQ       - Single Frame Queue (for incoming frames)
+
+                            // Define Tachyon Outbound Command Que
+                            // (Since many Tachyon registers are Read
+                            // only, maintain copies for debugging)
+                            // most Tach ques need power-of-2 sizes,
+                            // where registers are loaded with po2 -1
+#define TACH_SEST_LEN 512   // TachLite SEST
+
+#define ELS_EXCHANGES 64    // e.g. PLOGI, RSCN, ...
+// define the total number of outstanding (simultaneous) exchanges
+#define TACH_MAX_XID (TACH_SEST_LEN + ELS_EXCHANGES)  // ELS exchanges
+
+#define ERQ_LEN 128         // power of 2, max 4096
+
+// Inbound Message Queue structures...
+#define IMQ_LEN 512              // minimum 4 entries [(power of 2) - 1]
+typedef struct                   // 8 words - 32 bytes
+{
+  TachyonIMQE QEntry[IMQ_LEN];
+  ULONG producerIndex;   // IMQ Producer Index register
+                                 // @32 byte align
+  ULONG consumerIndex;   // Consumer Index register (in Tachyon)
+  ULONG length;          // Length register
+  ULONG base;
+} TachyonIMQ;                    // @ 32 * IMQ_LEN align
+
+
+
+typedef struct           // inbound completion message
+{
+  ULONG Type;
+  ULONG Index;
+  ULONG TransferLength;
+} TachyonInbCM;
+
+
+
+// arbitrary numeric tags for TL structures
+#define TL_FCHS 1  // TachLite Fibre Channel Header Structure
+#define TL_IWE 2  // initiator write entry (for SEST)
+#define TL_TWE 3  // target write entry (for SEST)
+#define TL_IRE 4  // initiator read entry (for SEST)
+#define TL_TRE 5  // target read entry (for SEST)
+#define TL_IRB 6  // I/O request block
+
+                                // for INCOMING frames
+#define SFQ_LEN 32              // minimum 32 entries, max 4096
+
+typedef struct                  // Single Frame Que
+{
+  TachFCHDR_CMND QEntry[SFQ_LEN]; // must be 64 bytes!!
+  ULONG producerIndex;   // IMQ Producer Index register
+                                 // @32 byte align
+  ULONG consumerIndex;   // Consumer Index register (in Tachyon)
+  ULONG length;          // Length register
+  ULONG base;
+} TachLiteSFQ;
+
+
+typedef struct                 // I/O Request Block flags
+{
+  UCHAR  BRD : 1;
+  UCHAR      : 1; // reserved
+  UCHAR  SFA : 1;
+  UCHAR  DNC : 1;
+  UCHAR  DIN : 1;
+  UCHAR  DCM : 1;
+  UCHAR  CTS : 1;
+  UCHAR  SBV : 1;  // IRB entry valid - IRB'B' only
+} IRBflags;
+
+typedef struct                  // I/O Request Block
+{                          // Request 'A'
+  ULONG Req_A_SFS_Len;     // total frame len (hdr + payload), min 32
+  ULONG Req_A_SFS_Addr;    // 32-bit pointer to FCHS struct (to be sent)
+  ULONG Req_A_SFS_D_ID;    // 24-bit FC destination (i.e. 8 bit al_pa)
+  ULONG Req_A_Trans_ID;    // X_ID (OX_ID or RX_ID) and/or Index in SEST
+                           // Request 'B'
+  ULONG Req_B_SFS_Len;     // total frame len (hdr + payload), min 32
+  ULONG Req_B_SFS_Addr;    // 32-bit pointer to FCHS struct (to be sent)
+  ULONG Req_B_SFS_D_ID;    // 24-bit FC destination (i.e. 8 bit al_pa)
+  ULONG Req_B_Trans_ID;    // X_ID (OX_ID or RX_ID) and/or Index in SEST
+} TachLiteIRB;
+
+
+typedef struct           // TachLite placeholder for IRBs
+{                        // aligned @sizeof(ERQ) for TachLite
+                         // MAX commands is sum of SEST len and ERQ
+                         // we know that each SEST entry requires an
+                         // IRB (ERQ) entry; in addition, we provide
+                         // ERQ_LEN
+  TachLiteIRB QEntry[ERQ_LEN]; // Base register; entries 32 bytes ea.
+  ULONG consumerIndex;   // Consumer Index register
+  ULONG producerIndex;   // ERQ Producer Index register
+  ULONG length;          // Length register
+  ULONG base;            // copy of base ptr for debug
+                         // struct is sized for largest expected cmnd (LOGIN)
+} TachLiteERQ;
+
+
+#define TL_MAX_SGPAGES 4  // arbitrary limit to # of TL Ext. S/G pages
+                          // stores array of allocated page blocks used
+                          // in extended S/G lists.  Affects amount of static
+                          // memory consumed by driver.
+#define TL_EXT_SG_PAGE_COUNT 256  // Number of Extended Scatter/Gather a/l PAIRS
+                                  // Tachyon register (IOBaseU 0x68)
+                                  // power-of-2 value ONLY!  4 min, 256 max
+
+                          // byte len is #Pairs * 2 ULONG/Pair * 4 bytes/ULONG
+#define TL_EXT_SG_PAGE_BYTELEN (TL_EXT_SG_PAGE_COUNT *2 *4)
+
+
+
+// SEST entry types: IWE, IRE, TWE, TRE
+typedef struct 
+{
+  ULONG Hdr_Len;
+  ULONG Hdr_Addr;
+  ULONG RSP_Len;
+  ULONG RSP_Addr;
+  ULONG Buff_Off;
+  ULONG Link;
+  ULONG RX_ID;
+  ULONG Data_Len;
+  ULONG Exp_RO;
+  ULONG Exp_Byte_Cnt;
+   // --- extended/local Gather Len/Address pairs
+  ULONG GLen1;
+  ULONG GAddr1;
+  ULONG GLen2;
+  ULONG GAddr2;
+  ULONG GLen3;
+  ULONG GAddr3;
+} TachLiteIWE;
+
+
+typedef struct 
+{
+  ULONG Seq_Accum;
+  ULONG reserved;       // must clear to 0
+  ULONG RSP_Len;
+  ULONG RSP_Addr;
+  ULONG Buff_Off;
+  ULONG Buff_Index;           // ULONG 5
+  ULONG Exp_RO;
+  ULONG Byte_Count;
+  ULONG reserved_;      // ULONG 8
+  ULONG Exp_Byte_Cnt;
+   // --- extended/local Scatter Len/Address pairs
+  ULONG SLen1;
+  ULONG SAddr1;
+  ULONG SLen2;
+  ULONG SAddr2;
+  ULONG SLen3;
+  ULONG SAddr3;
+} TachLiteIRE;
+
+
+typedef struct          // Target Write Entry
+{
+  ULONG Seq_Accum;      // dword 0
+  ULONG reserved;       // dword 1  must clear to 0
+  ULONG Remote_Node_ID;
+  ULONG reserved1;      // dword 3  must clear to 0
+  ULONG Buff_Off;
+  ULONG Buff_Index;     // ULONG 5
+  ULONG Exp_RO;
+  ULONG Byte_Count;
+  ULONG reserved_;      // ULONG 8
+  ULONG Exp_Byte_Cnt;
+   // --- extended/local Scatter Len/Address pairs
+  ULONG SLen1;
+  ULONG SAddr1;
+  ULONG SLen2;
+  ULONG SAddr2;
+  ULONG SLen3;
+  ULONG SAddr3;
+} TachLiteTWE;
+
+typedef struct      
+{
+  ULONG Hdr_Len;
+  ULONG Hdr_Addr;
+  ULONG RSP_Len;        // DWord 2
+  ULONG RSP_Addr;
+  ULONG Buff_Off;
+  ULONG Buff_Index;     // DWord 5
+  ULONG reserved;
+  ULONG Data_Len;
+  ULONG reserved_;
+  ULONG reserved__;
+   // --- extended/local Gather Len/Address pairs
+  ULONG GLen1;          // DWord A
+  ULONG GAddr1;
+  ULONG GLen2;
+  ULONG GAddr2;
+  ULONG GLen3;
+  ULONG GAddr3;
+} TachLiteTRE;
+
+typedef struct
+{
+  void *PoolPage[TL_MAX_SGPAGES];
+} SGPAGES, *PSGPAGES; // linked list of S/G pairs, by Exchange
+
+
+
+typedef struct                  // SCSI Exchange State Table
+{
+  union                         // Entry can be IWE, IRE, TWE, TRE
+  {                             // 64 bytes per entry
+    TachLiteIWE IWE;
+    TachLiteIRE IRE;
+    TachLiteTWE TWE;
+    TachLiteTRE TRE;
+  } u[TACH_SEST_LEN];
+
+  TachFCHDR DataHDR[TACH_SEST_LEN]; // for SEST FCP_DATA frame hdr (no pl)
+  TachFCHDR_RSP RspHDR[TACH_SEST_LEN]; // space for SEST FCP_RSP frame
+  SGPAGES sgPages[TACH_SEST_LEN]; // array of Pool-allocations
+  ULONG length;          // Length register
+  ULONG base;            // copy of base ptr for debug
+} TachSEST;
+
+
+
+typedef struct                  // each register has it's own address
+                                // and value (used for write-only regs)
+{
+  void* address;
+  volatile ULONG value;
+} FCREGISTER;
+
+typedef struct         // Host copy - TachLite Registers
+{
+  ULONG IOBaseL, IOBaseU;  // I/O port lower and upper TL register addresses
+  ULONG MemBase;           // memory mapped register addresses
+  void* ReMapMemBase;      // O/S VM reference for MemBase
+  ULONG wwn_hi;            // WWN is set once at startup
+  ULONG wwn_lo;
+  ULONG my_al_pa;          // al_pa received after LIP()
+  ULONG ROMCTR;            // flags for on-board RAM/ROM
+  ULONG RAMBase;           // on-board RAM (i.e. some Tachlites)
+  ULONG SROMBase;          // on-board EEPROM (some Tachlites)
+  ULONG PCIMCTR;           // PCI Master Control Reg (has bus width)
+
+  FCREGISTER INTEN;        // copy of interrupt enable mask
+  FCREGISTER INTPEND;      // interrupt pending
+  FCREGISTER INTSTAT;      // interrupt status
+  FCREGISTER SFQconsumerIndex; 
+  FCREGISTER ERQproducerIndex; 
+  FCREGISTER TYconfig;   // TachYon (chip level)
+  FCREGISTER TYcontrol;
+  FCREGISTER TYstatus;
+  FCREGISTER FMconfig;   // Frame Manager (FC loop level)
+  FCREGISTER FMcontrol;
+  FCREGISTER FMstatus;
+  FCREGISTER FMLinkStatus1;
+  FCREGISTER FMLinkStatus2;
+  FCREGISTER FMBB_CreditZero;
+  FCREGISTER status;
+  FCREGISTER ed_tov;     // error detect time-out value
+  FCREGISTER rcv_al_pa;  // received arb. loop physical address
+  FCREGISTER primitive;  // e.g. LIP(), OPN(), ...
+} TL_REGISTERS;
+
+
+
+typedef struct 
+{
+  ULONG ok;
+  ULONG invalidArgs;
+  ULONG linkDown;
+  ULONG linkUp;
+  ULONG outQueFull;
+  ULONG SESTFull;
+  ULONG hpe;    // host programming err (from Tach)
+  ULONG FC4aborted; // aborts from Application or upper driver layer
+  ULONG FC2aborted; // aborts from our driver's timeouts
+  ULONG timeouts;   // our driver timeout (on individual exchanges)
+  ULONG logouts;    // explicit - sent LOGO; implicit - device removed
+  ULONG retries;
+  ULONG linkFailTX;
+  ULONG linkFailRX;
+  ULONG CntErrors;  // byte count expected != count received (typ. SEST)
+  ULONG e_stores;   // elastic store errs
+  ULONG resets;     // hard or soft controller resets
+  ULONG FMinits;    // TACH Frame Manager Init (e.g. LIPs)
+  ULONG lnkQueFull;  // too many LOGIN, loop commands
+  ULONG ScsiQueFull; // too many FCP-SCSI inbound frames
+  ULONG LossofSignal;   // FM link status 1 regs
+  ULONG BadRXChar;   // FM link status 1 regs
+  ULONG LossofSync;   // FM link status 1 regs
+  ULONG Rx_EOFa;   // FM link status 2 regs (received EOFa)
+  ULONG Dis_Frm;   // FM link status 2 regs (discarded frames)
+  ULONG Bad_CRC;   // FM link status 2 regs
+  ULONG BB0_Timer; //  FM BB_Credit Zero Timer Reg
+  ULONG loopBreaks; // infinite loop exits
+  ULONG lastBB0timer;  // static accum. buffer needed by Tachlite
+} FCSTATS;
+
+
+typedef struct               // Config Options
+{                            // LS Bit first
+  USHORT        : 1;           // bit0:
+  USHORT  flogi : 1;           // bit1: We sent FLOGI - wait for Fabric logins
+  USHORT  fabric: 1;           // bit2: Tachyon detected Fabric (FM stat LG)
+  USHORT  LILPin: 1;           // bit3: We can use an FC-AL LILP frame
+  USHORT  target: 1;           // bit4: this Port has SCSI target capability
+  USHORT  initiator:    1;     // bit5: this Port has SCSI initiator capability
+  USHORT  extLoopback:  1;     // bit6: loopback at GBIC
+  USHORT  intLoopback:  1;     // bit7: loopback in HP silicon
+  USHORT        : 1;           // bit8:
+  USHORT        : 1;           // bit9:
+  USHORT        : 1;           // bit10:
+  USHORT        : 1;           // bit11:
+  USHORT        : 1;           // bit12:
+  USHORT        : 1;           // bit13:
+  USHORT        : 1;           // bit14:
+  USHORT        : 1;           // bit15:
+} FC_OPTIONS;
+
+
+
+typedef struct dyn_mem_pair
+{
+  void *BaseAllocated;  // address as allocated from O/S;
+  unsigned long AlignedAddress; // aligned address (used by Tachyon DMA)
+} ALIGNED_MEM;
+
+
+
+
+// these structs contain only CRUCIAL (stuff we actually use) parameters
+// from FC-PH(n) logins.  (Don't save entire LOGIN payload to save mem.)
+
+// Implicit logout happens when the loop goes down - we require PDISC
+// to restore.  Explicit logout is when WE decide never to talk to someone,
+// or when a target refuses to talk to us, i.e. sends us a LOGO frame or
+// LS_RJT reject in response to our PLOGI request.
+
+#define IMPLICIT_LOGOUT 1
+#define EXPLICIT_LOGOUT 2
+
+typedef struct 
+{
+  UCHAR channel; // SCSI "bus"
+  UCHAR target;
+  UCHAR InqDeviceType;  // byte 0 from SCSI Inquiry response
+  UCHAR VolumeSetAddressing;  // FCP-SCSI LUN coding (40h for VSA)
+  UCHAR LunMasking;     // True if selective presentation supported
+  UCHAR lun[CPQFCTS_MAX_LUN];
+} SCSI_NEXUS;
+
+
+typedef struct        
+{
+  union 
+  {
+    UCHAR ucWWN[8];  // a FC 64-bit World Wide Name/ PortID of target
+                     // addressing of single target on single loop...
+    u64 liWWN;
+  } u;
+
+  ULONG port_id;     // a FC 24-bit address of port (lower 8 bits = al_pa)
+
+  Scsi_Cmnd ScsiCmnd;   // command buffer for Report Luns
+#define REPORT_LUNS_PL 256  
+  UCHAR ReportLunsPayload[REPORT_LUNS_PL];
+  
+  SCSI_NEXUS ScsiNexus; // LUNs per FC device
+
+  ULONG LOGO_counter; // might try several times before logging out for good
+  ULONG LOGO_timer;   // after LIP, ports expecting PDISC must time-out and
+                      // LOGOut if successful PDISC not completed in 2 secs
+
+  ULONG concurrent_seq;  // must be 1 or greater
+  ULONG rx_data_size;    // e.g. 128, 256, 1024, 2048 per FC-PH spec
+  ULONG BB_credit;
+  ULONG EE_credit;
+
+  ULONG fcp_info;        // from PRLI (i.e. INITIATOR/ TARGET flags)
+                         // flags for login process
+  BOOLEAN Originator;    // Login sequence Originated (if false, we
+                         // responded to another port's login sequence)
+  BOOLEAN plogi;         // PLOGI frame ACCepted (originated or responded)
+  BOOLEAN pdisc;         // PDISC frame was ORIGINATED (self-login logic)
+  BOOLEAN prli;          // PRLI frame ACCepted (originated or responded)
+  BOOLEAN flogi;         // FLOGI frame ACCepted (originated or responded)
+  BOOLEAN logo;          // port permanently logged out (invalid login param)
+  BOOLEAN flogiReq;      // Fabric login required (set in LIP process)
+  UCHAR highest_ver;
+  UCHAR lowest_ver;
+
+  
+  // when the "target" (actually FC Port) is waiting for login
+  // (e.g. after Link reset), set the device_blocked bit;
+  // after Port completes login, un-block target.
+  UCHAR device_blocked; // see Scsi_Device struct
+
+                    // define singly-linked list of logged-in ports
+                    // once a port_id is identified, it is remembered,
+                    // even if the port is removed indefinitely
+  PVOID pNextPort;  // actually, type PFC_LOGGEDIN_PORT; void for Compiler
+
+} FC_LOGGEDIN_PORT, *PFC_LOGGEDIN_PORT;
+
+
+
+// This serves as the ESB (Exchange Status Block),
+// and has timeout counter; used for ABORTs
+typedef struct                
+{                                  // FC-1 X_IDs
+  ULONG type;            // ELS_PLOGI, SCSI_IWE, ... (0 if free)
+  PFC_LOGGEDIN_PORT pLoggedInPort; // FC device on other end of Exchange
+  Scsi_Cmnd *Cmnd;       // Linux SCSI command packet includes S/G list
+  ULONG timeOut;         // units of ??, DEC by driver, Abort when 0
+  ULONG reTries;         // need one or more retries?
+  ULONG status;          // flags indicating errors (0 if none)
+  TachLiteIRB IRB;       // I/O Request Block, gets copied to ERQ
+  TachFCHDR_GCMND fchs;  // location of IRB's Req_A_SFS_Addr
+} FC_EXCHANGE, *PFC_EXCHANGE;
+
+// Unfortunately, Linux limits our kmalloc() allocations to 128k.
+// Because of this and the fact that our ScsiRegister allocation
+// is also constrained, we move this large structure out for
+// allocation after Scsi Register.
+// (In other words, this cumbersome indirection is necessary
+// because of kernel memory allocation constraints!)
+
+typedef struct // we will allocate this dynamically
+{
+  FC_EXCHANGE fcExchange[ TACH_MAX_XID ];
+} FC_EXCHANGES;
+
+
+
+
+
+
+
+
+
+
+
+typedef struct
+{
+  char Name[64]; // name of controller ("HP Tachlite TL Rev2.0, 33MHz, 64bit bus")
+  //PVOID  pAdapterDevExt; // back pointer to device object/extension
+  ULONG ChipType;        // local numeric key for Tachyon Type / Rev.
+  ULONG status;              // our Driver - logical status
+  
+  TL_REGISTERS Registers;    // reg addresses & host memory copies
+                             // FC-4 mapping of 'transaction' to X_IDs
+  UCHAR LILPmap[32*4];       // Loop Position Map of ALPAs (late FC-AL only)
+  FC_OPTIONS Options;        // e.g. Target, Initiator, loopback...
+  UCHAR highest_FCPH_ver;    // FC-PH version limits
+  UCHAR lowest_FCPH_ver;     // FC-PH version limits
+
+  FC_EXCHANGES *Exchanges;  
+  ULONG fcLsExchangeLRU;       // Least Recently Used counter (Link Service)
+  ULONG fcSestExchangeLRU;       // Least Recently Used counter (FCP-SCSI)
+  FC_LOGGEDIN_PORT fcPorts;  // linked list of every FC port ever seen
+  FCSTATS fcStats;           // FC comm err counters
+
+                             // Host memory QUEUE pointers
+  TachLiteERQ *ERQ;          // Exchange Request Que 
+  TachyonIMQ *IMQ;           // Inbound Message Que 
+  TachLiteSFQ *SFQ;          // Single Frame Queue
+  TachSEST *SEST;            // SCSI Exchange State Table
+
+  // these function pointers are for "generic" functions, which are
+  // replaced with Host Bus Adapter types at
+  // runtime.
+  int (*CreateTachyonQues)( void* , int);
+  int (*DestroyTachyonQues)( void* , int);
+  int (*LaserControl)(void*, int );   // e.g. On/Off
+  int (*ResetTachyon)(void*, int );
+  void (*FreezeTachyon)(void*, int );
+  void (*UnFreezeTachyon)(void*, int );
+  int (*InitializeTachyon)(void*, int, int );
+  int (*InitializeFrameManager)(void*, int );
+  int (*ProcessIMQEntry)(void*);
+  int (*ReadWriteWWN)(void*, int ReadWrite);
+  int (*ReadWriteNVRAM)(void*, void*, int ReadWrite);
+
+} TACHYON, *PTACHYON;
+
+
+void cpqfcTSClearLinkStatusCounters(TACHYON * fcChip);
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode);
+int CpqTsDestroyTachLiteQues( void* , int);
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2);
+
+int CpqTsProcessIMQEntry(void* pHBA);
+int CpqTsResetTachLite(void *pHBA, int type);
+void CpqTsFreezeTachlite(void *pHBA, int type);
+void CpqTsUnFreezeTachlite(void *pHBA, int type);
+int CpqTsInitializeFrameManager(void *pHBA, int);
+int CpqTsLaserControl( void* addrBase, int opcode );
+int CpqTsReadWriteWWN(void*, int ReadWrite);
+int CpqTsReadWriteNVRAM(void*, void* data, int ReadWrite);
+
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter);
+void cpqfcTSWorkerThread( void *host);
+
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf );
+ULONG cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+	UCHAR *buf );
+
+BOOLEAN tl_write_i2c_nvram( void* GPIOin, void* GPIOout,
+  USHORT startOffset,  // e.g. 0x2f for WWN start
+  USHORT count,
+  UCHAR *buf );
+
+
+// define misc functions 
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]);
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]);
+void* fcMemManager( ALIGNED_MEM *dyn_mem_pair, ULONG n_alloc, ULONG ab,
+                   ULONG ulAlignedAddress);
+
+void BigEndianSwap(  UCHAR *source, UCHAR *dest,  USHORT cnt);
+
+//ULONG virt_to_phys( PVOID virtaddr );
+                  
+
+// Linux interrupt handler
+void cpqfcTS_intr_handler( int irq,void *dev_id,struct pt_regs *regs);
+void cpqfcTSheartbeat( unsigned long ptr );
+
+
+
+// The biggest Q element we deal with is Aborts - we
+// need 4 bytes for x_ID, and a Scsi_Cmnd (~284 bytes)
+//#define LINKQ_ITEM_SIZE ((4+sizeof(Scsi_Cmnd)+3)/4)
+#define LINKQ_ITEM_SIZE (3*16)
+typedef struct
+{
+  ULONG Type;              // e.g. LINKUP, SFQENTRY, PDISC, BLS_ABTS, ...
+  ULONG ulBuff[ LINKQ_ITEM_SIZE ];
+} LINKQ_ITEM;
+
+#define FC_LINKQ_DEPTH TACH_MAX_XID
+typedef struct
+{
+  ULONG producer;
+  ULONG consumer;  // when producer equals consumer, Q empty
+
+  LINKQ_ITEM Qitem[ FC_LINKQ_DEPTH ];
+
+} FC_LINK_QUE, *PFC_LINK_QUE;
+
+
+     // DPC routines post to here on Inbound SCSI frames
+     // User thread processes
+#define FC_SCSIQ_DEPTH 32
+
+typedef struct
+{
+  int Type;              // e.g. SCSI
+  ULONG ulBuff[ 3*16 ];
+} SCSIQ_ITEM;
+
+typedef struct
+{
+  ULONG producer;
+  ULONG consumer;  // when producer equals consumer, Q empty
+
+  SCSIQ_ITEM Qitem[ FC_SCSIQ_DEPTH ];
+
+} FC_SCSI_QUE, *PFC_SCSI_QUE;
+
+
+
+
+
+#define DYNAMIC_ALLOCATIONS 4  // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST
+
+// Linux space allocated per HBA (chip state, etc.)
+typedef struct 
+{
+  struct Scsi_Host *HostAdapter; // back pointer to Linux Scsi struct
+
+  TACHYON fcChip;    // All Tachyon registers, Queues, functions
+  ALIGNED_MEM dynamic_mem[DYNAMIC_ALLOCATIONS];
+
+  struct pci_dev *PciDev;
+
+  Scsi_Cmnd *LinkDnCmnd[CPQFCTS_REQ_QUEUE_LEN]; // collects Cmnds during LDn
+                                                // (for Acceptable targets)
+  Scsi_Cmnd *BoardLockCmnd[CPQFCTS_REQ_QUEUE_LEN]; // SEST was full
+  
+  Scsi_Cmnd *BadTargetCmnd[CPQFCTS_MAX_TARGET_ID]; // missing targets
+
+  u_char HBAnum;     // 0-based host number
+
+
+  struct timer_list cpqfcTStimer; // FC utility timer for implicit
+                                  // logouts, FC protocol timeouts, etc.
+  int fcStatsTime;  // Statistics delta reporting time
+
+  struct task_struct *worker_thread; // our kernel thread
+  int PortDiscDone;    // set by SendLogins(), cleared by LDn
+  
+  struct semaphore *TachFrozen;
+  struct semaphore *TYOBcomplete;    // handshake for Tach outbound frames
+  struct semaphore *fcQueReady;      // FibreChannel work for our kernel thread
+  struct semaphore *notify_wt;       // synchronizes kernel thread kill
+  struct semaphore *BoardLock;
+  
+  PFC_LINK_QUE fcLQ;             // the WorkerThread operates on this
+
+  spinlock_t hba_spinlock;           // held/released by WorkerThread
+
+} CPQFCHBA;
+
+#define	CPQ_SPINLOCK_HBA( x )   spin_lock(&x->hba_spinlock);
+#define CPQ_SPINUNLOCK_HBA(x)   spin_unlock(&x->hba_spinlock);
+
+
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+		PFC_LOGGEDIN_PORT pFcPort);
+
+
+void cpqfcTSTerminateExchange( CPQFCHBA*, SCSI_NEXUS *target, int );
+
+PFC_LOGGEDIN_PORT fcPortLoggedIn( 
+   CPQFCHBA *cpqfcHBAdata, 
+   TachFCHDR_GCMND* fchs, 
+   BOOLEAN, 
+   BOOLEAN);
+void fcProcessLoggedIn( 
+   CPQFCHBA *cpqfcHBAdata, TachFCHDR_GCMND* fchs);
+
+
+ULONG cpqfcTSBuildExchange( 
+  CPQFCHBA *cpqfcHBAdata,
+  ULONG type, // e.g. PLOGI
+  TachFCHDR_GCMND* InFCHS,  // incoming FCHS
+  void *Data,               // the CDB, scatter/gather, etc.  
+  LONG *ExchangeID );       // allocated exchange ID
+
+ULONG cpqfcTSStartExchange( 
+  CPQFCHBA *cpqfcHBAdata,
+  LONG ExchangeID );
+
+void cpqfcTSCompleteExchange( 
+       PTACHYON fcChip, 
+       ULONG exchange_ID);
+
+
+PFC_LOGGEDIN_PORT  fcFindLoggedInPort( 
+  PTACHYON fcChip, 
+  Scsi_Cmnd *Cmnd,  // (We want the channel/target/lun Nexus from Cmnd)
+  ULONG port_id,  // search linked list for al_pa, or
+  UCHAR wwn[8],    // search linked list for WWN, or...
+  PFC_LOGGEDIN_PORT *pLastLoggedInPort
+);
+
+// don't do this unless you have the right hardware!
+#define TRIGGERABLE_HBA 1
+#ifdef TRIGGERABLE_HBA
+void TriggerHBA( void*, int);
+#endif
+
+void cpqfcTSPutLinkQue( 
+  CPQFCHBA *cpqfcHBAdata, 
+  int Type, 
+  void *QueContent);
+
+void fcPutScsiQue( 
+  CPQFCHBA *cpqfcHBAdata, 
+  int Type, 
+  void *QueContent);
+
+void fcLinkQReset(
+   CPQFCHBA *);
+void fcScsiQReset(
+   CPQFCHBA *);
+void fcSestReset(
+   CPQFCHBA *);
+
+
+
+
+
+extern const UCHAR valid_al_pa[];
+extern const int number_of_al_pa;
+
+#define FCP_RESID_UNDER   0x80000
+#define FCP_RESID_OVER    0x40000
+#define FCP_SNS_LEN_VALID 0x20000
+#define FCP_RSP_LEN_VALID 0x10000
+
+// RSP_CODE definitions (dpANS Fibre Channel Protocol for SCSI, pg 34)
+#define FCP_DATA_LEN_NOT_BURST_LEN 0x1000000
+#define FCP_CMND_FIELD_INVALID     0x2000000
+#define FCP_DATA_RO_NOT_XRDY_RO    0x3000000
+#define FCP_TASKFUNCTION_NS        0x4000000
+#define FCP_TASKFUNCTION_FAIL      0x5000000
+
+// FCP-SCSI response status struct
+typedef struct  // see "TachFCHDR_RSP" definition - 64 bytes
+{
+  __u32 reserved;
+  __u32 reserved1;
+  __u32 fcp_status;    // field validity and SCSI status
+  __u32 fcp_resid;
+  __u32 fcp_sns_len;   // length of FCP_SNS_INFO field
+  __u32 fcp_rsp_len;   // length of FCP_RSP_INFO field (expect 8)
+  __u32 fcp_rsp_info;  // 4 bytes of FCP protocol response information
+  __u32 fcp_rsp_info2; // (4 more bytes, since most implementations use 8)
+  __u8  fcp_sns_info[36]; // bytes for SCSI sense (ASC, ASCQ)
+
+} FCP_STATUS_RESPONSE, *PFCP_STATUS_RESPONSE;
+
+
+// Fabric State Change Registration
+typedef struct scrpl
+{
+  __u32 command;
+  __u32 function;
+} SCR_PL;
+
+// Fabric Name Service Request
+typedef struct nsrpl
+{
+  __u32 CT_Rev;  // (& IN_ID)   WORD 0
+  __u32 FCS_Type;            // WORD 1
+  __u32 Command_code;        // WORD 2
+  __u32 reason_code;         // WORD 3
+  __u32 FCP;                 // WORD 4 (lower byte)
+  
+} NSR_PL;
+
+
+
+// "FC.H"
+#define MAX_RX_SIZE		0x800	// Max Receive Buffer Size is 2048
+#define MIN_RX_SIZE		0x100	// Min Size is 256, per FC-PLDA Spec
+#define	MAX_TARGET_RXIDS	SEST_DEPTH
+#define TARGET_RX_SIZE		SEST_BUFFER_LENGTH
+
+#define CLASS_1			0x01
+#define CLASS_2			0x02
+#define CLASS_3			0x03
+
+#define FC_PH42			0x08
+#define FC_PH43			0x09
+#define FC_PH3			0x20
+
+#define RR_TOV			2	// Minimum Time for target to wait for
+					// PDISC after a LIP.
+#define E_D_TOV			2	// Minimum Time to wait for Sequence
+					// Completion.
+#define R_A_TOV			0	// Minimum Time for Target to wait 
+					// before reclaiming resources.
+//
+//	R_CTL Field
+//
+//	Routing Bits (31-28)
+//
+#define FC4_DEVICE_DATA		0x00000000
+#define EXT_LINK_DATA		0x20000000
+#define FC4_LINK_DATA		0x30000000
+#define VIDEO_DATA		0x40000000
+#define BASIC_LINK_DATA		0x80000000
+#define LINK_CONTROL		0xC0000000
+#define ROUTING_MASK		0xF0000000
+
+//
+//	Information Bits (27-24)
+//
+#define UNCAT_INFORMATION	0x00000000
+#define SOLICITED_DATA		0x01000000
+#define UNSOLICITED_CONTROL	0x02000000
+#define SOLICITED_CONTROL	0x03000000
+#define UNSOLICITED_DATA	0x04000000
+#define DATA_DESCRIPTOR		0x05000000
+#define UNSOLICITED_COMMAND	0x06000000
+#define COMMAND_STATUS		0x07000000
+#define INFO_MASK		0x0F000000
+//
+//	(Link Control Codes)
+//
+#define ACK_1			0x00000000
+#define ACK_0_OR_N		0x01000000
+#define P_RJT			0x02000000 
+#define F_RJT			0x03000000 
+#define P_BSY			0x04000000
+#define FABRIC_BUSY_TO_DF	0x05000000	// Fabric Busy to Data Frame
+#define FABRIC_BUSY_TO_LC	0x06000000	// Fabric Busy to Link Ctl Frame
+#define LINK_CREDIT_RESET	0x07000000
+//
+//	(Link Service Command Codes)
+//
+//#define LS_RJT			0x01000000	// LS Reject
+
+#define LS_ACC			0x02000000	// LS Accept
+#define LS_PLOGI		0x03000000	// N_PORT Login
+#define LS_FLOGI		0x04000000	// F_PORT Login
+#define LS_LOGO			0x05000000	// Logout
+#define LS_ABTX			0x06000000	// Abort Exchange
+#define LS_RCS			0x07000000	// Read Connection Status
+#define LS_RES			0x08000000	// Read Exchange Status
+#define LS_RSS			0x09000000	// Read Sequence Status
+#define LS_RSI			0x0A000000	// Request Seq Initiative
+#define LS_ESTS			0x0B000000	// Establish Steaming
+#define LS_ESTC			0x0C000000	// Estimate Credit
+#define LS_ADVC			0x0D000000	// Advice Credit
+#define LS_RTV			0x0E000000	// Read Timeout Value
+#define LS_RLS			0x0F000000	// Read Link Status
+#define LS_ECHO			0x10000000	// Echo
+#define LS_TEST			0x11000000	// Test
+#define LS_RRQ			0x12000000	// Reinstate Rec. Qual.
+#define LS_PRLI			0x20000000	// Process Login
+#define LS_PRLO			0x21000000	// Process Logout
+#define LS_TPRLO		0x24000000	// 3rd Party Process Logout
+#define LS_PDISC		0x50000000	// Process Discovery
+#define LS_FDISC		0x51000000	// Fabric Discovery
+#define LS_ADISC		0x52000000	// Discover Address
+#define LS_RNC			0x53000000	// Report Node Capability
+#define LS_SCR                  0x62000000      // State Change Registration
+#define LS_MASK			0xFF000000	
+
+//
+// 	TYPE Bit Masks
+//
+#define BASIC_LINK_SERVICE	0x00000000
+#define EXT_LINK_SERVICE	0x01000000
+
+#define LLC			0x04000000
+#define LLC_SNAP		0x05000000
+#define SCSI_FCP		0x08000000
+#define SCSI_GPP		0x09000000
+#define IPI3_MASTER		0x11000000
+#define IPI3_SLAVE		0x12000000
+#define IPI3_PEER		0x13000000
+#define CP_IPI3_MASTER		0x15000000
+#define CP_IPI3_SLAVE		0x16000000
+#define CP_IPI3_PEER		0x17000000
+#define SBCCS_CHANNEL		0x19000000
+#define SBCCS_CONTROL		0x1A000000
+#define FIBRE_SERVICES		0x20000000
+#define FC_FG			0x21000000
+#define FC_XS			0x22000000
+#define FC_AL			0x23000000
+#define SNMP			0x24000000
+#define HIPPI_FP		0x40000000
+#define TYPE_MASK		0xFF000000
+
+typedef struct {
+	UCHAR seq_id_valid;
+	UCHAR seq_id;
+	USHORT reserved;  // 2 bytes reserved
+	ULONG ox_rx_id;
+	USHORT low_seq_cnt;
+	USHORT high_seq_cnt;
+} BA_ACC_PAYLOAD;
+
+typedef struct {
+	UCHAR reserved;
+	UCHAR reason_code;
+	UCHAR reason_explain;
+	UCHAR vendor_unique;
+} BA_RJT_PAYLOAD;
+
+
+typedef struct {
+	ULONG 	command_code;
+	ULONG 	sid;
+	USHORT	ox_id;
+	USHORT	rx_id;
+} RRQ_MESSAGE;
+
+typedef struct {
+	ULONG command_code;
+	UCHAR vendor;
+	UCHAR explain;
+	UCHAR reason;
+	UCHAR reserved;
+} REJECT_MESSAGE;
+
+
+#define	N_OR_F_PORT		0x1000
+#define RANDOM_RELATIVE_OFFSET	0x4000
+#define CONTINUOSLY_INCREASING	0x8000
+
+#define CLASS_VALID		0x8000
+#define INTERMIX_MODE		0x4000
+#define TRANSPARENT_STACKED	0x2000
+#define LOCKDOWN_STACKED	0x1000
+#define SEQ_DELIVERY		0x800
+
+#define XID_NOT_SUPPORTED	0x00
+#define XID_SUPPORTED		0x4000
+#define XID_REQUIRED		0xC000
+
+#define ASSOCIATOR_NOT_SUPPORTED	0x00
+#define ASSOCIATOR_SUPPORTED	0x1000
+#define ASSOCIATOR_REQUIRED	0x3000
+
+#define	INIT_ACK0_SUPPORT	0x800
+#define INIT_ACKN_SUPPORT	0x400
+
+#define	RECIP_ACK0_SUPPORT	0x8000
+#define RECIP_ACKN_SUPPORT	0x4000
+
+#define X_ID_INTERLOCK		0x2000
+
+#define ERROR_POLICY		0x1800		// Error Policy Supported
+#define ERROR_DISCARD		0x00		// Only Discard Supported
+#define ERROR_DISC_PROCESS	0x02		// Discard and process supported
+
+#define NODE_ID			0x01
+#define IEEE_EXT		0x20
+
+//
+// Categories Supported Per Sequence
+//
+#define	CATEGORIES_PER_SEQUENCE	0x300
+#define ONE_CATEGORY_SEQUENCE	0x00		// 1 Category per Sequence
+#define TWO_CATEGORY_SEQUENCE	0x01		// 2 Categories per Sequence
+#define MANY_CATEGORY_SEQUENCE	0x03		// > 2 Categories/Sequence
+
+typedef struct {
+
+	USHORT initiator_control;
+	USHORT service_options;
+
+	USHORT rx_data_size;
+	USHORT recipient_control;
+
+	USHORT ee_credit;
+	USHORT concurrent_sequences;
+
+	USHORT reserved;
+	USHORT open_sequences;
+
+} CLASS_PARAMETERS;
+
+typedef struct {
+	ULONG	login_cmd;
+	//
+	// Common Service Parameters
+	//
+	struct {
+
+		USHORT bb_credit;
+		UCHAR lowest_ver;
+		UCHAR highest_ver;
+
+		USHORT bb_rx_size;
+		USHORT common_features;
+
+		USHORT rel_offset;
+		USHORT concurrent_seq;
+
+
+		ULONG e_d_tov;
+	} cmn_services;
+
+	//
+	// Port Name
+	//
+	UCHAR port_name[8];
+
+	//
+	// Node/Fabric Name
+	//
+	UCHAR node_name[8];
+
+	//
+	// Class 1, 2 and 3 Service Parameters
+	//
+	CLASS_PARAMETERS	class1;
+	CLASS_PARAMETERS	class2;
+	CLASS_PARAMETERS	class3;
+
+	ULONG reserved[4];
+
+	//
+	// Vendor Version Level
+	//
+	UCHAR		vendor_id[2];
+	UCHAR		vendor_version[6];
+	ULONG		buffer_size;
+	USHORT		rxid_start;
+	USHORT		total_rxids;
+} LOGIN_PAYLOAD;
+
+
+typedef struct
+{
+  ULONG cmd;  // 4 bytes
+  UCHAR n_port_identifier[3];
+  UCHAR reserved;
+  UCHAR port_name[8];
+} LOGOUT_PAYLOAD;
+
+
+//
+//	PRLI Request Service Parameter Defines
+//
+#define PRLI_ACC			0x01
+#define PRLI_REQ			0x02
+#define ORIG_PROCESS_ASSOC_VALID	0x8000
+#define RESP_PROCESS_ASSOC_VALID	0x4000
+#define ESTABLISH_PAIR			0x2000
+#define DATA_OVERLAY_ALLOWED		0x40
+#define	INITIATOR_FUNCTION		0x20
+#define	TARGET_FUNCTION			0x10
+#define CMD_DATA_MIXED			0x08
+#define DATA_RESP_MIXED			0x04
+#define READ_XFER_RDY			0x02
+#define WRITE_XFER_RDY			0x01
+
+#define RESPONSE_CODE_MASK	0xF00
+#define REQUEST_EXECUTED	0x100
+#define NO_RESOURCES		0x200
+#define INIT_NOT_COMPLETE	0x300
+#define IMAGE_DOES_NOT_EXIST	0x400
+#define BAD_PREDEFINED_COND	0x500
+#define REQ_EXEC_COND		0x600
+#define NO_MULTI_PAGE		0x700
+
+typedef struct {
+	USHORT	payload_length;
+	UCHAR	page_length;
+	UCHAR	cmd;
+
+
+	ULONG	valid;
+
+	ULONG	orig_process_associator;
+
+	ULONG	resp_process_associator;
+	
+	ULONG	fcp_info;
+} PRLI_REQUEST;
+
+typedef struct {
+
+	USHORT	payload_length;
+	UCHAR	page_length;
+	UCHAR	cmd;
+
+	ULONG	valid;
+	ULONG	orig_process_associator;
+
+	ULONG	resp_process_associator;
+	ULONG	reserved;
+} PRLO_REQUEST;
+
+typedef struct {
+	ULONG	cmd;
+
+	ULONG	hard_address;
+	
+	UCHAR	port_name[8];
+
+	UCHAR	node_name[8];
+
+	ULONG	s_id;
+} ADISC_PAYLOAD;
+
+// J. McCarty's LINK.H
+//
+//	LS_RJT Reason Codes
+//
+
+#define INVALID_COMMAND_CODE	0x01
+#define LOGICAL_ERROR		0x03
+#define LOGICAL_BUSY		0x05
+#define PROTOCOL_ERROR		0x07
+#define UNABLE_TO_PERFORM	0x09
+#define COMMAND_NOT_SUPPORTED	0x0B
+#define LS_VENDOR_UNIQUE	0xFF
+
+//
+// 	LS_RJT Reason Codes Explanations
+//
+#define NO_REASON		0x00
+#define OPTIONS_ERROR		0x01
+#define INITIATOR_CTL_ERROR	0x03
+#define RECIPIENT_CTL_ERROR	0x05
+#define DATA_FIELD_SIZE_ERROR	0x07
+#define CONCURRENT_SEQ_ERROR	0x09
+#define CREDIT_ERROR		0x0B
+#define INVALID_PORT_NAME	0x0D
+#define INVALID_NODE_NAME	0x0E
+#define INVALID_CSP		0x0F	// Invalid Service Parameters
+#define INVALID_ASSOC_HDR	0x11	// Invalid Association Header
+#define ASSOC_HDR_REQUIRED	0x13	// Association Header Required
+#define LS_INVALID_S_ID		0x15
+#define INVALID_OX_RX_ID	0x17	// Invalid OX_ID RX_ID Combination
+#define CMD_IN_PROCESS		0x19
+#define INVALID_IDENTIFIER	0x1F	// Invalid N_PORT Identifier
+#define INVALID_SEQ_ID		0x21
+#define ABT_INVALID_XCHNG	0x23 	// Attempt to Abort an invalid Exchange
+#define ABT_INACTIVE_XCHNG	0x25 	// Attempt to Abort an inactive Exchange
+#define NEED_REC_QUAL		0x27	// Recovery Qualifier required
+#define NO_LOGIN_RESOURCES	0x29	// No resources to support login
+#define NO_DATA			0x2A	// Unable to supply requested data
+#define	REQUEST_NOT_SUPPORTED	0x2C	// Request Not Supported
+
+//
+//	Link Control Codes
+//
+
+//
+//	P_BSY Action Codes
+//
+#define SEQUENCE_TERMINATED	0x01000000
+#define SEQUENCE_ACTIVE		0x02000000
+
+//
+//	P_BSY Reason Codes
+//
+#define PHYS_NPORT_BUSY		0x010000
+#define NPORT_RESOURCE_BUSY	0x020000
+
+//
+// 	P_RJT, F_RJT Action Codes
+//
+
+#define RETRYABLE_ERROR		0x01000000
+#define NON_RETRYABLE_ERROR	0x02000000
+
+//
+// 	P_RJT, F_RJT Reason Codes
+//
+#define INVALID_D_ID		0x010000
+#define INVALID_S_ID		0x020000
+#define NPORT_NOT_AVAIL_TMP	0x030000
+#define NPORT_NOT_AVAIL_PERM	0x040000
+#define CLASS_NOT_SUPPORTED	0x050000
+#define USAGE_ERROR		0x060000
+#define TYPE_NOT_SUPPORTED	0x070000
+#define INVAL_LINK_CONTROL	0x080000
+#define INVAL_R_CTL		0x090000
+#define INVAL_F_CTL		0x0A0000
+#define INVAL_OX_ID		0x0B0000
+#define INVAL_RX_ID		0x0C0000
+#define INVAL_SEQ_ID		0x0D0000
+#define INVAL_DF_CTL		0x0E0000
+#define INVAL_SEQ_CNT		0x0F0000
+#define INVAL_PARAMS		0x100000
+#define EXCHANGE_ERROR		0x110000
+#define LS_PROTOCOL_ERROR	0x120000
+#define INCORRECT_LENGTH	0x130000
+#define UNEXPECTED_ACK		0x140000
+#define LOGIN_REQ		0x160000
+#define EXCESSIVE_SEQ		0x170000
+#define NO_EXCHANGE		0x180000
+#define SEC_HDR_NOT_SUPPORTED	0x190000
+#define NO_FABRIC		0x1A0000
+#define P_VENDOR_UNIQUE		0xFF0000
+
+//
+// 	BA_RJT Reason Codes
+//
+#define BA_INVALID_COMMAND	0x00010000
+#define BA_LOGICAL_ERROR	0x00030000
+#define BA_LOGICAL_BUSY		0x00050000
+#define BA_PROTOCOL_ERROR	0x00070000
+#define BA_UNABLE_TO_PERFORM	0x00090000
+
+//
+// 	BA_RJT Reason Explanation Codes
+//
+#define BA_NO_REASON		0x00000000
+#define BA_INVALID_OX_RX	0x00000300
+#define BA_SEQUENCE_ABORTED	0x00000500
+
+
+
+#endif /* CPQFCTSSTRUCTS_H	*/
+
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTStrigger.c linux/drivers/scsi/cpqfcTStrigger.c
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTStrigger.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTStrigger.c	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,30 @@
+// Routine to trigger Finisar GTA analyzer. Runs of GPIO2
+// NOTE: DEBUG ONLY! Could interfere with FCMNGR/Miniport operation
+// since it writes directly to the Tachyon board.  This function
+// developed for Compaq HBA Tachyon TS v1.2 (Rev X5 PCB)
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+
+void TriggerHBA( void* IOBaseUpper, int Print)
+{
+  __u32 long value;
+
+  // get initial value in hopes of not modifying any other GPIO line
+  IOBaseUpper += 0x188;  // TachTL/TS Control reg
+  
+  value = readl( IOBaseUpper);
+  // set HIGH to trigger external analyzer (tested on Dolche Finisar 1Gb GTA)
+  // The Finisar anaylzer triggers on low-to-high TTL transition
+  value |= 0x01; // set bit 0
+
+  writel( value, IOBaseUpper);
+
+  if( Print)
+    printk( " -GPIO0 set- ");
+}
+
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqfcTSworker.c linux/drivers/scsi/cpqfcTSworker.c
--- linux-2.2.18-pre8/drivers/scsi/cpqfcTSworker.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqfcTSworker.c	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,6238 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) 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.
+ * Written by Don Zimmerman
+*/
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/smp_lock.h>
+
+#define __KERNEL_SYSCALLS__
+
+#define SHUTDOWN_SIGS	(sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+
+
+#include "sd.h"
+#include "hosts.h"   // struct Scsi_Host definition for T handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+//#define LOGIN_DBG 1
+
+// REMARKS:
+// Since Tachyon chips may be permitted to wait from 500ms up to 2 sec
+// to empty an outgoing frame from its FIFO to the Fibre Channel stream,
+// we cannot do everything we need to in the interrupt handler.  Specifically,
+// every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be
+// suspended until the login sequences have been completed.  Login commands
+// are frames just like SCSI commands are frames; they are subject to the same
+// timeout issues and delays.  Also, various specs provide up to 2 seconds for
+// devices to log back in (i.e. respond with ACC to a login frame), so I/O to
+// that device has to be suspended.
+// A serious problem here occurs on highly loaded FC-AL systems.  If our FC port
+// has a low priority (e.g. high arbitrated loop physical address, alpa), and
+// some other device is hogging bandwidth (permissible under FC-AL), we might
+// time out thinking the link is hung, when it's simply busy.  Many such
+// considerations complicate the design.  Although Tachyon assumes control
+// (in silicon) for many link-specific issues, the Linux driver is left with the
+// rest, which turns out to be a difficult, time critical chore.
+
+// These "worker" functions will handle things like FC Logins; all
+// processes with I/O to our device must wait for the Login to complete
+// and (if successful) I/O to resume.  In the event of a malfunctioning or  
+// very busy loop, it may take hundreds of millisecs or even seconds to complete
+// a frame send.  We don't want to hang up the entire server (and all
+// processes which don't depend on Fibre) during this wait.
+
+// The Tachyon chip can have around 30,000 I/O operations ("exchanges")
+// open at one time.  However, each exchange must be initiated 
+// synchronously (i.e. each of the 30k I/O had to be started one at a
+// time by sending a starting frame via Tachyon's outbound que).  
+
+// To accomodate kernel "module" build, this driver limits the exchanges
+// to 256, because of the contiguous physical memory limitation of 128M.
+
+// Typical FC Exchanges are opened presuming the FC frames start without errors,
+// while Exchange completion is handled in the interrupt handler.  This
+// optimizes performance for the "everything's working" case.
+// However, when we have FC related errors or hot plugging of FC ports, we pause
+// I/O and handle FC-specific tasks in the worker thread.  These FC-specific
+// functions will handle things like FC Logins and Aborts.  As the Login sequence
+// completes to each and every target, I/O can resume to that target.  
+
+// Our kernel "worker thread" must share the HBA with threads calling 
+// "queuecommand".  We define a "BoardLock" semaphore which indicates
+// to "queuecommand" that the HBA is unavailable, and Cmnds are added to a
+// board lock Q.  When the worker thread finishes with the board, the board
+// lock Q commands are completed with status causing immediate retry.
+// Typically, the board is locked while Logins are in progress after an
+// FC Link Down condition.  When Cmnds are re-queued after board lock, the
+// particular Scsi channel/target may or may not have logged back in.  When
+// the device is waiting for login, the "prli" flag is clear, in which case
+// commands are passed to a Link Down Q.  Whenever the login finally completes,
+// the LinkDown Q is completed, again with status causing immediate retry.
+// When FC devices are logged in, we build and start FC commands to the
+// devices.
+
+// NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices 
+// that never log back in (e.g. physically removed) is NOT completely
+// understood.  I've still seen instances of system hangs on failed Write 
+// commands (possibly from the ext2 layer?) on device removal.  Such special
+// cases need to be evaluated from a system/application view - e.g., how
+// exactly does the system want me to complete commands when the device is
+// physically removed??
+
+// local functions
+
+static void SetLoginFields(
+  PFC_LOGGEDIN_PORT pLoggedInPort,
+  TachFCHDR_GCMND* fchs,
+  BOOLEAN PDisc,
+  BOOLEAN Originator);
+
+static void AnalyzeIncomingFrame( 
+       CPQFCHBA *cpqfcHBAdata,
+       ULONG QNdx );
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds );
+
+static int verify_PLOGI( PTACHYON fcChip,
+      TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+
+static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type);
+static void BuildLinkServicePayload( 
+              PTACHYON fcChip, ULONG type, void* payload);
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, 
+        PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID);
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata);
+
+static void RevalidateSEST( struct Scsi_Host *HostAdapter, 
+		        PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void IssueReportLunsCommand( 
+              CPQFCHBA* cpqfcHBAdata, 
+	      TachFCHDR_GCMND* fchs);
+
+
+// (see scsi_error.c comments on kernel task creation)
+
+void cpqfcTSWorkerThread( void *host)
+{
+  struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; 
+#ifdef PCI_KERNEL_TRACE
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+#endif
+  struct fs_struct *fs;
+  DECLARE_MUTEX_LOCKED(fcQueReady);
+  DECLARE_MUTEX_LOCKED(fcTYOBcomplete); 
+  DECLARE_MUTEX_LOCKED(TachFrozen);  
+  DECLARE_MUTEX_LOCKED(BoardLock);  
+
+  ENTER("WorkerThread");
+
+  lock_kernel();
+	/*
+	 * If we were started as result of loading a module, close all of the
+	 * user space pages.  We don't need them, and if we didn't close them
+	 * they would be locked into memory.
+	 */
+  exit_mm(current);
+
+  current->session = 1;
+  current->pgrp = 1;
+	
+  /* Become as one with the init task */
+	
+  exit_fs(current);	/* current->fs->count--; */
+  fs = init_task.fs;
+  // Some kernels compiled for SMP, while actually running
+  // on a uniproc machine, will return NULL for this call
+  if( !fs)
+  {
+    printk(" cpqfcTS FATAL: fs is NULL! Is this an SMP kernel on uniproc machine?\n ");
+  }
+ 
+  else
+  { 
+    current->fs = fs;
+    atomic_inc(&fs->count);
+  }
+
+  siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+
+
+  /*
+   * Set the name of this process.
+   */
+  sprintf(current->comm, "cpqfcTS_wt_%d", HostAdapter->host_no);
+
+  cpqfcHBAdata->fcQueReady = &fcQueReady;  // primary wait point
+  cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete;
+  cpqfcHBAdata->TachFrozen = &TachFrozen;
+    
+ 
+  cpqfcHBAdata->worker_thread = current;
+  
+  unlock_kernel();
+
+  if( cpqfcHBAdata->notify_wt != NULL )
+    up( cpqfcHBAdata->notify_wt); // OK to continue
+
+  while(1)
+  {
+    unsigned long flags;
+
+    down_interruptible( &fcQueReady);  // wait for something to do
+
+    if (signal_pending(current) )
+      break;
+    
+    PCI_TRACE( 0x90)
+    // first, take the IO lock so the SCSI upper layers can't call
+    // into our _quecommand function (this also disables INTs)
+    spin_lock_irqsave( &io_request_lock, flags); // STOP _que function
+    PCI_TRACE( 0x90)
+         
+    CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+    // next, set this pointer to indicate to the _quecommand function
+    // that the board is in use, so it should que the command and 
+    // immediately return (we don't actually require the semaphore function
+    // in this driver rev)
+
+    cpqfcHBAdata->BoardLock = &BoardLock;
+
+    PCI_TRACE( 0x90)
+
+    // release the IO lock (and re-enable interrupts)
+    spin_unlock_irqrestore( &io_request_lock, flags);
+
+    // disable OUR HBA interrupt (keep them off as much as possible
+    // during error recovery)
+    disable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+    // OK, let's process the Fibre Channel Link Q and do the work
+    cpqfcTS_WorkTask( HostAdapter);
+
+    // hopefully, no more "work" to do;
+    // re-enable our INTs for "normal" completion processing
+    enable_irq( cpqfcHBAdata->HostAdapter->irq);
+ 
+
+    cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued
+    CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+
+    // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+    CompleteBoardLockCmnd( cpqfcHBAdata);
+  
+
+  }
+  // hopefully, the signal was for our module exit...
+  if( cpqfcHBAdata->notify_wt != NULL )
+    up( cpqfcHBAdata->notify_wt); // yep, we're outta here
+}
+
+
+// Freeze Tachyon routine.
+// If Tachyon is already frozen, return FALSE
+// If Tachyon is not frozen, call freeze function, return TRUE
+//
+static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  BOOLEAN FrozeTach = FALSE;
+  // It's possible that the chip is already frozen; if so,
+  // "Freezing" again will NOT! generate another Freeze
+  // Completion Message.
+
+  if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000)
+  {  // (need to freeze...)
+    fcChip->FreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+
+    // 2. Get Tach freeze confirmation
+    // (synchronize SEST manipulation with Freeze Completion Message)
+    // we need INTs on so semaphore can be set.	
+    enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore
+    down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem.
+    // can we TIMEOUT semaphore wait?? TBD
+    disable_irq( cpqfcHBAdata->HostAdapter->irq); 
+
+    FrozeTach = TRUE;
+  }  // (else, already frozen)
+ 
+  return FrozeTach;
+}  
+
+
+
+
+// This is the kernel worker thread task, which processes FC
+// tasks which were queued by the Interrupt handler or by
+// other WorkTask functions.
+
+#define DBG 1
+//#undef DBG
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG QconsumerNdx;
+  LONG ExchangeID;
+  ULONG ulStatus=0;
+  TachFCHDR_GCMND fchs;
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+
+  ENTER("WorkTask");
+
+  // copy current index to work on
+  QconsumerNdx = fcLQ->consumer;
+
+  PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90)
+  
+
+  // NOTE: when this switch completes, we will "consume" the Que item
+//  printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type);
+  switch( fcLQ->Qitem[QconsumerNdx].Type )
+  {
+      // incoming frame - link service (ACC, UNSOL REQ, etc.)
+      // or FCP-SCSI command
+    case SFQ_UNKNOWN:  
+      AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx );
+
+      break;
+  
+    
+    
+    case EXCHANGE_QUEUED:  // an Exchange (i.e. FCP-SCSI) was previously
+                           // Queued because the link was down.  The  
+                           // heartbeat timer detected it and Queued it here.
+                           // We attempt to start it again, and if
+                           // successful we clear the EXCHANGE_Q flag.
+                           // If the link doesn't come up, the Exchange
+                           // will eventually time-out.
+
+      ExchangeID = (LONG)  // x_ID copied from DPC timeout function
+                   fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+
+      // It's possible that a Q'd exchange could have already
+      // been started by other logic (e.g. ABTS process)
+      // Don't start if already started (Q'd flag clear)
+
+      if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED )
+      {
+//        printk(" *Start Q'd x_ID %Xh: type %Xh ", 
+//          ExchangeID, Exchanges->fcExchange[ExchangeID].type);
+      
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID);
+        if( !ulStatus )
+        {
+//          printk("success* ");
+        }	
+        else
+        {
+#ifdef DBG
+      
+          if( ulStatus == EXCHANGE_QUEUED)
+            printk("Queued* ");
+          else
+            printk("failed* ");
+		
+#endif
+	} 
+      }
+      break;
+
+
+    case LINKDOWN:  
+      // (lots of things already done in INT handler) future here?
+      break;
+    
+    
+    case LINKACTIVE:   // Tachyon set the Lup bit in FM status
+                       // NOTE: some misbehaving FC ports (like Tach2.1)
+                       // can re-LIP immediately after a LIP completes.
+      
+      // if "initiator", need to verify LOGs with ports
+//      printk("\n*LNKUP* ");
+
+      if( fcChip->Options.initiator )
+        SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data
+                  // if SendLogins successfully completes, PortDiscDone
+                  // will be set.
+      
+      
+      // If SendLogins was successful, then we expect to get incoming
+      // ACCepts or REJECTs, which are handled below.
+
+      break;
+
+    // LinkService and Fabric request/reply processing
+    case ELS_FDISC:      // need to send Fabric Discovery (Login)
+    case ELS_FLOGI:      // need to send Fabric Login
+    case ELS_SCR:        // need to send State Change Registration
+    case FCS_NSR:        // need to send Name Service Request
+    case ELS_PLOGI:      // need to send PLOGI
+    case ELS_ACC:        // send generic ACCept
+    case ELS_PLOGI_ACC:  // need to send ELS ACCept frame to recv'd PLOGI
+    case ELS_PRLI_ACC:   // need to send ELS ACCept frame to recv'd PRLI
+    case ELS_LOGO:      // need to send ELS LOGO (logout)
+    case ELS_LOGO_ACC:  // need to send ELS ACCept frame to recv'd PLOGI
+    case ELS_RJT:         // ReJecT reply
+    case ELS_PRLI:       // need to send ELS PRLI
+ 
+    
+//      printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type);
+      // if PortDiscDone is not set, it means the SendLogins routine
+      // failed to complete -- assume that LDn occured, so login frames
+      // are invalid
+      if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+      {
+        printk("Discard Q'd ELS login frame\n");
+        break;  
+      }
+
+      ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI
+          (TachFCHDR_GCMND*)
+            fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+          NULL,         // no data (no scatter/gather list)
+          &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+      if( !ulStatus ) // Exchange setup?
+      {
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+        if( !ulStatus )
+        {
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+        }
+        else
+          // check reason for Exchange not being started - we might
+          // want to Queue and start later, or fail with error
+        {
+
+        }
+      }
+
+      else   // Xchange setup failed...
+        printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+
+      break;
+
+    case SCSI_REPORT_LUNS:
+      // pass the incoming frame (actually, it's a PRLI frame)
+      // so we can send REPORT_LUNS, in order to determine VSA/PDU
+      // FCP-SCSI Lun address mode
+      IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*)
+            fcLQ->Qitem[QconsumerNdx].ulBuff); 
+
+      break;
+      
+
+
+
+    case BLS_ABTS:       // need to ABORT one or more exchanges
+    {
+      LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+      BOOLEAN FrozeTach = FALSE;   
+     
+      if( x_ID > TACH_SEST_LEN )  // (in)sanity check
+      {
+//	printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID);
+	break;
+      }
+
+
+      if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE
+      {
+//	printk(" ABTS %Xh Scsi Cmnd null! ", x_ID);
+	
+       break;  // nothing to abort!
+      }
+
+//#define ABTS_DBG
+#ifdef ABTS_DBG
+      printk("INV SEST[%X] ", x_ID); 
+      if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT)
+      {
+        printk("FC2TO");
+      }
+      if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)
+      {
+        printk("IA");
+      }
+      if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED)
+      {
+        printk("PORTID");
+      }
+      if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED)
+      {
+        printk("DEVRM");
+      }
+      if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX)
+      {
+        printk("LKF");
+      }
+      if( Exchanges->fcExchange[x_ID].status & FRAME_TO)
+      {
+        printk("FRMTO");
+      }
+      if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY)
+      {
+        printk("ABSQ");
+      }
+      if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME)
+      {
+        printk("SFQFR");
+      }
+
+      if( Exchanges->fcExchange[ x_ID].type == 0x2000)
+        printk(" WR");
+      else if( Exchanges->fcExchange[ x_ID].type == 0x3000)
+        printk(" RD");
+      else if( Exchanges->fcExchange[ x_ID].type == 0x10)
+        printk(" ABTS");
+      else
+        printk(" %Xh", Exchanges->fcExchange[ x_ID].type); 
+
+      if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT))
+      {
+	printk(" Cmd %p, ", 
+          Exchanges->fcExchange[ x_ID].Cmnd);
+
+        printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n", 
+          cpqfcHBAdata->HBAnum,
+          Exchanges->fcExchange[ x_ID].Cmnd->channel,
+          Exchanges->fcExchange[ x_ID].Cmnd->target,
+          Exchanges->fcExchange[ x_ID].Cmnd->lun,
+          Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+      }
+      else  // assume that Cmnd ptr is invalid on _abort()
+      {
+	printk(" Cmd ptr invalid\n");
+      }
+     
+#endif      
+
+      
+      // Steps to ABORT a SEST exchange:
+      // 1. Freeze TL SCSI assists & ERQ (everything)
+      // 2. Receive FROZEN inbound CM (must succeed!)
+      // 3. Invalidate x_ID SEST entry 
+      // 4. Resume TL SCSI assists & ERQ (everything)
+      // 5. Build/start on exchange - change "type" to BLS_ABTS,
+      //    timeout to X sec (RA_TOV from PLDA is actually 0)
+      // 6. Set Exchange Q'd status if ABTS cannot be started,
+      //    or simply complete Exchange in "Terminate" condition
+
+  PCI_TRACEO( x_ID, 0xB4)
+      
+      // 1 & 2 . Freeze Tach & get confirmation of freeze
+      FrozeTach = FreezeTach( cpqfcHBAdata);
+
+      // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange.
+      // FC2_TIMEOUT means we are originating the abort, while
+      // TARGET_ABORT means we are ACCepting an abort.
+      // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are 
+      // all from Tachyon:
+      // Exchange was corrupted by LDn or other FC physical failure
+      // INITIATOR_ABORT means the upper layer driver/application
+      // requested the abort.
+
+
+	  
+      // clear bit 31 (VALid), to invalidate & take control from TL
+      fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF;
+
+
+      // examine and Tach's "Linked List" for IWEs that 
+      // received (nearly) simultaneous transfer ready (XRDY) 
+      // repair linked list if necessary (TBD!)
+      // (If we ignore the "Linked List", we will time out
+      // WRITE commands where we received the FCP-SCSI XFRDY
+      // frame (because Tachyon didn't processes it).  Linked List
+      // management should be done as an optimization.
+
+//       readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST ));
+
+
+      
+
+      // 4. Resume all Tachlite functions (for other open Exchanges)
+      // as quickly as possible to allow other exchanges to other ports
+      // to resume.  Freezing Tachyon may cause cascading errors, because
+      // any received SEST frame cannot be processed by the SEST.
+      // Don't "unfreeze" unless Link is operational
+      if( FrozeTach )  // did we just freeze it (above)?
+        fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+      
+
+  PCI_TRACEO( x_ID, 0xB4)
+
+      // Note there is no confirmation that the chip is "unfrozen".  Also,
+      // if the Link is down when unfreeze is called, it has no effect.
+      // Chip will unfreeze when the Link is back up.
+
+      // 5. Now send out Abort commands if possible
+      // Some Aborts can't be "sent" (Port_id changed or gone);
+      // if the device is gone, there is no port_id to send the ABTS to.
+
+      if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED)
+			  &&
+          !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) )
+      {
+        Exchanges->fcExchange[ x_ID].type = BLS_ABTS;
+        fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id;
+        ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          BLS_ABTS,
+          &fchs,        // (uses only s_id)
+          NULL,         // (no scatter/gather list for ABTS)
+          &x_ID );// ABTS on this Exchange ID
+
+        if( !ulStatus ) // Exchange setup build OK?
+        {
+
+            // ABTS may be needed because an Exchange was corrupted
+            // by a Link disruption.  If the Link is UP, we can
+	    // presume that this ABTS can start immediately; otherwise,
+	    // set Que'd status so the Login functions
+            // can restart it when the FC physical Link is restored
+          if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init?
+          {			    
+//                printk(" *set Q status x_ID %Xh on LDn* ", x_ID);
+                Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED;
+          }
+
+          else  // what FC device (port_id) does the Cmd belong to?
+          {
+            PFC_LOGGEDIN_PORT pLoggedInPort = 
+              Exchanges->fcExchange[ x_ID].pLoggedInPort;
+            
+            // if Port is logged in, we might start the abort.
+	
+            if( (pLoggedInPort != NULL) 
+			      &&
+                (pLoggedInPort->prli == TRUE) ) 
+            {
+              // it's possible that an Exchange has already been Queued
+              // to start after Login completes.  Check and don't
+	      // start it (again) here if Q'd status set
+//	    printk(" ABTS xchg %Xh ", x_ID);            
+ 	      if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED)
+	      {
+//		    printk("already Q'd ");
+	      }
+	      else
+	      {
+//	            printk("starting ");
+		
+                fcChip->fcStats.FC2aborted++; 
+                ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+                if( !ulStatus )
+                {
+                    // OK
+                    // submitted to Tach's Outbound Que (ERQ PI incremented)
+                }
+                else
+                {
+/*                   printk("ABTS exchange start failed -status %Xh, x_ID %Xh ",
+                        ulStatus, x_ID);
+*/
+                } 
+	      }
+	    }
+	    else
+	    {
+/*         	  printk(" ABTS NOT starting xchg %Xh, %p ",
+			       x_ID, pLoggedInPort);
+	          if( pLoggedInPort )
+	            printk("prli %d ", pLoggedInPort->prli);
+*/
+	    }		
+ 	  }
+        }
+        else  // what the #@!
+        {  // how do we fail to build an Exchange for ABTS??
+              printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n",
+                ulStatus, x_ID);
+        }
+      }
+      else   // abort without ABTS -- just complete exchange/Cmnd to Linux
+      {
+//            printk(" *Terminating x_ID %Xh on %Xh* ", 
+//		    x_ID, Exchanges->fcExchange[x_ID].status);
+        cpqfcTSCompleteExchange( fcChip, x_ID);
+
+
+      }
+    } // end of ABTS case
+      break;
+
+
+
+    case BLS_ABTS_ACC:   // need to ACCept one ABTS
+                         // (NOTE! this code not updated for Linux yet..)
+      
+
+      printk(" *ABTS_ACC* ");
+      // 1. Freeze TL
+
+      fcChip->FreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+
+      memcpy(  // copy the incoming ABTS frame
+        &fchs,
+        fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+        sizeof( fchs));
+
+      // 3. OK, Tachyon is frozen so we can invalidate SEST entry 
+      // (if necessary)
+      // Status FC2_TIMEOUT means we are originating the abort, while
+      // TARGET_ABORT means we are ACCepting an abort
+      
+      ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange
+//      printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID);
+
+
+      // sanity check on received ExchangeID
+      if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT )
+      {
+          // clear bit 31 (VALid), to invalidate & take control from TL
+//          printk("Invalidating SEST exchange %Xh\n", ExchangeID);
+          fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF;
+      }
+
+
+      // 4. Resume all Tachlite functions (for other open Exchanges)
+      // as quickly as possible to allow other exchanges to other ports
+      // to resume.  Freezing Tachyon for too long may royally screw
+      // up everything!
+      fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+      
+      // Note there is no confirmation that the chip is "unfrozen".  Also,
+      // if the Link is down when unfreeze is called, it has no effect.
+      // Chip will unfreeze when the Link is back up.
+
+      // 5. Now send out Abort ACC reply for this exchange
+      Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC;
+      
+      fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id;
+      ulStatus = cpqfcTSBuildExchange(
+            cpqfcHBAdata,
+            BLS_ABTS_ACC,
+            &fchs,
+            NULL,         // no data (no scatter/gather list)
+            &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+      if( !ulStatus ) // Exchange setup?
+      {
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+        if( !ulStatus )
+        {
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+        }
+        else
+          // check reason for Exchange not being started - we might
+          // want to Queue and start later, or fail with error
+        {
+
+        } 
+      }
+      break;
+
+
+    case BLS_ABTS_RJT:   // need to ReJecT one ABTS; reject implies the
+                         // exchange doesn't exist in the TARGET context.
+                         // ExchangeID has to come from LinkService space.
+
+      printk(" *ABTS_RJT* ");
+      ulStatus = cpqfcTSBuildExchange(
+            cpqfcHBAdata,
+            BLS_ABTS_RJT,
+            (TachFCHDR_GCMND*)
+              fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+            NULL,         // no data (no scatter/gather list)
+            &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+      if( !ulStatus ) // Exchange setup OK?
+      {
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+        // If it fails, we aren't required to retry.
+      }
+      if( ulStatus )
+      {
+        printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+      }
+      else
+      {
+        printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+      
+      }
+
+      break;
+
+
+
+    default:
+      break;
+  }                   // end switch
+//doNothing:
+    // done with this item - now set the NEXT index
+
+  if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test
+  {
+    fcLQ->consumer = 0;
+  }
+  else
+  { 
+    fcLQ->consumer++;
+  }
+
+  PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94)
+
+  LEAVE("WorkTask");
+  return;
+}
+
+
+
+
+// When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login)
+// commands come in, post to the LinkQ so that action can be taken outside the
+// interrupt handler.  
+// This circular Q works like Tachyon's que - the producer points to the next
+// (unused) entry.  Called by Interrupt handler, WorkerThread, Timer
+// sputlinkq
+void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata,
+  int Type, 
+  void *QueContent)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+//  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+  ULONG ndx;
+  
+  ENTER("cpqfcTSPutLinkQ");
+
+  ndx = fcLQ->producer;
+  
+  ndx += 1;  // test for Que full
+
+
+  
+  if( ndx >= FC_LINKQ_DEPTH ) // rollover test
+    ndx = 0;
+
+  if( ndx == fcLQ->consumer )   // QUE full test
+  {
+                       // QUE was full! lost LK command (fatal to logic)
+    fcChip->fcStats.lnkQueFull++;
+
+    printk("*LinkQ Full!*");
+    TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+/*
+    {
+      int i;
+      printk("LinkQ PI %d, CI %d\n", fcLQ->producer,
+        fcLQ->consumer);
+		      
+      for( i=0; i< FC_LINKQ_DEPTH; )
+      {
+	printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type);
+	if( (++i %8) == 0) printk("\n");
+      }
+  
+    }
+*/    
+    printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung
+  }
+  else                        // QUE next element
+  {
+    // Prevent certain multiple (back-to-back) requests.
+    // This is important in that we don't want to issue multiple
+    // ABTS for the same Exchange, or do multiple FM inits, etc.
+    // We can never be sure of the timing of events reported to
+    // us by Tach's IMQ, which can depend on system/bus speeds,
+    // FC physical link circumstances, etc.
+     
+    if( (fcLQ->producer != fcLQ->consumer)
+	    && 
+        (Type == FMINIT)  )
+    {
+      LONG lastNdx;  // compute previous producer index
+      if( fcLQ->producer)
+        lastNdx = fcLQ->producer- 1;
+      else
+	lastNdx = FC_LINKQ_DEPTH-1;
+
+
+      if( fcLQ->Qitem[lastNdx].Type == FMINIT)
+      {
+//        printk(" *skip FMINIT Q post* ");
+//        goto DoneWithPutQ;
+      }
+
+    }
+
+    // OK, add the Q'd item...
+    
+    fcLQ->Qitem[fcLQ->producer].Type = Type;
+   
+    memcpy(
+        fcLQ->Qitem[fcLQ->producer].ulBuff,
+        QueContent, 
+        sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff));
+
+    fcLQ->producer = ndx;  // increment Que producer
+
+    // set semaphore to wake up Kernel (worker) thread
+    // 
+    up( cpqfcHBAdata->fcQueReady );
+  }
+
+//DoneWithPutQ:
+
+  LEAVE("cpqfcTSPutLinkQ");
+}
+
+
+
+
+// reset device ext FC link Q
+void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata)
+   
+{
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+  fcLQ->producer = 0;
+  fcLQ->consumer = 0;
+
+}
+
+
+
+
+
+// When Tachyon gets an unassisted FCP-SCSI frame, post here so
+// an arbitrary context thread (e.g. IOCTL loopback test function)
+// can process it.
+
+// (NOTE: Not revised for Linux)
+// This Q works like Tachyon's que - the producer points to the next
+// (unused) entry.
+void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata,
+  int Type, 
+  void *QueContent)
+{
+//  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+//  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+//  ULONG ndx;
+
+//  ULONG *pExchangeID;
+//  LONG ExchangeID;
+
+/*
+  KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock);
+  ndx = pDevExt->fcScsiQue.producer + 1;  // test for Que full
+
+  if( ndx >= FC_SCSIQ_DEPTH ) // rollover test
+    ndx = 0;
+
+  if( ndx == pDevExt->fcScsiQue.consumer )   // QUE full test
+  {
+                       // QUE was full! lost LK command (fatal to logic)
+    fcChip->fcStats.ScsiQueFull++;
+#ifdef DBG
+    printk( "fcPutScsiQue - FULL!\n");
+#endif
+
+  }
+  else                        // QUE next element
+  {
+    pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type;
+    
+    if( Type == FCP_RSP )
+    {
+      // this TL inbound message type means that a TL SEST exchange has
+      // copied an FCP response frame into a buffer pointed to by the SEST
+      // entry.  That buffer is allocated in the SEST structure at ->RspHDR.
+      // Copy the RspHDR for use by the Que handler.
+      pExchangeID = (ULONG *)QueContent;
+      
+      memcpy(
+	      pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+        &fcChip->SEST->RspHDR[ *pExchangeID ],
+	      sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size)
+      
+    }
+    else
+    {
+      memcpy(
+	      pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+        QueContent, 
+	      sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff));
+    }
+      
+    pDevExt->fcScsiQue.producer = ndx;  // increment Que
+
+
+    KeSetEvent( &pDevExt->TYIBscsi,  // signal any waiting thread
+       0,                    // no priority boost
+		   FALSE );              // no waiting later for this event
+  }
+  KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock);
+*/
+}
+
+
+
+
+
+
+
+static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+		PFC_LOGGEDIN_PORT pFcPort)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+  if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric
+  {
+    fcChip->fcStats.logouts++;
+    printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n", 
+        (ULONG)pFcPort->u.liWWN,
+        (ULONG)(pFcPort->u.liWWN >>32),
+	pFcPort->port_id);
+
+  // Terminate I/O with this (Linux) Scsi target
+    cpqfcTSTerminateExchange( cpqfcHBAdata, 
+                            &pFcPort->ScsiNexus,
+	                    DEVICE_REMOVED);
+  }
+			
+  // Do an "implicit logout" - we can't really Logout the device
+  // (i.e. with LOGOut Request) because of port_id confusion
+  // (i.e. the Other port has no port_id).
+  // A new login for that WWN will have to re-write port_id (0 invalid)
+  pFcPort->port_id = 0;  // invalid!
+  pFcPort->pdisc = FALSE;
+  pFcPort->prli = FALSE;
+  pFcPort->plogi = FALSE;
+  pFcPort->flogi = FALSE;
+  pFcPort->LOGO_timer = 0;
+  pFcPort->device_blocked = TRUE; // block Scsi Requests
+}
+
+  
+// On FC-AL, there is a chance that a previously known device can
+// be quietly removed (e.g. with non-managed hub), 
+// while a NEW device (with different WWN) took the same alpa or
+// even 24-bit port_id.  This chance is unlikely but we must always
+// check for it.
+static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata,
+		PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  // set "other port" at beginning of fcPorts list
+  PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort;
+  while( pOtherPortWithPortId ) 
+  {
+    if( (pOtherPortWithPortId->port_id == 
+         pLoggedInPort->port_id) 
+		    &&
+         (pOtherPortWithPortId != pLoggedInPort) )
+    {
+      // trouble!  (Implicitly) Log the other guy out
+      printk(" *port_id %Xh is duplicated!* ", 
+        pOtherPortWithPortId->port_id);
+      cpqfcTSImplicitLogout( cpqfcHBAdata, pOtherPortWithPortId); 
+   }
+    pOtherPortWithPortId = pOtherPortWithPortId->pNextPort;
+  }
+}
+
+
+
+
+
+
+// Dynamic Memory Allocation for newly discovered FC Ports.
+// For simplicity, maintain fcPorts structs for ALL
+// for discovered devices, including those we never do I/O with
+// (e.g. Fabric addresses)
+
+static PFC_LOGGEDIN_PORT CreateFcPort( 
+	  CPQFCHBA* cpqfcHBAdata, 
+	  PFC_LOGGEDIN_PORT pLastLoggedInPort, 
+	  TachFCHDR_GCMND* fchs,
+	  LOGIN_PAYLOAD* plogi)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  PFC_LOGGEDIN_PORT pNextLoggedInPort = NULL;
+  int i;
+
+
+  printk("cpqfcTS: New FC port %06Xh WWN: ", fchs->s_id);
+  for( i=3; i>=0; i--)   // copy the LOGIN port's WWN
+    printk("%02X", plogi->port_name[i]);
+  for( i=7; i>3; i--)   // copy the LOGIN port's WWN
+    printk("%02X", plogi->port_name[i]);
+
+
+  // allocate mem for new port
+  // (these are small and rare allocations...)
+  pNextLoggedInPort = kmalloc( sizeof( FC_LOGGEDIN_PORT), GFP_ATOMIC );
+
+    
+  // allocation succeeded?  Fill out NEW PORT
+  if( pNextLoggedInPort )
+  {    
+                              // clear out any garbage (sometimes exists)
+    memset( pNextLoggedInPort, 0, sizeof( FC_LOGGEDIN_PORT));
+
+
+    // If we login to a Fabric, we don't want to treat it
+    // as a SCSI device...
+    if( (fchs->s_id & 0xFFF000) != 0xFFF000)
+    {
+      int i;
+      
+      // create a unique "virtual" SCSI Nexus (for now, just a
+      // new target ID) -- we will update channel/target on REPORT_LUNS
+      // special case for very first SCSI target...
+      if( cpqfcHBAdata->HostAdapter->max_id == 0)
+      {
+        pNextLoggedInPort->ScsiNexus.target = 0;
+        fcChip->fcPorts.ScsiNexus.target = -1; // don't use "stub"
+      }
+      else
+      {
+        pNextLoggedInPort->ScsiNexus.target =
+          cpqfcHBAdata->HostAdapter->max_id;
+      }
+
+      // initialize the lun[] Nexus struct for lun masking      
+      for( i=0; i< CPQFCTS_MAX_LUN; i++)
+        pNextLoggedInPort->ScsiNexus.lun[i] = 0xFF; // init to NOT USED
+      
+      pNextLoggedInPort->ScsiNexus.channel = 0; // cpqfcTS has 1 FC port
+      
+      printk(" SCSI Chan/Trgt %d/%d", 
+          pNextLoggedInPort->ScsiNexus.channel,
+          pNextLoggedInPort->ScsiNexus.target);
+ 
+      // tell Scsi layers about the new target...
+      cpqfcHBAdata->HostAdapter->max_id++; 
+//    printk("HostAdapter->max_id = %d\n",
+//      cpqfcHBAdata->HostAdapter->max_id);		    
+    }                          
+    else
+    {
+      // device is NOT SCSI (in case of Fabric)
+      pNextLoggedInPort->ScsiNexus.target = -1;  // invalid
+    }
+
+	  // create forward link to new port
+    pLastLoggedInPort->pNextPort = pNextLoggedInPort;
+    printk("\n");
+
+  }     
+  return pNextLoggedInPort;  // NULL on allocation failure
+}   // end NEW PORT (WWN) logic
+
+
+
+// For certain cases, we want to terminate exchanges without
+// sending ABTS to the device.  Examples include when an FC
+// device changed it's port_id after Loop re-init, or when
+// the device sent us a logout.  In the case of changed port_id,
+// we want to complete the command and return SOFT_ERROR to
+// force a re-try.  In the case of LOGOut, we might return
+// BAD_TARGET if the device is really gone.
+// Since we must ensure that Tachyon is not operating on the
+// exchange, we have to freeze the chip
+// sterminateex
+void cpqfcTSTerminateExchange( 
+  CPQFCHBA* cpqfcHBAdata, SCSI_NEXUS *ScsiNexus, int TerminateStatus)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG x_ID;
+
+  if( ScsiNexus )
+  {
+//    printk("TerminateExchange: ScsiNexus chan/target %d/%d\n",
+//		    ScsiNexus->channel, ScsiNexus->target);
+
+  } 
+  
+  for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+  {
+    if( Exchanges->fcExchange[x_ID].type )  // in use?
+    {
+      if( ScsiNexus == NULL ) // our HBA changed - term. all
+      {
+	Exchanges->fcExchange[x_ID].status = TerminateStatus;
+        cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); 
+      }
+      else
+      {
+	// If a device, according to WWN, has been removed, it's
+	// port_id may be used by another working device, so we
+	// have to terminate by SCSI target, NOT port_id.
+        if( Exchanges->fcExchange[x_ID].Cmnd) // Cmnd in progress?
+	{	                 
+	  if( (Exchanges->fcExchange[x_ID].Cmnd->target == ScsiNexus->target)
+			&&
+            (Exchanges->fcExchange[x_ID].Cmnd->channel == ScsiNexus->channel)) 
+          {
+            Exchanges->fcExchange[x_ID].status = TerminateStatus;
+            cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); // timed-out
+          }
+	}
+
+	// (in case we ever need it...)
+	// all SEST structures have a remote node ID at SEST DWORD 2
+        //          if( (fcChip->SEST->u[ x_ID ].TWE.Remote_Node_ID >> 8)
+        //                ==  port_id)
+      } 
+    }
+  }
+}
+
+
+static void ProcessELS_Request( 
+              CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+//  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+//  ULONG ox_id = (fchs->ox_rx_id >>16);
+  PFC_LOGGEDIN_PORT pLoggedInPort=NULL, pLastLoggedInPort;
+  BOOLEAN NeedReject = FALSE;
+  ULONG ls_reject_code = 0; // default don'n know??
+
+
+  // Check the incoming frame for a supported ELS type
+  switch( fchs->pl[0] & 0xFFFF)
+  {
+  case 0x0050: //  PDISC?
+
+    // Payload for PLOGI and PDISC is identical (request & reply)
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      
+      // PDISC payload OK. If critical login fields
+      // (e.g. WWN) matches last login for this port_id,
+      // we may resume any prior exchanges
+      // with the other port
+
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,     // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort != NULL)   // WWN found (prior login OK)
+      { 
+           
+ 	if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+	{
+          // Yes.  We were expecting PDISC?
+          if( pLoggedInPort->pdisc )
+	  {
+	    // Yes; set fields accordingly.     (PDISC, not Originator)
+            SetLoginFields( pLoggedInPort, fchs, TRUE, FALSE);
+       
+            // send 'ACC' reply 
+            cpqfcTSPutLinkQue( cpqfcHBAdata, 
+                          ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+                          fchs );
+
+	    // OK to resume I/O...
+	  }
+	  else
+ 	  {
+	    printk("Not expecting PDISC (pdisc=FALSE)\n");
+	    NeedReject = TRUE;
+	    // set reject reason code 
+            ls_reject_code = 
+              LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+	  }
+	}
+	else
+	{
+	  if( pLoggedInPort->port_id != 0)
+	  {
+  	    printk("PDISC PortID change: old %Xh, new %Xh\n",
+              pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+	  }
+          NeedReject = TRUE;
+          // set reject reason code 
+          ls_reject_code = 
+	    LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+		  
+	}
+      }
+      else
+      {
+	printk("PDISC Request from unknown WWN\n");
+        NeedReject = TRUE;
+          
+	// set reject reason code 
+        ls_reject_code = 
+          LS_RJT_REASON( LOGICAL_ERROR, INVALID_PORT_NAME);
+      }
+
+    }
+    else // Payload unacceptable
+    {
+      printk("payload unacceptable\n");
+      NeedReject = TRUE;  // reject code already set
+      
+    }
+
+    if( NeedReject)
+    {
+      ULONG port_id;
+      // The PDISC failed.  Set login struct flags accordingly,
+      // terminate any I/O to this port, and Q a PLOGI
+      if( pLoggedInPort )
+      {
+        pLoggedInPort->pdisc = FALSE;
+        pLoggedInPort->prli = FALSE;
+        pLoggedInPort->plogi = FALSE;
+	
+        cpqfcTSTerminateExchange( cpqfcHBAdata, 
+          &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+	port_id = pLoggedInPort->port_id;
+      }
+      else
+      {
+	port_id = fchs->s_id &0xFFFFFF;
+      }
+      fchs->reserved = ls_reject_code; // borrow this (unused) field
+      cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+    }
+   
+    break;
+
+
+
+  case 0x0003: //  PLOGI?
+
+    // Payload for PLOGI and PDISC is identical (request & reply)
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      BOOLEAN NeedReject = FALSE;
+      
+      // PDISC payload OK. If critical login fields
+      // (e.g. WWN) matches last login for this port_id,
+      // we may resume any prior exchanges
+      // with the other port
+
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,       // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort == NULL)   // WWN not found -New Port
+      { 
+      	pLoggedInPort = CreateFcPort( 
+			  cpqfcHBAdata, 
+			  pLastLoggedInPort, 
+			  fchs,
+			  &logi);
+        if( pLoggedInPort == NULL )
+        {
+          printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+          // Now Q a LOGOut Request, since we won't be talking to that device
+	
+          NeedReject = TRUE;  
+	  
+          // set reject reason code 
+          ls_reject_code = 
+            LS_RJT_REASON( LOGICAL_ERROR, NO_LOGIN_RESOURCES);
+	    
+	}
+      }
+      if( !NeedReject )
+      {
+      
+        // OK - we have valid fcPort ptr; set fields accordingly.   
+	//                         (not PDISC, not Originator)
+        SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); 
+
+        // send 'ACC' reply 
+        cpqfcTSPutLinkQue( cpqfcHBAdata, 
+                      ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+                      fchs );
+      }
+    }
+    else // Payload unacceptable
+    {
+      printk("payload unacceptable\n");
+      NeedReject = TRUE;  // reject code already set
+    }
+
+    if( NeedReject)
+    {
+      // The PDISC failed.  Set login struct flags accordingly,
+      // terminate any I/O to this port, and Q a PLOGI
+      pLoggedInPort->pdisc = FALSE;
+      pLoggedInPort->prli = FALSE;
+      pLoggedInPort->plogi = FALSE;
+	
+      fchs->reserved = ls_reject_code; // borrow this (unused) field
+
+      // send 'RJT' reply 
+      cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+    }
+   
+    // terminate any exchanges with this device...
+    if( pLoggedInPort )
+    {
+      cpqfcTSTerminateExchange( cpqfcHBAdata, 
+        &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+    }
+    break;
+
+
+
+  case 0x1020:  // PRLI?
+  {
+    BOOLEAN NeedReject = TRUE;
+    pLoggedInPort = fcFindLoggedInPort( 
+           fcChip, 
+           NULL,       // don't search Scsi Nexus
+	   (fchs->s_id & 0xFFFFFF),  // search linked list for port_id
+           NULL,     // DON'T search linked list for WWN
+           NULL);    // don't care
+      
+    if( pLoggedInPort == NULL ) 
+    {
+      // huh?
+      printk(" Unexpected PRLI Request -not logged in!\n");
+
+      // set reject reason code 
+      ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+      
+      // Q a LOGOut here?
+    }
+    else
+    {
+      // verify the PRLI ACC payload
+      if( !verify_PRLI( fchs, &ls_reject_code) )
+      {
+        // PRLI Reply is acceptable; were we expecting it?
+        if( pLoggedInPort->plogi ) 
+        { 
+  	  // yes, we expected the PRLI ACC  (not PDISC; not Originator)
+          SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+          // Q an ACCept Reply
+	  cpqfcTSPutLinkQue( cpqfcHBAdata,
+                        ELS_PRLI_ACC, 
+                        fchs );   
+	  
+	  NeedReject = FALSE;
+	}
+        else
+        {
+          // huh?
+          printk(" (unexpected) PRLI REQEST with plogi FALSE\n");
+
+          // set reject reason code 
+          ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+    
+    	  // Q a LOGOut here?
+	  
+        }
+      }
+      else
+      {
+        printk(" PRLI REQUEST payload failed verify\n");
+        // (reject code set by "verify")
+
+        // Q a LOGOut here?
+      }
+    }
+
+    if( NeedReject )
+    {
+      // Q a ReJecT Reply with reason code
+      fchs->reserved = ls_reject_code;
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs );  
+    }
+  }
+    break;
+ 
+
+    
+
+  case  0x0005:  // LOGOut?
+  {
+  // was this LOGOUT because we sent a ELS_PDISC to an FC device
+  // with changed (or new) port_id, or does the port refuse 
+  // to communicate to us?
+  // We maintain a logout counter - if we get 3 consecutive LOGOuts,
+  // give up!
+    LOGOUT_PAYLOAD logo;
+    BOOLEAN GiveUpOnDevice = FALSE;
+    ULONG ls_reject_code = 0;
+    
+    BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logo, sizeof(logo));
+
+    pLoggedInPort = fcFindLoggedInPort( 
+           fcChip, 
+           NULL,     // don't search Scsi Nexus
+	   0,        // don't search linked list for port_id
+           &logo.port_name[0],     // search linked list for WWN
+           NULL);    // don't care about end of list
+    
+    if( pLoggedInPort ) // found the device?
+    {
+      // Q an ACC reply 
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_LOGO_ACC, // Q Type
+                    fchs );       // device to respond to
+
+      // set login struct fields (LOGO_counter increment)
+      SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+      
+      // are we an Initiator?
+      if( fcChip->Options.initiator)  
+      {
+        // we're an Initiator, so check if we should 
+	// try (another?) login
+
+	// Fabrics routinely log out from us after
+	// getting device info - don't try to log them
+	// back in.
+	if( (fchs->s_id & 0xFFF000) == 0xFFF000 )
+	{
+	  ; // do nothing
+	}
+	else if( pLoggedInPort->LOGO_counter <= 3)
+	{
+	  // try (another) login (PLOGI request)
+	  
+          cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_PLOGI, // Q Type
+                    fchs );  
+	
+	  // Terminate I/O with "retry" potential
+	  cpqfcTSTerminateExchange( cpqfcHBAdata, 
+			            &pLoggedInPort->ScsiNexus,
+				    PORTID_CHANGED);
+	}
+	else
+	{
+	  printk(" Got 3 LOGOuts - terminating comm. with port_id %Xh\n",
+			  fchs->s_id &&0xFFFFFF);
+	  GiveUpOnDevice = TRUE;
+	}
+      }
+      else
+      {
+	GiveUpOnDevice = TRUE;
+      }
+
+
+      if( GiveUpOnDevice == TRUE )
+      {
+        cpqfcTSTerminateExchange( cpqfcHBAdata, 
+	                          &pLoggedInPort->ScsiNexus,
+		                  DEVICE_REMOVED);
+      }
+    }  
+    else  // we don't know this WWN!
+    {
+      // Q a ReJecT Reply with reason code
+      fchs->reserved = ls_reject_code;
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs );  
+    }
+  }
+    break;
+
+
+
+
+  // FABRIC only case
+  case 0x0461:  // ELS RSCN (Registered State Change Notification)?
+  {
+    int Ports;
+    int i;
+    __u32 Buff;
+    // Typically, one or more devices have been added to or dropped
+    // from the Fabric.
+    // The format of this frame is defined in FC-FLA (Rev 2.7, Aug 1997)
+    // The first 32-bit word has a 2-byte Payload Length, which
+    // includes the 4 bytes of the first word.  Consequently,
+    // this PL len must never be less than 4, must be a multiple of 4,
+    // and has a specified max value 256.
+    // (Endianess!)
+    Ports = ((fchs->pl[0] >>24) - 4) / 4;
+    Ports = Ports > 63 ? 63 : Ports;
+    
+    printk(" RSCN ports: %d\n", Ports);
+    if( Ports <= 0 )  // huh?
+    {
+      // ReJecT the command
+      fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 0);
+    
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs ); 
+      
+      break;
+    }
+    else  // Accept the command
+    {
+       cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_ACC, // Q Type
+                    fchs ); 
+    }
+    
+      // Check the "address format" to determine action.
+      // We have 3 cases:
+      // 0 = Port Address; 24-bit address of affected device
+      // 1 = Area Address; MS 16 bits valid
+      // 2 = Domain Address; MS 8 bits valid
+    for( i=0; i<Ports; i++)
+    { 
+      BigEndianSwap( (UCHAR*)&fchs->pl[i+1],(UCHAR*)&Buff, 4);
+      switch( Buff & 0xFF000000)
+      {
+
+      case 0:  // Port Address?
+	
+      case 0x01000000: // Area Domain?
+      case 0x02000000: // Domain Address
+        // For example, "port_id" 0x201300 
+	// OK, let's try a Name Service Request (Query)
+      fchs->s_id = 0xFFFFFC;  // Name Server Address
+      cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+
+      break;
+	
+	
+      default:  // huh? new value on version change?
+      break;
+      }
+    }
+  }    
+  break;    
+
+
+
+    
+  default:  // don't support this request (yet)
+    // set reject reason code 
+    fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 
+		                    REQUEST_NOT_SUPPORTED);
+    
+    cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs );     
+    break;  
+  }
+}
+
+
+static void ProcessELS_Reply( 
+		CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ox_id = (fchs->ox_rx_id >>16);
+  ULONG ls_reject_code;
+  PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+  
+  // If this is a valid reply, then we MUST have sent a request.
+  // Verify that we can find a valid request OX_ID corresponding to
+  // this reply
+
+  
+  if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+  {
+    printk(" *Discarding ACC/RJT frame, xID %04X/%04X* ", 
+		    ox_id, fchs->ox_rx_id & 0xffff);
+    goto Quit;  // exit this routine
+  }
+
+
+  // Is the reply a RJT (reject)?
+  if( (fchs->pl[0] & 0xFFFFL) == 0x01) // Reject reply?
+  {
+//  ******  REJECT REPLY  ********
+    switch( Exchanges->fcExchange[ox_id].type )
+    {
+	  
+    case ELS_FDISC:  // we sent out Fabric Discovery
+    case ELS_FLOGI:  // we sent out FLOGI
+
+      printk("RJT received on Fabric Login from %Xh, reason %Xh\n", 
+        fchs->s_id, fchs->pl[1]);    
+
+    break;
+
+    default:
+    break;
+    }
+      
+    goto Done;
+  }
+
+  // OK, we have an ACCept...
+  // What's the ACC type? (according to what we sent)
+  switch( Exchanges->fcExchange[ox_id].type )
+  {
+	  
+  case ELS_PLOGI:  // we sent out PLOGI
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      
+      // login ACC payload acceptable; search for WWN in our list
+      // of fcPorts
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,     // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort == NULL)         // WWN not found - new port
+      {
+
+	pLoggedInPort = CreateFcPort( 
+			  cpqfcHBAdata, 
+			  pLastLoggedInPort, 
+			  fchs,
+			  &logi);
+
+        if( pLoggedInPort == NULL )
+        {
+          printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+          // Now Q a LOGOut Request, since we won't be talking to that device
+	
+          goto Done;  // exit with error! dropped login frame
+	}
+      }
+      else      // WWN was already known.  Ensure that any open
+	        // exchanges for this WWN are terminated.
+      	// NOTE: It's possible that a device can change its 
+	// 24-bit port_id after a Link init or Fabric change 
+	// (e.g. LIP or Fabric RSCN).  In that case, the old
+	// 24-bit port_id may be duplicated, or no longer exist.
+      {
+
+        cpqfcTSTerminateExchange( cpqfcHBAdata, 
+          &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+      }
+
+      // We have an fcPort struct - set fields accordingly
+                                    // not PDISC, originator 
+      SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+			
+      // We just set a "port_id"; is it duplicated?
+      TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+      // For Fabric operation, we issued PLOGI to 0xFFFFFC
+      // so we can send SCR (State Change Registration) 
+      // Check for this special case...
+      if( fchs->s_id == 0xFFFFFC ) 
+      {
+        // PLOGI ACC was a Fabric response... issue SCR
+	fchs->s_id = 0xFFFFFD;  // address for SCR
+        cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_SCR, fchs);
+      }
+
+      else
+      {
+      // Now we need a PRLI to enable FCP-SCSI operation
+      // set flags and Q up a ELS_PRLI
+        cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PRLI, fchs);
+      }
+    }
+    else
+    {
+      // login payload unacceptable - reason in ls_reject_code
+      // Q up a Logout Request
+      printk("Login Payload unacceptable\n");
+
+    }
+    break;
+
+
+  // PDISC logic very similar to PLOGI, except we never want
+  // to allocate mem for "new" port, and we set flags differently
+  // (might combine later with PLOGI logic for efficiency)  
+  case ELS_PDISC:  // we sent out PDISC
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      BOOLEAN NeedLogin = FALSE;
+      
+      // login payload acceptable; search for WWN in our list
+      // of (previously seen) fcPorts
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,     // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort != NULL)   // WWN found?
+      {
+        // WWN has same port_id as last login?  (Of course, a properly
+	// working FC device should NEVER ACCept a PDISC if it's
+	// port_id changed, but check just in case...)
+	if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+	{
+          // Yes.  We were expecting PDISC?
+          if( pLoggedInPort->pdisc )
+	  {
+            int i;
+	    
+	    
+	    // PDISC expected -- set fields.  (PDISC, Originator)
+            SetLoginFields( pLoggedInPort, fchs, TRUE, TRUE);
+
+	    // We are ready to resume FCP-SCSI to this device...
+            // Do we need to start anything that was Queued?
+
+            for( i=0; i< TACH_SEST_LEN; i++)
+            {
+              // see if any exchange for this PDISC'd port was queued
+              if( ((fchs->s_id &0xFFFFFF) == 
+                   (Exchanges->fcExchange[i].fchs.d_id & 0xFFFFFF))
+                      &&
+                  (Exchanges->fcExchange[i].status & EXCHANGE_QUEUED))
+              {
+                fchs->reserved = i; // copy ExchangeID
+//                printk(" *Q x_ID %Xh after PDISC* ",i);
+
+                cpqfcTSPutLinkQue( cpqfcHBAdata, EXCHANGE_QUEUED, fchs );
+              }
+            }
+
+	    // Complete commands Q'd while we were waiting for Login
+
+	    UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+	  }
+	  else
+	  {
+	    printk("Not expecting PDISC (pdisc=FALSE)\n");
+	    NeedLogin = TRUE;
+	  }
+	}
+	else
+	{
+	  printk("PDISC PortID change: old %Xh, new %Xh\n",
+            pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+          NeedLogin = TRUE;
+		  
+	}
+      }
+      else
+      {
+	printk("PDISC ACC from unknown WWN\n");
+        NeedLogin = TRUE;
+      }
+
+      if( NeedLogin)
+      {
+	
+        // The PDISC failed.  Set login struct flags accordingly,
+	// terminate any I/O to this port, and Q a PLOGI
+	if( pLoggedInPort )  // FC device previously known?
+	{
+
+          cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_LOGO, // Q Type
+                    fchs );   // has port_id to send to 
+
+	  // There are a variety of error scenarios which can result
+  	  // in PDISC failure, so as a catchall, add the check for
+	  // duplicate port_id.
+	  TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+//    TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+          pLoggedInPort->pdisc = FALSE;
+          pLoggedInPort->prli = FALSE;
+          pLoggedInPort->plogi = FALSE;
+	
+          cpqfcTSTerminateExchange( cpqfcHBAdata, 
+	    &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+        }
+        cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs );
+      }
+    }
+    else
+    {
+      // login payload unacceptable - reason in ls_reject_code
+      // Q up a Logout Request
+      printk("ERROR: Login Payload unacceptable!\n");
+
+    }
+   
+    break;
+    
+
+
+  case ELS_PRLI:  // we sent out PRLI
+
+
+    pLoggedInPort = fcFindLoggedInPort( 
+           fcChip, 
+           NULL,       // don't search Scsi Nexus
+	   (fchs->s_id & 0xFFFFFF),  // search linked list for port_id
+           NULL,     // DON'T search linked list for WWN
+           NULL);    // don't care
+      
+    if( pLoggedInPort == NULL ) 
+    {
+      // huh?
+      printk(" Unexpected PRLI ACCept frame!\n");
+
+      // Q a LOGOut here?
+
+      goto Done;
+    }
+
+    // verify the PRLI ACC payload
+    if( !verify_PRLI( fchs, &ls_reject_code) )
+    {
+      // PRLI Reply is acceptable; were we expecting it?
+      if( pLoggedInPort->plogi ) 
+      { 
+	// yes, we expected the PRLI ACC  (not PDISC; Originator)
+	SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+
+        // OK, let's send a REPORT_LUNS command to determine
+	// whether VSA or PDA FCP-LUN addressing is used.
+	
+        cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+	
+	// It's possible that a device we were talking to changed 
+	// port_id, and has logged back in.  This function ensures
+	// that I/O will resume.
+        UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+
+      }
+      else
+      {
+        // huh?
+        printk(" (unexpected) PRLI ACCept with plogi FALSE\n");
+
+        // Q a LOGOut here?
+        goto Done;
+      }
+    }
+    else
+    {
+      printk(" PRLI ACCept payload failed verify\n");
+
+      // Q a LOGOut here?
+    }
+
+    break;
+ 
+  case ELS_FLOGI:  // we sent out FLOGI (Fabric Login)
+
+    // update the upper 16 bits of our port_id in Tachyon
+    // the switch adds those upper 16 bits when responding
+    // to us (i.e. we are the destination_id)
+    fcChip->Registers.my_al_pa = (fchs->d_id & 0xFFFFFF);
+    writel( fcChip->Registers.my_al_pa,  
+      fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+
+    // now send out a PLOGI to the well known port_id 0xFFFFFC
+    fchs->s_id = 0xFFFFFC;
+    cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs);
+  
+   break; 
+
+
+  case ELS_FDISC:  // we sent out FDISC (Fabric Discovery (Login))
+
+   printk( " ELS_FDISC success ");
+   break;
+   
+
+  case ELS_SCR:  // we sent out State Change Registration
+    // now we can issue Name Service Request to find any
+    // Fabric-connected devices we might want to login to.
+   
+	
+    fchs->s_id = 0xFFFFFC;  // Name Server Address
+    cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+    
+
+    break;
+
+    
+  default:
+    printk(" *Discarding unknown ACC frame, xID %04X/%04X* ", 
+   		    ox_id, fchs->ox_rx_id & 0xffff);
+    break;
+  }
+
+  
+Done:
+  // Regardless of whether the Reply is valid or not, the
+  // the exchange is done - complete
+  cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
+	  
+Quit:    
+  return;
+}
+
+
+
+
+
+
+// ****************  Fibre Channel Services  **************
+// This is where we process the Directory (Name) Service Reply
+// to know which devices are on the Fabric
+
+static void ProcessFCS_Reply( 
+	CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ox_id = (fchs->ox_rx_id >>16);
+//  ULONG ls_reject_code;
+//  PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+  
+  // If this is a valid reply, then we MUST have sent a request.
+  // Verify that we can find a valid request OX_ID corresponding to
+  // this reply
+
+  if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+  {
+    printk(" *Discarding Reply frame, xID %04X/%04X* ", 
+		    ox_id, fchs->ox_rx_id & 0xffff);
+    goto Quit;  // exit this routine
+  }
+
+
+  // OK, we were expecting it.  Now check to see if it's a
+  // "Name Service" Reply, and if so force a re-validation of
+  // Fabric device logins (i.e. Start the login timeout and
+  // send PDISC or PLOGI)
+  // (Endianess Byte Swap?)
+  if( fchs->pl[1] == 0x02FC )  // Name Service
+  {
+    // got a new (or NULL) list of Fabric attach devices... 
+    // Invalidate current logins
+    
+    PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+    while( pLoggedInPort ) // for all ports which are expecting
+                           // PDISC after the next LIP, set the
+                           // logoutTimer
+    {
+
+      if( (pLoggedInPort->port_id & 0xFFFF00)  // Fabric device?
+		      &&
+          (pLoggedInPort->port_id != 0xFFFFFC) ) // NOT the F_Port
+      {
+        pLoggedInPort->LOGO_timer = 6;  // what's the Fabric timeout??
+                                // suspend any I/O in progress until
+                                // PDISC received...
+        pLoggedInPort->prli = FALSE;   // block FCP-SCSI commands
+      }
+	    
+      pLoggedInPort = pLoggedInPort->pNextPort;
+    }
+    
+    if( fchs->pl[2] == 0x0280)  // ACCept?
+    {
+      // Send PLOGI or PDISC to these Fabric devices
+      SendLogins( cpqfcHBAdata, &fchs->pl[4] );  
+    }
+
+
+    // As of this writing, the only reason to reject is because NO
+    // devices are left on the Fabric.  We already started
+    // "logged out" timers; if the device(s) don't come
+    // back, we'll do the implicit logout in the heart beat 
+    // timer routine
+    else  // ReJecT
+    {
+      // this just means no Fabric device is visible at this instant
+    } 
+  }
+
+  // Regardless of whether the Reply is valid or not, the
+  // the exchange is done - complete
+  cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
+	  
+Quit:    
+  return;
+}
+
+
+
+
+
+
+
+static void AnalyzeIncomingFrame( 
+        CPQFCHBA *cpqfcHBAdata,
+        ULONG QNdx )
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+  TachFCHDR_GCMND* fchs = 
+    (TachFCHDR_GCMND*)fcLQ->Qitem[QNdx].ulBuff;
+//  ULONG ls_reject_code;  // reason for rejecting login
+  LONG ExchangeID;
+//  FC_LOGGEDIN_PORT *pLoggedInPort;
+  BOOLEAN AbortAccept;  
+
+  ENTER("AnalyzeIncomingFrame");
+
+
+
+  switch( fcLQ->Qitem[QNdx].Type) // FCP or Unknown
+  {
+
+  case SFQ_UNKNOWN:  // unknown frame (e.g. LIP position frame, NOP, etc.)
+ 
+
+      // *********  FC-4 Device Data/ Fibre Channel Service *************
+    if( ((fchs->d_id &0xF0000000) == 0)   // R_CTL (upper nibble) 0x0?
+                &&   
+      (fchs->f_ctl & 0x20000000) )  // TYPE 20h is Fibre Channel Service
+    {
+
+      // ************** FCS Reply **********************
+
+      if( (fchs->d_id & 0xff000000L) == 0x03000000L)  // (31:23 R_CTL)
+      {
+	ProcessFCS_Reply( cpqfcHBAdata, fchs );
+
+      }  // end of  FCS logic
+
+    }
+    
+
+      // ***********  Extended Link Service **************
+
+    else if( fchs->d_id & 0x20000000   // R_CTL 0x2?
+                  &&   
+      (fchs->f_ctl & 0x01000000) )  // TYPE = 1
+    {
+
+                           // these frames are either a response to
+                           // something we sent (0x23) or "unsolicited"
+                           // frames (0x22).
+
+
+      // **************Extended Link REPLY **********************
+                           // R_CTL Solicited Control Reply
+
+      if( (fchs->d_id & 0xff000000L) == 0x23000000L)  // (31:23 R_CTL)
+      {
+
+	ProcessELS_Reply( cpqfcHBAdata, fchs );
+
+      }  // end of  "R_CTL Solicited Control Reply"
+
+
+
+
+       // **************Extended Link REQUEST **********************
+       // (unsolicited commands from another port or task...)
+
+                           // R_CTL Ext Link REQUEST
+      else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+              (fchs->ox_rx_id != 0xFFFFFFFFL) ) // (ignore LIP frame)
+      {
+
+
+
+	ProcessELS_Request( cpqfcHBAdata, fchs );
+
+      }
+
+
+
+        // ************** LILP **********************
+      else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+               (fchs->ox_rx_id == 0xFFFFFFFFL)) // (e.g., LIP frames)
+
+      {
+        // SANMark specifies that when available, we must use
+	// the LILP frame to determine which ALPAs to send Port Discovery
+	// to...
+
+        if( fchs->pl[0] == 0x0711L) //  ELS_PLOGI?
+	{
+//	  UCHAR *ptr = (UCHAR*)&fchs->pl[1];
+//	  printk(" %d ALPAs found\n", *ptr);
+	  memcpy( fcChip->LILPmap, &fchs->pl[1], 32*4); // 32 DWORDs
+	  fcChip->Options.LILPin = 1; // our LILPmap is valid!
+	  // now post to make Port Discovery happen...
+          cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, fchs);  
+	}
+      }
+    }
+
+     
+    // *****************  BASIC LINK SERVICE *****************
+    
+    else if( fchs->d_id & 0x80000000  // R_CTL:
+                    &&           // Basic Link Service Request
+           !(fchs->f_ctl & 0xFF000000) )  // type=0 for BLS
+    {
+
+      // Check for ABTS (Abort Sequence)
+      if( (fchs->d_id & 0x8F000000) == 0x81000000)
+      {
+        // look for OX_ID, S_ID pair that matches in our
+        // fcExchanges table; if found, reply with ACCept and complete
+        // the exchange
+
+        // Per PLDA, an ABTS is sent by an initiator; therefore
+        // assume that if we have an exhange open to the port who
+        // sent ABTS, it will be the d_id of what we sent.  
+        for( ExchangeID = 0, AbortAccept=FALSE;
+             ExchangeID < TACH_SEST_LEN; ExchangeID++)
+        {
+            // Valid "target" exchange 24-bit port_id matches? 
+            // NOTE: For the case of handling Intiator AND Target
+            // functions on the same chip, we can have TWO Exchanges
+            // with the same OX_ID -- OX_ID/FFFF for the CMND, and
+            // OX_ID/RX_ID for the XRDY or DATA frame(s).  Ideally,
+            // we would like to support ABTS from Initiators or Targets,
+            // but it's not clear that can be supported on Tachyon for
+            // all cases (requires more investigation).
+            
+          if( (Exchanges->fcExchange[ ExchangeID].type == SCSI_TWE ||
+               Exchanges->fcExchange[ ExchangeID].type == SCSI_TRE)
+                  &&
+             ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+             (fchs->s_id & 0xFFFFFF)) )
+          {
+              
+              // target xchnge port_id matches -- how about OX_ID?
+            if( (Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id &0xFFFF0000)
+                    == (fchs->ox_rx_id & 0xFFFF0000) )
+                    // yes! post ACCept response; will be completed by fcStart
+            {
+              Exchanges->fcExchange[ ExchangeID].status = TARGET_ABORT;
+                
+                // copy (add) rx_id field for simplified ACCept reply
+              fchs->ox_rx_id = 
+                Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id;
+                
+              cpqfcTSPutLinkQue( cpqfcHBAdata,
+                            BLS_ABTS_ACC, // Q Type 
+                            fchs );    // void QueContent
+              AbortAccept = TRUE;
+      printk("ACCepting ABTS for x_ID %8.8Xh, SEST pair %8.8Xh\n", 
+             fchs->ox_rx_id, Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id);
+              break;      // ABTS can affect only ONE exchange -exit loop
+            }
+          }
+        }  // end of FOR loop
+        if( !AbortAccept ) // can't ACCept ABTS - send Reject
+        {
+      printk("ReJecTing: can't find ExchangeID %8.8Xh for ABTS command\n", 
+            fchs->ox_rx_id);
+          if( Exchanges->fcExchange[ ExchangeID].type 
+                &&
+              !(fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len
+               & 0x80000000))
+          {
+            cpqfcTSCompleteExchange( fcChip, ExchangeID);
+          }
+          else
+          {
+            printk("Unexpected ABTS ReJecT! SEST[%X] Dword 0: %Xh\n", 
+              ExchangeID, fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len);
+          }
+        }
+      }
+
+      // Check for BLS {ABTS? (Abort Sequence)} ACCept
+      else if( (fchs->d_id & 0x8F000000) == 0x84000000)
+      {
+        // target has responded with ACC for our ABTS;
+	// complete the indicated exchange with ABORTED status 
+        // Make no checks for correct RX_ID, since
+	// all we need to conform ABTS ACC is the OX_ID.
+        // Verify that the d_id matches!
+ 
+        ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+//	printk("ABTS ACC x_ID 0x%04X 0x%04X, status %Xh\n", 
+//          fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff,
+//          Exchanges->fcExchange[ExchangeID].status);
+
+
+	
+        if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+        {
+            // Does "target" exchange 24-bit port_id match? 
+            // (See "NOTE" above for handling Intiator AND Target in
+            // the same device driver)
+            // First, if this is a target response, then we originated
+	    // (initiated) it with BLS_ABTS:
+	  
+          if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+                  &&
+	    // Second, does the source of this ACC match the destination
+            // of who we originally sent it to?
+             ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+             (fchs->s_id & 0xFFFFFF)) )
+          {
+            cpqfcTSCompleteExchange( fcChip, ExchangeID );
+	  }
+        }              
+      }
+      // Check for BLS {ABTS? (Abort Sequence)} ReJecT
+      else if( (fchs->d_id & 0x8F000000) == 0x85000000)
+      {
+        // target has responded with RJT for our ABTS;
+	// complete the indicated exchange with ABORTED status 
+        // Make no checks for correct RX_ID, since
+	// all we need to conform ABTS ACC is the OX_ID.
+        // Verify that the d_id matches!
+ 
+        ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+//	printk("BLS_ABTS RJT on Exchange 0x%04X 0x%04X\n", 
+//          fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff);
+
+        if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+	{  
+            // Does "target" exchange 24-bit port_id match? 
+            // (See "NOTE" above for handling Intiator AND Target in
+            // the same device driver)
+            // First, if this is a target response, then we originated
+	    // (initiated) it with BLS_ABTS:
+		  
+          if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+                  &&
+	    // Second, does the source of this ACC match the destination
+            // of who we originally sent it to?
+             ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+             (fchs->s_id & 0xFFFFFF)) )
+          {
+	    // YES! NOTE: There is a bug in CPQ's RA-4000 box 
+	    // where the "reason code" isn't returned in the payload
+	    // For now, simply presume the reject is because the target
+	    // already completed the exchange...
+	    
+//            printk("complete x_ID %Xh on ABTS RJT\n", ExchangeID);
+            cpqfcTSCompleteExchange( fcChip, ExchangeID );
+	  }
+	} 
+      }  // end of ABTS check
+    }  // end of Basic Link Service Request
+    break;
+  
+    default:
+      printk("AnalyzeIncomingFrame: unknown type: %Xh(%d)\n",
+        fcLQ->Qitem[QNdx].Type,
+        fcLQ->Qitem[QNdx].Type);
+    break;
+  }
+}
+
+
+// Function for Port Discovery necessary after every FC 
+// initialization (e.g. LIP).
+// Also may be called if from Fabric Name Service logic.
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds )
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ulStatus=0;
+  TachFCHDR_GCMND fchs;  // copy fields for transmission
+  int i;
+  ULONG loginType;
+  LONG ExchangeID;
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  __u32 PortIds[ number_of_al_pa];
+  int NumberOfPorts=0;
+
+  // We're going to presume (for now) that our limit of Fabric devices
+  // is the same as the number of alpa on a private loop (126 devices).
+  // (Of course this could be changed to support however many we have
+  // memory for).
+  memset( &PortIds[0], 0, sizeof(PortIds));
+   
+  // First, check if this login is for our own Link Initialization
+  // (e.g. LIP on FC-AL), or if we have knowledge of Fabric devices
+  // from a switch.  If we are logging into Fabric devices, we'll
+  // have a non-NULL FabricPortId pointer
+  
+  if( FabricPortIds != NULL) // may need logins
+  {
+    int LastPort=FALSE;
+    i = 0;
+    while( !LastPort)
+    {
+      // port IDs From NSR payload; byte swap needed?
+      BigEndianSwap( (UCHAR*)FabricPortIds, (UCHAR*)&PortIds[i], 4);
+ 
+//      printk("FPortId[%d] %Xh ", i, PortIds[i]);
+      if( PortIds[i] & 0x80000000)
+	LastPort = TRUE;
+      
+      PortIds[i] &= 0xFFFFFF; // get 24-bit port_id
+      // some non-Fabric devices (like the Crossroads Fibre/Scsi bridge)
+      // erroneously use ALPA 0.
+      if( PortIds[i]  ) // need non-zero port_id...
+        i++;
+      
+      if( i >= number_of_al_pa ) // (in)sanity check
+	break;
+      FabricPortIds++;  // next...
+    }
+
+    NumberOfPorts = i;
+//    printk("NumberOf Fabric ports %d", NumberOfPorts);
+  }
+  
+  else  // need to send logins on our "local" link
+  {
+  
+    // are we a loop port?  If so, check for reception of LILP frame,
+    // and if received use it (SANMark requirement)
+    if( fcChip->Options.LILPin )
+    {
+      int j=0;
+      // sanity check on number of ALPAs from LILP frame...
+      // For format of LILP frame, see FC-AL specs or 
+      // "Fibre Channel Bench Reference", J. Stai, 1995 (ISBN 1-879936-17-8)
+      // First byte is number of ALPAs
+      i = fcChip->LILPmap[0] >= (32*4) ? 32*4 : fcChip->LILPmap[0];
+      NumberOfPorts = i;
+//      printk(" LILP alpa count %d ", i);
+      while( i > 0)
+      {
+	PortIds[j] = fcChip->LILPmap[1+ j];
+	j++; i--;
+      }
+    }
+    else  // have to send login to everybody
+    {
+      int j=0;
+      i = number_of_al_pa;
+      NumberOfPorts = i;
+      while( i > 0)
+      {
+        PortIds[j] = valid_al_pa[j]; // all legal ALPAs
+	j++; i--;
+      }
+    }
+  }
+
+
+  // Now we have a copy of the port_ids (and how many)...
+  for( i = 0; i < NumberOfPorts; i++)
+  {
+    // 24-bit FC Port ID
+    fchs.s_id = PortIds[i];  // note: only 8-bits used for ALPA
+
+
+    // don't log into ourselves (Linux Scsi disk scan will stop on
+    // no TARGET support error on us, and quit trying for rest of devices)
+    if( (fchs.s_id & 0xFF ) == (fcChip->Registers.my_al_pa & 0xFF) )
+      continue;
+
+    // fabric login needed?
+    if( (fchs.s_id == 0) || 
+        (fcChip->Options.fabric == 1) )
+    {
+      fcChip->Options.flogi = 1;  // fabric needs longer for login
+      // Do we need FLOGI or FDISC?
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+             NULL,           // don't search SCSI Nexus
+             0xFFFFFC,       // search linked list for Fabric port_id
+             NULL,           // don't search WWN
+             NULL);          // (don't care about end of list)
+
+      if( pLoggedInPort )    // If found, we have prior experience with
+                             // this port -- check whether PDISC is needed
+      {
+        if( pLoggedInPort->flogi )
+	{
+	  // does the switch support FDISC?? (FLOGI for now...)
+          loginType = ELS_FLOGI;  // prior FLOGI still valid
+	}
+        else
+          loginType = ELS_FLOGI;  // expired FLOGI
+      }
+      else                      // first FLOGI?
+        loginType = ELS_FLOGI;  
+
+
+      fchs.s_id = 0xFFFFFE;   // well known F_Port address
+
+      // Fabrics are not required to support FDISC, and
+      // it's not clear if that helps us anyway, since
+      // we'll want a Name Service Request to re-verify
+      // visible devices...
+      // Consequently, we always want our upper 16 bit
+      // port_id to be zero (we'll be rejected if we
+      // use our prior port_id if we've been plugged into
+      // a different switch port).
+      // Trick Tachyon to send to ALPA 0 (see TL/TS UG, pg 87)
+      // If our ALPA is 55h for instance, we want the FC frame
+      // s_id to be 0x000055, while Tach's my_al_pa register
+      // must be 0x000155, to force an OPN at ALPA 0 
+      // (the Fabric port)
+      fcChip->Registers.my_al_pa &= 0xFF; // only use ALPA for FLOGI
+      writel( fcChip->Registers.my_al_pa | 0x0100,  
+        fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+    }
+
+    else // not FLOGI...
+    {
+      // should we send PLOGI or PDISC?  Check if any prior port_id
+      // (e.g. alpa) completed a PLOGI/PRLI exchange by checking 
+      // the pdisc flag.
+
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+             NULL,           // don't search SCSI Nexus
+             fchs.s_id,      // search linked list for al_pa
+             NULL,           // don't search WWN
+             NULL);          // (don't care about end of list)
+
+             
+
+      if( pLoggedInPort )      // If found, we have prior experience with
+                             // this port -- check whether PDISC is needed
+      {
+        if( pLoggedInPort->pdisc )
+	{
+          loginType = ELS_PDISC;  // prior PLOGI and PRLI maybe still valid
+           
+	}
+        else
+          loginType = ELS_PLOGI;  // prior knowledge, but can't use PDISC
+      }
+      else                      // never talked to this port_id before
+        loginType = ELS_PLOGI;  // prior knowledge, but can't use PDISC
+    }
+
+
+    
+    ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          loginType,            // e.g. PLOGI
+          &fchs,        // no incoming frame (we are originator)
+          NULL,         // no data (no scatter/gather list)
+          &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+    if( !ulStatus ) // Exchange setup OK?
+    {
+      ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+      if( !ulStatus )
+      {
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+
+	if( loginType == ELS_PDISC )
+	{
+	  // now, we really shouldn't Revalidate SEST exchanges until
+	  // we get an ACC reply from our target and verify that
+	  // the target address/WWN is unchanged.  However, when a fast
+	  // target gets the PDISC, they can send SEST Exchange data
+	  // before we even get around to processing the PDISC ACC.
+	  // Consequently, we lose the I/O.
+	  // To avoid this, go ahead and Revalidate when the PDISC goes
+	  // out, anticipating that the ACC will be truly acceptable
+	  // (this happens 99.9999....% of the time).
+	  // If we revalidate a SEST write, and write data goes to a
+	  // target that is NOT the one we originated the WRITE to,
+	  // that target is required (FCP-SCSI specs, etc) to discard 
+	  // our WRITE data.
+
+          // Re-validate SEST entries (Tachyon hardware assists)
+          RevalidateSEST( cpqfcHBAdata->HostAdapter, pLoggedInPort); 
+    //TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+	}
+      }
+      else  // give up immediately on error
+      {
+#ifdef LOGIN_DBG
+        printk("SendLogins: fcStartExchange failed: %Xh\n", ulStatus );
+#endif
+        break;
+      }
+
+              
+      if( fcChip->Registers.FMstatus.value & 0x080 ) // LDn during Port Disc.
+      {
+        ulStatus = LNKDWN_OSLS;
+#ifdef LOGIN_DBG
+        printk("SendLogins: PortDisc aborted (LDn) @alpa %Xh\n", fchs.s_id);
+#endif
+        break;
+      }
+        // Check the exchange for bad status (i.e. FrameTimeOut),
+        // and complete on bad status (most likely due to BAD_ALPA)
+        // on LDn, DPC function may already complete (ABORT) a started
+        // exchange, so check type first (type = 0 on complete).
+      if( Exchanges->fcExchange[ExchangeID].status )
+      {
+#ifdef LOGIN_DBG 
+ 	printk("completing x_ID %X on status %Xh\n", 
+          ExchangeID, Exchanges->fcExchange[ExchangeID].status);
+#endif
+        cpqfcTSCompleteExchange( fcChip, ExchangeID);
+      }
+    }
+    else   // Xchange setup failed...
+    {
+#ifdef LOGIN_DBG
+      printk("FC: cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+#endif
+      break;
+    }
+  }
+  if( !ulStatus )
+  {
+    // set the event signifying that all ALPAs were sent out.
+#ifdef LOGIN_DBG
+    printk("SendLogins: PortDiscDone\n");
+#endif
+    cpqfcHBAdata->PortDiscDone = 1;
+
+
+    // TL/TS UG, pg. 184
+    // 0x0065 = 100ms for RT_TOV
+    // 0x01f5 = 500ms for ED_TOV
+    fcChip->Registers.ed_tov.value = 0x006501f5L; 
+    writel( fcChip->Registers.ed_tov.value,
+      (fcChip->Registers.ed_tov.address));
+
+    // set the LP_TOV back to ED_TOV (i.e. 500 ms)
+    writel( 0x00000010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+  }
+  else
+  {
+    printk("SendLogins: failed at xchng %Xh, alpa %Xh, status %Xh\n", 
+      ExchangeID, fchs.s_id, ulStatus);
+  }
+  LEAVE("SendLogins");
+
+}
+
+
+// for REPORT_LUNS documentation, see "In-Depth Exploration of Scsi",
+// D. Deming, 1994, pg 7-19 (ISBN 1-879936-08-9)
+static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd)
+{
+  struct Scsi_Host *HostAdapter = Cmnd->host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LOGGEDIN_PORT pLoggedInPort; 
+  int LunListLen=0;
+  int i;
+  ULONG x_ID = 0xFFFFFFFF;
+  UCHAR *ucBuff = Cmnd->request_buffer;
+
+//  printk("cpqfcTS: ReportLunsDone \n");
+  // first, we need to find the Exchange for this command,
+  // so we can find the fcPort struct to make the indicated
+  // changes.
+  for( i=0; i< TACH_SEST_LEN; i++)
+  {
+    if( Exchanges->fcExchange[i].type   // exchange defined?
+                   &&
+       (Exchanges->fcExchange[i].Cmnd == Cmnd) ) // matches?
+	      
+    {
+      x_ID = i;  // found exchange!
+      break;
+    }
+  }
+  if( x_ID == 0xFFFFFFFF)
+  {
+//    printk("cpqfcTS: ReportLuns failed - no FC Exchange\n");
+    goto Done;  // Report Luns FC Exchange gone; 
+                // exchange probably Terminated by Implicit logout
+  }
+
+
+  // search linked list for the port_id we sent INQUIRY to
+  pLoggedInPort = fcFindLoggedInPort( fcChip,
+    NULL,     // DON'T search Scsi Nexus (we will set it)
+    Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,        
+    NULL,     // DON'T search linked list for FC WWN
+    NULL);    // DON'T care about end of list
+ 
+  if( !pLoggedInPort )
+  {
+//    printk("cpqfcTS: ReportLuns failed - device gone\n");
+    goto Done; // error! can't find logged in Port
+  }    
+  LunListLen = ucBuff[3];
+  LunListLen += ucBuff[2]>>8;
+
+  if( !LunListLen )  // failed
+  {
+    // generically speaking, a soft error means we should retry...
+    if( (Cmnd->result >> 16) == DID_SOFT_ERROR )
+    {
+      if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+	        (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+      {
+        TachFCHDR_GCMND *fchs = &Exchanges->fcExchange[ x_ID].fchs;
+      // did we fail because of "check condition, device reset?"
+      // e.g. the device was reset (i.e., at every power up)
+      // retry the Report Luns
+      
+      // who are we sending it to?
+      // we know this because we have a copy of the command
+      // frame from the original Report Lun command -
+      // switch the d_id/s_id fields, because the Exchange Build
+      // context is "reply to source".
+      
+        fchs->s_id = fchs->d_id; // (temporarily re-use the struct)
+        cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+      }
+    }
+    else  // probably, the device doesn't support Report Luns
+      pLoggedInPort->ScsiNexus.VolumeSetAddressing = 0;  
+  }
+  else  // we have LUN info - check VSA mode
+  {
+    // for now, assume all LUNs will have same addr mode
+    // for VSA, payload byte 8 will be 0x40; otherwise, 0
+    pLoggedInPort->ScsiNexus.VolumeSetAddressing = ucBuff[8];  
+      
+    // Since we got a Report Luns answer, set lun masking flag
+    pLoggedInPort->ScsiNexus.LunMasking = 1;
+
+    if( LunListLen > 8*CPQFCTS_MAX_LUN)   // We expect CPQFCTS_MAX_LUN max
+      LunListLen = 8*CPQFCTS_MAX_LUN;
+
+/*   
+    printk("Device WWN %08X%08X Reports Luns @: ", 
+          (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), 
+          (ULONG)(pLoggedInPort->u.liWWN>>32));
+	    
+    for( i=8; i<LunListLen+8; i+=8)
+    {  
+      printk("%02X%02X ", ucBuff[i], ucBuff[i+1] );
+    }
+    printk("\n");
+*/
+    
+    // Since the device was kind enough to tell us where the
+    // LUNs are, lets ensure they are contiguous for Linux's
+    // SCSI driver scan, which expects them to start at 0.
+    // Since Linux only supports 8 LUNs, only copy the first
+    // eight from the report luns command
+
+    // e.g., the Compaq RA4x00 f/w Rev 2.54 and above may report
+    // LUNs 4001, 4004, etc., because other LUNs are masked from
+    // this HBA (owned by someone else).  We'll make those appear as
+    // LUN 0, 1... to Linux
+    {
+      int j;
+      int AppendLunList = 0;
+      // Walk through the LUN list.  The 'j' array number is
+      // Linux's lun #, while the value of .lun[j] is the target's
+      // lun #.
+      // Once we build a LUN list, it's possible for a known device 
+      // to go offline while volumes (LUNs) are added.  Later,
+      // the device will do another PLOGI ... Report Luns command,
+      // and we must not alter the existing Linux Lun map.
+      // (This will be very rare).
+      for( j=0; j < CPQFCTS_MAX_LUN; j++)
+      {
+        if( pLoggedInPort->ScsiNexus.lun[j] != 0xFF )
+	{
+	  AppendLunList = 1;
+	  break;
+	}
+      }
+      if( AppendLunList )
+      {
+	int k;
+        int FreeLunIndex;
+//        printk("cpqfcTS: AppendLunList\n");
+
+	// If we get a new Report Luns, we cannot change
+	// any existing LUN mapping! (Only additive entry)
+	// For all LUNs in ReportLun list
+	// if RL lun != ScsiNexus lun
+	//   if RL lun present in ScsiNexus lun[], continue
+	//   else find ScsiNexus lun[]==FF and add, continue
+	
+        for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+	{
+          if( pLoggedInPort->ScsiNexus.lun[j] != ucBuff[i+1] )
+	  {
+	    // something changed from the last Report Luns
+	    printk(" cpqfcTS: Report Lun change!\n");
+	    for( k=0, FreeLunIndex=CPQFCTS_MAX_LUN; 
+                 k < CPQFCTS_MAX_LUN; k++)
+            {
+	      if( pLoggedInPort->ScsiNexus.lun[k] == 0xFF)
+	      {
+		FreeLunIndex = k;
+		break;
+	      }
+              if( pLoggedInPort->ScsiNexus.lun[k] == ucBuff[i+1] )
+		break; // we already masked this lun
+	    }
+	    if( k >= CPQFCTS_MAX_LUN )
+	    {
+	      printk(" no room for new LUN %d\n", ucBuff[i+1]);
+	    }
+	    else if( k == FreeLunIndex )  // need to add LUN
+	    {
+	      pLoggedInPort->ScsiNexus.lun[k] = ucBuff[i+1];
+//	      printk("add [%d]->%02d\n", k, pLoggedInPort->ScsiNexus.lun[k]);
+	      
+	    }
+	    else
+	    {
+	      // lun already known
+	    }
+	    break;
+	  }
+	}
+	// print out the new list...
+	for( j=0; j< CPQFCTS_MAX_LUN; j++)
+	{
+	  if( pLoggedInPort->ScsiNexus.lun[j] == 0xFF)
+	    break; // done
+//	  printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+	}
+      }
+      else
+      {
+//	  printk("Linux SCSI LUNs[] -> Device LUNs: ");
+	// first time - this is easy
+        for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+	{
+          pLoggedInPort->ScsiNexus.lun[j] = ucBuff[i+1];
+//	  printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+	}
+//	printk("\n");
+      }
+    }
+  }
+
+Done:  
+}
+
+// After successfully getting a "Process Login" (PRLI) from an
+// FC port, we want to Discover the LUNs so that we know the
+// addressing type (e.g., FCP-SCSI Volume Set Address, Peripheral
+// Unit Device), and whether SSP (Selective Storage Presentation or
+// Lun Masking) has made the LUN numbers non-zero based or 
+// non-contiguous.  To remain backward compatible with the SCSI-2
+// driver model, which expects a contiguous LUNs starting at 0,
+// will use the ReportLuns info to map from "device" to "Linux" 
+// LUNs.
+static void IssueReportLunsCommand( 
+              CPQFCHBA* cpqfcHBAdata, 
+	      TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  PFC_LOGGEDIN_PORT pLoggedInPort; 
+  Scsi_Cmnd *Cmnd;
+  LONG x_ID;
+  ULONG ulStatus;
+  UCHAR *ucBuff;
+
+
+  if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+  {
+    printk("Discard Q'd ReportLun command\n");
+    goto Done;
+  }
+
+  // find the device (from port_id) we're talking to
+  pLoggedInPort = fcFindLoggedInPort( fcChip,
+        NULL,     // DON'T search Scsi Nexus 
+  	fchs->s_id & 0xFFFFFF,        
+	NULL,     // DON'T search linked list for FC WWN
+        NULL);    // DON'T care about end of list
+  if( pLoggedInPort ) // we'd BETTER find it!
+  {
+
+
+    if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+      goto Done;  // forget it - FC device not a "target"
+
+    // now use the port's Scsi Command buffer for the 
+    // Report Luns Command
+ 
+    Cmnd = &pLoggedInPort->ScsiCmnd; 
+    ucBuff = pLoggedInPort->ReportLunsPayload;
+    
+    memset( Cmnd, 0, sizeof(Scsi_Cmnd));
+    memset( ucBuff, 0, REPORT_LUNS_PL);
+    
+    Cmnd->scsi_done = ScsiReportLunsDone;
+    Cmnd->host = cpqfcHBAdata->HostAdapter;
+
+    Cmnd->request_buffer = pLoggedInPort->ReportLunsPayload; 
+    Cmnd->request_bufflen = REPORT_LUNS_PL; 
+	    
+    Cmnd->cmnd[0] = 0xA0;
+    Cmnd->cmnd[8] = REPORT_LUNS_PL >> 8;
+    Cmnd->cmnd[9] = (UCHAR)REPORT_LUNS_PL;
+    Cmnd->cmd_len = 12;
+
+    Cmnd->channel = pLoggedInPort->ScsiNexus.channel;
+    Cmnd->target = pLoggedInPort->ScsiNexus.target;
+    
+	    
+    ulStatus = cpqfcTSBuildExchange(
+      cpqfcHBAdata,
+      SCSI_IRE, 
+      fchs,
+      Cmnd,         // buffer for Report Lun data
+      &x_ID );// fcController->fcExchanges index, -1 if failed
+
+    if( !ulStatus ) // Exchange setup?
+    {
+      ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+      if( !ulStatus )
+      {
+        // submitted to Tach's Outbound Que (ERQ PI incremented)
+        // waited for completion for ELS type (Login frames issued
+        // synchronously)
+      }
+      else
+        // check reason for Exchange not being started - we might
+        // want to Queue and start later, or fail with error
+      {
+  
+      }
+    }
+
+    else   // Xchange setup failed...
+      printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+  }
+  else     // like, we just got a PRLI ACC, and now the port is gone?
+  {
+    printk(" can't send ReportLuns - no login for port_id %Xh\n",
+	    fchs->s_id & 0xFFFFFF);
+  }
+
+
+
+Done:
+
+}
+
+
+
+
+
+
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata)
+{
+  int i;
+  for( i = CPQFCTS_REQ_QUEUE_LEN-1; i>= 0; i--)
+  {
+    if( cpqfcHBAdata->BoardLockCmnd[i] != NULL )
+    {
+      Scsi_Cmnd *Cmnd = cpqfcHBAdata->BoardLockCmnd[i];
+      cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+      Cmnd->result = (DID_SOFT_ERROR << 16);  // ask for retry
+//      printk(" BoardLockCmnd[%d] %p Complete, chnl/target/lun %d/%d/%d\n",
+//        i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+      if( Cmnd->scsi_done != NULL)
+        (*Cmnd->scsi_done)(Cmnd);
+    }
+  }
+}
+
+
+
+
+
+
+// runs every 1 second for FC exchange timeouts and implicit FC device logouts
+
+void cpqfcTSheartbeat( unsigned long ptr )
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)ptr;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; 
+  ULONG i;
+  unsigned long flags;
+  DECLARE_MUTEX_LOCKED(BoardLock);
+  
+  PCI_TRACE( 0xA8)
+
+  if( cpqfcHBAdata->BoardLock) // Worker Task Running?
+    goto Skip;
+
+  spin_lock_irqsave( &io_request_lock, flags); // STOP _que function
+
+  PCI_TRACE( 0xA8)
+
+
+  cpqfcHBAdata->BoardLock = &BoardLock; // stop Linux SCSI command queuing
+  
+  // release the IO lock (and re-enable interrupts)
+  spin_unlock_irqrestore( &io_request_lock, flags);
+  
+  // Ensure no contention from  _quecommand or Worker process 
+  CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+  
+  PCI_TRACE( 0xA8)
+  
+
+  disable_irq( cpqfcHBAdata->HostAdapter->irq);  // our IRQ
+
+  // Complete the "bad target" commands (normally only used during
+  // initialization, since we aren't supposed to call "scsi_done"
+  // inside the queuecommand() function).
+
+  for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+  {
+    if( cpqfcHBAdata->BadTargetCmnd[i] )
+    {
+      Scsi_Cmnd *Cmnd = cpqfcHBAdata->BadTargetCmnd[i];
+      cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+      Cmnd->result = (DID_BAD_TARGET << 16);
+      if( Cmnd->scsi_done != NULL)
+        (*Cmnd->scsi_done)(Cmnd);
+    }
+    else
+      break;
+  }
+
+  
+  // logged in ports -- re-login check (ports required to verify login with
+  // PDISC after LIP within 2 secs)
+
+  // prevent contention
+  while( pLoggedInPort ) // for all ports which are expecting
+                         // PDISC after the next LIP, check to see if
+                         // time is up!
+  {
+      // Important: we only detect "timeout" condition on TRANSITION
+      // from non-zero to zero
+    if( pLoggedInPort->LOGO_timer )  // time-out "armed"?
+    {
+      if( !(--pLoggedInPort->LOGO_timer) ) // DEC from 1 to 0?
+      {
+          // LOGOUT time!  Per PLDA, PDISC hasn't complete in 2 secs, so
+          // issue LOGO request and destroy all I/O with other FC port(s).
+        
+/*          
+        printk(" ~cpqfcTS heartbeat: LOGOut!~ ");
+        printk("Linux SCSI Chanl/Target %d/%d (port_id %06Xh) WWN %08X%08X\n", 
+        pLoggedInPort->ScsiNexus.channel, 
+        pLoggedInPort->ScsiNexus.target, 
+	pLoggedInPort->port_id,
+          (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), 
+          (ULONG)(pLoggedInPort->u.liWWN>>32));
+
+*/
+        cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+
+      }
+      // else simply decremented - maybe next time...
+    }
+    pLoggedInPort = pLoggedInPort->pNextPort;
+  }
+
+
+
+  
+  
+  // ************  FC EXCHANGE TIMEOUT CHECK **************
+  
+  for( i=0; i< TACH_MAX_XID; i++)
+  {
+    if( Exchanges->fcExchange[i].type )  // exchange defined?
+    {
+
+      if( !Exchanges->fcExchange[i].timeOut ) // time expired
+      {
+        // Set Exchange timeout status
+        Exchanges->fcExchange[i].status |= FC2_TIMEOUT;
+
+        if( i >= TACH_SEST_LEN ) // Link Service Exchange
+        {
+          cpqfcTSCompleteExchange( fcChip, i);  // Don't "abort" LinkService
+        }
+        
+        else  // SEST Exchange TO -- may post ABTS to Worker Thread Que
+        {
+	  // (Make sure we don't keep timing it out; let other functions
+	  // complete it or set the timeOut as needed)
+	  Exchanges->fcExchange[i].timeOut = 30000; // seconds default
+
+          if( Exchanges->fcExchange[i].type 
+                  & 
+              (BLS_ABTS | BLS_ABTS_ACC )  )
+          {
+	    // For BLS_ABTS*, an upper level might still have
+	    // an outstanding command waiting for low-level completion.
+	    // Also, in the case of a WRITE, we MUST get confirmation
+	    // of either ABTS ACC or RJT before re-using the Exchange.
+	    // It's possible that the RAID cache algorithm can hang
+	    // if we fail to complete a WRITE to a LBA, when a READ
+	    // comes later to that same LBA.  Therefore, we must
+	    // ensure that the target verifies receipt of ABTS for
+	    // the exchange
+	   
+	    printk("~TO Q'd ABTS (x_ID %Xh)~ ", i); 
+//            TriggerHBA( fcChip->Registers.ReMapMemBase);
+
+	    // On timeout of a ABTS exchange, check to
+	    // see if the FC device has a current valid login.
+	    // If so, restart it.
+	    pLoggedInPort = fcFindLoggedInPort( fcChip,
+              Exchanges->fcExchange[i].Cmnd, // find Scsi Nexus
+              0,        // DON'T search linked list for FC port id
+	      NULL,     // DON'T search linked list for FC WWN
+              NULL);    // DON'T care about end of list
+
+	    // device exists?
+	    if( pLoggedInPort ) // device exists?
+	    {
+	      if( pLoggedInPort->prli ) // logged in for FCP-SCSI?
+	      {
+		// attempt to restart the ABTS
+		printk(" ~restarting ABTS~ ");
+                cpqfcTSStartExchange( cpqfcHBAdata, i );
+
+	      }
+	    }
+          }
+	  else  // not an ABTS
+	  { 
+           
+            // We expect the WorkerThread to change the xchng type to
+            // abort and set appropriate timeout.
+            cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i ); // timed-out
+	  }
+        }
+      }
+      else  // time not expired...
+      {
+        // decrement timeout: 1 or more seconds left
+        --Exchanges->fcExchange[i].timeOut;
+      }
+    }
+  }
+
+
+  enable_irq( cpqfcHBAdata->HostAdapter->irq);
+ 
+
+  CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+  cpqfcHBAdata->BoardLock = NULL; // Linux SCSI commands may be queued
+
+  // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+  CompleteBoardLockCmnd( cpqfcHBAdata);
+ 
+
+  // restart the timer to run again (1 sec later)
+Skip:
+  mod_timer( &cpqfcHBAdata->cpqfcTStimer, jiffies + HZ);
+  
+  PCI_TRACEO( i, 0xA8)
+  return;
+}
+
+
+// put valid FC-AL physical address in spec order
+static const UCHAR valid_al_pa[]={
+    0xef, 0xe8, 0xe4, 0xe2, 
+    0xe1, 0xE0, 0xDC, 0xDA, 
+    0xD9, 0xD6, 0xD5, 0xD4, 
+    0xD3, 0xD2, 0xD1, 0xCe, 
+    0xCd, 0xCc, 0xCb, 0xCa, 
+    0xC9, 0xC7, 0xC6, 0xC5, 
+    0xC3, 0xBc, 0xBa, 0xB9,
+    0xB6, 0xB5, 0xB4, 0xB3, 
+    0xB2, 0xB1, 0xae, 0xad,
+    0xAc, 0xAb, 0xAa, 0xA9, 
+
+    0xA7, 0xA6, 0xA5, 0xA3, 
+    0x9f, 0x9e, 0x9d, 0x9b, 
+    0x98, 0x97, 0x90, 0x8f, 
+    0x88, 0x84, 0x82, 0x81, 
+    0x80, 0x7c, 0x7a, 0x79, 
+    0x76, 0x75, 0x74, 0x73, 
+    0x72, 0x71, 0x6e, 0x6d, 
+    0x6c, 0x6b, 0x6a, 0x69, 
+    0x67, 0x66, 0x65, 0x63, 
+    0x5c, 0x5a, 0x59, 0x56, 
+    
+    0x55, 0x54, 0x53, 0x52, 
+    0x51, 0x4e, 0x4d, 0x4c, 
+    0x4b, 0x4a, 0x49, 0x47, 
+    0x46, 0x45, 0x43, 0x3c,
+    0x3a, 0x39, 0x36, 0x35, 
+    0x34, 0x33, 0x32, 0x31, 
+    0x2e, 0x2d, 0x2c, 0x2b, 
+    0x2a, 0x29, 0x27, 0x26, 
+    0x25, 0x23, 0x1f, 0x1E,
+    0x1d, 0x1b, 0x18, 0x17, 
+
+    0x10, 0x0f, 8, 4, 2, 1 }; // ALPA 0 (Fabric) is special case
+
+const int number_of_al_pa = (sizeof(valid_al_pa) );
+
+
+
+// this function looks up an al_pa from the table of valid al_pa's
+// we decrement from the last decimal loop ID, because soft al_pa
+// (our typical case) are assigned with highest priority (and high al_pa)
+// first.  See "In-Depth FC-AL", R. Kembel pg. 38
+// INPUTS:
+//   al_pa - 24 bit port identifier (8 bit al_pa on private loop)
+// RETURN:
+//  Loop ID - serves are index to array of logged in ports
+//  -1      - invalid al_pa (not all 8 bit values are legal)
+
+#if (0)
+static int GetLoopID( ULONG al_pa )
+{
+  int i;
+
+  for( i = number_of_al_pa -1; i >= 0; i--)  // dec.
+  {
+    if( valid_al_pa[i] == (UCHAR)al_pa )  // take lowest 8 bits
+      return i;  // success - found valid al_pa; return decimal LoopID
+  }
+  return -1; // failed - not found
+}
+#endif
+
+
+// Search the singly (forward) linked list "fcPorts" looking for 
+// either the SCSI target (if != -1), port_id (if not NULL), 
+// or WWN (if not null), in that specific order.
+// If we find a SCSI nexus (from Cmnd arg), set the SCp.phase
+// field according to VSA or PDU
+// RETURNS:
+//   Ptr to logged in port struct if found
+//     (NULL if not found)
+//   pLastLoggedInPort - ptr to last struct (for adding new ones)
+// 
+PFC_LOGGEDIN_PORT  fcFindLoggedInPort( 
+  PTACHYON fcChip, 
+  Scsi_Cmnd *Cmnd, // search linked list for Scsi Nexus (channel/target/lun)
+  ULONG port_id,   // search linked list for al_pa, or
+  UCHAR wwn[8],    // search linked list for WWN, or...
+  PFC_LOGGEDIN_PORT *pLastLoggedInPort )
+             
+{
+  PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; 
+  BOOLEAN target_id_valid=FALSE;
+  BOOLEAN port_id_valid=FALSE;
+  BOOLEAN wwn_valid=FALSE;
+  int i;
+
+
+  if( Cmnd != NULL )
+    target_id_valid = TRUE;
+  
+  else if( port_id ) // note! 24-bit NULL address is illegal
+    port_id_valid = TRUE;
+
+  else
+  {
+    for( i=0; i<8; i++)  // valid WWN passed?  NULL WWN invalid
+    {
+      if( wwn ) // non-null arg? (OK to pass NULL when not searching WWN)
+      {
+        if( wwn[i] != 0 )
+          wwn_valid = TRUE;  // any non-zero byte makes (presumably) valid
+      }
+    }
+  }
+                // check other options ...
+
+
+  // In case multiple search options are given, we use a priority
+  // scheme:
+  // While valid pLoggedIn Ptr
+  //   If port_id is valid
+  //     if port_id matches, return Ptr
+  //   If wwn is valid
+  //     if wwn matches, return Ptr
+  //   Next Ptr in list
+  //
+  // Return NULL (not found)
+ 
+      
+  while( pLoggedInPort ) // NULL marks end of list (1st ptr always valid)
+  {
+    if( pLastLoggedInPort ) // caller's pointer valid?
+      *pLastLoggedInPort = pLoggedInPort;  // end of linked list
+    
+    if( target_id_valid )
+    {
+      // check Linux Scsi Cmnd for channel/target Nexus match
+      // (all luns are accessed through matching "pLoggedInPort")
+      if( (pLoggedInPort->ScsiNexus.target == Cmnd->target)
+                &&
+          (pLoggedInPort->ScsiNexus.channel == Cmnd->channel))
+      {
+        // For "passthru" modes, the IOCTL caller is responsible
+	// for setting the FCP-LUN addressing
+	if( !Cmnd->SCp.sent_command ) // NOT passthru?
+	{
+	
+          // set the FCP-LUN addressing type
+          Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing;	
+
+          // set the Device Type we got from the snooped INQUIRY string
+	  Cmnd->SCp.Message = pLoggedInPort->ScsiNexus.InqDeviceType;
+
+	  // handle LUN masking; if not "default" (illegal) lun value,
+	  // the use it.  These lun values are set by a successful
+	  // Report Luns command
+          if( pLoggedInPort->ScsiNexus.LunMasking == 1) 
+	  {
+            // we KNOW all the valid LUNs... 0xFF is invalid!
+            Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun];
+	  }
+	  else
+	    Cmnd->SCp.have_data_in = Cmnd->lun; // Linux & target luns match
+	}
+	break; // found it!
+      }
+    }
+    
+    if( port_id_valid ) // look for alpa first
+    {
+      if( pLoggedInPort->port_id == port_id )
+        break;  // found it!
+    }
+    if( wwn_valid ) // look for wwn second
+    {
+
+      if( !memcmp( &pLoggedInPort->u.ucWWN[0], &wwn[0], 8))
+      {  
+                 // all 8 bytes of WWN match
+        break;   // found it!
+      }
+    }
+                
+    pLoggedInPort = pLoggedInPort->pNextPort; // try next port
+  }
+
+  return pLoggedInPort;
+}
+
+
+
+
+// 
+// We need to examine the SEST table and re-validate
+// any open Exchanges for this LoggedInPort
+// To make Tachyon pay attention, Freeze FCP assists,
+// set VAL bits, Unfreeze FCP assists
+static void RevalidateSEST( struct Scsi_Host *HostAdapter, 
+		        PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG x_ID;
+  BOOLEAN TachFroze = FALSE;
+  
+  
+  // re-validate any SEST exchanges that are permitted
+  // to survive the link down (e.g., good PDISC performed)
+  for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+  {
+
+    // If the SEST entry port_id matches the pLoggedInPort,
+    // we need to re-validate
+    if( (Exchanges->fcExchange[ x_ID].type == SCSI_IRE)
+         || 
+	(Exchanges->fcExchange[ x_ID].type == SCSI_IWE))
+    {
+		     
+      if( (Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF)  // (24-bit port ID)
+            == pLoggedInPort->port_id) 
+      {
+//	printk(" re-val xID %Xh ", x_ID);
+        if( !TachFroze )  // freeze if not already frozen
+          TachFroze |= FreezeTach( cpqfcHBAdata);
+        fcChip->SEST->u[ x_ID].IWE.Hdr_Len |= 0x80000000; // set VAL bit
+      }
+    } 
+  }
+
+  if( TachFroze) 
+  { 
+    fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+  }
+} 
+
+
+// Complete an Linux Cmnds that we Queued because
+// our FC link was down (cause immediate retry)
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, 
+		        PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+//  Scsi_Device *sdev = HostAdapter->host_queue;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  Scsi_Cmnd* *SCptr = &cpqfcHBAdata->LinkDnCmnd[0];
+  Scsi_Cmnd *Cmnd;
+  int indx;
+
+ 
+  
+  // if the device was previously "blocked", make sure
+  // we unblock it so Linux SCSI will resume
+
+  pLoggedInPort->device_blocked = FALSE; // clear our flag
+
+  // check the Link Down command ptr buffer;
+  // we can complete now causing immediate retry
+  for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++, SCptr++)
+  {
+    if( *SCptr != NULL ) // scsi command to complete?
+    {
+#ifdef DUMMYCMND_DBG
+      printk("complete Cmnd %p in LinkDnCmnd[%d]\n", *SCptr,indx);
+#endif
+      Cmnd = *SCptr;
+
+
+      // Are there any Q'd commands for this target?
+      if( (Cmnd->target == pLoggedInPort->ScsiNexus.target)
+	       &&
+          (Cmnd->channel == pLoggedInPort->ScsiNexus.channel) )
+      {
+        Cmnd->result = (DID_SOFT_ERROR <<16); // force retry
+        if( Cmnd->scsi_done != NULL)
+          (*Cmnd->scsi_done)(Cmnd);
+        else
+          printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n",
+		  pLoggedInPort->port_id);
+        *SCptr = NULL;  // free this slot for next use
+      }
+    }
+  }
+}
+
+  
+//#define WWN_DBG 1
+
+static void SetLoginFields(
+  PFC_LOGGEDIN_PORT pLoggedInPort,
+  TachFCHDR_GCMND* fchs,
+  BOOLEAN PDisc,
+  BOOLEAN Originator)
+{
+  LOGIN_PAYLOAD logi;       // FC-PH Port Login
+  PRLI_REQUEST prli;  // copy for BIG ENDIAN switch
+  int i;
+#ifdef WWN_DBG
+  ULONG ulBuff;
+#endif
+
+  BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+  pLoggedInPort->Originator = Originator;
+  pLoggedInPort->port_id = fchs->s_id & 0xFFFFFF;
+  
+  switch( fchs->pl[0] & 0xffff )
+  {
+  case 0x00000002:  //  PLOGI or PDISC ACCept?
+    if( PDisc )     // PDISC accept
+      goto PDISC_case;
+
+  case 0x00000003:  //  ELS_PLOGI or ELS_PLOGI_ACC
+
+  // Login BB_credit typically 0 for Tachyons
+    pLoggedInPort->BB_credit = logi.cmn_services.bb_credit;
+
+    // e.g. 128, 256, 1024, 2048 per FC-PH spec
+    // We have to use this when setting up SEST Writes,
+    // since that determines frame size we send.
+    pLoggedInPort->rx_data_size = logi.class3.rx_data_size;
+    pLoggedInPort->plogi = TRUE;
+    pLoggedInPort->pdisc = FALSE;
+    pLoggedInPort->prli = FALSE;    // ELS_PLOGI resets
+    pLoggedInPort->flogi = FALSE;   // ELS_PLOGI resets
+    pLoggedInPort->logo = FALSE;    // ELS_PLOGI resets
+    pLoggedInPort->LOGO_counter = 0;// ELS_PLOGI resets
+    pLoggedInPort->LOGO_timer = 0;// ELS_PLOGI resets
+
+    // was this PLOGI to a Fabric?
+    if( pLoggedInPort->port_id == 0xFFFFFC ) // well know address
+      pLoggedInPort->flogi = TRUE;
+
+
+    for( i=0; i<8; i++)   // copy the LOGIN port's WWN
+      pLoggedInPort->u.ucWWN[i] = logi.port_name[i];  
+
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("PLOGI port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+    printk("%08Xh fcPort %p\n", ulBuff, pLoggedInPort);
+#endif
+    break;
+
+
+
+  
+  case 0x00000005:  //  ELS_LOGO (logout)
+
+
+    pLoggedInPort->plogi = FALSE;
+    pLoggedInPort->pdisc = FALSE;
+    pLoggedInPort->prli = FALSE;   // ELS_PLOGI resets
+    pLoggedInPort->flogi = FALSE;  // ELS_PLOGI resets
+    pLoggedInPort->logo = TRUE;    // ELS_PLOGI resets
+    pLoggedInPort->LOGO_counter++; // ELS_PLOGI resets
+    pLoggedInPort->LOGO_timer = 0;
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("LOGO port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+    printk("%08Xh\n", ulBuff);
+#endif
+    break;
+
+
+
+PDISC_case:
+  case 0x00000050: //  ELS_PDISC or ELS_PDISC_ACC
+    pLoggedInPort->LOGO_timer = 0;  // stop the time-out
+      
+    pLoggedInPort->prli = TRUE;     // ready to accept FCP-SCSI I/O
+    
+
+      
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("PDISC port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+    printk("%08Xh\n", ulBuff);
+#endif
+
+
+    
+    break;
+
+
+    
+  case  0x1020L: //  PRLI?
+  case  0x1002L: //  PRLI ACCept?
+    BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli));
+
+    pLoggedInPort->fcp_info = prli.fcp_info; // target/initiator flags
+    pLoggedInPort->prli = TRUE;  // PLOGI resets, PDISC doesn't
+
+    pLoggedInPort->pdisc = TRUE;  // expect to send (or receive) PDISC
+                                  // next time
+    pLoggedInPort->LOGO_timer = 0;  // will be set next LinkDown
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("PRLI port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+      printk("%08Xh\n", ulBuff);
+#endif
+
+    break;
+
+  }
+
+  return;
+}
+
+
+
+
+
+
+static void BuildLinkServicePayload( PTACHYON fcChip, ULONG type, void* payload)
+{
+  LOGIN_PAYLOAD *plogi;  // FC-PH Port Login
+  LOGIN_PAYLOAD PlogiPayload;   // copy for BIG ENDIAN switch
+  PRLI_REQUEST  *prli;          // FCP-SCSI Process Login
+  PRLI_REQUEST  PrliPayload;    // copy for BIG ENDIAN switch
+  LOGOUT_PAYLOAD  *logo;
+  LOGOUT_PAYLOAD  LogoutPayload;
+//  PRLO_REQUEST  *prlo;
+//  PRLO_REQUEST  PrloPayload;
+  REJECT_MESSAGE rjt, *prjt;
+
+  memset( &PlogiPayload, 0, sizeof( PlogiPayload));
+  plogi = &PlogiPayload;    // load into stack buffer,
+                                // then BIG-ENDIAN switch a copy to caller
+
+
+  switch( type )  // payload type can be ELS_PLOGI, ELS_PRLI, ADISC, ...
+  {
+    case ELS_FDISC:
+    case ELS_FLOGI:
+    case ELS_PLOGI_ACC:   // FC-PH PORT Login Accept
+    case ELS_PLOGI:   // FC-PH PORT Login
+    case ELS_PDISC:   // FC-PH2 Port Discovery - same payload as ELS_PLOGI
+      plogi->login_cmd = LS_PLOGI;
+      if( type == ELS_PDISC)
+        plogi->login_cmd = LS_PDISC;
+      else if( type == ELS_PLOGI_ACC )
+        plogi->login_cmd = LS_ACC;
+
+      plogi->cmn_services.bb_credit = 0x00;
+      plogi->cmn_services.lowest_ver = fcChip->lowest_FCPH_ver;
+      plogi->cmn_services.highest_ver = fcChip->highest_FCPH_ver;
+      plogi->cmn_services.bb_rx_size = TACHLITE_TS_RX_SIZE;
+      plogi->cmn_services.common_features = CONTINUOSLY_INCREASING |
+              RANDOM_RELATIVE_OFFSET;
+
+             // fill in with World Wide Name based Port Name - 8 UCHARs
+             // get from Tach registers WWN hi & lo
+      LoadWWN( fcChip, plogi->port_name, 0);
+             // fill in with World Wide Name based Node/Fabric Name - 8 UCHARs
+             // get from Tach registers WWN hi & lo
+      LoadWWN( fcChip, plogi->node_name, 1);
+
+	// For Seagate Drives.
+	//
+      plogi->cmn_services.common_features |= 0x800;
+      plogi->cmn_services.rel_offset = 0xFE;
+      plogi->cmn_services.concurrent_seq = 1;
+      plogi->class1.service_options = 0x00;
+      plogi->class2.service_options = 0x00;
+      plogi->class3.service_options = CLASS_VALID;
+      plogi->class3.initiator_control = 0x00;
+      plogi->class3.rx_data_size = MAX_RX_PAYLOAD;
+      plogi->class3.recipient_control =
+             ERROR_DISCARD | ONE_CATEGORY_SEQUENCE;
+      plogi->class3.concurrent_sequences = 1;
+      plogi->class3.open_sequences = 1;
+      plogi->vendor_id[0] = 'C'; plogi->vendor_id[1] = 'Q';
+      plogi->vendor_version[0] = 'C'; plogi->vendor_version[1] = 'Q';
+      plogi->vendor_version[2] = ' '; plogi->vendor_version[3] = '0';
+      plogi->vendor_version[4] = '0'; plogi->vendor_version[5] = '0';
+
+
+      // FLOGI specific fields... (see FC-FLA, Rev 2.7, Aug 1999, sec 5.1)
+      if( (type == ELS_FLOGI) || (type == ELS_FDISC) )
+      {
+        if( type == ELS_FLOGI )
+  	  plogi->login_cmd = LS_FLOGI;  
+	else
+  	  plogi->login_cmd = LS_FDISC;  
+
+        plogi->cmn_services.lowest_ver = 0x20;
+        plogi->cmn_services.common_features = 0x0800;
+        plogi->cmn_services.rel_offset = 0;
+        plogi->cmn_services.concurrent_seq = 0;
+
+        plogi->class3.service_options = 0x8800;
+        plogi->class3.rx_data_size = 0;
+        plogi->class3.recipient_control = 0;
+        plogi->class3.concurrent_sequences = 0;
+        plogi->class3.open_sequences = 0;
+      }
+      
+              // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&PlogiPayload, payload,  sizeof(PlogiPayload));
+      break;
+
+    
+    case ELS_ACC:       // generic Extended Link Service ACCept	    
+      plogi->login_cmd = LS_ACC;
+              // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&PlogiPayload, payload,  4);
+      break;
+
+
+      
+    case ELS_SCR:    // Fabric State Change Registration
+    {
+      SCR_PL scr;     // state change registration
+
+      memset( &scr, 0, sizeof(scr));
+
+      scr.command = LS_SCR;  // 0x62000000
+                             // see FC-FLA, Rev 2.7, Table A.22 (pg 82)
+      scr.function = 3;      // 1 = Events detected by Fabric
+                             // 2 = N_Port detected registration
+                             // 3 = Full registration
+      
+      // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&scr, payload,  sizeof(SCR_PL));
+    }
+    
+    break;
+
+    
+    case FCS_NSR:    // Fabric Name Service Request
+    {
+      NSR_PL nsr;    // Name Server Req. payload
+
+      memset( &nsr, 0, sizeof(NSR_PL));
+
+                             // see Brocade Fabric Programming Guide,
+                             // Rev 1.3, pg 4-44
+      nsr.CT_Rev = 0x01000000;
+      nsr.FCS_Type = 0xFC020000;
+      nsr.Command_code = 0x01710000;
+      nsr.FCP = 8;
+     
+      // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&nsr, payload,  sizeof(NSR_PL));
+    }
+    
+    break;
+
+
+
+    
+    case ELS_LOGO:   // FC-PH PORT LogOut
+      logo = &LogoutPayload;    // load into stack buffer,
+                                // then BIG-ENDIAN switch a copy to caller
+      logo->cmd = LS_LOGO;
+                                // load the 3 UCHARs of the node name
+                                // (if private loop, upper two UCHARs 0)
+      logo->reserved = 0;
+
+      logo->n_port_identifier[0] = (UCHAR)(fcChip->Registers.my_al_pa);
+      logo->n_port_identifier[1] =
+                     (UCHAR)(fcChip->Registers.my_al_pa>>8);
+      logo->n_port_identifier[2] =
+                     (UCHAR)(fcChip->Registers.my_al_pa>>16);
+             // fill in with World Wide Name based Port Name - 8 UCHARs
+             // get from Tach registers WWN hi & lo
+      LoadWWN( fcChip, logo->port_name, 0);
+
+      BigEndianSwap( (UCHAR*)&LogoutPayload,
+                     payload,  sizeof(LogoutPayload) );  // 16 UCHAR struct
+      break;
+
+
+    case ELS_LOGO_ACC:     // Logout Accept (FH-PH pg 149, table 74)
+      logo = &LogoutPayload;    // load into stack buffer,
+                                // then BIG-ENDIAN switch a copy to caller
+      logo->cmd = LS_ACC;
+      BigEndianSwap( (UCHAR*)&LogoutPayload, payload, 4 );  // 4 UCHAR cmnd
+      break;
+      
+
+    case ELS_RJT:          // ELS_RJT link service reject (FH-PH pg 155)
+
+      prjt = (REJECT_MESSAGE*)payload;  // pick up passed data
+      rjt.command_code = ELS_RJT;
+                       // reverse fields, because of Swap that follows...
+      rjt.vendor = prjt->reserved; // vendor specific
+      rjt.explain = prjt->reason; //
+      rjt.reason = prjt->explain; //
+      rjt.reserved = prjt->vendor; //
+                       // BIG-ENDIAN switch a copy to caller
+      BigEndianSwap( (UCHAR*)&rjt, payload, 8 );  // 8 UCHAR cmnd
+      break;
+
+
+
+
+
+    case ELS_PRLI_ACC:  // Process Login ACCept
+    case ELS_PRLI:  // Process Login
+    case ELS_PRLO:  // Process Logout
+      memset( &PrliPayload, 0, sizeof( PrliPayload));
+      prli = &PrliPayload;      // load into stack buffer,
+
+      if( type == ELS_PRLI )
+        prli->cmd = 0x20;  // Login
+      else if( type == ELS_PRLO )
+        prli->cmd = 0x21;  // Logout
+      else if( type == ELS_PRLI_ACC )
+      {
+        prli->cmd = 0x02;  // Login ACCept
+        prli->valid = REQUEST_EXECUTED;
+      }
+
+
+      prli->valid |= SCSI_FCP | ESTABLISH_PAIR;
+      prli->fcp_info = READ_XFER_RDY;
+      prli->page_length = 0x10;
+      prli->payload_length = 20;
+                                // Can be initiator AND target
+
+      if( fcChip->Options.initiator )
+        prli->fcp_info |= INITIATOR_FUNCTION;
+      if( fcChip->Options.target )
+        prli->fcp_info |= TARGET_FUNCTION;
+
+      BigEndianSwap( (UCHAR*)&PrliPayload, payload,  prli->payload_length);
+      break;
+
+
+
+    default:  // no can do - programming error
+      printk(" BuildLinkServicePayload unknown!\n");
+      break;
+  }
+}
+
+// loads 8 UCHARs for PORT name or NODE name base on
+// controller's WWN.
+void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type)
+{
+  UCHAR* bPtr, i;
+
+  switch( type )
+  {
+    case 0:  // Port_Name
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+      for( i =0; i<4; i++)
+        dest[i] = *bPtr++;
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+      for( i =4; i<8; i++)
+        dest[i] = *bPtr++;
+      break;
+    case 1:  // Node/Fabric _Name
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+      for( i =0; i<4; i++)
+        dest[i] = *bPtr++;
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+      for( i =4; i<8; i++)
+        dest[i] = *bPtr++;
+      break;
+  }
+  
+}
+
+
+
+// We check the Port Login payload for required values.  Note that
+// ELS_PLOGI and ELS_PDISC (Port DISCover) use the same payload.
+
+
+int verify_PLOGI( PTACHYON fcChip,
+                  TachFCHDR_GCMND* fchs, 
+                  ULONG* reject_explain)
+{
+  LOGIN_PAYLOAD	login;
+
+                  // source, dest, len (should be mult. of 4)
+  BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&login,  sizeof(login));
+
+                            // check FC version
+                            // if other port's highest supported version
+                            // is less than our lowest, and 
+                            // if other port's lowest
+  if( login.cmn_services.highest_ver < fcChip->lowest_FCPH_ver ||
+      login.cmn_services.lowest_ver > fcChip->highest_FCPH_ver )
+  {
+    *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+    return LOGICAL_ERROR;
+  }
+
+                            // Receive Data Field Size must be >=128
+                            // per FC-PH
+  if (login.cmn_services.bb_rx_size < 128)
+  {
+    *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, DATA_FIELD_SIZE_ERROR);
+    return LOGICAL_ERROR;
+  }
+
+                            // Only check Class 3 params
+  if( login.class3.service_options & CLASS_VALID)
+  {
+    if (login.class3.rx_data_size < 128)
+    {
+      *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INVALID_CSP);
+      return LOGICAL_ERROR;
+    }
+    if( login.class3.initiator_control & XID_REQUIRED)
+    {
+      *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INITIATOR_CTL_ERROR);
+      return LOGICAL_ERROR;
+    }
+  }
+  return 0;   // success
+}
+
+
+
+
+int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain)
+{
+  PRLI_REQUEST prli;  // buffer for BIG ENDIAN
+
+                  // source, dest, len (should be mult. of 4)
+  BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli,  sizeof(prli));
+
+  if( prli.fcp_info == 0 )  // i.e., not target or initiator?
+  {
+    *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+    return LOGICAL_ERROR;
+  }
+
+  return 0;  // success
+}
+
+
+// SWAP UCHARs as required by Fibre Channel (i.e. BIG ENDIAN)
+// INPUTS:
+//   source   - ptr to LITTLE ENDIAN ULONGS
+//   cnt      - number of UCHARs to switch (should be mult. of ULONG)
+// OUTPUTS:
+//   dest     - ptr to BIG ENDIAN copy
+// RETURN:
+//   none
+//
+void BigEndianSwap( UCHAR *source, UCHAR *dest,  USHORT cnt)
+{
+  int i,j;
+
+  source+=3;   // start at MSB of 1st ULONG
+  for( j=0; j < cnt; j+=4, source+=4, dest+=4)  // every ULONG
+  {
+    for( i=0; i<4; i++)  // every UCHAR in ULONG
+          *(dest+i) = *(source-i);
+  }
+}
+
+
+
+
+// Build FC Exchanges............
+
+static void  buildFCPstatus( 
+  PTACHYON fcChip, 
+  ULONG ExchangeID);
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type );
+
+static ULONG build_SEST_sgList( 
+  ULONG *SESTalPairStart,
+  Scsi_Cmnd *Cmnd,
+  ULONG *sgPairs,
+  PSGPAGES sgPages  // link list of TL Ext. S/G pages from O/S Pool
+);
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd, 
+  UCHAR* payload, ULONG type, ULONG fcp_dl );
+
+
+/*
+                             IRB
+      ERQ           __________________
+  |          |   / | Req_A_SFS_Len    |        ____________________
+  |----------|  /  | Req_A_SFS_Addr   |------->|  Reserved         |
+  |   IRB    | /   | Req_A_D_ID       |        | SOF EOF TimeStamp |
+  |-----------/    | Req_A_SEST_Index |-+      | R_CTL |   D_ID    |
+  |   IRB    |     | Req_B...         | |      | CS_CTL|   S_ID    | 
+  |-----------\    |                  | |      | TYPE  |   F_CTL   |
+  |   IRB    | \   |                  | |      | SEQ_ID  | SEQ_CNT |
+  |-----------  \  |                  | +-->+--| OX_ID   | RX_ID   |
+  |          |   \ |__________________|     |  |       RO          |
+                                            |  | pl (payload/cmnd) |
+                                            |  |        .....      |
+                                            |  |___________________|
+                                            |
+                                            |
++-------------------------------------------+
+|
+|
+|                        e.g. IWE    
+|    SEST           __________________             for FCP_DATA
+| |          |   / |       | Hdr_Len  |        ____________________
+| |----------|  /  |  Hdr_Addr_Addr   |------->|  Reserved         |
+| |   [0]    | /   |Remote_ID| RSP_Len|        | SOF EOF TimeStamp |
+| |-----------/    |   RSP_Addr       |---+    | R_CTL |   D_ID    |
++->   [1]    |     |       | Buff_Off |   |    | CS_CTL|   S_ID    | 
+  |-----------\    |BuffIndex| Link   |   |    | TYPE  |   F_CTL   |
+  |   [2]    | \   | Rsvd  |   RX_ID  |   |    | SEQ_ID  | SEQ_CNT |
+  |-----------  \  |    Data_Len      |   |    | OX_ID   | RX_ID   |
+  |    ...   |   \ |     Exp_RO       |   |    |       RO          |
+  |----------|     |   Exp_Byte_Cnt   |   |    |___________________|
+  | SEST_LEN |  +--|    Len           |   |                                             
+  |__________|  |  |   Address        |   |                                              
+                |  |    ...           |   |         for FCP_RSP  
+                |  |__________________|   |    ____________________
+                |                         +----|  Reserved         |   
+                |                              | SOF EOF TimeStamp |
+                |                              | R_CTL |   D_ID    |
+                |                              | CS_CTL|   S_ID    | 
+                +--- local or extended         |     ....          |
+                     scatter/gather lists
+                     defining upper-layer
+                     data (e.g. from user's App)
+
+
+*/
+// All TachLite commands must start with a SFS (Single Frame Sequence)
+// command.  In the simplest case (a NOP Basic Link command),
+// only one frame header and ERQ entry is required.  The most complex
+// case is the SCSI assisted command, which requires an ERQ entry,
+// SEST entry, and several frame headers and data buffers all
+// logically linked together.
+// Inputs:
+//   cpqfcHBAdata  - controller struct
+//   type          - PLOGI, SCSI_IWE, etc.
+//   InFCHS        - Incoming Tachlite FCHS which prompted this exchange
+//                   (only s_id set if we are originating)
+//   Data          - PVOID to data struct consistent with "type"
+//   fcExchangeIndex - pointer to OX/RD ID value of built exchange
+// Return:
+//   fcExchangeIndex - OX/RD ID value if successful
+//   0    - success
+//  INVALID_ARGS    - NULL/ invalid passed args
+//  BAD_ALPA        - Bad source al_pa address
+//  LNKDWN_OSLS     - Link Down (according to this controller)
+//  OUTQUE_FULL     - Outbound Que full
+//  DRIVERQ_FULL    - controller's Exchange array full
+//  SEST_FULL       - SEST table full
+//
+// Remarks:
+// Psuedo code:
+// Check for NULL pointers / bad args
+// Build outgoing FCHS - the header/payload struct
+// Build IRB (for ERQ entry)
+// if SCSI command, build SEST entry (e.g. IWE, TRE,...)
+// return success
+
+//sbuildex
+ULONG cpqfcTSBuildExchange(
+  CPQFCHBA *cpqfcHBAdata,
+  ULONG type, // e.g. PLOGI
+  TachFCHDR_GCMND* InFCHS,  // incoming FCHS
+  void *Data,               // the CDB, scatter/gather, etc.  
+  LONG *fcExchangeIndex )   // points to allocated exchange, 
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ulStatus = 0;  // assume OK
+  USHORT ox_ID, rx_ID=0xFFFF;
+  ULONG SfsLen=0L;
+  TachLiteIRB* pIRB;
+  IRBflags IRB_flags;
+  UCHAR *pIRB_flags = (UCHAR*)&IRB_flags;
+  TachFCHDR_GCMND* CMDfchs;
+  TachFCHDR* dataHDR;     // 32 byte HEADER ONLY FCP-DATA buffer
+  TachFCHDR_RSP* rspHDR;     // 32 byte header + RSP payload
+  Scsi_Cmnd *Cmnd = (Scsi_Cmnd*)Data;   // Linux Scsi CDB, S/G, ...
+  TachLiteIWE* pIWE;
+  TachLiteIRE* pIRE;
+  TachLiteTWE* pTWE;
+  TachLiteTRE* pTRE;
+  ULONG fcp_dl;           // total byte length of DATA transfered
+  ULONG fl;               // frame length (FC frame size, 128, 256, 512, 1024)
+  ULONG sgPairs;          // number of valid scatter/gather pairs
+  int FCP_SCSI_command;
+  BA_ACC_PAYLOAD *ba_acc;
+  BA_RJT_PAYLOAD *ba_rjt;
+
+                          // check passed ARGS
+  if( !fcChip->ERQ )      // NULL ptr means uninitialized Tachlite chip
+    return INVALID_ARGS;
+
+
+  if( type == SCSI_IRE ||
+      type == SCSI_TRE ||
+      type == SCSI_IWE ||
+      type == SCSI_TWE)
+    FCP_SCSI_command = 1;
+
+  else
+    FCP_SCSI_command = 0;
+
+
+                     // for commands that pass payload data (e.g. SCSI write)
+                     // examine command struct - verify that the
+                     // length of s/g buffers is adequate for total payload
+                     // length (end of list is NULL address)
+
+  if( FCP_SCSI_command )
+  {
+    if( Data )     // must have data descriptor (S/G list -- at least
+                   // one address with at least 1 byte of data)
+    {
+      // something to do (later)?
+    }
+
+    else
+      return INVALID_ARGS;  // invalid DATA ptr
+  }
+
+    
+
+         // we can build an Exchange for later Queuing (on the TL chip)
+         // if an empty slot is available in the DevExt for this controller
+         // look for available Exchange slot...
+
+  if( type != FCP_RESPONSE &&
+      type != BLS_ABTS &&
+      type != BLS_ABTS_ACC )  // already have Exchange slot!
+    *fcExchangeIndex = FindFreeExchange( fcChip, type );
+
+  if( *fcExchangeIndex != -1 )   // Exchange is available?
+  {
+                     // assign tmp ptr (shorthand)
+    CMDfchs = &Exchanges->fcExchange[ *fcExchangeIndex].fchs; 
+
+
+    if( Cmnd != NULL ) // (necessary for ABTS cases)
+    {
+      Exchanges->fcExchange[ *fcExchangeIndex].Cmnd = Cmnd; // Linux Scsi
+      Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort = 
+	fcFindLoggedInPort( fcChip,
+          Exchanges->fcExchange[ *fcExchangeIndex].Cmnd, // find Scsi Nexus
+          0,        // DON'T search linked list for FC port id
+	  NULL,     // DON'T search linked list for FC WWN
+          NULL);    // DON'T care about end of list
+
+    }
+
+
+                     // Build the command frame header (& data) according
+                     // to command type
+
+                     // fields common for all SFS frame types
+    CMDfchs->reserved = 0L; // must clear
+    CMDfchs->sof_eof = 0x75000000L;  // SOFi3:EOFn  no UAM; LCr=0, no TS
+    
+             // get the destination port_id from incoming FCHS
+             // (initialized before calling if we're Originator)
+             // Frame goes to port it was from - the source_id
+    
+    CMDfchs->d_id = InFCHS->s_id &0xFFFFFF; // destination (add R_CTL later)
+    CMDfchs->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+    // now enter command-specific fields
+    switch( type )
+    {
+
+    case BLS_NOP:   // FC defined basic link service command NO-OP
+                // ensure unique X_IDs! (use tracking function)
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32L;        // add len to LSB (header only - no payload)
+
+                   // TYPE[31-24] 00 Basic Link Service
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+      CMDfchs->d_id |= 0x80000000L;  // R_CTL = 80 for NOP (Basic Link Ser.)
+      CMDfchs->f_ctl = 0x00310000L;  // xchng originator, 1st seq,....
+      CMDfchs->seq_cnt = 0x0L;
+      CMDfchs->ox_rx_id = 0xFFFF;        // RX_ID for now; OX_ID on start
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      CMDfchs->pl[0] = 0xaabbccddL;   // words 8-15 frame data payload (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // seconds
+                                      // (NOP should complete ~instantly)
+      break;
+
+
+    
+    
+    case BLS_ABTS_ACC:  // Abort Sequence ACCept
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32 + 12;    // add len to LSB (header + 3 DWORD payload)
+
+      CMDfchs->d_id |= 0x84000000L;  // R_CTL = 84 for BASIC ACCept
+                   // TYPE[31-24] 00 Basic Link Service
+                   // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+      CMDfchs->f_ctl = 0x00910000L;  // xchnge responder, last seq, xfer SI
+                   // CMDfchs->seq_id & count might be set from DataHdr?
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+                        // (Timeout in case of weird error)
+      
+      // now set the ACCept payload...
+      ba_acc = (BA_ACC_PAYLOAD*)&CMDfchs->pl[0];
+      memset( ba_acc, 0, sizeof( BA_ACC_PAYLOAD));
+      // Since PLDA requires (only) entire Exchange aborts, we don't need
+      // to worry about what the last sequence was.
+
+      // We expect that a "target" task is accepting the abort, so we
+      // can use the OX/RX ID pair 
+      ba_acc->ox_rx_id = CMDfchs->ox_rx_id;
+ 
+      // source, dest, #bytes
+      BigEndianSwap((UCHAR *)&CMDfchs->ox_rx_id, (UCHAR *)&ba_acc->ox_rx_id, 4);
+
+      ba_acc->low_seq_cnt = 0;
+      ba_acc->high_seq_cnt = 0xFFFF;
+
+
+      break;
+    
+
+    case BLS_ABTS_RJT:  // Abort Sequence ACCept
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32 + 12;    // add len to LSB (header + 3 DWORD payload)
+
+      CMDfchs->d_id |= 0x85000000L;  // R_CTL = 85 for BASIC ReJecT
+                   // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+                   // TYPE[31-24] 00 Basic Link Service
+      CMDfchs->f_ctl = 0x00910000L;  // xchnge responder, last seq, xfer SI
+                   // CMDfchs->seq_id & count might be set from DataHdr?
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+                        // (Timeout in case of weird error)
+      
+      CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // copy from sender!
+      
+      // now set the ReJecT payload...
+      ba_rjt = (BA_RJT_PAYLOAD*)&CMDfchs->pl[0];
+      memset( ba_rjt, 0, sizeof( BA_RJT_PAYLOAD));
+
+      // We expect that a "target" task couldn't find the Exhange in the
+      // array of active exchanges, so we use a new LinkService X_ID.
+      // See Reject payload description in FC-PH (Rev 4.3), pg. 140
+      ba_rjt->reason_code = 0x09; // "unable to perform command request"
+      ba_rjt->reason_explain = 0x03; // invalid OX/RX ID pair
+
+
+      break;
+    
+    
+    
+    case BLS_ABTS:   // FC defined basic link service command ABTS 
+                     // Abort Sequence
+                     
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32L;        // add len to LSB (header only - no payload)
+
+                   // TYPE[31-24] 00 Basic Link Service
+                   // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+      CMDfchs->d_id |= 0x81000000L;  // R_CTL = 81 for ABTS
+      CMDfchs->f_ctl = 0x00110000L;  // xchnge originator, last seq, xfer SI
+                   // CMDfchs->seq_id & count might be set from DataHdr?
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+                        // (ABTS must timeout when responder is gone)
+      break;
+
+    
+    
+    case FCS_NSR:    // Fabric Name Service Request
+       Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+                         // OX_ID, linked to Driver Transaction ID
+                         // (fix-up at Queing time)
+      CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+                                    // OX_ID set at ERQueing time
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += (32L + sizeof(NSR_PL)); // add len (header & NSR payload)
+
+      CMDfchs->d_id |= 0x02000000L;  // R_CTL = 02 for -
+                                   // Name Service Request: Unsolicited 
+                   // TYPE[31-24] 01 Extended Link Service
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+      CMDfchs->f_ctl = 0x20210000L;
+                   // OX_ID will be fixed-up at Tachyon enqueing time
+      CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+      BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+   
+    
+    
+    
+    
+      break;
+    
+    
+    
+    
+    case ELS_PLOGI:  // FC-PH extended link service command Port Login
+      // (May, 2000)
+      // NOTE! This special case facilitates SANMark testing.  The SANMark
+      // test script for initialization-timeout.fcal.SANMark-1.fc
+      // "eats" the OPN() primitive without issuing an R_RDY, causing
+      // Tachyon to report LST (loop state timeout), which causes a
+      // LIP.  To avoid this, simply send out the frame (i.e. assuming a
+      // buffer credit of 1) without waiting for R_RDY.  Many FC devices
+      // (other than Tachyon) have been doing this for years.  We don't
+      // ever want to do this for non-Link Service frames unless the
+      // other device really did report non-zero login BB credit (i.e.
+      // in the PLOGI ACCept frame).
+//      CMDfchs->sof_eof |= 0x00000400L;  // LCr=1
+    
+    case ELS_FDISC:  // Fabric Discovery (Login)
+    case ELS_FLOGI:  // Fabric Login
+    case ELS_SCR:    // Fabric State Change Registration
+    case ELS_LOGO:   // FC-PH extended link service command Port Logout
+    case ELS_PDISC:  // FC-PH extended link service cmnd Port Discovery
+    case ELS_PRLI:   // FC-PH extended link service cmnd Process Login
+
+      Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+                         // OX_ID, linked to Driver Transaction ID
+                         // (fix-up at Queing time)
+      CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+                                    // OX_ID set at ERQueing time
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      if( type == ELS_LOGO )
+        SfsLen += (32L + 16L); //  add len (header & PLOGI payload)
+      else if( type == ELS_PRLI )
+        SfsLen += (32L + 20L); //  add len (header & PRLI payload)
+      else if( type == ELS_SCR )
+        SfsLen += (32L + sizeof(SCR_PL)); //  add len (header & SCR payload)
+      else
+        SfsLen += (32L + 116L); //  add len (header & PLOGI payload)
+
+      CMDfchs->d_id |= 0x22000000L;  // R_CTL = 22 for -
+                                   // Extended Link_Data: Unsolicited Control
+                   // TYPE[31-24] 01 Extended Link Service
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+      CMDfchs->f_ctl = 0x01210000L;
+                   // OX_ID will be fixed-up at Tachyon enqueing time
+      CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+      BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+      break;
+
+
+
+    case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+    case ELS_RJT:          // extended link service reject (add reason)
+    case ELS_ACC:      // ext. link service generic accept
+    case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+    case ELS_PRLI_ACC: // ext. link service process login accept
+
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // assume done
+                // ensure unique X_IDs! (use tracking function)
+                // OX_ID from initiator cmd
+      ox_ID = (USHORT)(InFCHS->ox_rx_id >> 16); 
+      rx_ID = 0xFFFF; // RX_ID, linked to Driver Exchange ID
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      if( type == ELS_RJT )
+      {
+        SfsLen += (32L + 8L); //  add len (header + payload)
+
+        // ELS_RJT reason codes (utilize unused "reserved" field)
+        CMDfchs->pl[0] = 1;
+        CMDfchs->pl[1] = InFCHS->reserved;  
+          
+      }
+      else if( (type == ELS_LOGO_ACC) || (type == ELS_ACC)  )
+        SfsLen += (32L + 4L); //  add len (header + payload)
+      else if( type == ELS_PLOGI_ACC )
+        SfsLen += (32L + 116L); //  add len (header + payload)
+      else if( type == ELS_PRLI_ACC )
+        SfsLen += (32L + 20L); //  add len (header + payload)
+
+      CMDfchs->d_id |= 0x23000000L;  // R_CTL = 23 for -
+                                   // Extended Link_Data: Control Reply
+                   // TYPE[31-24] 01 Extended Link Service
+                   // f_ctl[23:0] exchg responder, last seq, e_s, tsi
+      CMDfchs->f_ctl = 0x01990000L;
+      CMDfchs->seq_cnt = 0x0L;
+      CMDfchs->ox_rx_id = 0L;        // clear
+      CMDfchs->ox_rx_id = ox_ID; // load upper 16 bits
+      CMDfchs->ox_rx_id <<= 16;      // shift them
+
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+      BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+      break;
+
+
+                         // Fibre Channel SCSI 'originator' sequences...
+                         // (originator means 'initiator' in FCP-SCSI)
+    case SCSI_IWE: // TachLite Initiator Write Entry
+    {
+      PFC_LOGGEDIN_PORT pLoggedInPort = 
+        Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort;
+
+      Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // FC2 timeout
+                       
+      // first, build FCP_CMND
+      // unique X_ID fix-ups in StartExchange 
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS FCP-CMND (not SEST index)
+
+      // NOTE: unlike FC LinkService login frames, normal
+      // SCSI commands are sent without outgoing verification
+      IRB_flags.DCM = 1;    // Disable completion message for Cmnd frame
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 64L;        // add len to LSB (header & CMND payload)
+
+      CMDfchs->d_id |= (0x06000000L);  // R_CTL = 6 for command
+
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+                   //             valid RO
+      CMDfchs->f_ctl = 0x08210008L;
+      CMDfchs->seq_cnt = 0x0L;
+      CMDfchs->ox_rx_id = 0L;        // clear for now (-or- in later)
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+                   // now, fill out FCP-DATA header
+                   // (use buffer inside SEST object)
+      dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+      dataHDR->reserved = 0L; // must clear
+      dataHDR->sof_eof = 0x75002000L;  // SOFi3:EOFn  no UAM; no CLS, noLCr, no TS
+      dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+      dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] xfer S.I.| valid RO
+      dataHDR->f_ctl = 0x08010008L;
+      dataHDR->seq_cnt = 0x02000000L;  // sequence ID: df_ctl : seqence count
+      dataHDR->ox_rx_id = 0L;        // clear; fix-up dataHDR fields later
+      dataHDR->ro = 0x0L;    // relative offset (n/a)
+
+                   // Now setup the SEST entry
+      pIWE = &fcChip->SEST->u[ *fcExchangeIndex ].IWE;
+      
+                   // fill out the IWE:
+
+                // VALid entry:Dir outbound:DCM:enable CM:enal INT: FC frame len
+      pIWE->Hdr_Len = 0x8e000020L; // data frame Len always 32 bytes
+      
+      
+      // from login parameters with other port, what's the largest frame
+      // we can send? 
+      if( pLoggedInPort == NULL) 
+      {
+	ulStatus = INVALID_ARGS;  // failed! give up
+	break;
+      }
+      if( pLoggedInPort->rx_data_size  >= 2048)
+        fl = 0x00020000;  // 2048 code (only support 1024!)
+      else if( pLoggedInPort->rx_data_size  >= 1024)
+        fl = 0x00020000;  // 1024 code
+      else if( pLoggedInPort->rx_data_size  >= 512)
+        fl = 0x00010000;  // 512 code
+      else
+	fl = 0;  // 128 bytes -- should never happen
+      
+      
+      pIWE->Hdr_Len |= fl; // add xmit FC frame len for data phase
+      pIWE->Hdr_Addr = virt_to_bus( dataHDR );
+      pIWE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+      pIWE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+      
+      memset( &fcChip->SEST->RspHDR[ *fcExchangeIndex].pl, 0, 
+        sizeof( FCP_STATUS_RESPONSE) );  // clear out previous status
+  
+      pIWE->RSP_Addr = virt_to_bus(
+                         &fcChip->SEST->RspHDR[ *fcExchangeIndex ]);
+
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pIWE->GLen1, 
+        Cmnd,       // S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+      if( !fcp_dl ) // error building S/G list?
+      {
+        ulStatus = MEMPOOL_FAIL;
+        break;      // give up
+      }
+
+                             // Now that we know total data length in
+                             // the passed S/G buffer, set FCP CMND frame
+      build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pIWE->Buff_Off = 0x78000000L; // extended data | (no offset)
+      else               // local data pointers (in SEST)
+        pIWE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+                              // ULONG 5
+      pIWE->Link = 0x0000ffffL;   // Buff_Index | Link
+
+      pIWE->RX_ID = 0x0L;     // DWord 6: RX_ID set by target XFER_RDY
+
+                                      // DWord 7
+      pIWE->Data_Len = 0L;    // TL enters rcv'd XFER_RDY BURST_LEN
+      pIWE->Exp_RO = 0L;      // DWord 8
+                              // DWord 9
+      pIWE->Exp_Byte_Cnt = fcp_dl;  // sum of gather buffers
+    }
+    break;
+
+
+
+
+
+    case SCSI_IRE: // TachLite Initiator Read Entry
+
+      if( Cmnd->timeout != 0)
+      {
+//	printk("Cmnd->timeout %d\n", Cmnd->timeout);
+	// per Linux Scsi
+        Exchanges->fcExchange[ *fcExchangeIndex].timeOut = Cmnd->timeout;
+      }
+      else  // use our best guess, based on FC & device
+      {
+
+        if( Cmnd->SCp.Message == 1 ) // Tape device? (from INQUIRY)	
+	{
+	  // turn off our timeouts (for now...)
+          Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 0xFFFFFFFF; 
+	}
+	else
+	{
+          Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+          Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // per SCSI req.
+	}
+      }
+
+  
+      // first, build FCP_CMND
+
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS FCP-CMND (not SEST index)
+                            // NOTE: unlike FC LinkService login frames,
+                            // normal SCSI commands are sent "open loop"
+      IRB_flags.DCM = 1;    // Disable completion message for Cmnd frame
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 64L;        // add len to LSB (header & CMND payload)
+
+      CMDfchs->d_id |= (0x06000000L);  // R_CTL = 6 for command
+
+             // TYPE[31-24] 8 for FCP SCSI
+             // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+             //             valid RO
+      CMDfchs->f_ctl = 0x08210008L;
+      CMDfchs->seq_cnt = 0x0L;
+      // x_ID & data direction bit set later
+      CMDfchs->ox_rx_id = 0xFFFF;        // clear
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+
+
+                   // Now setup the SEST entry
+      pIRE = &fcChip->SEST->u[ *fcExchangeIndex ].IRE;
+
+      // fill out the IRE:
+      // VALid entry:Dir outbound:enable CM:enal INT:
+      pIRE->Seq_Accum = 0xCE000000L; // VAL,DIR inbound,DCM| INI,DAT,RSP
+
+      pIRE->reserved = 0L;
+      pIRE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+      pIRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+                            
+      pIRE->RSP_Addr = virt_to_bus(
+                              &fcChip->SEST->RspHDR[ *fcExchangeIndex ]);
+      
+      
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pIRE->SLen1, 
+        Cmnd,       // SCSI command Data desc. with S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+      
+      
+      if( !fcp_dl ) // error building S/G list?
+      {
+	// It is permissible to have a ZERO LENGTH Read command.
+	// If there is the case, simply set fcp_dl (and Exp_Byte_Cnt)
+	// to 0 and continue.
+	if( Cmnd->request_bufflen == 0 )
+	{
+	  fcp_dl = 0; // no FC DATA frames expected
+
+	}
+	else
+	{
+          ulStatus = MEMPOOL_FAIL;
+          break;      // give up
+	}
+      }
+
+      // now that we know the S/G length, build CMND payload
+      build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pIRE->Buff_Off = 0x00000000; // DWord 4: extended s/g list, no offset
+      else
+        pIRE->Buff_Off = 0x80000000; // local data, no offset
+      
+      pIRE->Buff_Index = 0x0L;    // DWord 5: Buff_Index | Reserved
+
+      pIRE->Exp_RO  = 0x0L;       // DWord 6: Expected Rel. Offset
+
+      pIRE->Byte_Count = 0;  // DWord 7: filled in by TL on err
+      pIRE->reserved_ = 0;   // DWord 8: reserved
+                             // NOTE: 0 length READ is OK.
+      pIRE->Exp_Byte_Cnt = fcp_dl;// DWord 9: sum of scatter buffers
+      
+      break;
+
+
+
+
+                         // Fibre Channel SCSI 'responder' sequences...
+                         // (originator means 'target' in FCP-SCSI)
+    case SCSI_TWE: // TachLite Target Write Entry
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+                       // first, build FCP_CMND
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (XFER_RDY)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+      CMDfchs->d_id |= (0x05000000L);  // R_CTL = 5 for XFER_RDY
+
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] exchg responder, 1st seq, xfer S.I.
+                //             valid RO
+      CMDfchs->f_ctl = 0x08810008L;
+      CMDfchs->seq_cnt = 0x01000000; // sequence ID: df_ctl: sequence count
+                       // use originator (other port's) OX_ID
+      CMDfchs->ox_rx_id = InFCHS->ox_rx_id;     // we want upper 16 bits
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+                   // now, fill out FCP-RSP header
+                   // (use buffer inside SEST object)
+
+      rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+      rspHDR->reserved = 0L; // must clear
+      rspHDR->sof_eof = 0x75000000L;  // SOFi3:EOFn  no UAM; no CLS, noLCr, no TS
+      rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+      rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] responder|last seq| xfer S.I.
+      rspHDR->f_ctl = 0x08910000L;
+      rspHDR->seq_cnt = 0x03000000;  // sequence ID
+      rspHDR->ox_rx_id = InFCHS->ox_rx_id; // gives us OX_ID
+      rspHDR->ro = 0x0L;    // relative offset (n/a)
+
+
+                   // Now setup the SEST entry
+                   
+      pTWE = &fcChip->SEST->u[ *fcExchangeIndex ].TWE;
+
+      // fill out the TWE:
+
+      // VALid entry:Dir outbound:enable CM:enal INT:
+      pTWE->Seq_Accum = 0xC4000000L;  // upper word flags
+      pTWE->reserved = 0L;
+      pTWE->Remote_Node_ID = 0L; // no more auto RSP frame! (TL/TS change)
+      pTWE->Remote_Node_ID |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+      
+
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pTWE->SLen1, 
+        Cmnd,       // S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+      
+      
+      if( !fcp_dl ) // error building S/G list?
+      {
+        ulStatus = MEMPOOL_FAIL;
+        break;      // give up
+      }
+
+      // now that we know the S/G length, build CMND payload
+      build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pTWE->Buff_Off = 0x00000000; // extended s/g list, no offset
+      else
+        pTWE->Buff_Off = 0x80000000; // local data, no offset
+      
+      pTWE->Buff_Index = 0;     // Buff_Index | Link
+      pTWE->Exp_RO = 0;
+      pTWE->Byte_Count = 0;  // filled in by TL on err
+      pTWE->reserved_ = 0;
+      pTWE->Exp_Byte_Cnt = fcp_dl;// sum of scatter buffers
+      
+      break;
+
+
+
+
+
+
+    case SCSI_TRE: // TachLite Target Read Entry
+
+      // It doesn't make much sense for us to "time-out" a READ,
+      // but we'll use it for design consistency and internal error recovery.
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+      // I/O request block settings...
+      *pIRB_flags = 0;      // clear IRB flags
+                                  // check PRLI (process login) info
+                                  // to see if Initiator Requires XFER_RDY
+                                  // if not, don't send one!
+                                  // { PRLI check...}
+      IRB_flags.SFA = 0;    // don't send XFER_RDY - start data
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+
+      
+      // now, fill out FCP-DATA header
+                   // (use buffer inside SEST object)
+      dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+
+      dataHDR->reserved = 0L; // must clear
+      dataHDR->sof_eof = 0x75000000L;  // SOFi3:EOFn no UAM; no CLS,noLCr,no TS
+      dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+      dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] exchg responder, not 1st seq, xfer S.I.
+                   //             valid RO
+      dataHDR->f_ctl = 0x08810008L;
+      dataHDR->seq_cnt = 0x01000000;  // sequence ID (no XRDY)
+      dataHDR->ox_rx_id = InFCHS->ox_rx_id & 0xFFFF0000; // we want upper 16 bits
+      dataHDR->ro = 0x0L;    // relative offset (n/a)
+
+                   // now, fill out FCP-RSP header
+                   // (use buffer inside SEST object)
+      rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+
+      rspHDR->reserved = 0L; // must clear
+      rspHDR->sof_eof = 0x75000000L;  // SOFi3:EOFn  no UAM; no CLS, noLCr, no TS
+      rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+      rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] responder|last seq| xfer S.I.
+      rspHDR->f_ctl = 0x08910000L;
+      rspHDR->seq_cnt = 0x02000000;  // sequence ID: df_ctl: sequence count
+
+      rspHDR->ro = 0x0L;    // relative offset (n/a)
+
+
+      // Now setup the SEST entry
+      pTRE = &fcChip->SEST->u[ *fcExchangeIndex ].TRE;
+
+
+      // VALid entry:Dir outbound:enable CM:enal INT:
+      pTRE->Hdr_Len = 0x86010020L; // data frame Len always 32 bytes
+      pTRE->Hdr_Addr = virt_to_bus( dataHDR );
+      pTRE->RSP_Len = 64L; // hdr+data (TL assisted RSP frame)
+      pTRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+      pTRE->RSP_Addr = virt_to_bus( rspHDR );
+
+
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pTRE->GLen1, 
+        Cmnd,       // S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+      
+      
+      if( !fcp_dl ) // error building S/G list?
+      {
+        ulStatus = MEMPOOL_FAIL;
+        break;      // give up
+      }
+
+      // no payload or command to build -- READ doesn't need XRDY
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pTRE->Buff_Off = 0x78000000L; // extended data | (no offset)
+      else               // local data pointers (in SEST)
+        pTRE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+                                            // ULONG 5
+      pTRE->Buff_Index = 0L;   // Buff_Index | reserved
+      pTRE->reserved = 0x0L;   // DWord 6
+
+                                     // DWord 7: NOTE: zero length will
+                                     // hang TachLite!
+      pTRE->Data_Len = fcp_dl; // e.g. sum of scatter buffers
+
+      pTRE->reserved_ = 0L;     // DWord 8
+      pTRE->reserved__ = 0L;    // DWord 9
+
+      break;
+
+
+
+
+
+    
+
+    case FCP_RESPONSE: 
+                  // Target response frame: this sequence uses an OX/RX ID
+                  // pair from a completed SEST exchange.  We built most
+                  // of the response frame when we created the TWE/TRE.
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (RSP)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += sizeof(TachFCHDR_RSP);// add SFS len (header & RSP payload)
+      
+
+      Exchanges->fcExchange[ *fcExchangeIndex].type = 
+        FCP_RESPONSE; // change Exchange type to "response" phase
+
+      // take advantage of prior knowledge of OX/RX_ID pair from
+      // previous XFER outbound frame (still in fchs of exchange)
+      fcChip->SEST->RspHDR[ *fcExchangeIndex ].ox_rx_id = 
+        CMDfchs->ox_rx_id;
+
+      // Check the status of the DATA phase of the exchange so we can report
+      // status to the initiator
+      buildFCPstatus( fcChip, *fcExchangeIndex); // set RSP payload fields
+
+      memcpy(
+        CMDfchs,  // re-use same XFER fchs for Response frame
+        &fcChip->SEST->RspHDR[ *fcExchangeIndex ],
+        sizeof( TachFCHDR_RSP ));
+      
+        
+      break;
+
+    default:
+      printk("cpqfcTS: don't know how to build FC type: %Xh(%d)\n", type,type);
+      break;
+
+    }
+
+    
+    
+    if( !ulStatus)  // no errors above?
+    {
+      // FCHS is built; now build IRB
+
+      // link the just built FCHS (the "command") to the IRB entry 
+      // for this Exchange.
+      pIRB = &Exchanges->fcExchange[ *fcExchangeIndex].IRB; 
+    
+                          // len & flags according to command type above
+      pIRB->Req_A_SFS_Len = SfsLen;  // includes IRB flags & len
+      pIRB->Req_A_SFS_Addr = virt_to_bus(CMDfchs); // TL needs physical addr
+                                                  // of frame to send
+      pIRB->Req_A_SFS_D_ID = CMDfchs->d_id << 8; // Dest_ID must be consistent!
+
+    // Exchange is complete except for "fix-up" fields to be set
+    // at Tachyon Queuing time:
+    //    IRB->Req_A_Trans_ID (OX_ID/ RX_ID):  
+    //        for SEST entry, lower bits correspond to actual FC Exchange ID
+    //    fchs->OX_ID or RX_ID
+    }
+    else
+    {
+#ifdef DBG     
+      printk( "FC Error: SEST build Pool Allocation failed\n");
+#endif
+      // return resources...
+      cpqfcTSCompleteExchange( fcChip, *fcExchangeIndex);  // SEST build failed
+    }
+  }
+  else  // no Exchanges available
+  {
+    ulStatus = SEST_FULL;
+    printk( "FC Error: no fcExchanges available\n");
+  }
+  return ulStatus;
+}
+
+
+
+
+
+
+// set RSP payload fields
+static void  buildFCPstatus( PTACHYON fcChip, ULONG ExchangeID) 
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ExchangeID];  // shorthand
+  PFCP_STATUS_RESPONSE pFcpStatus;
+  
+  memset( &fcChip->SEST->RspHDR[ ExchangeID ].pl, 0,
+    sizeof( FCP_STATUS_RESPONSE) );
+  if( pExchange->status ) // something wrong?
+  {
+    pFcpStatus = (PFCP_STATUS_RESPONSE)  // cast RSP buffer for this xchng
+      &fcChip->SEST->RspHDR[ ExchangeID ].pl;
+    if( pExchange->status & COUNT_ERROR )
+    {
+      
+      // set FCP response len valid (so we can report count error)
+      pFcpStatus->fcp_status |= FCP_RSP_LEN_VALID;
+      pFcpStatus->fcp_rsp_len = 0x04000000;  // 4 byte len (BIG Endian)
+
+      pFcpStatus->fcp_rsp_info = FCP_DATA_LEN_NOT_BURST_LEN; // RSP_CODE
+    }
+  }
+}
+
+
+
+
+// This routine builds scatter/gather lists into SEST entries
+// INPUTS:
+//   SESTalPair - SEST address @DWordA "Local Buffer Length"
+//   sgList     - Scatter/Gather linked list of Len/Address data buffers
+// OUTPUT:
+//   sgPairs - number of valid address/length pairs
+// Remarks:
+//   The SEST data buffer pointers only depend on number of
+//   length/ address pairs, NOT on the type (IWE, TRE,...)
+//   Up to 3 pairs can be referenced in the SEST - more than 3
+//   require this Extended S/G list page.  The page holds 4, 8, 16...
+//   len/addr pairs, per Scatter/Gather List Page Length Reg.
+//   TachLite allows pages to be linked to any depth.
+
+//#define DBG_SEST_SGLIST 1 // for printing out S/G pairs with Ext. pages
+
+static ULONG build_SEST_sgList( 
+    ULONG *SESTalPairStart,  // the 3 len/address buffers in SEST
+    Scsi_Cmnd *Cmnd,
+    ULONG *sgPairs, 
+    PSGPAGES sgPages)  // link list of TL Ext. S/G pages from O/S Pool
+    
+{
+  ULONG i, AllocatedPages=0; // Tach Ext. S/G page allocations
+  ULONG* alPair = SESTalPairStart;
+  ULONG alignedPageAddress;  // TL hardware alignment requirement
+  int PairCount;
+  unsigned long ulBuff;
+  ULONG total_data_len=0; // (in bytes)
+  ULONG bytes_to_go = Cmnd->request_bufflen; // total xfer (S/G sum)
+  ULONG thisMappingLen;
+  struct scatterlist *sgl;  // S/G list (Linux format)
+
+
+
+  if( !Cmnd->use_sg )  // no S/G list?
+  {
+    *sgPairs = 1;      // use "local" S/G pair in SEST entry
+                       // (for now, ignore address bits above #31)
+    *alPair++ = bytes_to_go & 0x7ffff; // bits 18-0, length
+    ulBuff = virt_to_bus( Cmnd->request_buffer);
+#if BITS_PER_LONG > 32
+    if( ulBuff >>32 )
+    {
+      printk("FATAL! Tachyon DMA address %p exceeds 32 bits\n", (void*)ulBuff );
+      return 0;
+    }
+#endif
+    *alPair = (ULONG)ulBuff;      
+    return bytes_to_go;
+  }
+
+
+  // [TBD - update for Linux to support > 32 bits addressing]
+  // since the format for local & extended S/G lists is different,
+  // check if S/G pairs exceeds 3.
+  *sgPairs = Cmnd->use_sg;
+  sgl = (struct scatterlist*)Cmnd->request_buffer;  
+  
+  if( *sgPairs <= 3 ) // need "local" SEST list
+  {
+    while( bytes_to_go)
+    {
+      thisMappingLen = sgl->length;  // we want them ALL on every pass
+      bytes_to_go = bytes_to_go - thisMappingLen;
+
+      // we have L/A pair; L = thisMappingLen, A = physicalAddress
+      // load into SEST...
+      total_data_len += thisMappingLen & 0x7ffff;  // mask in valid bits
+                                                   // per SEST format
+      *alPair = thisMappingLen & 0x7ffff; // bits 18-0, length
+//      physicalAddress.HighPart <= 19;  // shift to bit 19
+
+                   // pick up bits 44-32 of upper 64-bit address
+                   // and load into 31-19 LBAU (upper addr) of SEST entry
+//      *alPair++ |=(ULONG)((physicalAddress.HighPart & 0xFFF8)); 
+      // on Tachlite TS's local S/G, we can handle 13 extra address bits
+      // i.e., bits 31-19 are actually bits  44-32 of physicalAddress
+
+      alPair++;
+
+      ulBuff = virt_to_bus( sgl->address);
+#if BITS_PER_LONG > 32
+      if( ulBuff >>32 )
+      {
+        printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff );
+        return 0;
+      }
+#endif
+      *alPair++ = (ULONG)ulBuff; // lower 32 bits (31-0)
+
+      ++sgl;  // next S/G pair
+#ifdef DBG_SEST_SGLIST
+      printk(" thisLen %d ", thisMappingLen);
+      printk(" remain %d\n", bytes_to_go);
+#endif
+
+    }
+  }
+
+
+
+
+  else    // more than 3 pairs requires Extended S/G page (Pool Allocation)
+  {
+    // clear out SEST DWORDs (local S/G addr) C-F (A-B set in following logic)
+
+
+    
+    for( i=2; i<6; i++)
+      alPair[i] = 0;
+
+    PairCount = TL_EXT_SG_PAGE_COUNT;    // forces initial page allocation
+
+    while( bytes_to_go )
+    {
+
+
+      // Per SEST format, we can support 524287 byte lenghts per
+      // S/G pair.  Typical user buffers are 4k, and very rarely
+      // exceed 12k due to fragmentation of physical memory pages.
+      // However, on certain O/S system (not "user") buffers (on platforms 
+      // with huge memories like 256Meg), it's possible to exceed this
+      // length in a single S/G address/len mapping.
+      //
+      // Check for Tachyon length boundary
+      //
+      if( sgl->length > 0x7ffff )
+      {
+        // never ask for more than we can handle
+  	thisMappingLen = sgl->length & 0x7ffff;  
+      }
+      else
+        thisMappingLen = sgl->length;        
+      
+
+
+      // should we load into "this" extended S/G page, or allocate
+      // new page?
+
+      if( PairCount >= TL_EXT_SG_PAGE_COUNT )
+      {
+        // have we exceeded the max possible extended pages?      
+        if( AllocatedPages >= TL_MAX_SGPAGES)
+        {
+          printk("Error: aborted loop on %d Ext. S/G page allocations\n",
+            AllocatedPages);
+
+          total_data_len = 0;  // failure!! Ext. S/G is All-or-none affair
+          break; // failed
+        }
+        
+        // Allocate the TL Extended S/G list page from O/S pool.  We have
+        // to allocated twice what we want to ensure required TL alignment
+        // (Tachlite TL/TS User Man. Rev 6.0, p 168)
+        // We store the original allocated PVOID so we can free later
+
+        sgPages->PoolPage[ AllocatedPages] = 
+          kmalloc( TL_EXT_SG_PAGE_BYTELEN*2,GFP_ATOMIC); // double for alignment
+
+        
+        if( !sgPages->PoolPage[ AllocatedPages] )  // Allocation failed?
+        {
+
+          printk("Error: Allocation failed @ %d S/G page allocations\n",
+            AllocatedPages);
+
+          total_data_len = 0;  // failure!! Ext. S/G is All-or-none affair
+          break;               // give up
+        }
+                               // clear out memory we just allocated                     
+        memset( sgPages->PoolPage[AllocatedPages], 0,
+          TL_EXT_SG_PAGE_BYTELEN*2);
+
+      
+        // align the memory - TL requires sizeof() Ext. S/G page alignment.
+        // We doubled the actual required size so we could mask off LSBs 
+        // to get desired offset
+
+        ulBuff = virt_to_bus( sgPages->PoolPage[AllocatedPages]);
+
+#if BITS_PER_LONG > 32
+        if( ulBuff >>32 )
+        {
+          printk("cqpfcTS: Tach ext. S/G DMA address %p > 32 bits\n", 
+		  (void*)ulBuff );
+          return 0;
+        }
+#endif
+	
+        ulBuff += TL_EXT_SG_PAGE_BYTELEN; // ensures we pass align. boundary
+        ulBuff &= (0xFFFFFFFF - (TL_EXT_SG_PAGE_BYTELEN -1) );// mask off LSBs
+         
+        alignedPageAddress = (ULONG)ulBuff;
+#ifdef DBG_SEST_SGLIST
+        printk("new PoolPage: %p, alignedPageAddress %lXh\n", 
+          sgPages->PoolPage[AllocatedPages], ulBuff);
+#endif
+
+
+        // set pointer, in SEST if first Ext. S/G page, or in last pair
+        // of linked Ext. S/G pages...
+        // (Only 32-bit PVOIDs, so just load lower 32 bits)
+        // NOTE: the Len field must be '0' if this is the first Ext. S/G
+        // pointer in SEST, and not 0 otherwise.
+        if( alPair == SESTalPairStart) // initial Ext. S/G list?
+          *alPair = 0;
+        else // not the SEST entry... Len must be non-0, so
+             // arbitrarily set it to number bytes remaining
+          *alPair = ( bytes_to_go & 0x7ffff);
+
+#ifdef DBG_SEST_SGLIST
+        printk("PairCount %d @%p even %Xh, ", 
+          PairCount, alPair, *alPair);
+#endif
+        alPair++;  // next DWORD
+
+        *alPair = alignedPageAddress; // TL needs 32-bit physical
+#ifdef DBG_SEST_SGLIST
+        printk("odd %Xh\n", *alPair);
+#endif
+                   
+        // now reset the pointer to the ACTUAL (Extended) S/G page
+        // which will accept the Len/ PhysicalAddress pairs
+        alPair = bus_to_virt(alignedPageAddress);
+        
+        AllocatedPages++;
+        PairCount = 1;  // starting new Ext. S/G page
+      }  // end of new TL Ext. S/G page allocation
+
+      
+      *alPair = thisMappingLen; // bits 18-0, length (range check above)
+      
+      
+//      physicalAddress.HighPart <= 19;  // shift to bit 19
+      
+                   // pick up bits 44-32 of upper 64-bit address
+                   // and load into 31-19 LBAU (upper addr) of SEST entry
+//      *alPair |=(ULONG)((physicalAddress.HighPart & 0xFFF8)); 
+
+      
+#ifdef DBG_SEST_SGLIST
+      printk("PairCount %d @%p, even %Xh, ", 
+        PairCount, alPair, *alPair);
+#endif
+
+      alPair++;    // next DWORD
+      // on Tachlite TS's local S/G, we can handle 13 extra address bits
+      // i.e., bits 31-19 are actually bits  44-32 of physicalAddress
+
+
+      ulBuff = virt_to_bus( sgl->address);
+#if BITS_PER_LONG > 32
+      if( ulBuff >>32 )
+      {
+        printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff );
+        return 0;
+      }
+#endif
+      *alPair = (ULONG)ulBuff; // lower 32 bits (31-0)
+
+
+#ifdef DBG_SEST_SGLIST
+      printk("odd %Xh\n", *alPair);
+#endif
+      alPair++;    // next DWORD
+                                           
+
+      PairCount++; // next Length/Address pair
+      bytes_to_go -= thisMappingLen;
+      total_data_len += thisMappingLen;  
+      sgl++;  // next S/G pair
+    }
+  }
+  return total_data_len;
+}
+
+
+
+// The Tachlite SEST table is referenced to OX_ID (or RX_ID).  To optimize
+// performance and debuggability, we index the Exchange structure to FC X_ID
+// This enables us to build exchanges for later en-queing to Tachyon,
+// provided we have an open X_ID slot. At Tachyon queing time, we only 
+// need an ERQ slot; then "fix-up" references in the 
+// IRB, FCHS, etc. as needed.
+// RETURNS:
+// 0 if successful
+// non-zero on error
+//sstartex
+ULONG cpqfcTSStartExchange( 
+  CPQFCHBA *cpqfcHBAdata,                      
+  LONG ExchangeID )
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ ExchangeID ]; // shorthand
+  USHORT producer, consumer;
+  ULONG ulStatus=0;
+  short int ErqIndex;
+  BOOLEAN CompleteExchange = FALSE;  // e.g. ACC replies are complete
+  BOOLEAN SestType=FALSE;
+  ULONG InboundData=0;
+
+  // We will manipulate Tachlite chip registers here to successfully
+  // start exchanges. 
+
+  // Check that link is not down -- we can't start an exchange on a
+  // down link!
+
+  if( fcChip->Registers.FMstatus.value & 0x80) // LPSM offline?
+  {
+printk("fcStartExchange: PSM offline (%Xh), x_ID %Xh, type %Xh, port_id %Xh\n",
+         fcChip->Registers.FMstatus.value & 0xFF,
+         ExchangeID,
+         pExchange->type,
+         pExchange->fchs.d_id);
+
+    if( ExchangeID >= TACH_SEST_LEN )  // Link Service Outbound frame?
+    {
+      // Our most popular LinkService commands are port discovery types
+      // (PLOGI/ PDISC...), which are implicitly nullified by Link Down
+      // events, so it makes no sense to Que them.  However, ABTS should
+      // be queued, since exchange sequences are likely destroyed by
+      // Link Down events, and we want to notify other ports of broken
+      // sequences by aborting the corresponding exchanges.
+      if( pExchange->type != BLS_ABTS )
+      {
+	ulStatus = LNKDWN_OSLS;
+	goto Done;
+        // don't Que most LinkServ exchanges on LINK DOWN
+      }
+    }
+
+    printk("fcStartExchange: Que x_ID %Xh, type %Xh\n", 
+      ExchangeID, pExchange->type);
+    pExchange->status |= EXCHANGE_QUEUED;
+    ulStatus = EXCHANGE_QUEUED;
+    goto Done;
+  }
+
+  // Make sure ERQ has available space.
+  
+  producer = (USHORT)fcChip->ERQ->producerIndex; // copies for logical arith.
+  consumer = (USHORT)fcChip->ERQ->consumerIndex;
+  producer++;  // We are testing for full que by incrementing
+  
+  if( producer >= ERQ_LEN )  // rollover condition?
+    producer = 0;
+  if( consumer != producer ) // ERQ not full?
+  {
+    // ****************** Need Atomic access to chip registers!!********
+    
+    // remember ERQ PI for copying IRB
+    ErqIndex = (USHORT)fcChip->ERQ->producerIndex; 
+    fcChip->ERQ->producerIndex = producer; // this is written to Tachyon
+               // we have an ERQ slot! If SCSI command, need SEST slot
+               // otherwise we are done.
+
+    // Note that Tachyon requires that bit 15 of the OX_ID or RX_ID be
+    // set according to direction of data to/from Tachyon for SEST assists.
+    // For consistency, enforce this rule for Link Service (non-SEST)
+    // exchanges as well.
+
+    // fix-up the X_ID field in IRB
+    pExchange->IRB.Req_A_Trans_ID = ExchangeID & 0x7FFF; // 15-bit field
+
+    // fix-up the X_ID field in fchs -- depends on Originator or Responder,
+    // outgoing or incoming data?
+    switch( pExchange->type )
+    {
+               // ORIGINATOR types...  we're setting our OX_ID and
+               // defaulting the responder's RX_ID to 0xFFFF
+    
+    case SCSI_IRE:
+      // Requirement: set MSB of x_ID for Incoming TL data
+      // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+      InboundData = 0x8000;
+
+    case SCSI_IWE:   
+      SestType = TRUE;
+      pExchange->fchs.ox_rx_id = (ExchangeID | InboundData);
+      pExchange->fchs.ox_rx_id <<= 16;     // MSW shift
+      pExchange->fchs.ox_rx_id |= 0xffff;  // add default RX_ID
+      
+      // now fix-up the Data HDR OX_ID (TL automatically does rx_id)
+      // (not necessary for IRE -- data buffer unused)
+      if( pExchange->type == SCSI_IWE)
+      {
+        fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id = 
+          pExchange->fchs.ox_rx_id;
+
+      }
+
+      break;
+
+
+    case FCS_NSR:  // ext. link service Name Service Request
+    case ELS_SCR:  // ext. link service State Change Registration
+    case ELS_FDISC:// ext. link service login
+    case ELS_FLOGI:// ext. link service login
+    case ELS_LOGO: // FC-PH extended link service logout
+    case BLS_NOP:  // Basic link service No OPeration
+    case ELS_PLOGI:// ext. link service login (PLOGI)
+    case ELS_PDISC:// ext. link service login (PDISC)
+    case ELS_PRLI: // ext. link service process login
+
+      pExchange->fchs.ox_rx_id = ExchangeID;
+      pExchange->fchs.ox_rx_id <<= 16;  // MSW shift
+      pExchange->fchs.ox_rx_id |= 0xffff;  // and RX_ID
+
+      break;
+      
+
+
+
+               // RESPONDER types... we must set our RX_ID while preserving
+               // sender's OX_ID
+               // outgoing (or no) data
+    case ELS_RJT:       // extended link service reject 
+    case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+    case ELS_ACC:      // ext. generic link service accept
+    case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+    case ELS_PRLI_ACC: // ext. link service process login accept
+
+      CompleteExchange = TRUE;   // Reply (ACC or RJT) is end of exchange
+      pExchange->fchs.ox_rx_id |= (ExchangeID & 0xFFFF);
+
+      break;
+
+
+      // since we are a Responder, OX_ID should already be set by
+      // cpqfcTSBuildExchange().  We need to -OR- in RX_ID
+    case SCSI_TWE:
+      SestType = TRUE;
+      // Requirement: set MSB of x_ID for Incoming TL data
+      // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+
+      pExchange->fchs.ox_rx_id &= 0xFFFF0000;  // clear RX_ID
+      // Requirement: set MSB of RX_ID for Incoming TL data
+      // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+      pExchange->fchs.ox_rx_id |= (ExchangeID | 0x8000);
+      break;
+          
+    
+    case SCSI_TRE:
+      SestType = TRUE;
+      
+      // there is no XRDY for SEST target read; the data
+      // header needs to be updated. Also update the RSP
+      // exchange IDs for the status frame, in case it is sent automatically
+      fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id |= ExchangeID;
+      fcChip->SEST->RspHDR[ ExchangeID ].ox_rx_id = 
+        fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+      
+      // for easier FCP response logic (works for TWE and TRE), 
+      // copy exchange IDs.  (Not needed if TRE 'RSP' bit set)
+      pExchange->fchs.ox_rx_id =
+        fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+
+      break;
+
+
+    case FCP_RESPONSE:  // using existing OX_ID/ RX_ID pair,
+                        // start SFS FCP-RESPONSE frame
+      // OX/RX_ID should already be set! (See "fcBuild" above)
+      CompleteExchange = TRUE;   // RSP is end of FCP-SCSI exchange
+
+      
+      break;
+
+
+    case BLS_ABTS_RJT:  // uses new RX_ID, since SEST x_ID non-existent
+    case BLS_ABTS_ACC:  // using existing OX_ID/ RX_ID pair from SEST entry
+      CompleteExchange = TRUE;   // ACC or RJT marks end of FCP-SCSI exchange
+    case BLS_ABTS:  // using existing OX_ID/ RX_ID pair from SEST entry
+
+
+      break;
+
+
+    default:
+      printk("Error on fcStartExchange: undefined type %Xh(%d)\n",
+        pExchange->type, pExchange->type);
+      return INVALID_ARGS;
+    }
+    
+    
+      // X_ID fields are entered -- copy IRB to Tachyon's ERQ
+    
+
+    memcpy(
+        &fcChip->ERQ->QEntry[ ErqIndex ],  // dest.
+        &pExchange->IRB,
+        32);  // fixed (hardware) length!
+
+    PCI_TRACEO( ExchangeID, 0xA0)
+
+    // ACTION!  May generate INT and IMQ entry
+    writel( fcChip->ERQ->producerIndex,
+          fcChip->Registers.ERQproducerIndex.address);
+
+  
+    if( ExchangeID >= TACH_SEST_LEN )  // Link Service Outbound frame?
+    {
+    
+      // wait for completion! (TDB -- timeout and chip reset)
+      
+
+  PCI_TRACEO( ExchangeID, 0xA4)
+  
+      enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Sem.
+      
+      down_interruptible( cpqfcHBAdata->TYOBcomplete); 
+  
+      disable_irq( cpqfcHBAdata->HostAdapter->irq);
+  PCI_TRACE( 0xA4)
+
+      // On login exchanges, BAD_ALPA (non-existent port_id) results in 
+      // FTO (Frame Time Out) on the Outbound Completion message.
+      // If we got an FTO status, complete the exchange (free up slot)
+      if( CompleteExchange ||   // flag from Reply frames
+          pExchange->status )   // typically, can get FRAME_TO
+      {
+    	cpqfcTSCompleteExchange( fcChip, ExchangeID);  
+      }
+    }
+
+    else                         // SEST Exchange
+    {
+      ulStatus = 0;   // ship & pray success (e.g. FCP-SCSI)
+      
+      if( CompleteExchange )   // by Type of exchange (e.g. end-of-xchng)
+      {
+    	cpqfcTSCompleteExchange( fcChip, ExchangeID);  
+      }
+       
+      else
+        pExchange->status &= ~EXCHANGE_QUEUED;  // clear ExchangeQueued flag 
+
+    }
+  }
+
+  
+  else                // ERQ 'producer' = 'consumer' and QUE is full
+  {
+    ulStatus = OUTQUE_FULL; // Outbound (ERQ) Que full
+  }
+ 
+Done: 
+  PCI_TRACE( 0xA0)
+  return ulStatus; 
+}
+
+
+
+
+
+// Scan fcController->fcExchanges array for a usuable index (a "free"
+// exchange).
+// Inputs:
+//   fcChip - pointer to TachLite chip structure
+// Return:
+//  index - exchange array element where exchange can be built
+//  -1    - exchange array is full
+// REMARKS:
+// Although this is a (yuk!) linear search, we presume
+// that the system will complete exchanges about as quickly as
+// they are submitted.  A full Exchange array (and hence, max linear
+// search time for free exchange slot) almost guarantees a Fibre problem 
+// of some sort.
+// In the interest of making exchanges easier to debug, we want a LRU
+// (Least Recently Used) scheme.
+
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type )
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG i;
+  ULONG ulStatus=-1;  // assume failure
+
+
+  if( type == SCSI_IRE ||
+      type == SCSI_TRE ||
+      type == SCSI_IWE ||
+      type == SCSI_TWE)
+  {
+        // SCSI type - X_IDs should be from 0 to TACH_SEST_LEN-1
+    if( fcChip->fcSestExchangeLRU >= TACH_SEST_LEN) // rollover?
+      fcChip->fcSestExchangeLRU = 0;
+    i = fcChip->fcSestExchangeLRU; // typically it's already free!
+
+    if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+    {
+      ulStatus = 0; // success!
+    }
+    
+    else
+    {         // YUK! we need to do a linear search for free element.
+              // Fragmentation of the fcExchange array is due to excessively
+              // long completions or timeouts.
+      
+      while( TRUE )
+      {
+        if( ++i >= TACH_SEST_LEN ) // rollover check
+          i = 0;  // beginning of SEST X_IDs
+
+//        printk( "looping for SCSI xchng ID: i=%d, type=%Xh\n", 
+//         i, Exchanges->fcExchange[i].type);
+
+        if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+        {
+          ulStatus = 0; // success!
+          break;
+        }
+        if( i == fcChip->fcSestExchangeLRU ) // wrapped-around array?
+        {
+          printk( "SEST X_ID space full\n");
+          break;       // failed - prevent inf. loop
+        }
+      }
+    }
+    fcChip->fcSestExchangeLRU = i + 1; // next! (rollover check next pass)
+  }
+
+  
+  
+  else  // Link Service type - X_IDs should be from TACH_SEST_LEN 
+        // to TACH_MAX_XID
+  {
+    if( fcChip->fcLsExchangeLRU >= TACH_MAX_XID || // range check
+        fcChip->fcLsExchangeLRU < TACH_SEST_LEN ) // (e.g. startup)
+      fcChip->fcLsExchangeLRU = TACH_SEST_LEN;
+
+    i = fcChip->fcLsExchangeLRU; // typically it's already free!
+    if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+    {
+      ulStatus = 0; // success!
+    }
+    
+    else
+    {         // YUK! we need to do a linear search for free element
+              // Fragmentation of the fcExchange array is due to excessively
+              // long completions or timeouts.
+      
+      while( TRUE )
+      {
+        if( ++i >= TACH_MAX_XID ) // rollover check
+          i = TACH_SEST_LEN;// beginning of Link Service X_IDs
+
+//        printk( "looping for xchng ID: i=%d, type=%Xh\n", 
+//         i, Exchanges->fcExchange[i].type);
+
+        if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+        {
+          ulStatus = 0; // success!
+          break;
+        }
+        if( i == fcChip->fcLsExchangeLRU ) // wrapped-around array?
+        {
+          printk( "LinkService X_ID space full\n");
+          break;       // failed - prevent inf. loop
+        }
+      }
+    }
+    fcChip->fcLsExchangeLRU = i + 1; // next! (rollover check next pass)
+
+  }
+
+  if( !ulStatus )  // success?
+    Exchanges->fcExchange[i].type = type; // allocate it.
+  
+  else
+    i = -1;  // error - all exchanges "open"
+
+  return i;  
+}
+
+
+
+
+
+// We call this routine to free an Exchange for any reason:
+// completed successfully, completed with error, aborted, etc.
+
+// returns FALSE if Exchange failed and "retry" is acceptable
+// returns TRUE if Exchange was successful, or retry is impossible
+// (e.g. port/device gone).
+//scompleteexchange
+
+void cpqfcTSCompleteExchange( 
+       PTACHYON fcChip, 
+       ULONG x_ID)
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  
+  if( x_ID < TACH_SEST_LEN ) // SEST-based (or LinkServ for FCP exchange)
+  {
+    if( Exchanges->fcExchange[ x_ID ].Cmnd == NULL ) // what#@!
+    {
+//      TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+      printk(" x_ID %Xh, type %Xh, NULL ptr!\n", x_ID,
+			Exchanges->fcExchange[ x_ID ].type);
+
+      goto CleanUpSestResources;  // this path should be very rare.
+    }
+
+    // we have Linux Scsi Cmnd ptr..., now check our Exchange status
+    // to decide how to complete this SEST FCP exchange
+
+    if( Exchanges->fcExchange[ x_ID ].status ) // perhaps a Tach indicated problem,
+                                             // or abnormal exchange completion
+    {
+      // set FCP Link statistics
+     
+      if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT)
+        fcChip->fcStats.timeouts++;
+      if( Exchanges->fcExchange[ x_ID ].status & INITIATOR_ABORT)
+        fcChip->fcStats.FC4aborted++;
+      if( Exchanges->fcExchange[ x_ID ].status & COUNT_ERROR)
+        fcChip->fcStats.CntErrors++;
+      if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX)
+        fcChip->fcStats.linkFailTX++;
+      if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_RX)
+        fcChip->fcStats.linkFailRX++;
+      if( Exchanges->fcExchange[ x_ID ].status & OVERFLOW)
+        fcChip->fcStats.CntErrors++;
+
+      // First, see if the Scsi upper level initiated an ABORT on this
+      // exchange...
+      if( Exchanges->fcExchange[ x_ID ].status == INITIATOR_ABORT )
+      {
+        printk(" DID_ABORT, x_ID %Xh, Cmnd %p ", 
+            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        goto CleanUpSestResources;  // (we don't expect Linux _aborts)
+      }
+
+      // Did our driver timeout the Exchange, or did Tachyon indicate
+      // a failure during transmission?  Ask for retry with "SOFT_ERROR"
+      else if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT) 
+      {
+//        printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", 
+//            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+      }
+      
+      // Did frame(s) for an open exchange arrive in the SFQ,
+      // meaning the SEST was unable to process them?
+      else if( Exchanges->fcExchange[ x_ID ].status & SFQ_FRAME) 
+      {
+//        printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", 
+//            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+      }
+      
+      // Did our driver timeout the Exchange, or did Tachyon indicate
+      // a failure during transmission?  Ask for retry with "SOFT_ERROR"
+      else if( 
+               (Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) ||
+               (Exchanges->fcExchange[ x_ID ].status & PORTID_CHANGED) ||
+	       (Exchanges->fcExchange[ x_ID ].status & FRAME_TO)    ||
+	       (Exchanges->fcExchange[ x_ID ].status & INV_ENTRY)    ||
+	       (Exchanges->fcExchange[ x_ID ].status & ABORTSEQ_NOTIFY)    )
+
+
+      {
+//        printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", 
+//            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+
+
+      }
+
+      // e.g., a LOGOut happened, or device never logged back in.
+      else if( Exchanges->fcExchange[ x_ID ].status & DEVICE_REMOVED) 
+      {
+//	printk(" *LOGOut or timeout on login!* ");
+	// trigger?
+//        TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_BAD_TARGET <<16);
+      }      
+		
+		      
+      // Did Tachyon indicate a CNT error?  We need further analysis
+      // to determine if the exchange is acceptable
+      else if( Exchanges->fcExchange[ x_ID ].status == COUNT_ERROR)
+      {
+        UCHAR ScsiStatus;
+        FCP_STATUS_RESPONSE *pFcpStatus = 
+	  (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+
+      	ScsiStatus = pFcpStatus->fcp_status >>24;
+  
+	// If the command is a SCSI Read/Write type, we don't tolerate
+	// count errors of any kind; assume the count error is due to
+	// a dropped frame and ask for retry...
+	
+	if(( (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x8) ||
+	    (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x28) ||		
+            (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0xA) ||
+            (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x2A) )
+	                   &&
+                     ScsiStatus == 0 )
+	{
+          // ask for retry
+/*          printk("COUNT_ERROR retry, x_ID %Xh, status %Xh, Cmnd %p\n", 
+            x_ID, Exchanges->fcExchange[ x_ID ].status,
+            Exchanges->fcExchange[ x_ID ].Cmnd);*/
+          Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+	}
+	
+	else  // need more analysis
+	{
+	  cpqfcTSCheckandSnoopFCP(fcChip, x_ID);  // (will set ->result)
+	}
+      }
+      
+      // default: NOTE! We don't ever want to get here.  Getting here
+      // implies something new is happening that we've never had a test
+      // case for.  Need code maintenance!  Return "ERROR"
+      else
+      {
+        printk("DEFAULT result %Xh, x_ID %Xh, Cmnd %p\n", 
+          Exchanges->fcExchange[ x_ID ].status, x_ID, 
+	  Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_ERROR <<16);
+      }
+    }
+    else    // definitely no Tach problem, but perhaps an FCP problem
+    {
+      // set FCP Link statistic
+      fcChip->fcStats.ok++;
+      cpqfcTSCheckandSnoopFCP( fcChip, x_ID);  // (will set ->result)    
+    }
+
+    // OK, we've set the Scsi "->result" field, so proceed with calling
+    // Linux Scsi "done" (if not NULL), and free any kernel memory we
+    // may have allocated for the exchange.
+
+  PCI_TRACEO( (ULONG)Exchanges->fcExchange[x_ID].Cmnd, 0xAC);
+    // complete the command back to upper Scsi drivers
+    if( Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done != NULL)
+    {
+      // Calling "done" on an Linux _abort() aborted
+      // Cmnd causes a kernel panic trying to re-free mem.
+      // Actually, we shouldn't do anything with an _abort CMND
+      if( Exchanges->fcExchange[ x_ID ].Cmnd->result != (DID_ABORT<<16) )
+      {
+        PCI_TRACE(0xAC)
+        (*Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done)
+	   (Exchanges->fcExchange[ x_ID ].Cmnd);
+      }
+      else
+      {
+
+//	printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n",
+//			x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+      }
+    }
+    else{
+      printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID,
+	Exchanges->fcExchange[ x_ID ].type, 
+	Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]);	      
+      printk(" cpqfcTS: Null scsi_done function pointer!\n");
+    }
+
+
+    // Now, clean up non-Scsi_Cmnd items...
+CleanUpSestResources:
+    
+    // Was an Extended Scatter/Gather page allocated?  We know
+    // this by checking DWORD 4, bit 31 ("LOC") of SEST entry
+    if( !(fcChip->SEST->u[ x_ID ].IWE.Buff_Off & 0x80000000))
+    {
+      int i = 0;
+
+      // extended S/G list was used -- Free the allocated ext. S/G pages
+
+      while( fcChip->SEST->sgPages[x_ID].PoolPage[i] && 
+             (i < TL_MAX_SGPAGES) )
+      {
+        kfree( fcChip->SEST->sgPages[x_ID].PoolPage[i]);
+        fcChip->SEST->sgPages[x_ID].PoolPage[i] = NULL;
+        i++;
+      }
+    }
+  
+    Exchanges->fcExchange[ x_ID ].Cmnd = NULL; 
+  }  // Done with FCP (SEST) exchanges
+
+
+  // the remaining logic is common to ALL Exchanges: 
+  // FCP(SEST) and LinkServ.
+
+  Exchanges->fcExchange[ x_ID ].type = 0; // there -- FREE!  
+  Exchanges->fcExchange[ x_ID ].status = 0; 
+
+  PCI_TRACEO( x_ID, 0xAC)
+     
+  
+  return;
+}   // (END of CompleteExchange function)
+ 
+
+
+
+// Unfortunately, we must snoop all command completions in
+// order to manipulate certain return fields, and take note of
+// device types, etc., to facilitate the Fibre-Channel to SCSI
+// "mapping".  
+// (Watch for BIG Endian confusion on some payload fields)
+void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID)
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  Scsi_Cmnd *Cmnd = Exchanges->fcExchange[ x_ID].Cmnd;
+  FCP_STATUS_RESPONSE *pFcpStatus = 
+    (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+  UCHAR ScsiStatus;
+
+  ScsiStatus = pFcpStatus->fcp_status >>24;
+
+#ifdef FCP_COMPLETION_DBG
+  printk("ScsiStatus = 0x%X\n", ScsiStatus);
+#endif	
+
+  // First, check FCP status
+  if( pFcpStatus->fcp_status & FCP_RSP_LEN_VALID )
+  {
+    // check response code (RSP_CODE) -- most popular is bad len
+    // 1st 4 bytes of rsp info -- only byte 3 interesting
+    if( pFcpStatus->fcp_rsp_info & FCP_DATA_LEN_NOT_BURST_LEN )
+    { 
+
+      // do we EVER get here?
+      printk("cpqfcTS: FCP data len not burst len, x_ID %Xh\n", x_ID);
+    }
+  }
+
+  // for now, go by the ScsiStatus, and manipulate certain
+  // commands when necessary...
+  if( ScsiStatus == 0) // SCSI status byte "good"?
+  {
+    Cmnd->result = 0; // everything's OK
+
+    if( (Cmnd->cmnd[0] == INQUIRY)) 
+    {
+      UCHAR *InquiryData = Cmnd->request_buffer;
+      PFC_LOGGEDIN_PORT pLoggedInPort;
+
+      // We need to manipulate INQUIRY
+      // strings for COMPAQ RAID controllers to force
+      // Linux to scan additional LUNs.  Namely, set
+      // the Inquiry string byte 2 (ANSI-approved version)
+      // to 2.
+
+      if( !memcmp( &InquiryData[8], "COMPAQ", 6 ))
+      {
+        InquiryData[2] = 0x2;  // claim SCSI-2 compliance,
+                               // so multiple LUNs may be scanned.
+                               // (no SCSI-2 problems known in CPQ)
+      }
+        
+      // snoop the Inquiry to detect Disk, Tape, etc. type
+      // (search linked list for the port_id we sent INQUIRY to)
+      pLoggedInPort = fcFindLoggedInPort( fcChip,
+        NULL,     // DON'T search Scsi Nexus (we will set it)
+        Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,        
+        NULL,     // DON'T search linked list for FC WWN
+        NULL);    // DON'T care about end of list
+ 
+      if( pLoggedInPort )
+      {
+        pLoggedInPort->ScsiNexus.InqDeviceType = InquiryData[0];
+      }
+      else
+      {
+	printk("cpqfcTS: can't find LoggedIn FC port %06X for INQUIRY\n",
+          Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+      }
+    }
+  }
+
+
+  // Scsi Status not good -- pass it back to caller 
+
+  else
+  {
+    Cmnd->result = ScsiStatus; // SCSI status byte is 1st
+    
+    // check for valid "sense" data
+
+    if( pFcpStatus->fcp_status & FCP_SNS_LEN_VALID ) 
+    {            // limit Scsi Sense field length!
+      int SenseLen = pFcpStatus->fcp_sns_len >>24; // (BigEndian) lower byte
+      
+      SenseLen = SenseLen > sizeof( Cmnd->sense_buffer) ? 
+        sizeof( Cmnd->sense_buffer) : SenseLen;
+	   
+
+#ifdef FCP_COMPLETION_DBG	    
+      printk("copy sense_buffer %p, len %d, result %Xh\n",
+        Cmnd->sense_buffer, SenseLen, Cmnd->result);
+#endif	  
+
+      // NOTE: There is some dispute over the FCP response
+      // format.  Most FC devices assume that FCP_RSP_INFO
+      // is 8 bytes long, in spite of the fact that FCP_RSP_LEN
+      // is (virtually) always 0 and the field is "invalid".  
+      // Some other devices assume that
+      // the FCP_SNS_INFO begins after FCP_RSP_LEN bytes (i.e. 0)
+      // when the FCP_RSP is invalid (this almost appears to be
+      // one of those "religious" issues).
+      // Consequently, we test the usual position of FCP_SNS_INFO
+      // for 7Xh, since the SCSI sense format says the first
+      // byte ("error code") should be 0x70 or 0x71.  In practice,
+      // we find that every device does in fact have 0x70 or 0x71
+      // in the first byte position, so this test works for all
+      // FC devices.  
+      // (This logic is especially effective for the CPQ/DEC HSG80
+      // & HSG60 controllers).
+
+      if( (pFcpStatus->fcp_sns_info[0] & 0x70) == 0x70 )
+        memcpy( Cmnd->sense_buffer, 
+          &pFcpStatus->fcp_sns_info[0], SenseLen);
+      else
+      {
+        unsigned char *sbPtr = 
+		(unsigned char *)&pFcpStatus->fcp_sns_info[0];
+        sbPtr -= 8;  // back up 8 bytes hoping to find the
+	             // start of the sense buffer
+        memcpy( Cmnd->sense_buffer, sbPtr, SenseLen);
+      }
+
+      // in the special case of Device Reset, tell upper layer
+      // to immediately retry (with SOFT_ERROR status)
+      // look for Sense Key Unit Attention (0x6) with ASC Device
+      // Reset (0x29)
+      //	    printk("SenseLen %d, Key = 0x%X, ASC = 0x%X\n",
+      //		    SenseLen, Cmnd->sense_buffer[2], 
+      //                   Cmnd->sense_buffer[12]);
+      if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+	        (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+      {
+        Cmnd->result |= (DID_SOFT_ERROR << 16); // "Host" status byte 3rd
+      }
+ 
+      // check for SenseKey "HARDWARE ERROR", ASC InternalTargetFailure
+      else if(  ((Cmnd->sense_buffer[2] & 0xF) == 0x4) &&  // "hardware error"
+      	        (Cmnd->sense_buffer[12] == 0x44) ) // Addtl. Sense Code 
+      {
+//        printk("HARDWARE_ERROR, Channel/Target/Lun %d/%d/%d\n",
+//		Cmnd->channel, Cmnd->target, Cmnd->lun);
+      	Cmnd->result |= (DID_ERROR << 16); // "Host" status byte 3rd
+      }
+      
+    }  // (end of sense len valid)
+
+    // there is no sense data to help out Linux's Scsi layers...
+    // We'll just return the Scsi status and hope he will "do the 
+    // right thing"
+    else
+    {
+      // as far as we know, the Scsi status is sufficient
+      Cmnd->result |= (DID_OK << 16); // "Host" status byte 3rd
+    }
+  }
+}
+
+
+
+//PPPPPPPPPPPPPPPPPPPPPPPPP  PAYLOAD  PPPPPPPPP
+// build data PAYLOAD; SCSI FCP_CMND I.U.
+// remember BIG ENDIAN payload - DWord values must be byte-reversed
+// (hence the affinity for byte pointer building).
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd, 
+      UCHAR* payload, ULONG type, ULONG fcp_dl )
+{
+  int i;
+
+  
+  switch( type)
+  {
+		  
+    case SCSI_IWE: 
+    case SCSI_IRE:        
+      // 8 bytes FCP_LUN
+      // Peripheral Device or Volume Set addressing, and LUN mapping
+      // When the FC port was looked up, we copied address mode
+      // and any LUN mask to the scratch pad SCp.phase & .mode
+
+      *payload++ = (UCHAR)Cmnd->SCp.phase;
+
+      // Now, because of "lun masking" 
+      // (aka selective storage presentation),
+      // the contiguous Linux Scsi lun number may not match the
+      // device's lun number, so we may have to "map".  
+      
+      *payload++ = (UCHAR)Cmnd->SCp.have_data_in;
+      
+      // We don't know of anyone in the FC business using these 
+      // extra "levels" of addressing.  In fact, confusion still exists
+      // just using the FIRST level... ;-)
+      
+      *payload++ = 0;  // 2nd level addressing
+      *payload++ = 0;
+      *payload++ = 0;  // 3rd level addressing
+      *payload++ = 0;
+      *payload++ = 0;  // 4th level addressing
+      *payload++ = 0;
+
+      // 4 bytes Control Field FCP_CNTL
+      *payload++ = 0;    // byte 0: (MSB) reserved
+      *payload++ = 0;    // byte 1: task codes
+      *payload++ = 0;    // byte 2: task management flags
+		      // byte 3: (LSB) execution management codes
+		      // bit 0 write, bit 1 read (don't set together)
+      
+      if( fcp_dl != 0 )
+      {
+        if( type == SCSI_IWE )         // WRITE
+          *payload++ = 1;
+        else                           // READ
+          *payload++ = 2;
+      }
+      else
+      {
+	// On some devices, if RD or WR bits are set,
+	// and fcp_dl is 0, they will generate an error on the command.
+	// (i.e., if direction is specified, they insist on a length).
+	*payload++ = 0;                // no data (necessary for CPQ)
+      }
+
+
+      // NOTE: clean this up if/when MAX_COMMAND_SIZE is increased to 16
+      // FCP_CDB allows 16 byte SCSI command descriptor blk;
+      // Linux SCSI CDB array is MAX_COMMAND_SIZE (12 at this time...)
+      for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++)
+	*payload++ = Cmnd->cmnd[i];
+
+      if( Cmnd->cmd_len == 16 )
+      {
+        memcpy( payload, &Cmnd->SCp.buffers_residual, 4);
+      }
+      payload+= (16 - i);  
+
+		      // FCP_DL is largest number of expected data bytes
+		      // per CDB (i.e. read/write command)
+      *payload++ = (UCHAR)(fcp_dl >>24);  // (MSB) 8 bytes data len FCP_DL
+      *payload++ = (UCHAR)(fcp_dl >>16);
+      *payload++ = (UCHAR)(fcp_dl >>8);
+      *payload++ = (UCHAR)fcp_dl;    // (LSB)
+      break;
+
+    case SCSI_TWE:          // need FCP_XFER_RDY
+      *payload++ = 0;     // (4 bytes) DATA_RO (MSB byte 0)
+      *payload++ = 0;
+      *payload++ = 0;
+      *payload++ = 0;     // LSB (byte 3)
+			     // (4 bytes) BURST_LEN
+			     // size of following FCP_DATA payload
+      *payload++ = (UCHAR)(fcp_dl >>24);  // (MSB) 8 bytes data len FCP_DL
+      *payload++ = (UCHAR)(fcp_dl >>16);
+      *payload++ = (UCHAR)(fcp_dl >>8);
+      *payload++ = (UCHAR)fcp_dl;    // (LSB)
+		       // 4 bytes RESERVED
+      *payload++ = 0;
+      *payload++ = 0;
+      *payload++ = 0;
+      *payload++ = 0;
+      break;
+
+    default:
+      break;
+  }
+
+  return 0;
+}
+
diff -urN linux-2.2.18-pre8/drivers/scsi/cpqioctl.c linux/drivers/scsi/cpqioctl.c
--- linux-2.2.18-pre8/drivers/scsi/cpqioctl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/cpqioctl.c	Mon Dec  3 20:25:15 2001
@@ -0,0 +1,76 @@
+// Test program for CPQFCTS ioctl calls
+// build with:
+// gcc -o cpqioctl cpqioctl.c
+// ld -o cpqioctl /lib/crt0.o cpqioctl.o -lc
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include "../../include/scsi/scsi.h"
+#include "cpqfcTSioctl.h"
+
+typedef struct scsi_fctargaddress {
+        unsigned long host_port_id;
+        unsigned char host_wwn[8];
+} Scsi_FCTargAddress;
+
+int main(int argc, char **argv) {
+
+ int fd, i;
+ Scsi_FCTargAddress targ;
+ int uselect=0;
+
+
+
+  if ( argc < 2 ) {
+    printf("usage: cpqioctl <Devfile>\n");
+    exit(1);
+  }
+
+  if ( (fd = open(argv[1], O_RDONLY)) == -1) {
+    perror("open");
+    exit(1);
+  }
+
+  if ( ioctl(fd, SCSI_IOCTL_FC_TARGET_ADDRESS, &targ) ) {
+    perror("ioctl");
+    exit(1);
+  }
+
+
+  printf("portid: %08x. wwn: ", targ.host_port_id);
+
+  for (i=0;i<8;i++) printf(" %02x", targ.host_wwn[i]);
+  printf("\n");
+
+ while( uselect != 27 ) // not ESC key
+  {
+    printf("\n IOCTL \n");
+    printf( "1. Get PCI info\n");
+    printf( "2. Send Passthru\n");
+    printf( " ==> ");
+    scanf("%c", &uselect);
+
+    switch( uselect  )
+    {
+      case '1':
+      {
+        cciss_pci_info_struct pciinfo;
+	
+  	if( ioctl( fd, CCPQFCTS_GETPCIINFO ,&pciinfo ))
+          perror("ioctl");
+	else
+          printf( "\nPCI bus %d, dev_fn %d, board_id %Xh\n",
+            pciinfo.bus, pciinfo.dev_fn, pciinfo.board_id);
+      }
+
+    }
+  }
+
+
+  close(fd);
+  return 0;
+} 
diff -urN linux-2.2.18-pre8/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- linux-2.2.18-pre8/drivers/scsi/hosts.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/scsi/hosts.c	Mon Dec  3 20:25:15 2001
@@ -365,6 +365,11 @@
 #include "imm.h"
 #endif
 
+#ifdef CONFIG_SCSI_CPQFCTS
+#include "cpqfcTS.h"
+#endif 
+
+
 /*
 static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
 */
@@ -641,6 +646,10 @@
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
+#endif
+/* CPQFCTS needs to be last because it can never be the boot device. */
+#ifdef CONFIG_SCSI_CPQFCTS
+   CPQFCTS,
 #endif
 };
 
diff -urN linux-2.2.18-pre8/drivers/sound/Config.in linux/drivers/sound/Config.in
--- linux-2.2.18-pre8/drivers/sound/Config.in	Mon Dec  3 20:24:43 2001
+++ linux/drivers/sound/Config.in	Mon Dec  3 20:25:16 2001
@@ -307,7 +307,7 @@
     dep_tristate 'Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS
     if [ "$CONFIG_SOUND_WAVEARTIST" != "n" ]; then
       hex '  WaveArtist I/O base' CONFIG_WAVEARTIST_BASE 250
-      int '  WaveArtist IRQ' CONFIG_WAVEARTIST_IRQ 28
+      int '  WaveArtist IRQ' CONFIG_WAVEARTIST_IRQ 12
       int '  WaveArtist DMA' CONFIG_WAVEARTIST_DMA 3
       int '  WaveArtist second DMA' CONFIG_WAVEARTIST_DMA2 7
     fi
diff -urN linux-2.2.18-pre8/drivers/sound/waveartist.c linux/drivers/sound/waveartist.c
--- linux-2.2.18-pre8/drivers/sound/waveartist.c	Thu Jan  7 09:24:00 1999
+++ linux/drivers/sound/waveartist.c	Mon Dec  3 20:25:16 2001
@@ -2,13 +2,13 @@
  * drivers/sound/waveartist.c
  *
  * The low level driver for the RWA010 Rockwell Wave Artist
- * codec chip used in the Corel Computer NetWinder.
+ * codec chip used in the Rebel.com NetWinder.
  *
  * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
  */
 
 /*
- * Copyright (C) by Corel Computer 1998
+ * Copyright (C) by Rebel.com 1998-1999
  *
  * RWA010 specs received under NDA from Rockwell
  *
@@ -40,12 +40,20 @@
 #include <linux/delay.h>
 #include <linux/smp.h>
 
+#include <asm/dec21285.h>
 #include <asm/hardware.h>
 
 #include "soundmodule.h"
 #include "sound_config.h"
 #include "waveartist.h"
 
+#ifndef _ISA_DMA
+#define _ISA_DMA(x) (x)
+#endif
+#ifndef _ISA_IRQ
+#define _ISA_IRQ(x) (x)
+#endif
+
 #define	VNC_TIMER_PERIOD (HZ/4)	//check slider 4 times/sec
 
 #define	MIXER_PRIVATE3_RESET	0x53570000
@@ -141,8 +149,288 @@
 
 static int		 nr_waveartist_devs;
 static wavnc_info	 adev_info[MAX_AUDIO_DEV];
+
+static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level);
+
+/*
+ * Rebel.com Netwinder specifics...
+ */
 static struct timer_list vnc_timer;
 
+static void
+vnc_mute(wavnc_info *devc, int mute)
+{
+	extern spinlock_t gpio_lock;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	devc->mute_state = mute;
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+	static signed int old_slider_volume;
+	unsigned long flags;
+	signed int volume = 255;
+
+	*CSR_TIMER1_LOAD = 0x00ffffff;
+
+	save_flags(flags);
+	cli();
+
+	outb(0xFF, 0x201);
+	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+	while (volume && (inb(0x201) & 0x01))
+		volume--;
+
+	*CSR_TIMER1_CNTL = 0;
+
+	restore_flags(flags);
+	
+	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+	volume = 150 - (volume >> 5);
+#else
+	volume = (volume >> 6) - 25;
+#endif
+
+	if (volume < 0)
+		volume = 0;
+
+	if (volume > 100)
+		volume = 100;
+
+	/*
+	 * slider quite often reads +-8, so debounce this random noise
+	 */
+	if ((volume - old_slider_volume) > 7 ||
+	    (old_slider_volume - volume) > 7) {
+		old_slider_volume = volume;
+
+		DEB(printk("Slider volume: %d.\n", old_slider_volume));
+	}
+
+	return old_slider_volume;
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+	signed int slider_volume;
+	unsigned int temp;
+
+	/*
+	 * read the "buttons" state.
+	 *  Bit 4 = handset present,
+	 *  Bit 5 = offhook
+	 */
+	// the state should be "querable" via a private IOCTL call
+	temp = inb(0x201) & 0x30;
+
+	if (!devc->handset_mute_sw &&
+	    (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
+		devc->handset_state = temp;
+		devc->handset_mute_sw = 0;
+
+		vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
+	}
+
+	slider_volume = vnc_volume_slider(devc);
+
+	/*
+	 * If we're using software controlled volume, and
+	 * the slider moves by more than 20%, then we
+	 * switch back to slider controlled volume.
+	 */
+	if (devc->slider_vol > slider_volume) {
+		if (devc->slider_vol - slider_volume > 20)
+			devc->use_slider = 1;
+	} else {
+		if (slider_volume - devc->slider_vol > 20)
+			devc->use_slider = 1;
+	}
+
+	/*
+	 * use only left channel
+	 */
+	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+	if (slider_volume != temp && devc->use_slider) {
+		devc->slider_vol = slider_volume;
+
+		waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
+			slider_volume | slider_volume << 8);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+	int next_timeout;
+
+	if (vnc_slider(adev_info + data))
+		next_timeout = 5;	// mixer reported change
+	else
+		next_timeout = VNC_TIMER_PERIOD;
+
+	mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+	int val, temp;
+
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		/*
+		 * Use this call to override the automatic handset
+		 * behaviour - ignore handset.
+		 *  bit 7 = total control over handset - do not
+		 *          to plug/unplug
+		 *  bit 4 = internal mic
+		 *  bit 0 = mute internal speaker
+		 */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		devc->handset_mute_sw = val & 0x80;
+
+		temp = val & VNC_INTERNAL_SPKR;
+		if (devc->mute_state != temp)
+			vnc_mute(devc, temp);
+
+		devc->handset_state = val & VNC_INTERNAL_MIC;
+		waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
+		return 0;
+	} else if (cmd == SOUND_MIXER_PRIVATE2) {
+		/*
+		 * Use this call to get private settings
+		 */
+
+		val = 0;
+		if (devc->handset_mute_sw)
+		    val |= 0x80;	
+
+		if (devc->mute_state)
+		    val |= VNC_INTERNAL_SPKR;
+		    
+		if (devc->handset_state)
+		    val |= VNC_INTERNAL_MIC;
+
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
+
+		return 0;
+	}
+#if 0
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
+#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
+		int             val;
+
+		val = *(int *) arg;
+
+		printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
+
+		if (val == VNC_SOUND_PAUSE) {
+			wa_sendcmd(0x16);    //PAUSE the ADC
+		} else if (val == VNC_SOUND_RESUME) {
+			wa_sendcmd(0x18);    //RESUME the ADC
+		} else {
+			return -EINVAL;      //invalid parameters...
+		}
+		return 0;
+	}
+
+	if (cmd == SOUND_MIXER_PRIVATE3) {
+		long unsigned   flags;
+		int             mixer_reg[15];	//reg 14 is actually a command: read,write,reset
+
+		int             val;
+		int             i;
+
+		val = *(int *) arg;
+
+		if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
+			return (-EFAULT);
+
+		memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
+
+		if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { 	//reset command??
+			wavnc_mixer_reset(devc);
+			return (0);
+		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) {	//write command??
+//			printk("WaveArtist Mixer: Private write command.\n");
+
+			wa_sendcmd(0x32);	//Pair1 - word 1 and 5
+			wa_sendcmd(mixer_reg[0]);
+			wa_sendcmd(mixer_reg[4]);
+
+			wa_sendcmd(0x32);	//Pair2 - word 2 and 6
+			wa_sendcmd(mixer_reg[1]);
+			wa_sendcmd(mixer_reg[5]);
+
+			wa_sendcmd(0x32);	//Pair3 - word 3 and 7
+			wa_sendcmd(mixer_reg[2]);
+			wa_sendcmd(mixer_reg[6]);
+
+			wa_sendcmd(0x32);	//Pair4 - word 4 and 8
+			wa_sendcmd(mixer_reg[3]);
+			wa_sendcmd(mixer_reg[7]);
+
+			wa_sendcmd(0x32);	//Pair5 - word 9 and 10
+			wa_sendcmd(mixer_reg[8]);
+			wa_sendcmd(mixer_reg[9]);
+
+			wa_sendcmd(0x0031);		//set left and right PCM
+			wa_sendcmd(mixer_reg[0x0A]);
+			wa_sendcmd(mixer_reg[0x0B]);
+
+			wa_sendcmd(0x0131);		//set left and right FM
+			wa_sendcmd(mixer_reg[0x0C]);
+			wa_sendcmd(mixer_reg[0x0D]);
+
+			return 0;
+		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) {	//read command?
+//			printk("WaveArtist Mixer: Private read command.\n");
+
+			//first read all current values...
+			save_flags(flags);
+			cli();
+
+			for (i = 0; i < 14; i++) {
+				wa_sendcmd((i << 8) + 0x30);	// get ready for command nn30H
+
+				while (!(inb(STATR) & CMD_RF)) {
+				};	//wait for response ready...
+
+				mixer_reg[i] = inw(CMDR);
+			}
+			restore_flags(flags);
+
+			if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
+				return (-EFAULT);
+
+			memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
+			return 0;
+		} else
+			return -EINVAL;
+	}
+#endif
+	return -EINVAL;
+}
 
 static inline void
 waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
@@ -368,10 +656,12 @@
 	udelay(20);
 	inw(hw->io_base + CMDR);	// discard second word == 0
 
-	rev[0] = temp >> 8;
-	rev[1] = temp & 255;
-	rev[2] = '\0';
-
+	if (rev)
+	{
+	    rev[0] = temp >> 8;
+	    rev[1] = temp & 255;
+	    rev[2] = '\0';
+	}
 	return temp;
 }
 
@@ -1368,138 +1658,12 @@
 waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
 	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
-#if 0
-	//use this call to override the automatic handset behaviour - ignore handset
-	//bit 0x80 = total control over handset - do not react to plug/unplug
-	//bit 0x10 = 1 == internal mic, otherwise handset mic
-	//bit 0x01 = 1 == mute internal speaker, otherwise unmute
-
-	if (cmd == SOUND_MIXER_PRIVATE1) {
-		int             val, temp;
-
-		val = *(int *) arg;
-
-//		printk("MIXER_PRIVATE1: passed parameter = 0x%X.\n",val);
-		return -EINVAL;		//check if parameter is logical...
-
-		devc->soft_mute_flag = val;
-
-		temp = val & VNC_INTERNAL_SPKR;
-		if (temp != devc->mute_state) {
-//			printk("MIXER_PRIVATE1: mute_mono(0x%X).\n",temp);
-			vnc_mute(devc, temp);
-		}
-
-//		temp = devc->handset_state;
-
-		// do not check if it is not already in
-		// the right setting, since we are
-		// laying about the current state...
-
-//		if ((val & VNC_INTERNAL_MIC) != temp) {
-			devc->handset_state = val & VNC_INTERNAL_MIC;
-//			printk("MIXER_PRIVATE1: mixer_set(0x%X).\n",devc->handset_state);
-			wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
-//			devc->handset_state = temp;
-		}
-		return 0;
-	}
-#if 0
-	if (cmd == SOUND_MIXER_PRIVATE2) {
-#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
-#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
-		int             val;
-
-		val = *(int *) arg;
-
-		printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
-
-		if (val == VNC_SOUND_PAUSE) {
-			wa_sendcmd(0x16);    //PAUSE the ADC
-		} else if (val == VNC_SOUND_RESUME) {
-			wa_sendcmd(0x18);    //RESUME the ADC
-		} else {
-			return -EINVAL;      //invalid parameters...
-		}
-		return 0;
-	}
-#endif
-
-	if (cmd == SOUND_MIXER_PRIVATE3) {
-		long unsigned   flags;
-		int             mixer_reg[15];	//reg 14 is actually a command: read,write,reset
-
-		int             val;
-		int             i;
-
-		val = *(int *) arg;
 
-		if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
-			return (-EFAULT);
-
-		memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
-
-		if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { 	//reset command??
-			wavnc_mixer_reset(devc);
-			return (0);
-		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) {	//write command??
-//			printk("WaveArtist Mixer: Private write command.\n");
-
-			wa_sendcmd(0x32);	//Pair1 - word 1 and 5
-			wa_sendcmd(mixer_reg[0]);
-			wa_sendcmd(mixer_reg[4]);
+	if (cmd == SOUND_MIXER_PRIVATE1 ||
+	    cmd == SOUND_MIXER_PRIVATE2 ||
+	    cmd == SOUND_MIXER_PRIVATE3)
+		return vnc_private_ioctl(dev, cmd, arg);
 
-			wa_sendcmd(0x32);	//Pair2 - word 2 and 6
-			wa_sendcmd(mixer_reg[1]);
-			wa_sendcmd(mixer_reg[5]);
-
-			wa_sendcmd(0x32);	//Pair3 - word 3 and 7
-			wa_sendcmd(mixer_reg[2]);
-			wa_sendcmd(mixer_reg[6]);
-
-			wa_sendcmd(0x32);	//Pair4 - word 4 and 8
-			wa_sendcmd(mixer_reg[3]);
-			wa_sendcmd(mixer_reg[7]);
-
-			wa_sendcmd(0x32);	//Pair5 - word 9 and 10
-			wa_sendcmd(mixer_reg[8]);
-			wa_sendcmd(mixer_reg[9]);
-
-			wa_sendcmd(0x0031);		//set left and right PCM
-			wa_sendcmd(mixer_reg[0x0A]);
-			wa_sendcmd(mixer_reg[0x0B]);
-
-			wa_sendcmd(0x0131);		//set left and right FM
-			wa_sendcmd(mixer_reg[0x0C]);
-			wa_sendcmd(mixer_reg[0x0D]);
-
-			return 0;
-		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) {	//read command?
-//			printk("WaveArtist Mixer: Private read command.\n");
-
-			//first read all current values...
-			save_flags(flags);
-			cli();
-
-			for (i = 0; i < 14; i++) {
-				wa_sendcmd((i << 8) + 0x30);	// get ready for command nn30H
-
-				while (!(inb(STATR) & CMD_RF)) {
-				};	//wait for response ready...
-
-				mixer_reg[i] = inw(CMDR);
-			}
-			restore_flags(flags);
-
-			if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
-				return (-EFAULT);
-
-			memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
-			return 0;
-		} else
-			return -EINVAL;
-	}
-#endif
 	if (((cmd >> 8) & 0xff) == 'M') {
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
 			int val;
@@ -1660,143 +1824,11 @@
 	return -1;
 }
 
-/*
- * Corel Netwinder specifics...
- */
-static void
-vnc_mute(wavnc_info *devc, int mute)
-{
-	extern spinlock_t gpio_lock;
-	unsigned long flags;
-
-	spin_lock_irqsave(&gpio_lock, flags);
-	cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
-	spin_unlock_irqrestore(&gpio_lock, flags);
-
-	devc->mute_state = mute;
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
-	static signed int old_slider_volume;
-	unsigned long flags;
-	signed int volume = 255;
-
-	*CSR_TIMER1_LOAD = 0x00ffffff;
-
-	save_flags(flags);
-	cli();
-
-	outb(0xFF, 0x201);
-	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
-	while (volume && (inb(0x201) & 0x01))
-		volume--;
-
-	*CSR_TIMER1_CNTL = 0;
-
-	restore_flags(flags);
-	
-	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
-
-#ifndef REVERSE
-	volume = 150 - (volume >> 5);
-#else
-	volume = (volume >> 6) - 25;
-#endif
-
-	if (volume < 0)
-		volume = 0;
-
-	if (volume > 100)
-		volume = 100;
-
-	/*
-	 * slider quite often reads +-8, so debounce this random noise
-	 */
-	if ((volume - old_slider_volume) > 7 ||
-	    (old_slider_volume - volume) > 7) {
-		old_slider_volume = volume;
-
-		DEB(printk("Slider volume: %d.\n", old_slider_volume));
-	}
-
-	return old_slider_volume;
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
-	signed int slider_volume;
-	unsigned int temp;
-
-	/*
-	 * read the "buttons" state.
-	 *  Bit 4 = handset present,
-	 *  Bit 5 = offhook
-	 */
-	// the state should be "querable" via a private IOCTL call
-	temp = inb(0x201) & 0x30;
-
-	if (!devc->handset_mute_sw &&
-	    (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
-		devc->handset_state = temp;
-		devc->handset_mute_sw = 0;
-
-		vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
-	}
-
-	slider_volume = vnc_volume_slider(devc);
-
-	/*
-	 * If we're using software controlled volume, and
-	 * the slider moves by more than 20%, then we
-	 * switch back to slider controlled volume.
-	 */
-	if (devc->slider_vol > slider_volume) {
-		if (devc->slider_vol - slider_volume > 20)
-			devc->use_slider = 1;
-	} else {
-		if (slider_volume - devc->slider_vol > 20)
-			devc->use_slider = 1;
-	}
-
-	/*
-	 * use only left channel
-	 */
-	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
-	if (slider_volume != temp && devc->use_slider) {
-		devc->slider_vol = slider_volume;
-
-		waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
-			slider_volume | slider_volume << 8);
-
-		return 1;
-	}
-
-	return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
-	int next_timeout;
-
-	if (vnc_slider(adev_info + data))
-		next_timeout = 5;	// mixer reported change
-	else
-		next_timeout = VNC_TIMER_PERIOD;
-
-	mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
 int
 probe_waveartist(struct address_info *hw_config)
 {
 	wavnc_info *devc = &adev_info[nr_waveartist_devs];
+	int temp;
 
 	if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
 		printk("waveartist: too many audio devices\n");
@@ -1808,14 +1840,39 @@
 		return 0;
 	}
 
-	if (hw_config->irq > 31 || hw_config->irq < 16) {
+	if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) {
 		printk("WaveArtist: Bad IRQ %d\n", hw_config->irq);
 		return 0;
 	}
 
-	if (hw_config->dma != 3) {
+	if (hw_config->dma != _ISA_DMA(3)) {
 		printk("WaveArtist: Bad DMA %d\n", hw_config->dma);
 		return 0;
+	}
+
+	/*
+	 * check if hardware is there... Get version, expect something
+	 * like 'B1' = 0x4231 first check if SB regs are present - if
+	 * so - WA is there as well.  Try to write to the SB CD volume
+	 * mixer reg (and read it back) if OK - the hardware is there.
+	 * If bad rev the first time, wait and retry...
+	 */
+	outb(0x28, 0x224);
+	outb(0x55, 0x225);
+	if (inb(0x225) != 0x55) {
+		printk(KERN_INFO "WaveArtist: probe failed at port 0x%X.\n",
+			hw_config->io_base);
+		return 0;
+	}
+
+	temp = waveartist_getrev(hw_config, 0);
+	if ((temp>>8) != 0x42 || ((temp&0xFF) < 0x30)) {
+		mdelay(200);
+		temp = waveartist_getrev(hw_config, 0);
+		if ((temp>>8) != 0x42 || ((temp&0xFF) < 0x30)) {
+		    	printk(KERN_DEBUG "WaveArtist probe: error reading "
+				"device rev.(0x%04X).\n", temp);
+		}
 	}
 
 	hw_config->name = "WaveArtist";
diff -urN linux-2.2.18-pre8/drivers/sound/waveartist.h linux/drivers/sound/waveartist.h
--- linux-2.2.18-pre8/drivers/sound/waveartist.h	Thu Jan  7 09:24:00 1999
+++ linux/drivers/sound/waveartist.h	Mon Dec  3 20:25:16 2001
@@ -1,17 +1,12 @@
 
-// def file for Rockwell RWA010 chip set, as installed in Corel NetWinder
+// def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
 
 //registers
-#define	WA_BASE	0
-//x250
-
-#define CMDR	WA_BASE+0
-#define DATR	WA_BASE+2
-
-#define CTLR	WA_BASE+4
-#define	STATR	WA_BASE+5
-
-#define	IRQSTAT	WA_BASE+12
+#define CMDR	0
+#define DATR	2
+#define CTLR	4
+#define	STATR	5
+#define	IRQSTAT	12
 
 //bit defs
 //reg STATR
diff -urN linux-2.2.18-pre8/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux-2.2.18-pre8/drivers/usb/Config.in	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/Config.in	Mon Dec  3 20:25:16 2001
@@ -68,7 +68,7 @@
       dep_tristate '  USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET
       dep_tristate '  USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB
       dep_tristate '  D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV
-      dep_tristate '  Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_SCSI
+#      dep_tristate '  Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_SCSI
       dep_tristate '  USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB
    fi
    if [ "$CONFIG_NET" = "y" ]; then
diff -urN linux-2.2.18-pre8/drivers/usb/dc2xx.c linux/drivers/usb/dc2xx.c
--- linux-2.2.18-pre8/drivers/usb/dc2xx.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/dc2xx.c	Mon Dec  3 20:25:16 2001
@@ -43,6 +43,7 @@
  *	added timeouts to bulk_msg calls.  Minor updates, docs.
  * 03 Nov, 1999 -- update for 2.3.25 kernel API changes.
  * 08 Jan, 2000 .. multiple camera support
+ * 12 Aug, 2000 .. add some real locking, remove an Oops
  *
  * Thanks to:  the folk who've provided USB product IDs, sent in
  * patches, and shared their sucesses!
@@ -113,7 +114,7 @@
 	int			outEP;		/* write endpoint */
 	const struct camera	*info;		/* DC-240, etc */
 	int			subminor;	/* which minor dev #? */
-	int			isActive;	/* I/O taking place? */
+	struct semaphore	sem;		/* locks this struct */
 
 	/* this is non-null iff the device is open */
 	char			*buf;		/* buffer for I/O */
@@ -126,21 +127,25 @@
 /* Support multiple cameras, possibly of different types.  */
 static struct camera_state *minor_data [MAX_CAMERAS];
 
+/* make this an rwlock if contention becomes an issue */
+static DECLARE_MUTEX (state_table_mutex);
 
 static ssize_t camera_read (struct file *file,
 	char *buf, size_t len, loff_t *ppos)
 {
 	struct camera_state	*camera;
 	int			retries;
+	int			retval = 0;
 
 	if (len > MAX_PACKET_SIZE)
 		return -EINVAL;
 
 	camera = (struct camera_state *) file->private_data;
-	if (!camera->dev)
+	down (&camera->sem);
+	if (!camera->dev) {
+		up (&camera->sem);
 		return -ENODEV;
-	if (camera->isActive++)
-		return -EBUSY;
+	}
 
 	/* Big reads are common, for image downloading.  Smaller ones
 	 * are also common (even "directory listing" commands don't
@@ -149,37 +154,33 @@
 	 */
 	for (retries = 0; retries < MAX_READ_RETRY; retries++) {
 		int			count;
-		int			result;
 
 		if (signal_pending (current)) {
-			camera->isActive = 0;
-			return -EINTR;
-		}
-		if (!camera->dev) {
-			camera->isActive = 0;
-			return -ENODEV;
+			retval = -EINTR;
+			break;
 		}
 
-		result = usb_bulk_msg (camera->dev,
+		retval = usb_bulk_msg (camera->dev,
 			  usb_rcvbulkpipe (camera->dev, camera->inEP),
 			  camera->buf, len, &count, HZ*10);
 
-		dbg ("read (%d) - 0x%x %d", len, result, count);
+		dbg ("read (%d) - 0x%x %d", len, retval, count);
 
-		if (!result) {
+		if (!retval) {
 			if (copy_to_user (buf, camera->buf, count))
-				return -EFAULT;
-			camera->isActive = 0;
-			return count;
+				retval = -EFAULT;
+			else
+				retval = count;
+			break;
 		}
-		if (result != USB_ST_TIMEOUT)
+		if (retval != USB_ST_TIMEOUT)
 			break;
 		interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT);
 
 		dbg ("read (%d) - retry", len);
 	}
-	camera->isActive = 0;
-	return -EIO;
+	up (&camera->sem);
+	return retval;
 }
 
 static ssize_t camera_write (struct file *file,
@@ -192,10 +193,11 @@
 		return -EINVAL;
 
 	camera = (struct camera_state *) file->private_data;
-	if (!camera->dev)
+	down (&camera->sem);
+	if (!camera->dev) {
+		up (&camera->sem);
 		return -ENODEV;
-	if (camera->isActive++)
-		return -EBUSY;
+	}
 	
 	/* most writes will be small: simple commands, sometimes with
 	 * parameters.  putting images (like borders) into the camera
@@ -223,18 +225,13 @@
 					bytes_written = -EINTR;
 				goto done;
 			}
-			if (!camera->dev) {
-				if (!bytes_written)
-					bytes_written = -ENODEV;
-				goto done;
-			}
 
 			result = usb_bulk_msg (camera->dev,
 				 usb_sndbulkpipe (camera->dev, camera->outEP),
 				 obuf, thistime, &count, HZ*10);
 
 			if (result)
-				dbg ("write USB err - %x", result);
+				dbg ("write USB err - %d", result);
 
 			if (count) {
 				obuf += count;
@@ -263,47 +260,69 @@
 		buf += copy_size;
 	}
 done:
-	camera->isActive = 0;
+	up (&camera->sem);
 	dbg ("wrote %d", bytes_written); 
 	return bytes_written;
 }
 
 static int camera_open (struct inode *inode, struct file *file)
 {
-	struct camera_state	*camera;
+	struct camera_state	*camera = NULL;
 	int			subminor;
+	int			value = 0;
 
+	down (&state_table_mutex);
 	subminor = MINOR (inode->i_rdev) - USB_CAMERA_MINOR_BASE;
 	if (subminor < 0 || subminor >= MAX_CAMERAS
 			|| !(camera = minor_data [subminor])) {
+		up (&state_table_mutex);
 		return -ENODEV;
 	}
+	down (&camera->sem);
+	up (&state_table_mutex);
+
+	if (camera->buf) {
+		value = -EBUSY;
+		goto done;
+	}
 
 	if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) {
-		return -ENOMEM;
+		value = -ENOMEM;
+		goto done;
 	}
 
-	dbg ("open"); 
+	dbg ("open #%d", subminor); 
 
-	camera->isActive = 0;
 	file->private_data = camera;
-	return 0;
+done:
+	up (&camera->sem);
+	return value;
 }
 
 static int camera_release (struct inode *inode, struct file *file)
 {
-	struct camera_state *camera;
+	struct camera_state	*camera;
+	int			subminor;
 
 	camera = (struct camera_state *) file->private_data;
-	kfree (camera->buf);
+	down (&state_table_mutex);
+	down (&camera->sem);
+
+	if (camera->buf) {
+		kfree (camera->buf);
+		camera->buf = 0;
+	}
+	subminor = camera->subminor;
 
 	/* If camera was unplugged with open file ... */
 	if (!camera->dev) {
-		minor_data [camera->subminor] = NULL;
+		minor_data [subminor] = NULL;
 		kfree (camera);
 	}
+	up (&camera->sem);
+	up (&state_table_mutex);
 
-	dbg ("close"); 
+	dbg ("close #%d", subminor); 
 
 	return 0;
 }
@@ -329,7 +348,7 @@
 	struct usb_interface_descriptor	*interface;
 	struct usb_endpoint_descriptor	*endpoint;
 	int				direction, ep;
-	struct camera_state		*camera;
+	struct camera_state		*camera = NULL;
 
 	/* Is it a supported camera? */
 	for (i = 0; i < sizeof (cameras) / sizeof (struct camera); i++) {
@@ -364,27 +383,30 @@
 
 
 	/* select "subminor" number (part of a minor number) */
+	down (&state_table_mutex);
 	for (i = 0; i < MAX_CAMERAS; i++) {
 		if (!minor_data [i])
 			break;
 	}
 	if (i >= MAX_CAMERAS) {
 		info ("Ignoring additional USB Camera");
-		return NULL;
+		up (&state_table_mutex);
+		goto bye;
 	}
 
 	/* allocate & init camera state */
 	camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL);
 	if (!camera) {
 		err ("no memory!");
-		return NULL;
+		up (&state_table_mutex);
+		goto bye;
 	}
+
+	init_MUTEX (&camera->sem);
 	camera->info = camera_info;
 	camera->subminor = i;
-	camera->isActive = 0;
 	camera->buf = NULL;
 	init_waitqueue_head (&camera->wait);
-	info ("USB Camera #%d connected", camera->subminor);
 
 
 	/* get input and output endpoints (either order) */
@@ -413,19 +435,19 @@
 		goto error;
 	}
 
-
-	if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) {
-		err ("Failed usb_set_configuration");
-		goto error;
-	}
+	info ("USB Camera #%d connected", camera->subminor);
 
 	camera->dev = dev;
-	return camera;
+	usb_inc_dev_use (dev);
+	goto bye;
 
 error:
 	minor_data [camera->subminor] = NULL;
 	kfree (camera);
-	return NULL;
+	camera = NULL;
+bye:
+	up (&state_table_mutex);
+	return camera;
 }
 
 static void camera_disconnect(struct usb_device *dev, void *ptr)
@@ -433,6 +455,9 @@
 	struct camera_state	*camera = (struct camera_state *) ptr;
 	int			subminor = camera->subminor;
 
+	down (&state_table_mutex);
+	down (&camera->sem);
+
 	/* If camera's not opened, we can clean up right away.
 	 * Else apps see a disconnect on next I/O; the release cleans.
 	 */
@@ -443,6 +468,10 @@
 		camera->dev = NULL;
 
 	info ("USB Camera #%d disconnected", subminor);
+	usb_dec_dev_use (dev);
+
+	up (&camera->sem);
+	up (&state_table_mutex);
 }
 
 static /* const */ struct usb_driver camera_driver = {
diff -urN linux-2.2.18-pre8/drivers/usb/hub.c linux/drivers/usb/hub.c
--- linux-2.2.18-pre8/drivers/usb/hub.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/hub.c	Mon Dec  3 20:25:18 2001
@@ -588,6 +588,8 @@
 	 * so get rid of all our resources
 	 */
 	exit_files(current);  /* daemonize doesn't do exit_files */
+        current->files = init_task.files;
+        atomic_inc(&current->files->count);
 	daemonize();
 
 	/* Setup a nice name */
diff -urN linux-2.2.18-pre8/drivers/usb/mdc800.c linux/drivers/usb/mdc800.c
--- linux-2.2.18-pre8/drivers/usb/mdc800.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/mdc800.c	Mon Dec  3 20:25:18 2001
@@ -76,6 +76,7 @@
 #include <linux/init.h>
 #include <linux/malloc.h>
 #include <linux/module.h>
+#include <linux/smp_lock.h>
 
 #include <linux/usb.h>
 
@@ -582,6 +583,7 @@
 	int retval=0;
 	dbg ("Mustek MDC800 device closed.");
 
+	lock_kernel();
 	if (mdc800->open && (mdc800->state != NOT_CONNECTED))
 	{
 		mdc800->open=0;
@@ -593,6 +595,7 @@
 	{
 		retval=-EIO;
 	}
+	unlock_kernel();
 
 	return retval;
 }
diff -urN linux-2.2.18-pre8/drivers/usb/printer.c linux/drivers/usb/printer.c
--- linux-2.2.18-pre8/drivers/usb/printer.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/printer.c	Mon Dec  3 20:25:19 2001
@@ -1,10 +1,10 @@
 /*
- * printer.c  Version 0.5
+ * printer.c  Version 0.6
  *
  * Copyright (c) 1999 Michael Gee	<michael@linuxspecific.com>
  * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
- * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
  * Copyright (c) 2000 Randy Dunlap	<randy.dunlap@intel.com>
+ * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
  *
  * USB Printer Device Class driver for USB printers and printer cables
  *
@@ -16,6 +16,7 @@
  *	v0.3 - cleaner again, waitqueue fixes
  *	v0.4 - fixes in unidirectional mode
  *	v0.5 - add DEVICE_ID string support
+ *	v0.6 - never time out
  */
 
 /*
@@ -37,12 +38,13 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp_lock.h>
 #include <linux/signal.h>
 #include <linux/poll.h>
 #include <linux/init.h>
 #include <linux/malloc.h>
 #include <linux/lp.h>
-//#define DEBUG
+#undef DEBUG
 #include <linux/usb.h>
 
 #define USBLP_BUF_SIZE		8192
@@ -70,7 +72,7 @@
 #define USBLP_MINORS		16
 #define USBLP_MINOR_BASE	0
 
-#define USBLP_WRITE_TIMEOUT	(60*HZ)			/* 60 seconds */
+#define USBLP_WRITE_TIMEOUT	(5*HZ)			/* 5 seconds */
 
 struct usblp {
 	struct usb_device 	*dev;			/* USB device */
@@ -82,10 +84,10 @@
 	unsigned char		used;			/* True if open */
 	unsigned char		bidir;			/* interface is bidirectional */
 	unsigned char		*device_id_string;	/* IEEE 1284 DEVICE ID string (ptr) */
-					/* first 2 bytes are (big-endian) length */
+							/* first 2 bytes are (big-endian) length */
 };
 
-static struct usblp *usblp_table[USBLP_MINORS] = { NULL, /* ... */ };
+static struct usblp *usblp_table[USBLP_MINORS];
 
 /*
  * Functions for usblp control messages.
@@ -120,7 +122,8 @@
 		return;
 
 	if (urb->status)
-		warn("nonzero read/write bulk status received: %d", urb->status);
+		warn("usblp%d: nonzero read/write bulk status received: %d",
+			usblp->minor, urb->status);
 
 	wake_up_interruptible(&usblp->wait);
 }
@@ -129,31 +132,27 @@
  * Get and print printer errors.
  */
 
-static int usblp_check_status(struct usblp *usblp)
+static char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" };
+
+static int usblp_check_status(struct usblp *usblp, int err)
 {
-	unsigned char status;
+	unsigned char status, newerr = 0;
 
 	if (usblp_read_status(usblp, &status)) {
-		err("failed reading usblp status");
-		return -EIO;
+		err("usblp%d: failed reading printer status", usblp->minor);
+		return 0;
 	}
 
 	if (~status & LP_PERRORP) {
-		if (status & LP_POUTPA) {
-			info("usblp%d: out of paper", usblp->minor);
-			return -ENOSPC;
-		}
-		if (~status & LP_PSELECD) {
-			info("usblp%d: off-line", usblp->minor);
-			return -EIO;
-		}
-		if (~status & LP_PERRORP) {
-			info("usblp%d: on fire", usblp->minor);
-			return -EIO;
-		}
+		newerr = 3;
+		if (status & LP_POUTPA) newerr = 1;
+		if (~status & LP_PSELECD) newerr = 2;
 	}
 
-	return 0;
+	if (newerr != err)
+		info("usblp%d: %s", usblp->minor, usblp_messages[newerr]);
+
+	return newerr;
 }
 
 /*
@@ -169,16 +168,20 @@
 	if (minor < 0 || minor >= USBLP_MINORS)
 		return -ENODEV;
 
+	lock_kernel();
 	usblp  = usblp_table[minor];
 
+	retval = -ENODEV;
 	if (!usblp || !usblp->dev)
-		return -ENODEV;
+		goto out;
 
+	retval = -EBUSY;
 	if (usblp->used)
-		return -EBUSY;
+		goto out;
 
-	if ((retval = usblp_check_status(usblp))) {
-		return retval;
+	if ((retval = usblp_check_status(usblp, 0))) {
+		retval = retval > 1 ? -EIO : -ENOSPC;
+		goto out;
 	}
 
 	usblp->used = 1;
@@ -191,8 +194,9 @@
 		usblp->readcount = 0;
 		usb_submit_urb(&usblp->readurb);
 	}
-
-	return 0;
+out:
+	unlock_kernel();
+	return retval;
 }
 
 static int usblp_release(struct inode *inode, struct file *file)
@@ -226,27 +230,30 @@
 
 static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-	int	length;
 	struct usblp *usblp = file->private_data;
+	int length;
 
 	if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ))
 		return -EINVAL;
 
 	switch (_IOC_NR(cmd)) {
-	case IOCNR_GET_DEVICE_ID:		/* get the DEVICE_ID string */
-		length =  (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
-#if 0
-		dbg ("usblp_ioctl GET_DEVICE_ID: actlen=%d, user size=%d, string='%s'",
-			length, _IOC_SIZE(cmd), &usblp->device_id_string[2]);
-#endif
-		if (length > _IOC_SIZE(cmd))
-			length = _IOC_SIZE(cmd);	/* truncate */
-		if (copy_to_user ((unsigned char *)arg, usblp->device_id_string, (unsigned long) length))
-		    	return -EFAULT;
-		break;
 
-	default:
-		return -EINVAL;
+		case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+
+			length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
+
+			dbg ("usblp_ioctl GET_DEVICE_ID actlen: %d, size: %d, string: '%s'",
+				length, _IOC_SIZE(cmd), &usblp->device_id_string[2]);
+
+			if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */
+
+			if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length))
+				return -EFAULT;
+
+			break;
+
+		default:
+			return -EINVAL;
 	}
 
 	return 0;
@@ -255,7 +262,7 @@
 static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct usblp *usblp = file->private_data;
-	int retval, timeout, writecount = 0;
+	int timeout, err = 0, writecount = 0;
 
 	while (writecount < count) {
 
@@ -274,28 +281,17 @@
 			}
 		}
 
-		if (usblp->writeurb.status == -EINPROGRESS) {
-			usb_unlink_urb(&usblp->writeurb);
-			err("usblp%d: timed out", usblp->minor);
-			return -EIO;
-		}
-
 		if (!usblp->dev)
 			return -ENODEV;
 
-		if (!usblp->writeurb.status) {
-			writecount += usblp->writeurb.transfer_buffer_length;
-			usblp->writeurb.transfer_buffer_length = 0;
-		} else {
-			if (!(retval = usblp_check_status(usblp))) {
-				err("usblp%d: error %d writing to printer (retval=%d)",
-					usblp->minor, usblp->writeurb.status, retval);
-				return -EIO;
-			}
-
-			return retval;
+		if (usblp->writeurb.status) {
+			err = usblp_check_status(usblp, err);
+			continue;
 		}
 
+		writecount += usblp->writeurb.transfer_buffer_length;
+		usblp->writeurb.transfer_buffer_length = 0;
+
 		if (writecount == count)
 			continue;
 
@@ -366,14 +362,14 @@
 		interface = &dev->actconfig->interface[ifnum].altsetting[i];
 
 		if (interface->bInterfaceClass != 7 || interface->bInterfaceSubClass != 1 ||
-		   (interface->bInterfaceProtocol != 1 && interface->bInterfaceProtocol != 2) ||
-		   (interface->bInterfaceProtocol > interface->bNumEndpoints))
+		    interface->bInterfaceProtocol < 1 || interface->bInterfaceProtocol > 3 ||
+		   (interface->bInterfaceProtocol > 1 && interface->bNumEndpoints < 2))
 			continue;
 
 		if (alts == -1)
 			alts = i;
 
-		if (!bidir && interface->bInterfaceProtocol == 2) {
+		if (!bidir && interface->bInterfaceProtocol > 1) {
 			bidir = 1;
 			alts = i;
 		}
@@ -524,3 +520,6 @@
 
 module_init(usblp_init);
 module_exit(usblp_exit);
+
+MODULE_AUTHOR("Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap");
+MODULE_DESCRIPTION("USB Printer Device Class driver");
diff -urN linux-2.2.18-pre8/drivers/usb/rio500.c linux/drivers/usb/rio500.c
--- linux-2.2.18-pre8/drivers/usb/rio500.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/rio500.c	Mon Dec  3 20:25:19 2001
@@ -113,15 +113,15 @@
 		data = (void *) arg;
 		if (data == NULL)
 			break;
-		copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand),
-				   -EFAULT);
+		if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand)))
+			return -EFAULT;
 		if (rio_cmd.length > PAGE_SIZE)
 			return -EINVAL;
 		buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
 		if (buffer == NULL)
 			return -ENOMEM;
-		copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length,
-				   -EFAULT);
+		if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length))
+			return -EFAULT;
 
 		requesttype = rio_cmd.requesttype | USB_DIR_IN |
 		    USB_TYPE_VENDOR | USB_RECIP_DEVICE;
@@ -150,8 +150,9 @@
 				dbg("Executed ioctl. Result = %d (data=%04x)",
 				     le32_to_cpu(result),
 				     le32_to_cpu(*((long *) buffer)));
-				copy_to_user_ret(rio_cmd.buffer, buffer,
-						 rio_cmd.length, -EFAULT);
+				if (copy_to_user(rio_cmd.buffer, buffer,
+						 rio_cmd.length))
+					return -EFAULT;
 				retries = 0;
 			}
 
@@ -170,15 +171,15 @@
 		data = (void *) arg;
 		if (data == NULL)
 			break;
-		copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand),
-				   -EFAULT);
+		if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand)))
+			return -EFAULT;
 		if (rio_cmd.length > PAGE_SIZE)
 			return -EINVAL;
 		buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
 		if (buffer == NULL)
 			return -ENOMEM;
-		copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length,
-				   -EFAULT);
+		if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length))
+			return -EFAULT;
 
 		requesttype = rio_cmd.requesttype | USB_DIR_OUT |
 		    USB_TYPE_VENDOR | USB_RECIP_DEVICE;
@@ -445,3 +446,5 @@
 module_init(usb_rio_init);
 module_exit(usb_rio_cleanup);
 
+MODULE_AUTHOR("Cesar Miquel <miquel@df.uba.ar>");
+MODULE_DESCRIPTION("USB Rio 500 driver");
diff -urN linux-2.2.18-pre8/drivers/usb/usbkbd.c linux/drivers/usb/usbkbd.c
--- linux-2.2.18-pre8/drivers/usb/usbkbd.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/usbkbd.c	Mon Dec  3 20:25:20 2001
@@ -1,5 +1,5 @@
 /*
- * $Id: usbkbd.c,v 1.11 2000/05/29 09:01:52 vojtech Exp $
+ * $Id: usbkbd.c,v 1.16 2000/08/14 21:05:26 vojtech Exp $
  *
  *  Copyright (c) 1999-2000 Vojtech Pavlik
  *
@@ -36,6 +36,7 @@
 #include <linux/usb.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("USB HID Boot Protocol keyboard driver");
 
 static unsigned char usb_kbd_keycode[256] = {
 	  0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
@@ -46,8 +47,8 @@
 	105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
 	 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
 	120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
-	115,114,  0,  0,  0,  0,  0,124,  0,  0,  0,  0,  0,  0,  0,  0,
-	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	115,114,  0,  0,  0,124,  0,181,182,183,184,185,186,187,188,189,
+	190,191,192,193,194,195,196,197,198,  0,  0,  0,  0,  0,  0,  0,
 	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
@@ -62,7 +63,7 @@
 	unsigned char old[8];
 	struct urb irq, led;
 	devrequest dr;
-	unsigned char leds;
+	unsigned char leds, newleds;
 	char name[128];
 	int open;
 };
@@ -103,28 +104,37 @@
 
 	if (type != EV_LED) return -1;
 
-	if (kbd->led.status == -EINPROGRESS) {
-		warn("had to kill led urb");
-		usb_unlink_urb(&kbd->led);
-	}
 
-	kbd->leds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
-		    (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |
-		    (!!test_bit(LED_NUML,    dev->led));
+	kbd->newleds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
+		       (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |
+		       (!!test_bit(LED_NUML,    dev->led));
 
+	if (kbd->led.status == -EINPROGRESS)
+		return 0;
 
-	if (usb_submit_urb(&kbd->led)) {
+	if (kbd->leds == kbd->newleds)
+		return 0;
+
+	kbd->leds = kbd->newleds;
+	if (usb_submit_urb(&kbd->led))
 		err("usb_submit_urb(leds) failed");
-		return -1;
-	}
 
 	return 0;
 }
 
 static void usb_kbd_led(struct urb *urb)
 {
+	struct usb_kbd *kbd = urb->context;
+
 	if (urb->status)
 		warn("led urb status %d received", urb->status);
+	
+	if (kbd->leds == kbd->newleds)
+		return;
+
+	kbd->leds = kbd->newleds;
+	if (usb_submit_urb(&kbd->led))
+		err("usb_submit_urb(leds) failed");
 }
 
 static int usb_kbd_open(struct input_dev *dev)
diff -urN linux-2.2.18-pre8/drivers/usb/usbmouse.c linux/drivers/usb/usbmouse.c
--- linux-2.2.18-pre8/drivers/usb/usbmouse.c	Mon Dec  3 20:24:43 2001
+++ linux/drivers/usb/usbmouse.c	Mon Dec  3 20:25:20 2001
@@ -36,6 +36,7 @@
 #include <linux/usb.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("USB HID Boot Protocol mouse driver");
 
 struct usb_mouse {
 	signed char data[8];
diff -urN linux-2.2.18-pre8/drivers/video/Makefile linux/drivers/video/Makefile
--- linux-2.2.18-pre8/drivers/video/Makefile	Mon Sep  4 10:39:22 2000
+++ linux/drivers/video/Makefile	Mon Dec  3 20:25:20 2001
@@ -137,10 +137,10 @@
 endif
 
 ifeq ($(CONFIG_FB_CYBER2000),y)
-L_OBJS += cyber2000fb.o
+LX_OBJS += cyber2000fb.o
 else
   ifeq ($(CONFIG_FB_CYBER2000),m)
-  M_OBJS += cyber2000fb.o
+  MX_OBJS += cyber2000fb.o
   endif
 endif
 
diff -urN linux-2.2.18-pre8/drivers/video/acornfb.c linux/drivers/video/acornfb.c
--- linux-2.2.18-pre8/drivers/video/acornfb.c	Tue May 11 16:30:36 1999
+++ linux/drivers/video/acornfb.c	Mon Dec  3 20:25:20 2001
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/video/acorn.c
+ * linux/drivers/video/acornfb.c
  *
- * Copyright (C) 1998,1999 Russell King
+ * Copyright (C) 1998-2000 Russell King
  *
  * Frame buffer code for Acorn platforms
  *
@@ -34,6 +34,7 @@
 #include <video/fbcon-cfb4.h>
 #include <video/fbcon-cfb8.h>
 #include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb32.h>
 
 /*
  * Default resolution.
@@ -42,6 +43,25 @@
  */
 #define DEFAULT_XRES	640
 #define DEFAULT_YRES	480
+/*
+ * The order here defines which BPP we
+ * pick depending on which resolutions
+ * we have configured.
+ */
+#if   defined(FBCON_HAS_CFB4)
+# define DEFAULT_BPP	4
+#elif defined(FBCON_HAS_CFB8)
+# define DEFAULT_BPP	8
+#elif defined(FBCON_HAS_CFB16)
+# define DEFAULT_BPP	16
+#elif defined(FBCON_HAS_CFB2)
+# define DEFAULT_BPP	2
+#elif defined(FBCON_HAS_MFB)
+# define DEFAULT_BPP	1
+#else
+#error No suitable framebuffers configured
+#endif
+
 
 /*
  * define this to debug the video mode selection
@@ -94,7 +114,6 @@
 	unsigned int	palette_size;
 	  signed int	montype;
 	  signed int	currcon;
-	unsigned int	allow_modeset	: 1;
 	unsigned int	using_vram	: 1;
 	unsigned int	dpms		: 1;
 
@@ -106,14 +125,37 @@
 	} cmap;
 };
 
+struct vidc_timing {
+	u_int	h_cycle;
+	u_int	h_sync_width;
+	u_int	h_border_start;
+	u_int	h_display_start;
+	u_int	h_display_end;
+	u_int	h_border_end;
+	u_int	h_interlace;
+
+	u_int	v_cycle;
+	u_int	v_sync_width;
+	u_int	v_border_start;
+	u_int	v_display_start;
+	u_int	v_display_end;
+	u_int	v_border_end;
+
+	u_int	control;
+
+	/* VIDC20 only */
+	u_int	pll_ctl;
+};
+
 /*
  * Translation from RISC OS monitor types to actual
  * HSYNC and VSYNC frequency ranges.  These are
- * probably not right...
+ * probably not right, but they're the best info I
+ * have.  Allow 1% either way on the nominal for TVs.
  */
 #define NR_MONTYPES	6
 static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = {
-	{ 15625, 15625, 50, 50, 0 },	/* TV		*/
+	{ 15469, 15781, 49, 51, 0 },	/* TV		*/
 	{     0, 99999,  0, 99, 0 },	/* Multi Freq	*/
 	{ 58608, 58608, 64, 64, 0 },	/* Hi-res mono	*/
 	{ 30000, 70000, 60, 60, 0 },	/* VGA		*/
@@ -124,34 +166,12 @@
 static struct display global_disp;
 static struct fb_info fb_info;
 static struct acornfb_par current_par;
+static struct vidc_timing current_vidc;
 static struct fb_var_screeninfo __initdata init_var = {};
 
 extern int acornfb_depth;	/* set by setup.c */
 extern unsigned int vram_size;	/* set by setup.c */
 
-
-static struct vidc_timing {
-	u_int	h_cycle;
-	u_int	h_sync_width;
-	u_int	h_border_start;
-	u_int	h_display_start;
-	u_int	h_display_end;
-	u_int	h_border_end;
-	u_int	h_interlace;
-
-	u_int	v_cycle;
-	u_int	v_sync_width;
-	u_int	v_border_start;
-	u_int	v_display_start;
-	u_int	v_display_end;
-	u_int	v_border_end;
-
-	u_int	control;
-
-	/* VIDC20 only */
-	u_int	pll_ctl;
-} current_vidc;
-
 #ifdef HAS_VIDC
 
 #define VID_CTL_VS_NVSYNC	(1 << 3)
@@ -254,7 +274,9 @@
 
 	bandwidth = var->pixclock * 8 / var->bits_per_pixel;
 	/* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */
-	if (bandwidth > 71750)
+	if (bandwidth > 143500)
+		vidc_ctl |= VIDC_CTRL_FIFO_3_7;
+	else if (bandwidth > 71750)
 		vidc_ctl |= VIDC_CTRL_FIFO_2_6;
 	else if (bandwidth > 35875)
 		vidc_ctl |= VIDC_CTRL_FIFO_1_5;
@@ -648,15 +670,19 @@
 
 	ext_ctl = VIDC20_ECTL_DAC | VIDC20_ECTL_REG(3);
 
-	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
-		ext_ctl |= VIDC20_ECTL_HS_HSYNC;
-	else
-		ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
+	if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
+		ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
+	else {
+		if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+			ext_ctl |= VIDC20_ECTL_HS_HSYNC;
+		else
+			ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
 
-	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
-		ext_ctl |= VIDC20_ECTL_VS_VSYNC;
-	else
-		ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
+		if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+			ext_ctl |= VIDC20_ECTL_VS_VSYNC;
+		else
+			ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
+	}
 
 	outl(VIDC20_ECTL | ext_ctl, IO_VIDC_BASE);
 
@@ -740,8 +766,8 @@
  * Before selecting the timing parameters, adjust
  * the resolution to fit the rules.
  */
-static void
-acornfb_pre_adjust_timing(struct fb_var_screeninfo *var, int con)
+static int
+acornfb_adjust_timing(struct fb_var_screeninfo *var, int con)
 {
 	u_int font_line_len;
 	u_int fontht;
@@ -783,6 +809,13 @@
 	font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
 	min_size = var->xres * var->yres * var->bits_per_pixel / 8;
 
+	/*
+	 * If minimum screen size is greater than that we have
+	 * available, reject it.
+	 */
+	if (min_size > current_par.screen_size)
+		return -EINVAL;
+
 	/* Find int 'y', such that y * fll == s * sam < maxsize
 	 * y = s * sam / fll; s = maxsize / sam
 	 */
@@ -794,14 +827,16 @@
 			break;
 	}
 
-	if (min_size > size) {
-		/*
-		 * failed, use ypan
-		 */
-		size = current_par.screen_size;
-		var->yres_virtual = size / (font_line_len / fontht);
-	} else
-		var->yres_virtual = nr_y * fontht;
+	if (var->accel_flags & FB_ACCELF_TEXT) {
+		if (min_size > size) {
+			/*
+			 * failed, use ypan
+			 */
+			size = current_par.screen_size;
+			var->yres_virtual = size / (font_line_len / fontht);
+		} else
+			var->yres_virtual = nr_y * fontht;
+	}
 
 	current_par.screen_end = current_par.screen_base_p + size;
 
@@ -818,16 +853,7 @@
 		if (var->yoffset + var->yres > var->yres_virtual)
 			var->yoffset = var->yres_virtual - var->yres;
 	}
-}
 
-/*
- * After selecting the timing parameters, adjust
- * the timing to suit the chip.
- * NOTE! Only minor adjustments should be made here.
- */
-static void
-acornfb_post_adjust_timing(struct fb_var_screeninfo *var)
-{
 	/* hsync_len must be even */
 	var->hsync_len = (var->hsync_len + 1) & ~1;
 
@@ -854,6 +880,31 @@
 
 	if (var->vsync_len < 1)
 		var->vsync_len = 1;
+
+	return 0;
+}
+
+static int
+acornfb_validate_timing(struct fb_var_screeninfo *var,
+			struct fb_monspecs *monspecs)
+{
+	unsigned long hs, vs;
+
+	/*
+	 * hs(Hz) = 10^12 / (pixclock * xtotal)
+	 * vs(Hz) = hs(Hz) / ytotal
+	 *
+	 * No need to do long long divisions or anything
+	 * like that if you factor it correctly
+	 */
+	hs = 1953125000 / var->pixclock;
+	hs = hs * 512 /
+	     (var->xres + var->left_margin + var->right_margin + var->hsync_len);
+	vs = hs /
+	     (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
+
+	return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
+		hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
 }
 
 static inline void
@@ -895,32 +946,75 @@
 	return 0;
 }
 
+/*
+ * We have to take note of the VIDC20's 16-bit palette here.
+ * The VIDC20 looks up a 16 bit pixel as follows:
+ *
+ *   bits   111111
+ *          5432109876543210
+ *   red            ++++++++  (8 bits,  7 to 0)
+ *  green       ++++++++      (8 bits, 11 to 4)
+ *   blue   ++++++++          (8 bits, 15 to 8)
+ *
+ * We use a pixel which looks like:
+ *
+ *   bits   111111
+ *          5432109876543210
+ *   red               +++++  (5 bits,  4 to  0)
+ *  green         +++++       (5 bits,  9 to  5)
+ *   blue    +++++            (5 bits, 14 to 10)
+ */
 static int
 acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 		  u_int trans, struct fb_info *info)
 {
 	union palette pal;
+	int bpp = fb_display[current_par.currcon].var.bits_per_pixel;
 
 	if (regno >= current_par.palette_size)
 		return 1;
 
 	pal = acornfb_palette_encode(regno, red, green, blue, trans);
-	acornfb_palette_write(regno, pal);
 	current_par.palette[regno] = pal;
 
+#ifdef HAS_VIDC20
 	if (regno < 16) {
-		switch (info->disp->var.bits_per_pixel) {
+		switch (bpp) {
 #ifdef FBCON_HAS_CFB16
-		case 16:	/* RGB555 */
-			current_par.cmap.cfb16[regno] = (regno << 10) | (regno << 5) | regno;
+		case 16:
+			current_par.cmap.cfb16[regno] =
+				regno | regno << 5 | regno << 10;
+			break;
+#endif
+#ifdef FBCON_HAS_CFB32
+		case 32:
+			current_par.cmap.cfb32[regno] =
+				regno | regno << 8 | regno << 16;
 			break;
 #endif
-
 		default:
 			break;
 		}
 	}
 
+#ifdef FBCON_HAS_CFB16
+	if (bpp == 16) {
+		int i;
+
+		pal.p = 0;
+		outl(0x10000000, IO_VIDC_BASE);
+		for (i = 0; i < 256; i += 1) {
+			pal.vidc20.red   = current_par.palette[ i       & 31].vidc20.red;
+			pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
+			pal.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
+			outl(pal.p, IO_VIDC_BASE);
+			/* Palette register pointer auto-increments */
+		}
+	} else
+#endif
+#endif
+		acornfb_palette_write(regno, pal);
+
 	return 0;
 }
 
@@ -963,6 +1057,8 @@
 static int
 acornfb_decode_var(struct fb_var_screeninfo *var, int con, int *visual)
 {
+	int err;
+
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_MFB
 	case 1:
@@ -988,10 +1084,18 @@
 		*visual = FB_VISUAL_PSEUDOCOLOR;
 		break;
 #endif
+#ifdef HAS_VIDC20
+#ifdef FBCON_HAS_CFB16
 	case 16:
-	case 24:
+		*visual = FB_VISUAL_DIRECTCOLOR;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
 	case 32:
 		*visual = FB_VISUAL_TRUECOLOR;
+		break;
+#endif
+#endif
 	default:
 		return -EINVAL;
 	}
@@ -1000,25 +1104,60 @@
 		return -EINVAL;
 
 	/*
-	 * Adjust the resolution before using it.
+	 * Validate and adjust the resolution to
+	 * match the video generator hardware.
+	 */
+	err = acornfb_adjust_timing(var, con);
+	if (err)
+		return err;
+
+	/*
+	 * Validate the timing against the
+	 * monitor hardware.
 	 */
-	acornfb_pre_adjust_timing(var, con);
+	err = acornfb_validate_timing(var, &fb_info.monspecs);
+	if (err)
+		return err;
 
 #if defined(HAS_VIDC20)
-	var->red.length	   = 8;
-	var->transp.length = 4;
+	switch (var->bits_per_pixel) {
+	case 1: case 2: case 4: case 8:
+		var->red.offset    = 0;
+		var->red.length    = 8;
+		var->green         = var->red;
+		var->blue          = var->red;
+		var->transp.offset = 0;
+		var->transp.length = 4;
+		break;
+
+	case 16:
+		var->red.offset    = 0;
+		var->red.length    = 5;
+		var->green.offset  = 5;
+		var->green.length  = 5;
+		var->blue.offset   = 10;
+		var->blue.length   = 5;
+		var->transp.offset = 15;
+		var->transp.length = 1;
+		break;
+
+	case 32:
+		var->red.offset    = 0;
+		var->red.length    = 8;
+		var->green.offset  = 8;
+		var->green.length  = 8;
+		var->blue.offset   = 16;
+		var->blue.length   = 8;
+		var->transp.offset = 24;
+		var->transp.length = 4;
+		break;
+	}
 #elif defined(HAS_VIDC)
 	var->red.length	   = 4;
+	var->green         = var->red;
+	var->blue          = var->red;
 	var->transp.length = 1;
 #endif
-	var->green = var->red;
-	var->blue  = var->red;
-
-	/*
-	 * Now adjust the timing parameters
-	 */
-	acornfb_post_adjust_timing(var);
-
 	return 0;
 }
 
@@ -1071,9 +1210,6 @@
 	else
 		display = &global_disp;
 
-	if (!current_par.allow_modeset && con != -1)
-		return -EINVAL;
-
 	err = acornfb_decode_var(var, con, &visual);
 	if (err)
 		return err;
@@ -1151,11 +1287,18 @@
 #endif
 #ifdef FBCON_HAS_CFB16
 	case 16:
-		current_par.palette_size = VIDC_PALETTE_SIZE;
+		current_par.palette_size = 32;
 		display->dispsw = &fbcon_cfb16;
 		display->dispsw_data = current_par.cmap.cfb16;
 		break;
 #endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		current_par.palette_size = VIDC_PALETTE_SIZE;
+		display->dispsw = &fbcon_cfb32;
+		display->dispsw_data = current_par.cmap.cfb32;
+		break;
+#endif
 	default:
 		display->dispsw = &fbcon_dummy;
 		break;
@@ -1194,9 +1337,7 @@
 		outl(control, IOMD_VIDCR);
 #endif
 		acornfb_update_dma(var);
-
-		if (current_par.allow_modeset)
-			acornfb_set_timing(var);
+		acornfb_set_timing(var);
 
 		if (display->cmap.len)
 			cmap = &display->cmap;
@@ -1288,236 +1429,154 @@
 static void
 acornfb_blank(int blank, struct fb_info *info)
 {
-	int i;
+	union palette p;
+	int i, bpp = fb_display[current_par.currcon].var.bits_per_pixel;
 
-	if (blank)
+	if (bpp != 16) {
 		for (i = 0; i < current_par.palette_size; i++) {
-			union palette p;
-
-			p = acornfb_palette_encode(i, 0, 0, 0, 0);
+			if (blank)
+				p = acornfb_palette_encode(i, 0, 0, 0, 0);
+			else
+				p = current_par.palette[i];
 
 			acornfb_palette_write(i, p);
 		}
-	else
-		for (i = 0; i < current_par.palette_size; i++)
+	}
+#ifdef FBCON_HAS_CFB16
+	else {
+		p.p = 0;
+
+		for (i = 0; i < 256; i++) {
+			if (blank)
+				p = acornfb_palette_encode(i, 0, 0, 0, 0);
+			else {
+				p.vidc20.red   = current_par.palette[ i       & 31].vidc20.red;
+				p.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
+				p.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
+			}
 			acornfb_palette_write(i, current_par.palette[i]);
+		}
+	}
+#endif
 }
 
 /*
  * Everything after here is initialisation!!!
  */
-struct modey_params {
-	u_int	y_res;
-	u_int	u_margin;
-	u_int	b_margin;
-	u_int	vsync_len;
-	u_int	vf;
-};
-
-struct modex_params {
-	u_int	x_res;
-	u_int	l_margin;
-	u_int	r_margin;
-	u_int	hsync_len;
-	u_int	clock;
-	u_int	hf;
-	const struct modey_params *modey;
-};
-
-static const struct modey_params modey_640_15600[] __initdata = {
-	{  250,  38,  21,  3,  50 },	/*  640x 250, 50Hz */
-	{  256,  35,  18,  3,  50 },	/*  640x 256, 50Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_640_26800[] __initdata = {
-	{  512,  18,   1,  3,  50 },	/*  640x 512, 50Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_640_31500[] __initdata = {
-	{  250, 109,  88,  2,  70 },	/*  640x 250, 70Hz */
-	{  256, 106,  85,  2,  70 },	/*  640x 256, 70Hz */
-	{  352,  58,  37,  2,  70 },	/*  640x 352, 70Hz */
-	{  480,  32,  11,  2,  60 },	/*  640x 480, 60Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_800_35200[] __initdata = {
-	{  600,  22,   1,  2,  56 },	/*  800x 600, 56Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_896_21800[] __initdata = {
-	{  352,   9,   0,  3,  60 },	/*  896x 352, 60Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-/* everything after here is not supported */
-static const struct modey_params modey_1024_uk[] __initdata = {
-	{  768,   0,   0,  0,   0 },	/* 1024x 768 */
-	{    0,   0,   0,  0,   0 }
-};
+#define fb_videomode fb_2_3_videomode
 
-static const struct modey_params modey_1056_uk[] __initdata = {
-	{  250,   0,   0,  0,   0 },	/* 1056x 250 */
-	{  256,   0,   0,  0,   0 },	/* 1056x 256 */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_1152_uk[] __initdata = {
-	{  896,   0,   0,  0,   0 },	/* 1152x 896 */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_1280_63600[] __initdata = {
-	{ 1024,   0,   0,  0,  60 },	/* 1280x1024, 60Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_1600_uk[] __initdata = {
-	{ 1280,   0,   0,  0,   0 },	/* 1600x1280 */
-	{    0,   0,   0,  0,   0 }
-};
-
-/*
- * Horizontal video programming requirements.
- * This table is searched for the required horizontal
- * and required frequency, and then the tables above
- * are then searched for the required vertical
- * resolution.
- *
- * NOTE! we can match multiple entries, so we search
- * all horizontal entries for which the hfreq is within
- * the monitor's range.
- */
-static const struct modex_params modex_params[] __initdata = {
-	{						/* X:  640, 15.6kHz */
-		 640,  185, 123,  76, 16000, 15625, modey_640_15600
-	},
-	{						/* X:  640, 26.8kHz */
-		 640,  113,  87,  56, 24000, 26800, modey_640_26800
-	},
-	{						/* X:  640, 31.5kHz */
-		 640,   48,  16,  96, 25175, 31500, modey_640_31500
-	},
-	{						/* X:  800, 35.2kHz */
-		 800,  101,  23, 100, 36000, 35200, modey_800_35200
-	},
-	{						/* X:  896, 21.8kHz */
-		 896,   59,  27, 118, 24000, 21800, modey_896_21800
-	},
-	{						/* X: 1024 */
-		1024,    0,   0,   0,     0,     0, modey_1024_uk
-	},
-	{						/* X: 1056 */
-		1056,    0,   0,   0,     0,     0, modey_1056_uk
-	},
-	{						/* X: 1152 */
-		1152,    0,   0,   0,     0,     0, modey_1152_uk
-	},
-	{						/* X: 1280, 63.6kHz */
-		1280,    0,   0,   0,     0, 63600, modey_1280_63600
-	},
-	{						/* X: 1600 */
-		1600,    0,   0,   0,     0,     0, modey_1600_uk
-	},
-	{
+struct fb_videomode {
+    const char *name;	/* optional */
+    u32 refresh;	/* optional */
+    u32 xres;
+    u32 yres;
+    u32 pixclock;
+    u32 left_margin;
+    u32 right_margin;
+    u32 upper_margin;
+    u32 lower_margin;
+    u32 hsync_len;
+    u32 vsync_len;
+    u32 sync;
+    u32 vmode;
+};
+
+static struct fb_videomode modedb[] __initdata = {
+	{	/* 320x256 @ 50Hz */
+		NULL, 50,  320,  256, 125000,  92,  62,  35, 19,  38, 2,
+		FB_SYNC_COMP_HIGH_ACT,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x250 @ 50Hz, 15.6 kHz hsync */
+		NULL, 50,  640,  250,  62500, 185, 123,  38, 21,  76, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x256 @ 50Hz, 15.6 kHz hsync */
+		NULL, 50,  640,  256,  62500, 185, 123,  35, 18,  76, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x512 @ 50Hz, 26.8 kHz hsync */
+		NULL, 50,  640,  512,  41667, 113,  87,  18,  1,  56, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x250 @ 70Hz, 31.5 kHz hsync */
+		NULL, 70,  640,  250,  39722,  48,  16, 109, 88,  96, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x256 @ 70Hz, 31.5 kHz hsync */
+		NULL, 70,  640,  256,  39722,  48,  16, 106, 85,  96, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x352 @ 70Hz, 31.5 kHz hsync */
+		NULL, 70,  640,  352,  39722,  48,  16,  58, 37,  96, 2,
 		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x480 @ 60Hz, 31.5 kHz hsync */
+		NULL, 60,  640,  480,  39722,  48,  16,  32, 11,  96, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 800x600 @ 56Hz, 35.2 kHz hsync */
+		NULL, 56,  800,  600,  27778, 101,  23,  22,  1, 100, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 896x352 @ 60Hz, 21.8 kHz hsync */
+		NULL, 60,  896,  352,  41667,  59,  27,   9,  0, 118, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 1024x 768 @ 60Hz, 48.4 kHz hsync */
+		NULL, 60, 1024,  768,  15385, 160,  24,  29,  3, 136, 6,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 1280x1024 @ 60Hz, 63.8 kHz hsync */
+		NULL, 60, 1280, 1024,   9090, 186,  96,  38,  1, 160, 3,
+		0,
+		FB_VMODE_NONINTERLACED
 	}
 };
 
-__initfunc(static int
-acornfb_lookup_timing(struct fb_var_screeninfo *var))
-{
-	const struct modex_params *x;
-	const struct modey_params *y;
-
-	/*
-	 * We must adjust the resolution parameters
-	 * before selecting the timing parameters.
-	 */
-	acornfb_pre_adjust_timing(var, -1);
-
-	for (x = modex_params; x->x_res; x++) {
-
-		/*
-		 * Is this resolution one we're looking for?
-		 */
-		if (x->x_res != var->xres)
-			continue;
-
-		/*
-		 * Is the hsync frequency ok for our monitor?
-		 */
-		if (x->hf > fb_info.monspecs.hfmax ||
-		    x->hf < fb_info.monspecs.hfmin)
-			continue;
-
-		/*
-		 * Try to find a vertical resolution
-		 */
-		for (y = x->modey; y->y_res; y++) {
-			/*
-			 * Is this resolution one we're looking for?
-			 */
-			if (y->y_res != var->yres)
-				continue;
-
-			/*
-			 * Is the vsync frequency ok for our monitor?
-			 */
-			if (y->vf > fb_info.monspecs.vfmax ||
-			    y->vf < fb_info.monspecs.vfmin)
-				continue;
-
-			goto found;
-		}
-	}
-
-	var->pixclock = 0;
-
-	return -EINVAL;
-
-found:
-	/*
-	 * Why is pixclock in picoseconds?
-	 */
-	switch (x->clock) {
-	case 36000: var->pixclock =  27778;	break;
-	case 25175: var->pixclock =  39722;	break;
-	case 24000: var->pixclock =  41667;	break;
-	case 16000: var->pixclock =  62500;	break;
-	case 12000: var->pixclock =  83333;	break;
-	case  8000: var->pixclock = 125000;	break;
-	default:    var->pixclock =      0;     break;
-	}
-
-#ifdef DEBUG_MODE_SELECTION
-	printk(KERN_DEBUG "Found %dx%d at %d.%3dkHz, %dHz, pix %d\n",
-		x->x_res, y->y_res,
-		x->hf / 1000, x->hf % 1000,
-		y->vf, var->pixclock);
-#endif
-
-	var->left_margin	= x->l_margin;
-	var->right_margin	= x->r_margin;
-	var->upper_margin	= y->u_margin;
-	var->lower_margin	= y->b_margin;
-	var->hsync_len		= x->hsync_len;
-	var->vsync_len		= y->vsync_len;
-	var->sync		= 0;
-
-	/*
-	 * Now adjust the parameters we found
-	 */
-	acornfb_post_adjust_timing(var);
-
-	return 0;
+static struct fb_videomode __initdata
+acornfb_default_mode = {
+	name:		NULL,
+	refresh:	60,
+	xres:		640,
+	yres:		480,
+	pixclock:	39722,
+	left_margin:	56,
+	right_margin:	16,
+	upper_margin:	34,
+	lower_margin:	9,
+	hsync_len:	88,
+	vsync_len:	2,
+	sync:		0,
+	vmode:		FB_VMODE_NONINTERLACED
+};
+
+static int fb_find_mode(struct fb_var_screeninfo *var, struct fb_info *info,
+			const char *mode_option, const struct fb_videomode *db,
+			unsigned int dbsize, const struct fb_videomode *default_mode,
+			unsigned int default_bpp)
+{
+	var->xres = default_mode->xres;
+	var->yres = default_mode->yres;
+	var->xres_virtual = default_mode->xres;
+	var->yres_virtual = default_mode->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	var->bits_per_pixel = default_bpp;
+	var->activate = 0;
+	var->pixclock = default_mode->pixclock;
+	var->left_margin = default_mode->left_margin;
+	var->right_margin = default_mode->right_margin;
+	var->upper_margin = default_mode->upper_margin;
+	var->lower_margin = default_mode->lower_margin;
+	var->hsync_len = default_mode->hsync_len;
+	var->vsync_len = default_mode->vsync_len;
+	var->sync = default_mode->sync;
+	var->vmode = default_mode->vmode;
+	return 3;
 }
 
-__initfunc(static void
-acornfb_init_fbinfo(void))
+static void __init
+acornfb_init_fbinfo(void)
 {
 	static int first = 1;
 
@@ -1543,20 +1602,6 @@
 	 * setup initial parameters
 	 */
 	memset(&init_var, 0, sizeof(init_var));
-	init_var.xres		   = DEFAULT_XRES;
-	init_var.yres		   = DEFAULT_YRES;
-
-#if   defined(FBCON_HAS_CFB4)
-	init_var.bits_per_pixel	   = 4;
-#elif defined(FBCON_HAS_CFB8)
-	init_var.bits_per_pixel    = 8;
-#elif defined(FBCON_HAS_CFB2)
-	init_var.bits_per_pixel    = 2;
-#elif defined(FBCON_HAS_MFB)
-	init_var.bits_per_pixel    = 1;
-#else
-#error No suitable framebuffers configured
-#endif
 
 #if defined(HAS_VIDC20)
 	init_var.red.length	   = 8;
@@ -1572,6 +1617,7 @@
 	init_var.height		   = -1;
 	init_var.width		   = -1;
 	init_var.vmode		   = FB_VMODE_NONINTERLACED;
+	init_var.accel_flags	   = FB_ACCELF_TEXT;
 
 	current_par.dram_size	   = 0;
 	current_par.montype	   = -1;
@@ -1610,48 +1656,62 @@
  *	size can optionally be followed by 'M' or 'K' for
  *	MB or KB respectively.
  */
-__initfunc(static void
-acornfb_parse_font(char *opt))
+static void __init
+acornfb_parse_font(char *opt)
 {
 	strcpy(fb_info.fontname, opt);
 }
 
-__initfunc(static void
-acornfb_parse_mon(char *opt))
+static void __init
+acornfb_parse_mon(char *opt)
 {
-	fb_info.monspecs.hfmin = simple_strtoul(opt, &opt, 0);
-	if (*opt == '-')
-		fb_info.monspecs.hfmax = simple_strtoul(opt + 1, &opt, 0);
+	char *p = opt;
+
+	current_par.montype = -2;
+
+	fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
+	if (*p == '-')
+		fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
 	else
 		fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto bad;
 
-	fb_info.monspecs.vfmin = simple_strtoul(opt + 1, &opt, 0);
-	if (*opt == '-')
-		fb_info.monspecs.vfmax = simple_strtoul(opt + 1, &opt, 0);
+	fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
+	if (*p == '-')
+		fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
 	else
 		fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto check_values;
 
-	fb_info.monspecs.dpms = simple_strtoul(opt + 1, &opt, 0);
+	fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto check_values;
 
-	init_var.width = simple_strtoul(opt + 1, &opt, 0);
+	init_var.width = simple_strtoul(p + 1, &p, 0);
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto check_values;
 
-	init_var.height = simple_strtoul(opt + 1, NULL, 0);
+	init_var.height = simple_strtoul(p + 1, NULL, 0);
+
+check_values:
+	if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
+	    fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
+		goto bad;
+	return;
+
+bad:
+	printk("Acornfb: bad monitor settings: %s\n", opt);
+	current_par.montype = -1;
 }
 
-__initfunc(static void
-acornfb_parse_montype(char *opt))
+static void __init
+acornfb_parse_montype(char *opt)
 {
 	current_par.montype = -2;
 
@@ -1692,8 +1752,8 @@
 	}
 }
 
-__initfunc(static void
-acornfb_parse_dram(char *opt))
+static void __init
+acornfb_parse_dram(char *opt)
 {
 	unsigned int size;
 
@@ -1726,8 +1786,8 @@
 	{ NULL, NULL }
 };
 
-__initfunc(void
-acornfb_setup(char *options, int *ints))
+void __init
+acornfb_setup(char *options, int *ints)
 {
 	struct options *optp;
 	char *opt;
@@ -1763,8 +1823,8 @@
  * Detect type of monitor connected
  *  For now, we just assume SVGA
  */
-__initfunc(static int
-acornfb_detect_monitortype(void))
+static int __init
+acornfb_detect_monitortype(void)
 {
 	return 4;
 }
@@ -1800,22 +1860,45 @@
 	printk("acornfb: freed %dK memory\n", mb_freed);
 }
 
-__initfunc(void
-acornfb_init(void))
+void __init
+acornfb_init(void)
 {
 	unsigned long size;
 	u_int h_sync, v_sync;
+	int rc, i;
 
 	acornfb_init_fbinfo();
 
 	if (current_par.montype == -1)
 		current_par.montype = acornfb_detect_monitortype();
 
-	if (current_par.montype < 0 || current_par.montype > NR_MONTYPES)
+	if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
 		current_par.montype = 4;
 
-	fb_info.monspecs = monspecs[current_par.montype];
-	fb_info.monspecs.dpms = current_par.dpms;
+	if (current_par.montype > 0) {
+		fb_info.monspecs = monspecs[current_par.montype];
+		fb_info.monspecs.dpms = current_par.dpms;
+	}
+
+	/*
+	 * Try to select a suitable default mode
+	 */
+	for (i = 0; i < sizeof(modedb) / sizeof(*modedb); i++) {
+		unsigned long hs;
+
+		hs = modedb[i].refresh *
+		     (modedb[i].yres + modedb[i].upper_margin +
+		      modedb[i].lower_margin + modedb[i].vsync_len);
+		if (modedb[i].xres == DEFAULT_XRES &&
+		    modedb[i].yres == DEFAULT_YRES &&
+		    modedb[i].refresh >= fb_info.monspecs.vfmin &&
+		    modedb[i].refresh <= fb_info.monspecs.vfmax &&
+		    hs                >= fb_info.monspecs.hfmin &&
+		    hs                <= fb_info.monspecs.hfmax) {
+			acornfb_default_mode = modedb[i];
+			break;
+		}
+	}
 
 	current_par.currcon	   = -1;
 	current_par.screen_base	   = SCREEN2_BASE;
@@ -1839,7 +1922,7 @@
 
 	size = PAGE_ALIGN(size);
 
-#ifdef CONFIG_ARCH_RPC
+#if defined(HAS_VIDC20)
 	if (!current_par.using_vram) {
 		/*
 		 * RiscPC needs to allocate the DRAM memory
@@ -1847,17 +1930,30 @@
 		 * VRAM.  Archimedes/A5000 machines use a
 		 * fixed address for their framebuffers.
 		 */
-		current_par.screen_base = (unsigned long)kmalloc(size, GFP_KERNEL);
+		int order = 0;
+		unsigned long page, top;
+		while (size > (PAGE_SIZE * (1 << order)))
+			order++;
+		current_par.screen_base = __get_free_pages(GFP_KERNEL, order);
 		if (current_par.screen_base == 0) {
 			printk(KERN_ERR "acornfb: unable to allocate screen "
 			       "memory\n");
 			return;
 		}
+		top = current_par.screen_base + (PAGE_SIZE * (1 << order));
+		/* Mark the framebuffer pages as reserved so mmap will work. */
+		for (page = current_par.screen_base;
+		     page < PAGE_ALIGN(current_par.screen_base + size);
+		     page += PAGE_SIZE)
+			mem_map[MAP_NR(page)].flags |= (1 << PG_reserved);
+		/* Hand back any excess pages that we allocated. */
+		for (page = current_par.screen_base + size; page < top; page += PAGE_SIZE)
+			free_page(page);
 		current_par.screen_base_p =
 			virt_to_phys(current_par.screen_base);
 	}
 #endif
-#if defined(CONFIG_ARCH_A5K) || defined(CONFIG_ARCH_ARC)
+#if defined(HAS_VIDC)
 #define MAX_SIZE	480*1024
 	/*
 	 * Limit maximum screen size.
@@ -1873,22 +1969,20 @@
 	
 	current_par.screen_size	   = size;
 	current_par.palette_size   = VIDC_PALETTE_SIZE;
-	current_par.allow_modeset  = 1;
 
 	/*
 	 * Lookup the timing for this resolution.  If we can't
 	 * find it, then we can't restore it if we change
 	 * the resolution, so we disable this feature.
 	 */
-	if (acornfb_lookup_timing(&init_var))
-		current_par.allow_modeset = 0;
-
-	/*
-	 * Again, if this does not succeed, then we disallow
-	 * changes to the resolution parameters.
-	 */
-	if (acornfb_set_var(&init_var, -1, &fb_info))
-		current_par.allow_modeset = 0;
+	rc = fb_find_mode(&init_var, &fb_info, NULL, modedb,
+			 sizeof(modedb) / sizeof(*modedb),
+			 &acornfb_default_mode, DEFAULT_BPP);
+
+	if (!rc && fb_find_mode(&init_var, &fb_info, NULL, NULL, 0,
+				&acornfb_default_mode, DEFAULT_BPP)) {
+		printk("Acornfb: no valid mode found\n");
+	}
 
 	h_sync = 1953125000 / init_var.pixclock;
 	h_sync = h_sync * 512 / (init_var.xres + init_var.left_margin +
@@ -1901,6 +1995,15 @@
 		current_par.using_vram ? 'V' : 'D',
 		VIDC_NAME, init_var.xres, init_var.yres,
 		h_sync / 1000, h_sync % 1000, v_sync);
+
+	printk("Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
+		fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
+		fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
+		fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
+		fb_info.monspecs.dpms ? ", DPMS" : "");
+
+	if (acornfb_set_var(&init_var, -1, &fb_info))
+		printk(KERN_ERR "Acornfb: unable to set display parameters\n");
 
 	register_framebuffer(&fb_info);
 }
diff -urN linux-2.2.18-pre8/drivers/video/cyber2000fb.c linux/drivers/video/cyber2000fb.c
--- linux-2.2.18-pre8/drivers/video/cyber2000fb.c	Tue May 11 16:30:36 1999
+++ linux/drivers/video/cyber2000fb.c	Mon Dec  3 20:25:20 2001
@@ -35,7 +35,9 @@
 #define DEFAULT_YRES	480
 #define DEFAULT_BPP	8
 
-static volatile unsigned char	*CyberRegs;
+#define MMIO_SIZE	0x000c0000
+
+static char			*CyberRegs;
 
 #include "cyber2000fb.h"
 
@@ -45,7 +47,7 @@
 static struct display_switch	*dispsw;
 static struct fb_var_screeninfo __initdata init_var = {};
 
-#ifdef DEBUG
+#if defined(DEBUG) && defined(CONFIG_DEBUG_LL)
 static void debug_printf(char *fmt, ...)
 {
 	char buffer[128];
@@ -61,168 +63,6 @@
 #define debug_printf(x...) do { } while (0)
 #endif
 
-/*
- *    Predefined Video Modes
- */
-static const struct res cyber2000_res[] = {
-	{
-		640, 480,
-		{
-			0x5f, 0x4f, 0x50, 0x80, 0x52, 0x9d, 0x0b, 0x3e,
-			0x00, 0x40,
-			0xe9, 0x8b, 0xdf, 0x50, 0x00, 0xe6, 0x04, 0xc3
-		},
-		0x00,
-		{ 0xd2, 0xce, 0xdb, 0x54 }
-	},
-
-	{
-		800, 600,
-		{
-			0x7f, 0x63, 0x64, 0x00, 0x66, 0x10, 0x6f, 0xf0,
-			0x00, 0x60,
-			0x5b, 0x8f, 0x57, 0x64, 0x00, 0x59, 0x6e, 0xe3
-		},
-		0x00,
-		{ 0x52, 0x85, 0xdb, 0x54 }
-	},
-
-	{
-		1024, 768,
-		{
-			0x9f, 0x7f, 0x80, 0x80, 0x8b, 0x94, 0x1e, 0xfd,
-			0x00, 0x60,
-			0x03, 0x86, 0xff, 0x80, 0x0f, 0x00, 0x1e, 0xe3
-		},
-		0x00,
-		{ 0xd0, 0x52, 0xdb, 0x54 }
-	},
-#if 0
-	{
-		1152, 886,
-		{
-		},
-		{
-		}
-	},
-#endif
-	{
-		1280, 1024,
-		{
-			0xce, 0x9f, 0xa0, 0x8f, 0xa2, 0x1f, 0x28, 0x52,
-			0x00, 0x40,
-			0x08, 0x8f, 0xff, 0xa0, 0x00, 0x03, 0x27, 0xe3
-		},
-		0x1d,
-		{ 0xb4, 0x4b, 0xdb, 0x54 }
-	},
-
-	{
-		1600, 1200,
-		{
-			0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10,
-			0x00, 0x40,
-			0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3
-		},
-		0x1f,
-		{ 0xbd, 0x10, 0xdb, 0x54 }
-	}
-};
-
-#define NUM_TOTAL_MODES    arraysize(cyber2000_res)
-
-static const char igs_regs[] = {
-	0x10, 0x10,			0x12, 0x00,	0x13, 0x00,
-	0x30, 0x21,	0x31, 0x00,	0x32, 0x00,	0x33, 0x01,
-	0x50, 0x00,	0x51, 0x00,	0x52, 0x00,	0x53, 0x00,
-	0x54, 0x00,	0x55, 0x00,	0x56, 0x00,	0x57, 0x01,
-	0x58, 0x00,	0x59, 0x00,	0x5a, 0x00,
-	0x70, 0x0b,	0x71, 0x10,	0x72, 0x45,	0x73, 0x30,
-	0x74, 0x1b,	0x75, 0x1e,	0x76, 0x00,	0x7a, 0xc8
-};
-
-static const char crtc_idx[] = {
-	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-	0x08, 0x09,
-	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
-};
-
-static void cyber2000_init_hw(const struct res *res)
-{
-	int i;
-
-	debug_printf("init vga hw for %dx%d\n", res->xres, res->yres);
-
-	cyber2000_outb(0xef, 0x3c2);
-	cyber2000_crtcw(0x0b, 0x11);
-	cyber2000_attrw(0x00, 0x11);
-
-	cyber2000_seqw(0x01, 0x00);
-	cyber2000_seqw(0x01, 0x01);
-	cyber2000_seqw(0x0f, 0x02);
-	cyber2000_seqw(0x00, 0x03);
-	cyber2000_seqw(0x0e, 0x04);
-	cyber2000_seqw(0x03, 0x00);
-
-	for (i = 0; i < sizeof(crtc_idx); i++)
-		cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]);
-
-	for (i = 0x0a; i < 0x10; i++)
-		cyber2000_crtcw(0, i);
-
-	cyber2000_crtcw(0xff, 0x18);
-
-	cyber2000_grphw(0x00, 0x00);
-	cyber2000_grphw(0x00, 0x01);
-	cyber2000_grphw(0x00, 0x02);
-	cyber2000_grphw(0x00, 0x03);
-	cyber2000_grphw(0x00, 0x04);
-	cyber2000_grphw(0x60, 0x05);
-	cyber2000_grphw(0x05, 0x06);
-	cyber2000_grphw(0x0f, 0x07);
-	cyber2000_grphw(0xff, 0x08);
-
-	for (i = 0; i < 16; i++)
-		cyber2000_attrw(i, i);
-
-	cyber2000_attrw(0x01, 0x10);
-	cyber2000_attrw(0x00, 0x11);
-	cyber2000_attrw(0x0f, 0x12);
-	cyber2000_attrw(0x00, 0x13);
-	cyber2000_attrw(0x00, 0x14);
-
-	for (i = 0; i < sizeof(igs_regs); i += 2)
-		cyber2000_grphw(igs_regs[i+1], igs_regs[i]);
-
-	cyber2000_grphw(res->crtc_ofl, 0x11);
-
-	for (i = 0; i < 4; i += 1)
-		cyber2000_grphw(res->clk_regs[i], 0xb0 + i);
-
-	cyber2000_grphw(0x01, 0x90);
-	cyber2000_grphw(0x80, 0xb9);
-	cyber2000_grphw(0x00, 0xb9);
-
-	cyber2000_outb(0x56, 0x3ce);
-	i = cyber2000_inb(0x3cf);
-	cyber2000_outb(i | 4, 0x3cf);
-	cyber2000_outb(0x04, 0x3c6);
-	cyber2000_outb(i,    0x3cf);
-
-	cyber2000_outb(0x20, 0x3c0);
-	cyber2000_outb(0xff, 0x3c6);
-
-	for (i = 0; i < 256; i++) {
-		cyber2000_outb(i, 0x3c8);
-		cyber2000_outb(0, 0x3c9);
-		cyber2000_outb(0, 0x3c9);
-		cyber2000_outb(0, 0x3c9);
-	}
-}
-
-
-static struct fb_ops cyber2000fb_ops;
-
 /* -------------------- Hardware specific routines ------------------------- */
 
 /*
@@ -232,10 +72,10 @@
 {
 	int count = 10000;
 
-	while (cyber2000_inb(0xbf011) & 0x80) {
+	while (cyber2000_inb(CO_REG_CONTROL) & 0x80) {
 		if (!count--) {
 			debug_printf("accel_wait timed out\n");
-			cyber2000_outb(0, 0xbf011);
+			cyber2000_outb(0, CO_REG_CONTROL);
 			return;
 		}
 		udelay(10);
@@ -252,45 +92,54 @@
 cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
 		int height, int width)
 {
-	unsigned long src, dst, chwidth = p->var.xres_virtual * fontheight(p);
-	int v = 0x8000;
+	unsigned long src, dst;
+	unsigned int fh, fw;
+	int cmd = CO_CMD_L_PATTERN_FGCOL;
+
+	fw    = fontwidth(p);
+	sx    *= fw;
+	dx    *= fw;
+	width *= fw;
+	width -= 1;
 
 	if (sx < dx) {
-		sx += width - 1;
-		dx += width - 1;
-		v |= 4;
+		sx += width;
+		dx += width;
+		cmd |= CO_CMD_L_INC_LEFT;
 	}
 
+	fh     = fontheight(p);
+	sy     *= fh;
+	dy     *= fh;
+	height *= fh;
+	height -= 1;
+
 	if (sy < dy) {
-		sy += height - 1;
-		dy += height - 1;
-		v |= 2;
+		sy += height;
+		dy += height;
+		cmd |= CO_CMD_L_INC_UP;
 	}
 
-	sx *= fontwidth(p);
-	dx *= fontwidth(p);
-	src = sx + sy * chwidth;
-	dst = dx + dy * chwidth;
-	width = width * fontwidth(p) - 1;
-	height = height * fontheight(p) - 1;
+	src    = sx + sy * p->var.xres_virtual;
+	dst    = dx + dy * p->var.xres_virtual;
 
 	cyber2000_accel_wait();
-	cyber2000_outb(0x00,   0xbf011);
-	cyber2000_outb(0x03,   0xbf048);
-	cyber2000_outw(width,  0xbf060);
+	cyber2000_outb(0x00,  CO_REG_CONTROL);
+	cyber2000_outb(0x03,  CO_REG_FORE_MIX);
+	cyber2000_outw(width, CO_REG_WIDTH);
 
 	if (p->var.bits_per_pixel != 24) {
-		cyber2000_outl(dst, 0xbf178);
-		cyber2000_outl(src, 0xbf170);
+		cyber2000_outl(dst, CO_REG_DEST_PTR);
+		cyber2000_outl(src, CO_REG_SRC_PTR);
 	} else {
-		cyber2000_outl(dst * 3, 0xbf178);
-		cyber2000_outb(dst, 0xbf078);
-		cyber2000_outl(src * 3, 0xbf170);
+		cyber2000_outl(dst * 3, CO_REG_DEST_PTR);
+		cyber2000_outb(dst,     CO_REG_X_PHASE);
+		cyber2000_outl(src * 3, CO_REG_SRC_PTR);
 	}
 
-	cyber2000_outw(height, 0xbf062);
-	cyber2000_outw(v,      0xbf07c);
-	cyber2000_outw(0x2800, 0xbf07e);
+	cyber2000_outw(height, CO_REG_HEIGHT);
+	cyber2000_outw(cmd,    CO_REG_CMD_L);
+	cyber2000_outw(0x2800, CO_REG_CMD_H);
 }
 
 static void
@@ -298,35 +147,40 @@
 		int height, int width)
 {
 	unsigned long dst;
+	unsigned int fw, fh;
 	u32 bgx = attr_bgcol_ec(p, conp);
 
-	dst = sx * fontwidth(p) + sy * p->var.xres_virtual * fontheight(p);
-	width = width * fontwidth(p) - 1;
-	height = height * fontheight(p) - 1;
+	fw = fontwidth(p);
+	fh = fontheight(p);
+
+	dst    = sx * fw + sy * p->var.xres_virtual * fh;
+	width  = width * fw - 1;
+	height = height * fh - 1;
 
 	cyber2000_accel_wait();
-	cyber2000_outb(0x00,   0xbf011);
-	cyber2000_outb(0x03,   0xbf048);
-	cyber2000_outw(width,  0xbf060);
-	cyber2000_outw(height, 0xbf062);
+	cyber2000_outb(0x00,   CO_REG_CONTROL);
+	cyber2000_outb(0x03,   CO_REG_FORE_MIX);
+	cyber2000_outw(width,  CO_REG_WIDTH);
+	cyber2000_outw(height, CO_REG_HEIGHT);
 
 	switch (p->var.bits_per_pixel) {
+	case 15:
 	case 16:
 		bgx = ((u16 *)p->dispsw_data)[bgx];
 	case 8:
-		cyber2000_outl(dst, 0xbf178);
+		cyber2000_outl(dst, CO_REG_DEST_PTR);
 		break;
 
 	case 24:
-		cyber2000_outl(dst * 3, 0xbf178);
-		cyber2000_outb(dst, 0xbf078);
+		cyber2000_outl(dst * 3, CO_REG_DEST_PTR);
+		cyber2000_outb(dst, CO_REG_X_PHASE);
 		bgx = ((u32 *)p->dispsw_data)[bgx];
 		break;
 	}
 
-	cyber2000_outl(bgx,    0xbf058);
-	cyber2000_outw(0x8000, 0xbf07c);
-	cyber2000_outw(0x0800, 0xbf07e);
+	cyber2000_outl(bgx, CO_REG_FOREGROUND);
+	cyber2000_outw(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L);
+	cyber2000_outw(0x0800, CO_REG_CMD_H);
 }
 
 static void
@@ -415,14 +269,27 @@
 	current_par.palette[regno].blue  = blue;
 
 	switch (fb_display[current_par.currcon].var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
 	case 8:
 		cyber2000_outb(regno, 0x3c8);
 		cyber2000_outb(red,   0x3c9);
 		cyber2000_outb(green, 0x3c9);
 		cyber2000_outb(blue,  0x3c9);
 		break;
+#endif
 
 #ifdef FBCON_HAS_CFB16
+	case 15:
+		if (regno < 32) {
+			cyber2000_outb(regno << 3, 0x3c8);
+			cyber2000_outb(red, 0x3c9);
+			cyber2000_outb(green, 0x3c9);
+			cyber2000_outb(blue, 0x3c9);
+		}
+		if (regno < 16)
+			current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
+		break;
+
 	case 16:
 		if (regno < 64) {
 			/* write green */
@@ -464,86 +331,136 @@
 	return 0;
 }
 
-static int cyber2000fb_set_timing(struct fb_var_screeninfo *var)
-{
-	int width = var->xres_virtual;
-	int scr_pitch, fetchrow;
-	int i;
-	char b, col;
+struct par_info {
+	/*
+	 * Hardware
+	 */
+	unsigned char	clock_mult;
+	unsigned char	clock_div;
+	unsigned char	visualid;
+	unsigned char	pixformat;
+	unsigned char	crtc_ofl;
+	unsigned char	crtc[19];
+	unsigned int	width;
+	unsigned int	pitch;
 
-	switch (var->bits_per_pixel) {
-	case 8:	/* PSEUDOCOLOUR, 256 */
-		b = 0;
-		col = 1;
-		scr_pitch = var->xres_virtual / 8;
-		break;
+	/*
+	 * Other
+	 */
+	unsigned int	visual;
+};
 
-	case 16:/* DIRECTCOLOUR, 64k */
-		b = 1;
-		col = 2;
-		scr_pitch = var->xres_virtual / 8 * 2;
-		break;
-	case 24:/* TRUECOLOUR, 16m */
-		b = 2;
-		col = 4;
-		scr_pitch = var->xres_virtual / 8 * 3;
-		width *= 3;
-		break;
+static const char crtc_idx[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
+};
 
-	default:
-		return 1;
+static void cyber2000fb_set_timing(struct par_info *hw)
+{
+	unsigned int fetchrow, i;
+
+	/*
+	 * Blank palette
+	 */
+	for (i = 0; i < 256; i++) {
+		cyber2000_outb(i, 0x3c8);
+		cyber2000_outb(0, 0x3c9);
+		cyber2000_outb(0, 0x3c9);
+		cyber2000_outb(0, 0x3c9);
 	}
 
-	for (i = 0; i < NUM_TOTAL_MODES; i++)
-		if (var->xres == cyber2000_res[i].xres &&
-		    var->yres == cyber2000_res[i].yres)
-			break;
+	cyber2000_outb(0xef, 0x3c2);
+	cyber2000_crtcw(0x11, 0x0b);
+	cyber2000_attrw(0x11, 0x00);
 
-	if (i < NUM_TOTAL_MODES)
-		cyber2000_init_hw(cyber2000_res + i);
+	cyber2000_seqw(0x00, 0x01);
+	cyber2000_seqw(0x01, 0x01);
+	cyber2000_seqw(0x02, 0x0f);
+	cyber2000_seqw(0x03, 0x00);
+	cyber2000_seqw(0x04, 0x0e);
+	cyber2000_seqw(0x00, 0x03);
 
-	fetchrow = scr_pitch + 1;
+	for (i = 0; i < sizeof(crtc_idx); i++)
+		cyber2000_crtcw(crtc_idx[i], hw->crtc[i]);
+
+	for (i = 0x0a; i < 0x10; i++)
+		cyber2000_crtcw(i, 0);
+
+	cyber2000_grphw(0x11, hw->crtc_ofl);
+	cyber2000_grphw(0x00, 0x00);
+	cyber2000_grphw(0x01, 0x00);
+	cyber2000_grphw(0x02, 0x00);
+	cyber2000_grphw(0x03, 0x00);
+	cyber2000_grphw(0x04, 0x00);
+	cyber2000_grphw(0x05, 0x60);
+	cyber2000_grphw(0x06, 0x05);
+	cyber2000_grphw(0x07, 0x0f);
+	cyber2000_grphw(0x08, 0xff);
 
-	debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n",
-		     scr_pitch, fetchrow, col, b);
+	/* Attribute controller registers */
+	for (i = 0; i < 16; i++)
+		cyber2000_attrw(i, i);
 
-	cyber2000_outb(0x13, 0x3d4);
-	cyber2000_outb(scr_pitch, 0x3d5);
-	cyber2000_outb(0x14, 0x3ce);
-	cyber2000_outb(fetchrow, 0x3cf);
-	cyber2000_outb(0x15, 0x3ce);
-					/* FIXME: is this the right way round? */
-	cyber2000_outb(((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f), 0x3cf);
-	cyber2000_outb(0x77, 0x3ce);
-	cyber2000_outb(col, 0x3cf);
+	cyber2000_attrw(0x10, 0x01);
+	cyber2000_attrw(0x11, 0x00);
+	cyber2000_attrw(0x12, 0x0f);
+	cyber2000_attrw(0x13, 0x00);
+	cyber2000_attrw(0x14, 0x00);
+
+	/* PLL registers */
+	cyber2000_grphw(0xb0, hw->clock_mult);
+	cyber2000_grphw(0xb1, hw->clock_div);
+	cyber2000_grphw(0xb2, 0xdb);
+	cyber2000_grphw(0xb3, 0x54);		/* MCLK: 75MHz */
+	cyber2000_grphw(0x90, 0x01);
+	cyber2000_grphw(0xb9, 0x80);
+	cyber2000_grphw(0xb9, 0x00);
 
+	cyber2000_outb(0x56, 0x3ce);
+	i = cyber2000_inb(0x3cf);
+	cyber2000_outb(i | 4, 0x3cf);
+	cyber2000_outb(0x04, 0x3c6);
+	cyber2000_outb(i,    0x3cf);
 
-	cyber2000_outb(0x33, 0x3ce);
-	cyber2000_outb(0x1c, 0x3cf);
+	cyber2000_outb(0x20, 0x3c0);
+	cyber2000_outb(0xff, 0x3c6);
 
-	cyber2000_outw(width - 1, 0xbf018);
-	cyber2000_outw(width - 1, 0xbf218);
-	cyber2000_outb(b, 0xbf01c);
+	fetchrow = hw->pitch + 1;
+	cyber2000_grphw(0x14, fetchrow);
+	/* FIXME: is this the right way round? */
+	cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((hw->pitch >> 8) & 0x0f));
+	cyber2000_grphw(0x77, hw->visualid);
+	cyber2000_grphw(0x33, 0x1c);
 
-	return 0;
+	/*
+	 * Set up accelerator registers
+	 */
+	cyber2000_outw(hw->width, CO_REG_SRC_WIDTH);
+	cyber2000_outw(hw->width, CO_REG_DEST_WIDTH);
+	cyber2000_outb(hw->pixformat, CO_REG_PIX_FORMAT);
 }
 
-static inline void
+static inline int
 cyber2000fb_update_start(struct fb_var_screeninfo *var)
 {
-#if 0
 	unsigned int base;
 
 	base = var->yoffset * var->xres_virtual + var->xoffset;
 
-	cyber2000_outb(0x0c, 0x3d4);
-	cyber2000_outb(base, 0x3d5);
-	cyber2000_outb(0x0d, 0x3d4);
-	cyber2000_outb(base >> 8, 0x3d5);
-	/* FIXME: need the upper bits of the start offset */
-/*	cyber2000_outb(0x??, 0x3d4);
-	cyber2000_outb(base >> 16, 0x3d5);*/
-#endif
+	base >>= 2;
+
+	if (base >= 1 << 20)
+		return -EINVAL;
+
+	/*
+	 * FIXME: need the upper bits of the start offset
+	 */
+	cyber2000_grphw(0x10, base >> 16 | 0x10);
+	cyber2000_crtcw(0x0c, base >> 8);
+	cyber2000_crtcw(0x0d, base);
+
+	return 0;
 }
 
 /*
@@ -612,29 +529,257 @@
 	return err;
 }
 
+static int cyber2000fb_decode_crtc(struct par_info *hw, struct fb_var_screeninfo *var)
+{
+	unsigned int Htotal, Hblankend, Hsyncend;
+	unsigned int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
+#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
+
+	hw->crtc[13] = hw->pitch;
+	hw->crtc[17] = 0xe3;
+	hw->crtc[14] = 0;
+	hw->crtc[8]  = 0;
+
+	Htotal      = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+	if (Htotal > 2080)
+		return -EINVAL;
+
+	hw->crtc[0] = (Htotal >> 3) - 5;			/* Htotal	*/
+	hw->crtc[1] = (var->xres >> 3) - 1;			/* Hdispend	*/
+	hw->crtc[2] = var->xres >> 3;				/* Hblankstart	*/
+	hw->crtc[4] = (var->xres + var->right_margin) >> 3;	/* Hsyncstart	*/
+
+	Hblankend   = (Htotal - 4*8) >> 3;
+
+	hw->crtc[3] = BIT(Hblankend,  0, 0x1f,  0) |		/* Hblankend	*/
+		      BIT(1,          0, 0x01,  7);
+
+	Hsyncend    = (var->xres + var->right_margin + var->hsync_len) >> 3;
+
+	hw->crtc[5] = BIT(Hsyncend,   0, 0x1f,  0) |		/* Hsyncend	*/
+		      BIT(Hblankend,  5, 0x01,  7);
+
+	Vdispend    = var->yres - 1;
+	Vsyncstart  = var->yres + var->lower_margin;
+	Vsyncend    = var->yres + var->lower_margin + var->vsync_len;
+	Vtotal      = var->yres + var->lower_margin + var->vsync_len + var->upper_margin - 2;
+
+	if (Vtotal > 2047)
+		return -EINVAL;
+
+	Vblankstart = var->yres + 6;
+	Vblankend   = Vtotal - 10;
+
+	hw->crtc[6]  = Vtotal;
+	hw->crtc[7]  = BIT(Vtotal,     8, 0x01,  0) |
+			BIT(Vdispend,   8, 0x01,  1) |
+			BIT(Vsyncstart, 8, 0x01,  2) |
+			BIT(Vblankstart,8, 0x01,  3) |
+			BIT(1,          0, 0x01,  4) |
+	        	BIT(Vtotal,     9, 0x01,  5) |
+			BIT(Vdispend,   9, 0x01,  6) |
+			BIT(Vsyncstart, 9, 0x01,  7);
+	hw->crtc[9]  = BIT(0,          0, 0x1f,  0) |
+		        BIT(Vblankstart,9, 0x01,  5) |
+			BIT(1,          0, 0x01,  6);
+	hw->crtc[10] = Vsyncstart;
+	hw->crtc[11] = BIT(Vsyncend,   0, 0x0f,  0) |
+		       BIT(1,          0, 0x01,  7);
+	hw->crtc[12] = Vdispend;
+	hw->crtc[15] = Vblankstart;
+	hw->crtc[16] = Vblankend;
+	hw->crtc[18] = 0xff;
+
+	/* overflow - graphics reg 0x11 */
+/* 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10 4=LINECOMP:10 5-IVIDEO 6=FIXCNT */
+	hw->crtc_ofl =
+		BIT(Vtotal,     10, 0x01,  0) |
+		BIT(Vdispend,   10, 0x01,  1) |
+		BIT(Vsyncstart, 10, 0x01,  2) |
+		BIT(Vblankstart,10, 0x01,  3) |
+		1 << 4;
+
+	return 0;
+}
+
+/*
+ * The following was discovered by a good monitor,
+ * bit twiddling, theorising and but mostly luck.
+ * Strangely, it looks like everyone elses' PLL!
+ *
+ * Clock registers:
+ *   fclock = fpll / div2
+ *   fpll   = fref * mult / div1
+ * where:
+ *   fref = 14.318MHz (69842ps)
+ *   mult = reg0xb0.7:0
+ *   div1 = (reg0xb1.5:0 + 1)
+ *   div2 =  2^(reg0xb1.7:6)
+ *   fpll should be between 115 and 260 MHz
+ *  (8696ps and 3846ps)
+ */
 static int
-cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, int *visual)
+cyber2000fb_decode_clock(struct par_info *hw, struct fb_var_screeninfo *var)
 {
+	static unsigned int divisors_2000[] = { 1, 2, 4, 8 };
+	static unsigned int divisors_2010[] = { 1, 2, 4, 6 };
+	u_long pll_ps = var->pixclock;
+	const u_long ref_ps = 69842;
+	unsigned int *divisors;
+	u_int div2, t_div1, best_div1, best_mult;
+	int best_diff;
+
+	/*
+	 * Step 1:
+	 *   find div2 such that 115MHz < fpll < 260MHz
+	 *   and 0 <= div2 < 4
+	 */
+	if (current_par.dev_id == PCI_DEVICE_ID_INTERG_2010)
+		divisors = divisors_2010;
+	else
+		divisors = divisors_2000;
+ 
+	for (div2 = 0; div2 < 4; div2++) {
+		u_long new_pll;
+
+		new_pll = pll_ps / divisors[div2];
+		if (8696 > new_pll && new_pll > 3846) {
+			pll_ps = new_pll;
+			break;
+		}
+	}
+
+	if (div2 == 4)
+		return -EINVAL;
+
+	/*
+	 * Step 2:
+	 *  Given pll_ps and ref_ps, find:
+	 *    pll_ps * 0.995 < pll_ps_calc < pll_ps * 1.005
+	 *  where { 1 < best_div1 < 32, 1 < best_mult < 256 }
+	 *    pll_ps_calc = best_div1 / (ref_ps * best_mult)
+	 */
+	best_diff = 0x7fffffff;
+	best_mult = 32;
+	best_div1 = 255;
+	for (t_div1 = 32; t_div1 > 1; t_div1 -= 1) {
+		u_int rr, t_mult, t_pll_ps;
+		int diff;
+
+		/*
+		 * Find the multiplier for this divisor
+		 */
+		rr = ref_ps * t_div1;
+		t_mult = (rr + pll_ps / 2) / pll_ps;
+
+		/*
+		 * Is the multiplier within the correct range?
+		 */
+		if (t_mult > 256 || t_mult < 2)
+			continue;
+
+		/*
+		 * Calculate the actual clock period from this multiplier
+		 * and divisor, and estimate the error.
+		 */
+		t_pll_ps = (rr + t_mult / 2) / t_mult;
+		diff = pll_ps - t_pll_ps;
+		if (diff < 0)
+			diff = -diff;
+
+		if (diff < best_diff) {
+			best_diff = diff;
+			best_mult = t_mult;
+			best_div1 = t_div1;
+		}
+
+		/*
+		 * If we hit an exact value, there is no point in continuing.
+		 */
+		if (diff == 0)
+			break;
+	}
+
+	/*
+	 * Step 3:
+	 *  combine values
+	 */
+	hw->clock_mult = best_mult - 1;
+	hw->clock_div  = div2 << 6 | (best_div1 - 1);
+
+	return 0;
+}
+
+/*
+ * Decode the info required for the hardware.
+ * This involves the PLL parameters for the dot clock,
+ * CRTC registers, and accelerator settings.
+ */
+static int
+cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, struct par_info *hw)
+{
+	int err;
+
+	hw->width = var->xres_virtual;
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_CFB8
-	case 8:
-		*visual = FB_VISUAL_PSEUDOCOLOR;
+	case 8:	/* PSEUDOCOLOUR, 256 */
+		hw->visual    = FB_VISUAL_PSEUDOCOLOR;
+		hw->pixformat = PIXFORMAT_8BPP;
+		hw->visualid  = VISUALID_256;
+		hw->pitch     = hw->width >> 3;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB16
-	case 16:
-		*visual = FB_VISUAL_DIRECTCOLOR;
+	case 15:/* DIRECTCOLOUR, 32k */
+		hw->visual    = FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat = PIXFORMAT_16BPP;
+		hw->visualid  = VISUALID_32K;
+		hw->pitch     = hw->width >> 2;
+		break;
+
+	case 16:/* DIRECTCOLOUR, 64k */
+		hw->visual    = FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat = PIXFORMAT_16BPP;
+		hw->visualid  = VISUALID_64K;
+		hw->pitch     = hw->width >> 2;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB24
-	case 24:
-		*visual = FB_VISUAL_TRUECOLOR;
+	case 24:/* TRUECOLOUR, 16m */
+		hw->visual    = FB_VISUAL_TRUECOLOR;
+		hw->pixformat = PIXFORMAT_24BPP;
+		hw->visualid  = VISUALID_16M;
+		hw->width    *= 3;
+		hw->pitch     = hw->width >> 3;
 		break;
 #endif
 	default:
 		return -EINVAL;
 	}
 
+	err = cyber2000fb_decode_clock(hw, var);
+	if (err)
+		return err;
+
+	err = cyber2000fb_decode_crtc(hw, var);
+	if (err)
+		return err;
+
+	debug_printf("Clock: %02X %02X\n",
+		hw->clock_mult, hw->clock_div);
+	{
+		int i;
+
+		for (i = 0; i < 19; i++)
+			debug_printf("%2d ", i);
+		debug_printf("\n");
+		for (i = 0; i < 18; i++)
+			debug_printf("%02X ", hw->crtc[i]);
+		debug_printf("%02X\n", hw->crtc_ofl);
+	}
+	hw->width     -= 1;
+
 	return 0;
 }
 
@@ -648,17 +793,17 @@
 	struct display *display;
 
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-	strcpy(fix->id, "Cyber2000");
+	strcpy(fix->id, current_par.dev_name);
 
 	if (con >= 0)
 		display = fb_display + con;
 	else
 		display = &global_disp;
 
-	fix->smem_start	 = (char *)current_par.screen_base_p;
+	fix->smem_start	 = (void *)current_par.screen_base_p;
 	fix->smem_len	 = current_par.screen_size;
-	fix->mmio_start	 = (char *)current_par.regs_base_p;
-	fix->mmio_len	 = 0x000c0000;
+	fix->mmio_start	 = (void *)current_par.regs_base_p;
+	fix->mmio_len	 = MMIO_SIZE;
 	fix->type	 = display->type;
 	fix->type_aux	 = display->type_aux;
 	fix->xpanstep	 = 0;
@@ -694,14 +839,15 @@
 cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
 	struct display *display;
-	int err, chgvar = 0, visual;
+	struct par_info hw;
+	int err, chgvar = 0;
 
 	if (con >= 0)
 		display = fb_display + con;
 	else
 		display = &global_disp;
 
-	err = cyber2000fb_decode_var(var, con, &visual);
+	err = cyber2000fb_decode_var(var, con, &hw);
 	if (err)
 		return err;
 
@@ -737,15 +883,17 @@
 	}
 
 	display->var = *var;
+	display->var.activate &= ~FB_ACTIVATE_ALL;
 
-	display->screen_base	= (char *)current_par.screen_base;
-	display->visual		= visual;
+	if (var->activate & FB_ACTIVATE_ALL)
+		global_disp.var = display->var;
+
+	display->screen_base	= current_par.screen_base;
+	display->visual		= hw.visual;
 	display->type		= FB_TYPE_PACKED_PIXELS;
 	display->type_aux	= 0;
-	display->ypanstep	= 0;
+	display->ypanstep	= 1;
 	display->ywrapstep	= 0;
-	display->line_length	=
-	display->next_line	= (var->xres_virtual * var->bits_per_pixel) / 8;
 	display->can_soft_blank = 1;
 	display->inverse	= 0;
 
@@ -754,27 +902,33 @@
 	case 8:
 		dispsw = &fbcon_cfb8;
 		display->dispsw_data = NULL;
+		display->next_line = var->xres_virtual;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB16
+	case 15:
 	case 16:
 		dispsw = &fbcon_cfb16;
 		display->dispsw_data = current_par.c_table.cfb16;
+		display->next_line = var->xres_virtual * 2;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB24
 	case 24:
 		dispsw = &fbcon_cfb24;
 		display->dispsw_data = current_par.c_table.cfb24;
+		display->next_line = var->xres_virtual * 3;
 		break;
 #endif
 	default:
-		printk(KERN_WARNING "cyber2000: no support for %dbpp\n",
-		       display->var.bits_per_pixel);
+		printk(KERN_WARNING "%s: no support for %dbpp\n",
+		       current_par.dev_name, display->var.bits_per_pixel);
 		dispsw = &fbcon_dummy;
 		break;
 	}
 
+	display->line_length = display->next_line;
+
 	if (display->var.accel_flags & FB_ACCELF_TEXT &&
 	    dispsw != &fbcon_dummy)
 		display->dispsw = &fbcon_cyber_accel;
@@ -788,7 +942,7 @@
 		struct fb_cmap *cmap;
 
 		cyber2000fb_update_start(var);
-		cyber2000fb_set_timing(var);
+		cyber2000fb_set_timing(&hw);
 
 		if (display->cmap.len)
 			cmap = &display->cmap;
@@ -818,9 +972,9 @@
 		return -EINVAL;
 	if (y_bottom > fb_display[con].var.yres_virtual)
 		return -EINVAL;
-return -EINVAL;
 
-	cyber2000fb_update_start(var);
+	if (cyber2000fb_update_start(var))
+		return -EINVAL;
 
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -849,9 +1003,12 @@
 static int
 cyber2000fb_updatevar(int con, struct fb_info *info)
 {
+	int ret = 0;
+
 	if (con == current_par.currcon)
-		cyber2000fb_update_start(&fb_display[con].var);
-	return 0;
+		ret = cyber2000fb_update_start(&fb_display[con].var);
+
+	return ret;
 }
 
 static int
@@ -882,27 +1039,53 @@
 {
 	int i;
 
-	if (blank) {
+	/*
+	 *  Blank the screen if blank_mode != 0, else unblank. If
+	 *  blank == NULL then the caller blanks by setting the CLUT
+	 *  (Color Look Up Table) to all black. Return 0 if blanking
+	 *  succeeded, != 0 if un-/blanking failed due to e.g. a
+	 *  video mode which doesn't support it. Implements VESA
+	 *  suspend and powerdown modes on hardware that supports
+	 *  disabling hsync/vsync:
+	 *    blank_mode == 2: suspend vsync
+	 *    blank_mode == 3: suspend hsync
+	 *    blank_mode == 4: powerdown
+	 *
+	 *  wms...Enable VESA DMPS compatible powerdown mode
+	 *  run "setterm -powersave powerdown" to take advantage
+	 */
+     
+	switch (blank) {
+	case 4:	/* powerdown - both sync lines down */
+    		cyber2000_grphw(0x16, 0x05);
+		break;	
+	case 3:	/* hsync off */
+    		cyber2000_grphw(0x16, 0x01);
+		break;	
+	case 2:	/* vsync off */
+    		cyber2000_grphw(0x16, 0x04);
+		break;	
+	case 1:	/* just software blanking of screen */
+		cyber2000_grphw(0x16, 0x00);
 		for (i = 0; i < 256; i++) {
 			cyber2000_outb(i, 0x3c8);
 			cyber2000_outb(0, 0x3c9);
 			cyber2000_outb(0, 0x3c9);
 			cyber2000_outb(0, 0x3c9);
 		}
-	} else {
+		break;
+	default: /* case 0, or anything else: unblank */
+		cyber2000_grphw(0x16, 0x00);
 		for (i = 0; i < 256; i++) {
 			cyber2000_outb(i, 0x3c8);
 			cyber2000_outb(current_par.palette[i].red, 0x3c9);
 			cyber2000_outb(current_par.palette[i].green, 0x3c9);
 			cyber2000_outb(current_par.palette[i].blue, 0x3c9);
 		}
+		break;
 	}
 }
 
-__initfunc(void cyber2000fb_setup(char *options, int *ints))
-{
-}
-
 static struct fb_ops cyber2000fb_ops =
 {
 	cyber2000fb_open,
@@ -916,8 +1099,96 @@
 	cyber2000fb_ioctl
 };
 
-__initfunc(static void
-cyber2000fb_init_fbinfo(void))
+int cyber2000fb_attach(struct cyberpro_info *info)
+{
+	if (current_par.initialised) {
+		info->regs    = CyberRegs;
+		info->fb      = current_par.screen_base;
+		info->fb_size = current_par.screen_size;
+
+		strncpy(info->dev_name, current_par.dev_name, sizeof(info->dev_name));
+
+		MOD_INC_USE_COUNT;
+	}
+
+	return current_par.initialised;
+}
+
+void cyber2000fb_detach(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+EXPORT_SYMBOL(cyber2000fb_attach);
+EXPORT_SYMBOL(cyber2000fb_detach);
+
+#define fb_videomode fb_2_3_videomode
+
+/* stuff from 2.3 */
+struct fb_videomode {
+	const char *name;
+	u32 refresh;
+	u32 xres;
+	u32 yres;
+	u32 pixclock;
+	u32 left_margin;
+	u32 right_margin;
+	u32 upper_margin;
+	u32 lower_margin;
+	u32 hsync_len;
+	u32 vsync_len;
+	u32 sync;
+	u32 vmode;
+};
+
+static int fb_find_mode(struct fb_var_screeninfo *var, struct fb_info *info,
+			const char *mode_option, const struct fb_videomode *db,
+			unsigned int dbsize, const struct fb_videomode *default_mode,
+			unsigned int default_bpp)
+{
+	var->xres = default_mode->xres;
+	var->yres = default_mode->yres;
+	var->xres_virtual = default_mode->xres;
+	var->yres_virtual = default_mode->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	var->bits_per_pixel = default_bpp;
+	var->activate = 0;
+	var->pixclock = default_mode->pixclock;
+	var->left_margin = default_mode->left_margin;
+	var->right_margin = default_mode->right_margin;
+	var->upper_margin = default_mode->upper_margin;
+	var->lower_margin = default_mode->lower_margin;
+	var->hsync_len = default_mode->hsync_len;
+	var->vsync_len = default_mode->vsync_len;
+	var->sync = default_mode->sync;
+	var->vmode = default_mode->vmode;
+	return 3;
+}
+
+/*
+ * These parameters give
+ * 640x480, hsync 31.5kHz, vsync 60Hz
+ */
+static struct fb_videomode __initdata
+cyber2000fb_default_mode = {
+	name:		NULL,
+	refresh:	60,
+	xres:		640,
+	yres:		480,
+	pixclock:	39722,
+	left_margin:	56,
+	right_margin:	16,
+	upper_margin:	34,
+	lower_margin:	9,
+	hsync_len:	88,
+	vsync_len:	2,
+	sync:		FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	vmode:		FB_VMODE_NONINTERLACED
+};
+
+static void __init 
+cyber2000fb_init_fbinfo(void)
 {
 	static int first = 1;
 
@@ -941,18 +1212,15 @@
 	 * setup initial parameters
 	 */
 	memset(&init_var, 0, sizeof(init_var));
-	init_var.xres_virtual		=
-	init_var.xres			= DEFAULT_XRES;
-	init_var.yres_virtual		=
-	init_var.yres			= DEFAULT_YRES;
-	init_var.bits_per_pixel		= DEFAULT_BPP;
 
 	init_var.red.msb_right		= 0;
 	init_var.green.msb_right	= 0;
 	init_var.blue.msb_right		= 0;
 
 	switch(init_var.bits_per_pixel) {
-	case 8:
+	default:
+		init_var.bits_per_pixel = 8;
+	case 8: /* PSEUDOCOLOUR */
 		init_var.bits_per_pixel	= 8;
 		init_var.red.offset	= 0;
 		init_var.red.length	= 8;
@@ -962,7 +1230,17 @@
 		init_var.blue.length	= 8;
 		break;
 
-	case 16:
+	case 15: /* RGB555 */
+		init_var.bits_per_pixel = 15;
+		init_var.red.offset	= 10;
+		init_var.red.length	= 5;
+		init_var.green.offset	= 5;
+		init_var.green.length	= 5;
+		init_var.blue.offset	= 0;
+		init_var.blue.length	= 5;
+		break;
+
+	case 16: /* RGB565 */
 		init_var.bits_per_pixel = 16;
 		init_var.red.offset	= 11;
 		init_var.red.length	= 5;
@@ -972,7 +1250,7 @@
 		init_var.blue.length	= 5;
 		break;
 
-	case 24:
+	case 24: /* RGB888 */
 		init_var.bits_per_pixel = 24;
 		init_var.red.offset	= 16;
 		init_var.red.length	= 8;
@@ -988,36 +1266,175 @@
 	init_var.height			= -1;
 	init_var.width			= -1;
 	init_var.accel_flags		= FB_ACCELF_TEXT;
-	init_var.sync			= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
-	init_var.vmode			= FB_VMODE_NONINTERLACED;
 }
 
 /*
+ * Cyber2000 options:
+ *
+ *  font:fontname
+ *	Set the fontname
+ *
+ *  res:XxY
+ *	Set the default display resolution
+ */
+static void __init
+cyber2000fb_parse_font(char *opt)
+{
+	strcpy(fb_info.fontname, opt);
+}
+
+static struct options {
+	char *name;
+	void (*parse)(char *opt);
+} opt_table[] __initdata = {
+	{ "font",	cyber2000fb_parse_font		},
+	{ NULL,		NULL				}
+};
+
+int __init
+cyber2000fb_setup(char *options)
+{
+	struct options *optp;
+	char *opt;
+
+	if (!options || !*options)
+		return 0;
+
+	cyber2000fb_init_fbinfo();
+
+	for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) {
+		if (!*opt)
+			continue;
+
+		for (optp = opt_table; optp->name; optp++) {
+			int optlen;
+
+			optlen = strlen(optp->name);
+
+			if (strncmp(opt, optp->name, optlen) == 0 &&
+			    opt[optlen] == ':') {
+				optp->parse(opt + optlen + 1);
+				break;
+			}
+		}
+
+		if (!optp->name)
+			printk(KERN_ERR "CyberPro20x0: unknown parameter: %s\n",
+				opt);
+	}
+	return 0;
+}
+
+static char igs_regs[] __initdata = {
+	0x10, 0x10,			0x12, 0x00,	0x13, 0x00,
+					0x16, 0x00,
+/*	0x30, 0x21,*/	0x31, 0x00,	0x32, 0x00,	0x33, 0x01,
+	0x50, 0x00,	0x51, 0x00,	0x52, 0x00,	0x53, 0x00,
+	0x54, 0x00,	0x55, 0x00,	0x56, 0x00,	0x57, 0x01,
+	0x58, 0x00,	0x59, 0x00,	0x5a, 0x00,
+	0x70, 0x0b,/*	0x71, 0x10,	0x72, 0x45,*/	0x73, 0x30,
+	0x74, 0x1b,	0x75, 0x1e,	0x76, 0x00,	0x7a, 0xc8
+};
+
+static void __init cyber2000fb_hw_init(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof(igs_regs); i += 2)
+		cyber2000_grphw(igs_regs[i], igs_regs[i+1]);
+}
+
+static unsigned short device_ids[] __initdata = {
+	PCI_DEVICE_ID_INTERG_2000,
+	PCI_DEVICE_ID_INTERG_2010,
+	PCI_DEVICE_ID_INTERG_5000
+};
+
+/*
  *    Initialization
  */
-__initfunc(void cyber2000fb_init(void))
+int __init cyber2000fb_init(void)
 {
-	struct pci_dev *dev;
+	struct pci_dev *dev = NULL;
 	u_int h_sync, v_sync;
+	u_long mmio_base, smem_base, smem_size;
+	int err = 0, i;
+
+	for (i = 0; i < sizeof(device_ids) / sizeof(device_ids[0]); i++) {
+		dev = pci_find_device(PCI_VENDOR_ID_INTERG,
+				      device_ids[i], NULL);
+		if (dev)
+			break;
+	}
 
-	dev = pci_find_device(PCI_VENDOR_ID_INTERG, 0x2000, NULL);
 	if (!dev)
-		return;
+		return -ENXIO;
+
+	sprintf(current_par.dev_name, "CyberPro%4X", dev->device);
 
-	CyberRegs = bus_to_virt(dev->base_address[0]) + 0x00800000;/*FIXME*/
+	smem_base = dev->base_address[0] & ~3;
+	mmio_base = smem_base + 0x00800000;
+	current_par.dev_id = dev->device;
+
+	/*
+	 * Map in the registers
+	 */
+	CyberRegs = ioremap(mmio_base, MMIO_SIZE);
+	if (!CyberRegs) {
+		printk("%s: unable to map memory mapped IO\n",
+		       current_par.dev_name);
+		err = -ENOMEM;
+		goto release_mmio_resource;
+	}
 
 	cyber2000_outb(0x18, 0x46e8);
 	cyber2000_outb(0x01, 0x102);
 	cyber2000_outb(0x08, 0x46e8);
 
+	/*
+	 * get the video RAM size from the VGA register.
+	 * This should have been already initialised by the BIOS,
+	 * but if it's garbage, claim default 1MB VRAM (woody)
+	 */
+	cyber2000_outb(0x72, 0x3ce);
+	switch (cyber2000_inb(0x3cf) & 3) {
+	case 2:	 smem_size = 0x00400000; break;
+	case 1:	 smem_size = 0x00200000; break;
+	default: smem_size = 0x00100000; break;
+	}
+
+	/*
+	 * Map in screen memory
+	 */
+	current_par.screen_base = ioremap(smem_base, smem_size);
+	if (!current_par.screen_base) {
+		printk("%s: unable to map screen memory\n",
+		       current_par.dev_name);
+		err = -ENOMEM;
+		goto release_smem_resource;
+	}
+current_par.screen_base += IO_FUDGE_FACTOR;
+	current_par.screen_size   = smem_size;
+	current_par.screen_base_p = smem_base + 0x80000000;
+	current_par.regs_base_p   = mmio_base + 0x80000000;
+	current_par.currcon	  = -1;
+
 	cyber2000fb_init_fbinfo();
 
-	current_par.currcon		= -1;
-	current_par.screen_base_p	= 0x80000000 + dev->base_address[0];
-	current_par.screen_base		= (u_int)bus_to_virt(dev->base_address[0]);
-	current_par.screen_size		= 0x00200000;
-	current_par.regs_base_p		= 0x80800000 + dev->base_address[0];
+	if (!fb_find_mode(&init_var, &fb_info, NULL,
+	    NULL, 0, &cyber2000fb_default_mode, 8)) {
+		printk("%s: no valid mode found\n",
+			current_par.dev_name);
+		goto release_smem_resource;
+	}
 
+	init_var.yres_virtual = smem_size * 8 /
+			(init_var.bits_per_pixel * init_var.xres_virtual);
+
+	if (init_var.yres_virtual < init_var.yres)
+		init_var.yres_virtual = init_var.yres;
+    
+	cyber2000fb_hw_init();
 	cyber2000fb_set_var(&init_var, -1, &fb_info);
 
 	h_sync = 1953125000 / init_var.pixclock;
@@ -1026,23 +1443,40 @@
 	v_sync = h_sync / (init_var.yres + init_var.upper_margin +
 		 init_var.lower_margin + init_var.vsync_len);
 
-	printk("Cyber2000: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+	printk("%s: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+		current_par.dev_name,
 		current_par.screen_size >> 10,
 		init_var.xres, init_var.yres,
 		h_sync / 1000, h_sync % 1000, v_sync);
 
-	if (register_framebuffer(&fb_info) < 0)
-		return;
+	if (register_framebuffer(&fb_info) < 0) {
+		err = -EINVAL;
+		goto release_smem;
+	}
+
+	current_par.initialised = 1;
 
 	MOD_INC_USE_COUNT;	/* TODO: This driver cannot be unloaded yet */
-}
+	return 0;
 
+release_smem:
+	iounmap(current_par.screen_base);
+release_smem_resource:
+	iounmap(CyberRegs);
+release_mmio_resource:
 
+	return err;
+}
 
 #ifdef MODULE
-int init_module(void)
+int __init init_module(void)
 {
-	cyber2000fb_init();
+	int ret;
+
+	ret = cyber2000fb_init();
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -1052,6 +1486,12 @@
 	   decremented to zero */
 	unregister_framebuffer(&fb_info);
 	/* TODO: clean up ... */
+
+	iounmap(current_par.screen_base);
+	iounmap(CyberRegs);
+
+	release_mem_region(smem_base, current_par.screen_size);
+	release_mem_region(mmio_base, MMIO_SIZE);
 }
 
 #endif				/* MODULE */
diff -urN linux-2.2.18-pre8/drivers/video/cyber2000fb.h linux/drivers/video/cyber2000fb.h
--- linux-2.2.18-pre8/drivers/video/cyber2000fb.h	Tue May 11 16:30:36 1999
+++ linux/drivers/video/cyber2000fb.h	Mon Dec  3 20:25:20 2001
@@ -5,27 +5,28 @@
  */
 
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
-#define cyber2000_outb(dat,reg)	(CyberRegs[reg] = dat)
-#define cyber2000_outw(dat,reg)	(*(unsigned short *)&CyberRegs[reg] = dat)
-#define cyber2000_outl(dat,reg)	(*(unsigned long *)&CyberRegs[reg] = dat)
-
-#define cyber2000_inb(reg)	(CyberRegs[reg])
-#define cyber2000_inw(reg)	(*(unsigned short *)&CyberRegs[reg])
-#define cyber2000_inl(reg)	(*(unsigned long *)&CyberRegs[reg])
 
-static inline void cyber2000_crtcw(int val, int reg)
+#define cyber2000_outb(dat,reg)	writeb(dat, CyberRegs + reg)
+#define cyber2000_outw(dat,reg)	writew(dat, CyberRegs + reg)
+#define cyber2000_outl(dat,reg)	writel(dat, CyberRegs + reg)
+
+#define cyber2000_inb(reg)	readb(CyberRegs + reg)
+#define cyber2000_inw(reg)	readw(CyberRegs + reg)
+#define cyber2000_inl(reg)	readl(CyberRegs + reg)
+
+static inline void cyber2000_crtcw(int reg, int val)
 {
 	cyber2000_outb(reg, 0x3d4);
 	cyber2000_outb(val, 0x3d5);
 }
 
-static inline void cyber2000_grphw(int val, int reg)
+static inline void cyber2000_grphw(int reg, int val)
 {
 	cyber2000_outb(reg, 0x3ce);
 	cyber2000_outb(val, 0x3cf);
 }
 
-static inline void cyber2000_attrw(int val, int reg)
+static inline void cyber2000_attrw(int reg, int val)
 {
 	cyber2000_inb(0x3da);
 	cyber2000_outb(reg, 0x3c0);
@@ -33,14 +34,14 @@
 	cyber2000_outb(val, 0x3c0);
 }
 
-static inline void cyber2000_seqw(int val, int reg)
+static inline void cyber2000_seqw(int reg, int val)
 {
 	cyber2000_outb(reg, 0x3c4);
 	cyber2000_outb(val, 0x3c5);
 }
 
 struct cyber2000fb_par {
-	unsigned long	screen_base;
+	char *		screen_base;
 	unsigned long	screen_base_p;
 	unsigned long	regs_base;
 	unsigned long	regs_base_p;
@@ -48,6 +49,10 @@
 	unsigned long	screen_size;
 	unsigned int	palette_size;
 	  signed int	currcon;
+	char		dev_name[32];
+	unsigned int	initialised;
+	unsigned int	dev_id;
+
 	/*
 	 * palette
 	 */
@@ -69,10 +74,161 @@
 	} c_table;
 };
 
-struct res {
-	int	xres;
-	int	yres;
-	unsigned char crtc_regs[18];
-	unsigned char crtc_ofl;
-	unsigned char clk_regs[4];
+#define PIXFORMAT_8BPP		0
+#define PIXFORMAT_16BPP		1
+#define PIXFORMAT_24BPP		2
+
+#define VISUALID_256		1
+#define VISUALID_64K		2
+#define VISUALID_16M		4
+#define VISUALID_32K		6
+
+#define K_CAP_X2_CTL1		0x49
+
+#define CAP_X_START		0x60
+#define CAP_X_END		0x62
+#define CAP_Y_START		0x64
+#define CAP_Y_END		0x66
+#define CAP_DDA_X_INIT		0x68
+#define CAP_DDA_X_INC		0x6a
+#define CAP_DDA_Y_INIT		0x6c
+#define CAP_DDA_Y_INC		0x6e
+
+#define EXT_FIFO_CTL		0x74
+
+#define CAP_PIP_X_START		0x80
+#define CAP_PIP_X_END		0x82
+#define CAP_PIP_Y_START		0x84
+#define CAP_PIP_Y_END		0x86
+
+#define CAP_NEW_CTL1		0x88
+
+#define CAP_NEW_CTL2		0x89
+
+#define CAP_MODE1		0xa4
+#define CAP_MODE1_8BIT			0x01	/* enable 8bit capture mode	*/
+#define CAP_MODE1_CCIR656		0x02	/* CCIR656 mode			*/
+#define CAP_MODE1_IGNOREVGT		0x04	/* ignore VGT			*/
+#define CAP_MODE1_ALTFIFO		0x10	/* use alternate FIFO for capture */
+#define CAP_MODE1_SWAPUV		0x20	/* swap UV bytes		*/
+#define CAP_MODE1_MIRRORY		0x40	/* mirror vertically		*/
+#define CAP_MODE1_MIRRORX		0x80	/* mirror horizontally		*/
+
+#define CAP_MODE2		0xa5
+
+#define Y_TV_CTL		0xae
+
+#define EXT_MEM_START		0xc0		/* ext start address 21 bits */
+#define HOR_PHASE_SHIFT		0xc2		/* high 3 bits */
+#define EXT_SRC_WIDTH		0xc3		/* ext offset phase  10 bits */
+#define EXT_SRC_HEIGHT		0xc4		/* high 6 bits */
+#define EXT_X_START		0xc5		/* ext->screen, 16 bits */
+#define EXT_X_END		0xc7		/* ext->screen, 16 bits */
+#define EXT_Y_START		0xc9		/* ext->screen, 16 bits */
+#define EXT_Y_END		0xcb		/* ext->screen, 16 bits */
+#define EXT_SRC_WIN_WIDTH	0xcd		/* 8 bits */
+#define EXT_COLOUR_COMPARE	0xce		/* 24 bits */
+#define EXT_DDA_X_INIT		0xd1		/* ext->screen 16 bits */
+#define EXT_DDA_X_INC		0xd3		/* ext->screen 16 bits */
+#define EXT_DDA_Y_INIT		0xd5		/* ext->screen 16 bits */
+#define EXT_DDA_Y_INC		0xd7		/* ext->screen 16 bits */
+
+#define VID_FIFO_CTL		0xd9
+
+#define VID_CAP_VFC		0xdb
+#define VID_CAP_VFC_YUV422		0x00	/* formats - does this cause conversion? */
+#define VID_CAP_VFC_RGB555		0x01
+#define VID_CAP_VFC_RGB565		0x02
+#define VID_CAP_VFC_RGB888_24		0x03
+#define VID_CAP_VFC_RGB888_32		0x04
+#define VID_CAP_VFC_DUP_PIX_ZOON	0x08	/* duplicate pixel zoom			*/
+#define VID_CAP_VFC_MOD_3RD_PIX		0x20	/* modify 3rd duplicated pixel		*/
+#define VID_CAP_VFC_DBL_H_PIX		0x40	/* double horiz pixels			*/
+#define VID_CAP_VFC_UV128		0x80	/* UV data offset by 128		*/
+
+#define VID_DISP_CTL1		0xdc
+#define VID_DISP_CTL1_INTRAM		0x01	/* video pixels go to internal RAM	*/
+#define VID_DISP_CTL1_IGNORE_CCOMP	0x02	/* ignore colour compare registers	*/
+#define VID_DISP_CTL1_NOCLIP		0x04	/* do not clip to 16235,16240		*/
+#define VID_DISP_CTL1_UV_AVG		0x08	/* U/V data is averaged			*/
+#define VID_DISP_CTL1_Y128		0x10	/* Y data offset by 128			*/
+#define VID_DISP_CTL1_VINTERPOL_OFF	0x20	/* vertical interpolation off		*/
+#define VID_DISP_CTL1_VID_OUT_WIN_FULL	0x40	/* video out window full		*/
+#define VID_DISP_CTL1_ENABLE_VID_WINDOW	0x80	/* enable video window			*/
+
+#define VID_FIFO_CTL1		0xdd
+
+#define VFAC_CTL1		0xe8
+#define VFAC_CTL1_CAPTURE		0x01	/* capture enable			*/
+#define VFAC_CTL1_VFAC_ENABLE		0x02	/* vfac enable				*/
+#define VFAC_CTL1_FREEZE_CAPTURE	0x04	/* freeze capture			*/
+#define VFAC_CTL1_FREEZE_CAPTURE_SYNC	0x08	/* sync freeze capture			*/
+#define VFAC_CTL1_VALIDFRAME_SRC	0x10	/* select valid frame source		*/
+#define VFAC_CTL1_PHILIPS		0x40	/* select Philips mode			*/
+#define VFAC_CTL1_MODVINTERPOLCLK	0x80	/* modify vertical interpolation clocl	*/
+
+#define VFAC_CTL2		0xe9
+#define VFAC_CTL2_INVERT_VIDDATAVALID	0x01	/* invert video data valid		*/
+#define VFAC_CTL2_INVERT_GRAPHREADY	0x02	/* invert graphic ready output sig	*/
+#define VFAC_CTL2_INVERT_DATACLK	0x04	/* invert data clock signal		*/
+#define VFAC_CTL2_INVERT_HSYNC		0x08	/* invert hsync input			*/
+#define VFAC_CTL2_INVERT_VSYNC		0x10	/* invert vsync input			*/
+#define VFAC_CTL2_INVERT_FRAME		0x20	/* invert frame odd/even input		*/
+#define VFAC_CTL2_INVERT_BLANK		0x40	/* invert blank output			*/
+#define VFAC_CTL2_INVERT_OVSYNC		0x80	/* invert other vsync input		*/
+
+#define VFAC_CTL3		0xea
+
+#define CAP_MEM_START		0xeb		/* 18 bits */
+#define CAP_MAP_WIDTH		0xed		/* high 6 bits */
+#define CAP_PITCH		0xee		/* 8 bits */
+
+#define CAP_CTL_MISC		0xef
+#define CAP_CTL_MISC_HDIV		0x01
+#define CAP_CTL_MISC_HDIV4		0x02
+#define CAP_CTL_MISC_ODDEVEN		0x04
+#define CAP_CTL_MISC_HSYNCDIV2		0x08
+#define CAP_CTL_MISC_SYNCTZHIGH		0x10
+#define CAP_CTL_MISC_SYNCTZOR		0x20
+#define CAP_CTL_MISC_DISPUSED		0x80
+
+#define REG_BANK		0xfa
+#define REG_BANK_Y			0x01
+#define REG_BANK_K			0x05
+
+
+#define CO_CMD_L_PATTERN_FGCOL	0x8000
+#define CO_CMD_L_INC_LEFT	0x0004
+#define CO_CMD_L_INC_UP		0x0002
+
+#define CO_CMD_H_SRC_PIXMAP	0x2000
+#define CO_CMD_H_BLITTER	0x0800
+
+#define CO_REG_CONTROL		0xbf011
+#define CO_REG_SRC_WIDTH	0xbf018
+#define CO_REG_PIX_FORMAT	0xbf01c
+#define CO_REG_FORE_MIX		0xbf048
+#define CO_REG_FOREGROUND	0xbf058
+#define CO_REG_WIDTH		0xbf060
+#define CO_REG_HEIGHT		0xbf062
+#define CO_REG_X_PHASE		0xbf078
+#define CO_REG_CMD_L		0xbf07c
+#define CO_REG_CMD_H		0xbf07e
+#define CO_REG_SRC_PTR		0xbf170
+#define CO_REG_DEST_PTR		0xbf178
+#define CO_REG_DEST_WIDTH	0xbf218
+
+struct cyberpro_info {
+	unsigned char	*regs;
+	char		*fb;
+	char		dev_name[32];
+	unsigned int	fb_size;
 };
+
+/*
+ * Note! Writing to the Cyber20x0 registers from an interrupt
+ * routine is definitely a bad idea atm.
+ */
+int cyber2000fb_attach(struct cyberpro_info *info);
+void cyber2000fb_detach(void);
+
diff -urN linux-2.2.18-pre8/fs/Config.in linux/fs/Config.in
--- linux-2.2.18-pre8/fs/Config.in	Mon Dec  3 20:24:43 2001
+++ linux/fs/Config.in	Mon Dec  3 20:25:20 2001
@@ -90,6 +90,9 @@
       define_bool CONFIG_LOCKD n
     fi
   fi
+  if [ "$CONFIG_NFS_FS" != "n" -o "$CONFIG_NFSD" != "n" ]; then
+    bool '   NFS Version 3' CONFIG_NFS_V3
+  fi
   tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS
   if [ "$CONFIG_SMB_FS" != "n" ]; then
      bool '   Use a default NLS' CONFIG_SMB_NLS_DEFAULT
diff -urN linux-2.2.18-pre8/fs/adfs/Makefile linux/fs/adfs/Makefile
--- linux-2.2.18-pre8/fs/adfs/Makefile	Wed Jun 24 14:30:09 1998
+++ linux/fs/adfs/Makefile	Mon Dec  3 20:25:20 2001
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile.
 
 O_TARGET := adfs.o
-O_OBJS   := dir.o file.o inode.o map.o namei.o super.o
+O_OBJS   := dir.o dir_f.o dir_fplus.o file.o inode.o map.o super.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre8/fs/adfs/adfs.h linux/fs/adfs/adfs.h
--- linux-2.2.18-pre8/fs/adfs/adfs.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/adfs/adfs.h	Mon Dec  3 20:25:20 2001
@@ -0,0 +1,136 @@
+/* Internal data structures for ADFS */
+
+#define ADFS_FREE_FRAG		 0
+#define ADFS_BAD_FRAG		 1
+#define ADFS_ROOT_FRAG		 2
+
+#define ADFS_NDA_OWNER_READ	(1 << 0)
+#define ADFS_NDA_OWNER_WRITE	(1 << 1)
+#define ADFS_NDA_LOCKED		(1 << 2)
+#define ADFS_NDA_DIRECTORY	(1 << 3)
+#define ADFS_NDA_EXECUTE	(1 << 4)
+#define ADFS_NDA_PUBLIC_READ	(1 << 5)
+#define ADFS_NDA_PUBLIC_WRITE	(1 << 6)
+
+#include "dir_f.h"
+
+/*
+ * Directory handling
+ */
+struct adfs_dir {
+	struct super_block	*sb;
+
+	int			nr_buffers;
+	struct buffer_head	*bh[4];
+	unsigned int		pos;
+	unsigned int		parent_id;
+
+	struct adfs_dirheader	dirhead;
+	union  adfs_dirtail	dirtail;
+};
+
+/*
+ * This is the overall maximum name length
+ */
+#define ADFS_MAX_NAME_LEN	256
+struct object_info {
+	__u32		parent_id;		/* parent object id	*/
+	__u32		file_id;		/* object id		*/
+	__u32		loadaddr;		/* load address		*/
+	__u32		execaddr;		/* execution address	*/
+	__u32		size;			/* size			*/
+	__u8		attr;			/* RISC OS attributes	*/
+	unsigned char	name_len;		/* name length		*/
+	char		name[ADFS_MAX_NAME_LEN];/* file name		*/
+};
+
+struct adfs_dir_ops {
+	int	(*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir);
+	int	(*setpos)(struct adfs_dir *dir, unsigned int fpos);
+	int	(*getnext)(struct adfs_dir *dir, struct object_info *obj);
+	int	(*update)(struct adfs_dir *dir, struct object_info *obj);
+	int	(*create)(struct adfs_dir *dir, struct object_info *obj);
+	int	(*remove)(struct adfs_dir *dir, struct object_info *obj);
+	void	(*free)(struct adfs_dir *dir);
+};
+
+struct adfs_discmap {
+	struct buffer_head	*dm_bh;
+	__u32			dm_startblk;
+	unsigned int		dm_startbit;
+	unsigned int		dm_endbit;
+};
+
+/* dir stuff */
+
+
+/* Inode stuff */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+int adfs_get_block(struct inode *inode, long block,
+		   struct buffer_head *bh, int create);
+#else
+int adfs_bmap(struct inode *inode, int block);
+#endif
+struct inode *adfs_iget(struct super_block *sb, struct object_info *obj);
+void adfs_read_inode(struct inode *inode);
+void adfs_write_inode(struct inode *inode);
+int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
+
+/* map.c */
+extern int adfs_map_lookup(struct super_block *sb, int frag_id, int offset);
+extern unsigned int adfs_map_free(struct super_block *sb);
+
+/* Misc */
+void __adfs_error(struct super_block *sb, const char *function,
+		  const char *fmt, ...);
+#define adfs_error(sb, fmt...) __adfs_error(sb, __FUNCTION__, fmt)
+
+/* namei.c */
+extern struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry);
+
+/* super.c */
+
+/*
+ * Inodes and file operations
+ */
+
+/* dir_*.c */
+extern struct inode_operations adfs_dir_inode_operations;
+extern struct file_operations adfs_dir_operations;
+extern struct adfs_dir_ops adfs_f_dir_ops;
+extern struct adfs_dir_ops adfs_fplus_dir_ops;
+
+extern int adfs_dir_update(struct super_block *sb, struct object_info *obj);
+
+/* file.c */
+extern struct inode_operations adfs_file_inode_operations;
+extern struct file_operations adfs_file_operations;
+
+extern inline __u32 signed_asl(__u32 val, signed int shift)
+{
+	if (shift >= 0)
+		val <<= shift;
+	else
+		val >>= -shift;
+	return val;
+}
+
+/*
+ * Calculate the address of a block in an object given the block offset
+ * and the object identity.
+ *
+ * The root directory ID should always be looked up in the map [3.4]
+ */
+extern inline int
+__adfs_block_map(struct super_block *sb, unsigned int object_id,
+		 unsigned int block)
+{
+	if (object_id & 255) {
+		unsigned int off;
+
+		off = (object_id & 255) - 1;
+		block += off << sb->u.adfs_sb.s_log2sharesize;
+	}
+
+	return adfs_map_lookup(sb, object_id >> 8, block);
+}
diff -urN linux-2.2.18-pre8/fs/adfs/dir.c linux/fs/adfs/dir.c
--- linux-2.2.18-pre8/fs/adfs/dir.c	Thu May 13 23:25:58 1999
+++ linux/fs/adfs/dir.c	Mon Dec  3 20:25:20 2001
@@ -1,347 +1,297 @@
 /*
- *  linux/fs/adfs/dir.c
+ * linux/fs/adfs/dir.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1999-2000 Russell King
+ *
+ * Common directory handling for ADFS
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
 
-static ssize_t adfs_dirread (struct file *filp, char *buf,
-			     size_t siz, loff_t *ppos)
-{
-	return -EISDIR;
-}
+#include <asm/spinlock.h>
 
-static int adfs_readdir (struct file *, void *, filldir_t);
-
-static struct file_operations adfs_dir_operations = {
-	NULL,			/* lseek - default */
-	adfs_dirread,		/* read */
-	NULL,			/* write - bad */
-	adfs_readdir,		/* readdir */
-	NULL,			/* select - default */
-	NULL,			/* ioctl */
-	NULL,			/* mmap */
-	NULL,			/* no special open code */
-	NULL,			/* flush */
-	NULL,			/* no special release code */
-	file_fsync,		/* fsync */
-	NULL,			/* fasync */
-	NULL,			/* check_media_change */
-	NULL			/* revalidate */
-};
+#include "adfs.h"
 
 /*
- * directories can handle most operations...
+ * For future.  This should probably be per-directory.
  */
-struct inode_operations adfs_dir_inode_operations = {
-	&adfs_dir_operations,	/* default directory file-ops */
-	NULL,			/* create */
-	adfs_lookup,		/* lookup */
-	NULL,			/* link */
-	NULL,			/* unlink */
-	NULL,			/* symlink */
-	NULL,			/* mkdir */
-	NULL,			/* rmdir */
-	NULL,			/* mknod */
-	NULL,			/* rename */
-	NULL,			/* read link */
-	NULL,			/* follow link */
-	NULL,			/* read page */
-	NULL,			/* write page */
-	NULL,			/* bmap */
-	NULL,			/* truncate */
-	NULL,			/* permission */
-	NULL			/* smap */
-};
+static rwlock_t adfs_dir_lock;
 
-unsigned int adfs_val (unsigned char *p, int len)
+static int
+adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	unsigned int val = 0;
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+	struct object_info obj;
+	struct adfs_dir dir;
+	int ret = 0;
+
+	if (filp->f_pos >> 32)
+		goto out;
+
+	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+	if (ret)
+		goto out;
+
+	switch ((unsigned long)filp->f_pos) {
+	case 0:
+		if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
+			goto free_out;
+		filp->f_pos += 1;
+
+	case 1:
+		if (filldir(dirent, "..", 2, 1, dir.parent_id) < 0)
+			goto free_out;
+		filp->f_pos += 1;
 
-	switch (len) {
-	case 4:
-		val |= p[3] << 24;
-	case 3:
-		val |= p[2] << 16;
-	case 2:
-		val |= p[1] << 8;
 	default:
-		val |= p[0];
+		break;
 	}
-	return val;
-}
-
-static unsigned int adfs_filetype (unsigned int load)
-{
-	if ((load & 0xfff00000) != 0xfff00000)
-		return (unsigned int) -1;
-	return (load >> 8) & 0xfff;
-}
 
-static unsigned int adfs_time (unsigned int load, unsigned int exec)
-{
-	unsigned int high, low;
+	read_lock(&adfs_dir_lock);
 
-	/* Check for unstamped files.  */
-	if ((load & 0xfff00000) != 0xfff00000)
-		return 0;
-
-	high = ((load << 24) | (exec >> 8));
-	low  =  exec & 255;
-
-	/* Files dated pre 1970.  */
-	if (high < 0x336e996a)
-		return 0;
+	ret = ops->setpos(&dir, filp->f_pos - 2);
+	if (ret)
+		goto unlock_out;
+	while (ops->getnext(&dir, &obj) == 0) {
+		if (filldir(dirent, obj.name, obj.name_len,
+			    filp->f_pos, obj.file_id) < 0)
+			goto unlock_out;
+		filp->f_pos += 1;
+	}
 
-	high -= 0x336e996a;
+unlock_out:
+	read_unlock(&adfs_dir_lock);
 
-	/* Files dated post 2038 ish.  */
-	if (high > 0x31ffffff)
-		return 0x7fffffff;
-
-	/* 65537 = h256,l1
-	 * (h256 % 100) = 56         h256 / 100 = 2
-	 *    56 << 8 = 14336           2 * 256 = 512
-	 *      + l1 = 14337
-	 *        / 100 = 143
-	 *          + 512 = 655
-	 */
-	return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
-}
+free_out:
+	ops->free(&dir);
 
-int adfs_readname (char *buf, char *ptr, int maxlen)
-{
-	int size = 0;
-	while (*ptr >= ' ' && maxlen--) {
-		switch (*ptr) {
-		case '/':
-			*buf++ = '.';
-			break;
-		default:
-			*buf++ = *ptr;
-			break;
-		}
-		ptr++;
-		size ++;
-	}
-	*buf = '\0';
-	return size;
+out:
+	return ret;
 }
 
-int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp)
+int
+adfs_dir_update(struct super_block *sb, struct object_info *obj)
 {
-	struct super_block *sb;
-	int i, size;
+	int ret = -EINVAL;
+#if 0
+	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+	struct adfs_dir dir;
 
-	sb = inode->i_sb;
+	printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
+		 obj->file_id, obj->parent_id);
 
-	size = 2048 >> sb->s_blocksize_bits;
-
-	for (i = 0; i < size; i++) {
-		int block;
-
-		block = adfs_parent_bmap (inode, i);
-		if (block)
-			bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
-		else
-			adfs_error (sb, "adfs_dir_read_parent",
-				    "directory %lu with a hole at offset %d", inode->i_ino, i);
-		if (!block || !bhp[i]) {
-			int j;
-			for (j = i - 1; j >= 0; j--)
-				brelse (bhp[j]);
-			return 0;
-		}
+	if (!ops->update) {
+		ret = -EINVAL;
+		goto out;
 	}
-	return i;
+
+	ret = ops->read(sb, obj->parent_id, 0, &dir);
+	if (ret)
+		goto out;
+
+	write_lock(&adfs_dir_lock);
+	ret = ops->update(&dir, obj);
+	write_unlock(&adfs_dir_lock);
+
+	ops->free(&dir);
+out:
+#endif
+	return ret;
 }
 
-int adfs_dir_read (struct inode *inode, struct buffer_head **bhp)
+static int
+adfs_match(struct qstr *name, struct object_info *obj)
 {
-	struct super_block *sb;
-	int i, size;
+	int i;
 
-	if (!inode || !S_ISDIR(inode->i_mode))
+	if (name->len != obj->name_len)
 		return 0;
 
-	sb = inode->i_sb;
+	for (i = 0; i < name->len; i++) {
+		char c1, c2;
 
-	size = inode->i_size >> sb->s_blocksize_bits;
+		c1 = name->name[i];
+		c2 = obj->name[i];
 
-	for (i = 0; i < size; i++) {
-		int block;
+		if (c1 >= 'A' && c1 <= 'Z')
+			c1 += 'a' - 'A';
+		if (c2 >= 'A' && c2 <= 'Z')
+			c2 += 'a' - 'A';
 
-		block = adfs_bmap (inode, i);
-		if (block)
-			bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
-		else
-			adfs_error (sb, "adfs_dir_read",
-				    "directory %lX,%lX with a hole at offset %d",
-				    inode->i_ino, inode->u.adfs_i.file_id, i);
-		if (!block || !bhp[i]) {
-			int j;
-			for (j = i - 1; j >= 0; j--)
-				brelse (bhp[j]);
+		if (c1 != c2)
 			return 0;
-		}
 	}
-	return i;
+	return 1;
 }
 
-int adfs_dir_check (struct inode *inode, struct buffer_head **bhp, int buffers, union adfs_dirtail *dtp)
+static int
+adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
 {
-	struct adfs_dirheader dh;
-	union adfs_dirtail dt;
+	struct super_block *sb = inode->i_sb;
+	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+	struct adfs_dir dir;
+	int ret;
+
+	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+	if (ret)
+		goto out;
+
+	if (inode->u.adfs_i.parent_id != dir.parent_id) {
+		adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
+			   inode->u.adfs_i.parent_id, dir.parent_id);
+		ret = -EIO;
+		goto free_out;
+	}
 
-	memcpy (&dh, bhp[0]->b_data, sizeof (dh));
-	memcpy (&dt, bhp[3]->b_data + 471, sizeof(dt));
+	obj->parent_id = inode->i_ino;
 
-	if (memcmp (&dh.startmasseq, &dt.new.endmasseq, 5) ||
-	    (memcmp (&dh.startname, "Nick", 4) &&
-	     memcmp (&dh.startname, "Hugo", 4))) {
-		adfs_error (inode->i_sb, "adfs_check_dir",
-			    "corrupted directory inode %lX,%lX",
-			    inode->i_ino, inode->u.adfs_i.file_id);
-		return 1;
+	/*
+	 * '.' is handled by reserved_lookup() in fs/namei.c
+	 */
+	if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
+		/*
+		 * Currently unable to fill in the rest of 'obj',
+		 * but this is better than nothing.  We need to
+		 * ascend one level to find it's parent.
+		 */
+		obj->name_len = 0;
+		obj->file_id  = obj->parent_id;
+		goto free_out;
 	}
-	if (dtp)
-		*dtp = dt;
-	return 0;
-}
 
-void adfs_dir_free (struct buffer_head **bhp, int buffers)
-{
-	int i;
+	read_lock(&adfs_dir_lock);
+
+	ret = ops->setpos(&dir, 0);
+	if (ret)
+		goto unlock_out;
+
+	ret = -ENOENT;
+	while (ops->getnext(&dir, obj) == 0) {
+		if (adfs_match(name, obj)) {
+			ret = 0;
+			break;
+		}
+	}
+
+unlock_out:
+	read_unlock(&adfs_dir_lock);
 
-	for (i = buffers - 1; i >= 0; i--)
-		brelse (bhp[i]);
+free_out:
+	ops->free(&dir);
+out:
+	return ret;
 }
 
-/* convert a disk-based directory entry to a Linux ADFS directory entry */
-static inline void
-adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de)
+static ssize_t
+adfs_dir_no_read(struct file *filp, char *buf, size_t siz, loff_t *ppos)
 {
-	ide->name_len =	adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN);
-	ide->file_id  = adfs_val(de->dirinddiscadd, 3);
-	ide->size     = adfs_val(de->dirlen, 4);
-	ide->mode     = de->newdiratts;
-	ide->mtime    = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4));
-	ide->filetype = adfs_filetype(adfs_val(de->dirload, 4));
+	return -EISDIR;
 }
 
-int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
-		  int buffers, int pos, unsigned long parent_object_id,
-		  struct adfs_idir_entry *ide)
-{
-	struct adfs_direntry de;
-	int thissize, buffer, offset;
+struct file_operations adfs_dir_operations = {
+	read:		adfs_dir_no_read,
+	readdir:	adfs_readdir,
+	fsync:		file_fsync,
+};
 
-	offset = pos & (sb->s_blocksize - 1);
-	buffer = pos >> sb->s_blocksize_bits;
+static int
+adfs_hash(struct dentry *parent, struct qstr *qstr)
+{
+	const unsigned int name_len = parent->d_sb->u.adfs_sb.s_namelen;
+	const unsigned char *name;
+	unsigned long hash;
+	int i;
 
-	if (buffer > buffers)
+	if (qstr->len < name_len)
 		return 0;
 
-	thissize = sb->s_blocksize - offset;
-	if (thissize > 26)
-		thissize = 26;
-
-	memcpy (&de, bhp[buffer]->b_data + offset, thissize);
-	if (thissize != 26)
-		memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
+	/*
+	 * Truncate the name in place, avoids
+	 * having to define a compare function.
+	 */
+	qstr->len = i = name_len;
+	name = qstr->name;
+	hash = init_name_hash();
+	while (i--) {
+		char c;
+
+		c = *name++;
+		if (c >= 'A' && c <= 'Z')
+			c += 'a' - 'A';
 
-	if (!de.dirobname[0])
-		return 0;
+		hash = partial_name_hash(c, hash);
+	}
+	qstr->hash = end_name_hash(hash);
 
-	ide->inode_no = adfs_inode_generate (parent_object_id, pos);
-	adfs_dirent_to_idirent(ide, &de);
-	return 1;
+	return 0;
 }
 
-int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
-			 int buffers, unsigned int pos,
-			 struct adfs_idir_entry *ide)
+/*
+ * Compare two names, taking note of the name length
+ * requirements of the underlying filesystem.
+ */
+static int
+adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
 {
-	struct adfs_direntry de;
-	int offset, buffer, thissize;
-
-	offset = pos & (sb->s_blocksize - 1);
-	buffer = pos >> sb->s_blocksize_bits;
-
-	if (buffer > buffers)
-		return 0;
-
-	thissize = sb->s_blocksize - offset;
-	if (thissize > 26)
-		thissize = 26;
-
-	memcpy (&de, bhp[buffer]->b_data + offset, thissize);
-	if (thissize != 26)
-		memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
-
-	if (!de.dirobname[0])
-		return 0;
+	int i;
 
-	adfs_dirent_to_idirent(ide, &de);
-	return 1;
-}	
+	if (entry->len != name->len)
+		return 1;
 
-static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir)
-{
-	struct inode *inode = filp->f_dentry->d_inode;
-	struct super_block *sb;
-	struct buffer_head *bh[4];
-	union  adfs_dirtail dt;
-	unsigned long parent_object_id, dir_object_id;
-	int buffers, pos;
-
-	sb = inode->i_sb;
-
-	if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2)
-		return -ENOENT;
-
-	if (!(buffers = adfs_dir_read (inode, bh))) {
-		adfs_error (sb, "adfs_readdir", "unable to read directory");
-		return -EINVAL;
-	}
+	for (i = 0; i < name->len; i++) {
+		char a, b;
 
-	if (adfs_dir_check (inode, bh, buffers, &dt)) {
-		adfs_dir_free (bh, buffers);
-		return -ENOENT;
-	}
+		a = entry->name[i];
+		b = name->name[i];
 
-	parent_object_id = adfs_val (dt.new.dirparent, 3);
-	dir_object_id = adfs_inode_objid (inode);
+		if (a >= 'A' && a <= 'Z')
+			a += 'a' - 'A';
+		if (b >= 'A' && b <= 'Z')
+			b += 'a' - 'A';
 
-	if (filp->f_pos < 2) {
-		if (filp->f_pos < 1) {
-			if (filldir (dirent, ".", 1, 0, inode->i_ino) < 0)
-				return 0;
-			filp->f_pos ++;
-		}
-		if (filldir (dirent, "..", 2, 1,
-			     adfs_inode_generate (parent_object_id, 0)) < 0)
-			return 0;
-		filp->f_pos ++;
+		if (a != b)
+			return 1;
 	}
+	return 0;
+}
 
-	pos = 5 + (filp->f_pos - 2) * 26;
-	while (filp->f_pos < 79) {
-		struct adfs_idir_entry ide;
-
-		if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, &ide))
-			break;
+struct dentry_operations adfs_dentry_operations = {
+	d_hash:		adfs_hash,
+	d_compare:	adfs_compare,
+};
 
-		if (filldir (dirent, ide.name, ide.name_len, filp->f_pos, ide.inode_no) < 0)
-			return 0;
-		filp->f_pos ++;
-		pos += 26;
+struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = NULL;
+	struct object_info obj;
+	int error;
+
+	dentry->d_op = &adfs_dentry_operations;	
+	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
+	if (error == 0) {
+		error = -EACCES;
+		/*
+		 * This only returns NULL if get_empty_inode
+		 * fails.
+		 */
+		inode = adfs_iget(dir->i_sb, &obj);
+		if (inode)
+			error = 0;
 	}
-	adfs_dir_free (bh, buffers);
-	return 0;
+	d_add(dentry, inode);
+	return ERR_PTR(error);
 }
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations adfs_dir_inode_operations = {
+	default_file_ops:	&adfs_dir_operations,
+	lookup:		adfs_lookup,
+};
diff -urN linux-2.2.18-pre8/fs/adfs/dir_f.c linux/fs/adfs/dir_f.c
--- linux-2.2.18-pre8/fs/adfs/dir_f.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/adfs/dir_f.c	Mon Dec  3 20:25:20 2001
@@ -0,0 +1,475 @@
+/*
+ *  linux/fs/adfs/dir_f.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * E and F format directory handling
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+#include <asm/spinlock.h>
+
+#include "adfs.h"
+#include "dir_f.h"
+
+static void adfs_f_free(struct adfs_dir *dir);
+
+/*
+ * Read an (unaligned) value of length 1..4 bytes
+ */
+static inline unsigned int adfs_readval(unsigned char *p, int len)
+{
+	unsigned int val = 0;
+
+	switch (len) {
+	case 4:		val |= p[3] << 24;
+	case 3:		val |= p[2] << 16;
+	case 2:		val |= p[1] << 8;
+	default:	val |= p[0];
+	}
+	return val;
+}
+
+static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
+{
+	switch (len) {
+	case 4:		p[3] = val >> 24;
+	case 3:		p[2] = val >> 16;
+	case 2:		p[1] = val >> 8;
+	default:	p[0] = val;
+	}
+}
+
+static inline int adfs_readname(char *buf, char *ptr, int maxlen)
+{
+	char *old_buf = buf;
+
+	while (*ptr >= ' ' && maxlen--) {
+		if (*ptr == '/')
+			*buf++ = '.';
+		else
+			*buf++ = *ptr;
+		ptr++;
+	}
+	*buf = '\0';
+
+	return buf - old_buf;
+}
+
+static inline void adfs_writename(char *to, char *from, int maxlen)
+{
+	int i;
+
+	for (i = 0; i < maxlen; i++) {
+		if (from[i] == '\0')
+			break;
+		if (from[i] == '.')
+			to[i] = '/';
+		else
+			to[i] = from[i];
+	}
+
+	for (; i < maxlen; i++)
+		to[i] = '\0';
+}
+
+#define ror13(v) ((v >> 13) | (v << 19))
+
+#define dir_u8(idx)				\
+	({ int _buf = idx >> blocksize_bits;	\
+	   int _off = idx - (_buf << blocksize_bits);\
+	  *(u8 *)(bh[_buf]->b_data + _off);	\
+	})
+
+#define dir_u32(idx)				\
+	({ int _buf = idx >> blocksize_bits;	\
+	   int _off = idx - (_buf << blocksize_bits);\
+	  *(u32 *)(bh[_buf]->b_data + _off);	\
+	})
+
+#define bufoff(_bh,_idx)			\
+	({ int _buf = _idx >> blocksize_bits;	\
+	   int _off = _idx - (_buf << blocksize_bits);\
+	  (u8 *)(_bh[_buf]->b_data + _off);	\
+	})
+
+/*
+ * There are some algorithms that are nice in
+ * assembler, but a bitch in C...  This is one
+ * of them.
+ */
+static u8
+adfs_dir_checkbyte(const struct adfs_dir *dir)
+{
+	struct buffer_head * const *bh = dir->bh;
+	const int blocksize_bits = dir->sb->s_blocksize_bits;
+	union { u32 *ptr32; u8 *ptr8; } ptr, end;
+	u32 dircheck = 0;
+	int last = 5 - 26;
+	int i = 0;
+
+	/*
+	 * Accumulate each word up to the last whole
+	 * word of the last directory entry.  This
+	 * can spread across several buffer heads.
+	 */
+	do {
+		last += 26;
+		do {
+			dircheck = cpu_to_le32(dir_u32(i)) ^ ror13(dircheck);
+
+			i += sizeof(u32);
+		} while (i < (last & ~3));
+	} while (dir_u8(last) != 0);
+
+	/*
+	 * Accumulate the last few bytes.  These
+	 * bytes will be within the same bh.
+	 */
+	if (i != last) {
+		ptr.ptr8 = bufoff(bh, i);
+		end.ptr8 = ptr.ptr8 + last - i;
+
+		do
+			dircheck = *ptr.ptr8++ ^ ror13(dircheck);
+		while (ptr.ptr8 < end.ptr8);
+	}
+
+	/*
+	 * The directory tail is in the final bh
+	 * Note that contary to the RISC OS PRMs,
+	 * the first few bytes are NOT included
+	 * in the check.  All bytes are in the
+	 * same bh.
+	 */
+	ptr.ptr8 = bufoff(bh, 2008);
+	end.ptr8 = ptr.ptr8 + 36;
+
+	do {
+		unsigned int v = *ptr.ptr32++;
+		dircheck = cpu_to_le32(v) ^ ror13(dircheck);
+	} while (ptr.ptr32 < end.ptr32);
+
+	return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
+}
+
+/*
+ * Read and check that a directory is valid
+ */
+int
+adfs_dir_read(struct super_block *sb, unsigned long object_id,
+	      unsigned int size, struct adfs_dir *dir)
+{
+	const unsigned int blocksize_bits = sb->s_blocksize_bits;
+	int blk = 0;
+
+	/*
+	 * Directories which are not a multiple of 2048 bytes
+	 * are considered bad v2 [3.6]
+	 */
+	if (size & 2047)
+		goto bad_dir;
+
+	size >>= blocksize_bits;
+
+	dir->nr_buffers = 0;
+	dir->sb = sb;
+
+	for (blk = 0; blk < size; blk++) {
+		int phys;
+
+		phys = __adfs_block_map(sb, object_id, blk);
+		if (!phys) {
+			adfs_error(sb, "dir object %lX has a hole at offset %d",
+				   object_id, blk);
+			goto release_buffers;
+		}
+
+		dir->bh[blk] = bread(sb->s_dev, phys, sb->s_blocksize);
+		if (!dir->bh[blk])
+			goto release_buffers;
+	}
+
+	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
+	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
+
+	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
+	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
+		goto bad_dir;
+
+	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
+	    memcmp(&dir->dirhead.startname, "Hugo", 4))
+		goto bad_dir;
+
+	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
+		goto bad_dir;
+
+	dir->nr_buffers = blk;
+
+	return 0;
+
+bad_dir:
+	adfs_error(sb, "corrupted directory fragment %lX",
+		   object_id);
+release_buffers:
+	for (blk -= 1; blk >= 0; blk -= 1)
+		brelse(dir->bh[blk]);
+
+	dir->sb = NULL;
+
+	return -EIO;
+}
+
+/*
+ * convert a disk-based directory entry to a Linux ADFS directory entry
+ */
+static inline void
+adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
+{
+	obj->name_len =	adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
+	obj->file_id  = adfs_readval(de->dirinddiscadd, 3);
+	obj->loadaddr = adfs_readval(de->dirload, 4);
+	obj->execaddr = adfs_readval(de->direxec, 4);
+	obj->size     = adfs_readval(de->dirlen,  4);
+	obj->attr     = de->newdiratts;
+}
+
+/*
+ * convert a Linux ADFS directory entry to a disk-based directory entry
+ */
+static inline void
+adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
+{
+	adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
+	adfs_writeval(de->dirload, 4, obj->loadaddr);
+	adfs_writeval(de->direxec, 4, obj->execaddr);
+	adfs_writeval(de->dirlen,  4, obj->size);
+	de->newdiratts = obj->attr;
+}
+
+/*
+ * get a directory entry.  Note that the caller is responsible
+ * for holding the relevent locks.
+ */
+int
+__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
+{
+	struct super_block *sb = dir->sb;
+	struct adfs_direntry de;
+	int thissize, buffer, offset;
+
+	buffer = pos >> sb->s_blocksize_bits;
+
+	if (buffer > dir->nr_buffers)
+		return -EINVAL;
+
+	offset = pos & (sb->s_blocksize - 1);
+	thissize = sb->s_blocksize - offset;
+	if (thissize > 26)
+		thissize = 26;
+
+	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
+	if (thissize != 26)
+		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
+		       26 - thissize);
+
+	if (!de.dirobname[0])
+		return -ENOENT;
+
+	adfs_dir2obj(obj, &de);
+
+	return 0;
+}
+
+int
+__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
+{
+	struct super_block *sb = dir->sb;
+	struct adfs_direntry de;
+	int thissize, buffer, offset;
+
+	buffer = pos >> sb->s_blocksize_bits;
+
+	if (buffer > dir->nr_buffers)
+		return -EINVAL;
+
+	offset = pos & (sb->s_blocksize - 1);
+	thissize = sb->s_blocksize - offset;
+	if (thissize > 26)
+		thissize = 26;
+
+	/*
+	 * Get the entry in total
+	 */
+	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
+	if (thissize != 26)
+		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
+		       26 - thissize);
+
+	/*
+	 * update it
+	 */
+	adfs_obj2dir(&de, obj);
+
+	/*
+	 * Put the new entry back
+	 */
+	memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
+	if (thissize != 26)
+		memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
+		       26 - thissize);
+
+	return 0;
+}
+
+/*
+ * the caller is responsible for holding the necessary
+ * locks.
+ */
+static int
+adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
+{
+	int pos, ret;
+
+	ret = -ENOENT;
+
+	for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
+		struct object_info obj;
+
+		if (!__adfs_dir_get(dir, pos, &obj))
+			break;
+
+		if (obj.file_id == object_id) {
+			ret = pos;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int
+adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
+{
+	int ret;
+
+	if (sz != ADFS_NEWDIR_SIZE)
+		return -EIO;
+
+	ret = adfs_dir_read(sb, id, sz, dir);
+	if (ret)
+		adfs_error(sb, "unable to read directory");
+	else
+		dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
+
+	return ret;
+}
+
+static int
+adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
+{
+	if (fpos >= ADFS_NUM_DIR_ENTRIES)
+		return -ENOENT;
+
+	dir->pos = 5 + fpos * 26;
+	return 0;
+}
+
+static int
+adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
+{
+	unsigned int ret;
+
+	ret = __adfs_dir_get(dir, dir->pos, obj);
+	if (ret == 0)
+		dir->pos += 26;
+
+	return ret;
+}
+
+static int
+adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
+{
+	struct super_block *sb = dir->sb;
+	int ret, i;
+
+	ret = adfs_dir_find_entry(dir, obj->file_id);
+	if (ret < 0) {
+		adfs_error(dir->sb, "unable to locate entry to update");
+		goto out;
+	}
+
+	__adfs_dir_put(dir, ret, obj);
+ 
+	/*
+	 * Increment directory sequence number
+	 */
+	dir->bh[0]->b_data[0] += 1;
+	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
+
+	ret = adfs_dir_checkbyte(dir);
+	/*
+	 * Update directory check byte
+	 */
+	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
+
+#if 1
+	{
+	const unsigned int blocksize_bits = sb->s_blocksize_bits;
+
+	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
+	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
+
+	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
+	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
+		goto bad_dir;
+
+	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
+	    memcmp(&dir->dirhead.startname, "Hugo", 4))
+		goto bad_dir;
+
+	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
+		goto bad_dir;
+	}
+#endif
+	for (i = dir->nr_buffers - 1; i >= 0; i--)
+		mark_buffer_dirty(dir->bh[i], 1);
+
+	ret = 0;
+out:
+	return ret;
+#if 1
+bad_dir:
+	adfs_error(dir->sb, "whoops!  I broke a directory!");
+	return -EIO;
+#endif
+}
+
+static void
+adfs_f_free(struct adfs_dir *dir)
+{
+	int i;
+
+	for (i = dir->nr_buffers - 1; i >= 0; i--) {
+		brelse(dir->bh[i]);
+		dir->bh[i] = NULL;
+	}
+
+	dir->nr_buffers = 0;
+	dir->sb = NULL;
+}
+
+struct adfs_dir_ops adfs_f_dir_ops = {
+	adfs_f_read,
+	adfs_f_setpos,
+	adfs_f_getnext,
+	adfs_f_update,
+	NULL,
+	NULL,
+	adfs_f_free
+};
diff -urN linux-2.2.18-pre8/fs/adfs/dir_f.h linux/fs/adfs/dir_f.h
--- linux-2.2.18-pre8/fs/adfs/dir_f.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/adfs/dir_f.h	Mon Dec  3 20:25:20 2001
@@ -0,0 +1,61 @@
+/*
+ * linux/fs/adfs/dir_f.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * Structures of directories on the F format disk
+ */
+#ifndef ADFS_DIR_F_H
+#define ADFS_DIR_F_H
+
+/*
+ * Directory header
+ */
+struct adfs_dirheader {
+	unsigned char startmasseq;
+	unsigned char startname[4];
+};
+
+#define ADFS_NEWDIR_SIZE	2048
+#define ADFS_NUM_DIR_ENTRIES	77
+
+/*
+ * Directory entries
+ */
+struct adfs_direntry {
+#define ADFS_F_NAME_LEN 10
+	char dirobname[ADFS_F_NAME_LEN];
+	__u8 dirload[4];
+	__u8 direxec[4];
+	__u8 dirlen[4];
+	__u8 dirinddiscadd[3];
+	__u8 newdiratts;
+};
+
+/*
+ * Directory tail
+ */
+union adfs_dirtail {
+	struct {
+		unsigned char dirlastmask;
+		char dirname[10];
+		unsigned char dirparent[3];
+		char dirtitle[19];
+		unsigned char reserved[14];
+		unsigned char endmasseq;
+		unsigned char endname[4];
+		unsigned char dircheckbyte;
+	} old;
+	struct {
+		unsigned char dirlastmask;
+		unsigned char reserved[2];
+		unsigned char dirparent[3];
+		char dirtitle[19];
+		char dirname[10];
+		unsigned char endmasseq;
+		unsigned char endname[4];
+		unsigned char dircheckbyte;
+	} new;
+};
+
+#endif
diff -urN linux-2.2.18-pre8/fs/adfs/dir_fplus.c linux/fs/adfs/dir_fplus.c
--- linux-2.2.18-pre8/fs/adfs/dir_fplus.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/adfs/dir_fplus.c	Mon Dec  3 20:25:20 2001
@@ -0,0 +1,178 @@
+/*
+ *  linux/fs/adfs/dir_fplus.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+#include <asm/spinlock.h>
+
+#include "adfs.h"
+#include "dir_fplus.h"
+
+static int
+adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
+{
+	struct adfs_bigdirheader *h;
+	struct adfs_bigdirtail *t;
+	unsigned long block;
+	unsigned int blk, size;
+	int i, ret = -EIO;
+
+	dir->nr_buffers = 0;
+
+	block = __adfs_block_map(sb, id, 0);
+	if (!block) {
+		adfs_error(sb, "dir object %X has a hole at offset 0", id);
+		goto out;
+	}
+
+	dir->bh[0] = bread(sb->s_dev, block, sb->s_blocksize);
+	if (!dir->bh[0])
+		goto out;
+	dir->nr_buffers += 1;
+
+	h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+	size = le32_to_cpu(h->bigdirsize);
+	if (size != sz) {
+		printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n"
+				" does not match directory size\n");
+	}
+
+	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
+	    h->bigdirversion[2] != 0 || size & 2047 ||
+	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME))
+		goto out;
+
+	size >>= sb->s_blocksize_bits;
+	for (blk = 1; blk < size; blk++) {
+		block = __adfs_block_map(sb, id, blk);
+		if (!block) {
+			adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
+			goto out;
+		}
+
+		dir->bh[blk] = bread(sb->s_dev, block, sb->s_blocksize);
+		if (!dir->bh[blk])
+			goto out;
+		dir->nr_buffers = blk;
+	}
+
+	t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8));
+
+	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
+	    t->bigdirendmasseq != h->startmasseq ||
+	    t->reserved[0] != 0 || t->reserved[1] != 0)
+		goto out;
+
+	dir->parent_id = le32_to_cpu(h->bigdirparent);
+	dir->sb = sb;
+	return 0;
+out:
+	for (i = 0; i < dir->nr_buffers; i++)
+		brelse(dir->bh[i]);
+	dir->sb = NULL;
+	return ret;
+}
+
+static int
+adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
+{
+	struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+	int ret = -ENOENT;
+
+	if (fpos <= le32_to_cpu(h->bigdirentries)) {
+		dir->pos = fpos;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static void
+dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
+{
+	struct super_block *sb = dir->sb;
+	unsigned int buffer, partial, remainder;
+
+	buffer = offset >> sb->s_blocksize_bits;
+	offset &= sb->s_blocksize - 1;
+
+	partial = sb->s_blocksize - offset;
+
+	if (partial >= len)
+		memcpy(to, dir->bh[buffer]->b_data + offset, len);
+	else {
+		char *c = (char *)to;
+
+		remainder = len - partial;
+
+		memcpy(c, dir->bh[buffer]->b_data + offset, partial);
+		memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder);
+	}
+}
+
+static int
+adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
+{
+	struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+	struct adfs_bigdirentry bde;
+	unsigned int offset;
+	int i, ret = -ENOENT;
+
+	if (dir->pos >= le32_to_cpu(h->bigdirentries))
+		goto out;
+
+	offset = offsetof(struct adfs_bigdirheader, bigdirname);
+	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
+	offset += dir->pos * sizeof(struct adfs_bigdirentry);
+
+	dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
+
+	obj->loadaddr = le32_to_cpu(bde.bigdirload);
+	obj->execaddr = le32_to_cpu(bde.bigdirexec);
+	obj->size     = le32_to_cpu(bde.bigdirlen);
+	obj->file_id  = le32_to_cpu(bde.bigdirindaddr);
+	obj->attr     = le32_to_cpu(bde.bigdirattr);
+	obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
+
+	offset = offsetof(struct adfs_bigdirheader, bigdirname);
+	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
+	offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
+	offset += le32_to_cpu(bde.bigdirobnameptr);
+
+	dir_memcpy(dir, offset, obj->name, obj->name_len);
+	for (i = 0; i < obj->name_len; i++)
+		if (obj->name[i] == '/')
+			obj->name[i] = '.';
+
+	dir->pos += 1;
+	ret = 0;
+out:
+	return ret;
+}
+
+static void
+adfs_fplus_free(struct adfs_dir *dir)
+{
+	int i;
+
+	for (i = 0; i < dir->nr_buffers; i++)
+		brelse(dir->bh[i]);
+	dir->sb = NULL;
+}
+
+struct adfs_dir_ops adfs_fplus_dir_ops = {
+	adfs_fplus_read,
+	adfs_fplus_setpos,
+	adfs_fplus_getnext,
+	NULL,
+	NULL,
+	NULL,
+	adfs_fplus_free
+};
diff -urN linux-2.2.18-pre8/fs/adfs/dir_fplus.h linux/fs/adfs/dir_fplus.h
--- linux-2.2.18-pre8/fs/adfs/dir_fplus.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/adfs/dir_fplus.h	Mon Dec  3 20:25:20 2001
@@ -0,0 +1,41 @@
+/*
+ * linux/fs/adfs/dir_fplus.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * Structures of directories on the F+ format disk
+ */
+
+#define ADFS_FPLUS_NAME_LEN	255
+
+#define BIGDIRSTARTNAME ('S' | 'B' << 8 | 'P' << 16 | 'r' << 24)
+#define BIGDIRENDNAME	('o' | 'v' << 8 | 'e' << 16 | 'n' << 24)
+
+struct adfs_bigdirheader {
+	__u8	startmasseq;
+	__u8	bigdirversion[3];
+	__u32	bigdirstartname;
+	__u32	bigdirnamelen;
+	__u32	bigdirsize;
+	__u32	bigdirentries;
+	__u32	bigdirnamesize;
+	__u32	bigdirparent;
+	char	bigdirname[1];
+};
+
+struct adfs_bigdirentry {
+	__u32	bigdirload;
+	__u32	bigdirexec;
+	__u32	bigdirlen;
+	__u32	bigdirindaddr;
+	__u32	bigdirattr;
+	__u32	bigdirobnamelen;
+	__u32	bigdirobnameptr;
+};
+
+struct adfs_bigdirtail {
+	__u32	bigdirendname;
+	__u8	bigdirendmasseq;
+	__u8	reserved[2];
+	__u8	bigdircheckbyte;
+};
diff -urN linux-2.2.18-pre8/fs/adfs/file.c linux/fs/adfs/file.c
--- linux-2.2.18-pre8/fs/adfs/file.c	Mon Dec  3 20:24:43 2001
+++ linux/fs/adfs/file.c	Mon Dec  3 20:25:20 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/fs/adfs/file.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  * from:
  *
  *  linux/fs/ext2/file.c
@@ -19,51 +19,23 @@
  *
  *  adfs regular file handling primitives           
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/fcntl.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
 
-/*
- * We have mostly NULLs here: the current defaults are OK for
- * the adfs filesystem.
- */
-static struct file_operations adfs_file_operations = {
-	NULL,			/* lseek - default	*/
-	generic_file_read,	/* read			*/
-	NULL,			/* write		*/
-	NULL,			/* readdir - bad	*/
-	NULL,			/* select - default	*/
-	NULL,			/* ioctl		*/
-	generic_file_mmap,	/* mmap			*/
-	NULL,			/* open - not special	*/
-	NULL,			/* flush		*/
-	NULL,			/* release		*/
-	file_fsync,		/* fsync		*/
-	NULL,			/* fasync		*/
-	NULL,			/* check_media_change	*/
-	NULL			/* revalidate		*/
+#include "adfs.h"
+
+struct file_operations adfs_file_operations = {
+	read:		generic_file_read,
+	mmap:		generic_file_mmap,
+	fsync:		file_fsync,
 };
 
 struct inode_operations adfs_file_inode_operations = {
-	&adfs_file_operations,	/* default file operations	*/
-	NULL,			/* create			*/
-	NULL,			/* lookup			*/
-	NULL,			/* link				*/
-	NULL,			/* unlink			*/
-	NULL,			/* symlink			*/
-	NULL,			/* mkdir			*/
-	NULL,			/* rmdir			*/
-	NULL,			/* mknod			*/
-	NULL,			/* rename			*/
-	NULL,			/* readlink			*/
-	NULL,			/* follow_link			*/
-	generic_readpage,	/* readpage			*/
-	NULL,			/* writepage			*/
-	adfs_bmap,		/* bmap				*/
-	NULL,			/* truncate			*/
-	NULL,			/* permission			*/
-	NULL			/* smap				*/
+	default_file_ops:	&adfs_file_operations,
+	readpage:	generic_readpage,
+	bmap:		adfs_bmap,
 };
diff -urN linux-2.2.18-pre8/fs/adfs/inode.c linux/fs/adfs/inode.c
--- linux-2.2.18-pre8/fs/adfs/inode.c	Tue Jan  4 10:12:22 2000
+++ linux/fs/adfs/inode.c	Mon Dec  3 20:25:20 2001
@@ -1,9 +1,9 @@
 /*
  *  linux/fs/adfs/inode.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
@@ -13,210 +13,310 @@
 #include <linux/locks.h>
 #include <linux/mm.h>
 
+#include "adfs.h"
+
 /*
- * Old Inode numbers:
- *  bit 30 - 16		FragID of parent object
- *  bit 15		0			1
- *  bit 14 - 0		FragID of object	Offset into parent FragID
- *
- * New Inode numbers:
- *  Inode = Frag ID of parent (14) + Frag Offset (8) + (index into directory + 1)(8)
+ * Lookup/Create a block at offset 'block' into 'inode'.  We currently do
+ * not support creation of new blocks, so we return -EIO for this case.
  */
-#define inode_frag(ino)		((ino) >> 8)
-#define inode_idx(ino)		((ino) & 0xff)
-#define inode_dirindex(idx)	(((idx) & 0xff) * 26 - 21)
+int adfs_bmap(struct inode *inode, int block)
+{
+	if (block >= inode->i_blocks)
+		return 0;
 
-#define frag_id(x)		(((x) >> 8) & 0x7fff)
-#define off(x)			(((x) & 0xff) ? (((x) & 0xff) - 1) << sb->u.adfs_sb.s_dr->log2sharesize : 0)
+	return __adfs_block_map(inode->i_sb, inode->i_ino, block);
+}
 
-static inline int adfs_inode_validate_no (struct super_block *sb, unsigned int inode_no)
+static inline unsigned int
+adfs_filetype(struct inode *inode)
 {
-	unsigned long max_frag_id;
+	unsigned int type;
 
-	max_frag_id = sb->u.adfs_sb.s_map_size * sb->u.adfs_sb.s_ids_per_zone;
+	if (inode->u.adfs_i.stamped)
+		type = (inode->u.adfs_i.loadaddr >> 8) & 0xfff;
+	else
+		type = (unsigned int) -1;
 
-	return (inode_no & 0x800000ff) ||
-	       (frag_id (inode_frag (inode_no)) > max_frag_id) ||
-	       (frag_id (inode_frag (inode_no)) < 2);
+	return type;
 }
 
-int adfs_inode_validate (struct inode *inode)
+/*
+ * Convert ADFS attributes and filetype to Linux permission.
+ */
+static umode_t
+adfs_atts2mode(struct super_block *sb, struct inode *inode)
 {
-	struct super_block *sb = inode->i_sb;
+	unsigned int filetype, attr = inode->u.adfs_i.attr;
+	umode_t mode, rmask;
 
-	return adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00) ||
-	       adfs_inode_validate_no (sb, inode->u.adfs_i.file_id << 8);
-}
+	if (attr & ADFS_NDA_DIRECTORY) {
+		mode = S_IRUGO & sb->u.adfs_sb.s_owner_mask;
+		return S_IFDIR | S_IXUGO | mode;
+	}
 
-unsigned long adfs_inode_generate (unsigned long parent_id, int diridx)
-{
-	if (!parent_id)
-		return -1;
+	filetype = adfs_filetype(inode);
+
+	switch (filetype) {
+	case 0xfc0:	/* LinkFS */
+		return S_IFLNK|S_IRWXUGO;
+
+	case 0xfe6:	/* UnixExec */
+		rmask = S_IRUGO | S_IXUGO;
+		break;
+
+	default:
+		rmask = S_IRUGO;
+	}
+
+	mode = S_IFREG;
+
+	if (attr & ADFS_NDA_OWNER_READ)
+		mode |= rmask & sb->u.adfs_sb.s_owner_mask;
 
-	if (diridx)
-		diridx = (diridx + 21) / 26;
+	if (attr & ADFS_NDA_OWNER_WRITE)
+		mode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask;
 
-	return (parent_id << 8) | diridx;
+	if (attr & ADFS_NDA_PUBLIC_READ)
+		mode |= rmask & sb->u.adfs_sb.s_other_mask;
+
+	if (attr & ADFS_NDA_PUBLIC_WRITE)
+		mode |= S_IWUGO & sb->u.adfs_sb.s_other_mask;
+	return mode;
 }
 
-unsigned long adfs_inode_objid (struct inode *inode)
+/*
+ * Convert Linux permission to ADFS attribute.  We try to do the reverse
+ * of atts2mode, but there is not a 1:1 translation.
+ */
+static int
+adfs_mode2atts(struct super_block *sb, struct inode *inode)
 {
-	if (adfs_inode_validate (inode)) {
-		adfs_error (inode->i_sb, "adfs_inode_objid",
-			    "bad inode number: %lu (%X,%X)",
-			    inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
-		return 0;
-	}
+	umode_t mode;
+	int attr;
+
+	/* FIXME: should we be able to alter a link? */
+	if (S_ISLNK(inode->i_mode))
+		return inode->u.adfs_i.attr;
+
+	if (S_ISDIR(inode->i_mode))
+		attr = ADFS_NDA_DIRECTORY;
+	else
+		attr = 0;
 
-	return inode->u.adfs_i.file_id;
+	mode = inode->i_mode & sb->u.adfs_sb.s_owner_mask;
+	if (mode & S_IRUGO)
+		attr |= ADFS_NDA_OWNER_READ;
+	if (mode & S_IWUGO)
+		attr |= ADFS_NDA_OWNER_WRITE;
+
+	mode = inode->i_mode & sb->u.adfs_sb.s_other_mask;
+	mode &= ~sb->u.adfs_sb.s_owner_mask;
+	if (mode & S_IRUGO)
+		attr |= ADFS_NDA_PUBLIC_READ;
+	if (mode & S_IWUGO)
+		attr |= ADFS_NDA_PUBLIC_WRITE;
+
+	return attr;
 }
 
-int adfs_bmap (struct inode *inode, int block)
+/*
+ * Convert an ADFS time to Unix time.  ADFS has a 40-bit centi-second time
+ * referenced to 1 Jan 1900 (til 2248)
+ */
+static unsigned int
+adfs_adfs2unix_time(struct inode *inode)
 {
-	struct super_block *sb = inode->i_sb;
-	unsigned int blk;
+	unsigned int high, low;
 
-	if (adfs_inode_validate (inode)) {
-		adfs_error (sb, "adfs_bmap",
-			    "bad inode number: %lu (%X,%X)",
-			    inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
-		return 0;
-	}
+	if (inode->u.adfs_i.stamped == 0)
+		return CURRENT_TIME;
 
-	if (block < 0) {
-		adfs_error(sb, "adfs_bmap", "block(%d) < 0", block);
-		return 0;
-	}
+	high = inode->u.adfs_i.loadaddr << 24;
+	low  = inode->u.adfs_i.execaddr;
 
-	if (block > inode->i_blocks)
+	high |= low >> 8;
+	low  &= 255;
+
+	/* Files dated pre  01 Jan 1970 00:00:00. */
+	if (high < 0x336e996a)
 		return 0;
 
-	block += off(inode->u.adfs_i.file_id);
+	/* Files dated post 18 Jan 2038 03:14:05. */
+	if (high >= 0x656e9969)
+		return 0x7ffffffd;
 
-	if (frag_id(inode->u.adfs_i.file_id) == ADFS_ROOT_FRAG)
-		blk = sb->u.adfs_sb.s_map_block + block;
-	else
-		blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), block);
-	return blk;
+	/* discard 2208988800 (0x336e996a00) seconds of time */
+	high -= 0x336e996a;
+
+	/* convert 40-bit centi-seconds to 32-bit seconds */
+	return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
 }
 
-unsigned int adfs_parent_bmap (struct inode *inode, int block)
+/*
+ * Convert an Unix time to ADFS time.  We only do this if the entry has a
+ * time/date stamp already.
+ */
+static void
+adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
 {
-	struct super_block *sb = inode->i_sb;
-	unsigned int blk, fragment;
+	unsigned int high, low;
 
-	if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
-		adfs_error (sb, "adfs_parent_bmap",
-			    "bad inode number: %lu (%X,%X)",
-			    inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
-		return 0;
+	if (inode->u.adfs_i.stamped) {
+		/* convert 32-bit seconds to 40-bit centi-seconds */
+		low  = (secs & 255) * 100;
+		high = (secs / 256) * 100 + (low << 8) + 0x336e996a;
+
+		inode->u.adfs_i.loadaddr = (high >> 24) |
+				(inode->u.adfs_i.loadaddr & ~0xff);
+		inode->u.adfs_i.execaddr = (low & 255) | (high << 8);
 	}
-
-	fragment = inode_frag (inode->i_ino);
-	if (frag_id (fragment) == ADFS_ROOT_FRAG)
-		blk = sb->u.adfs_sb.s_map_block + off(fragment) + block;
-	else
-		blk = adfs_map_lookup (sb, frag_id (fragment), off(fragment) + block);
-	return blk;	
 }
 
-static int adfs_atts2mode(struct super_block *sb, unsigned char mode, unsigned int filetype)
+/*
+ * Fill in the inode information from the object information.
+ *
+ * Note that this is an inode-less filesystem, so we can't use the inode
+ * number to reference the metadata on the media.  Instead, we use the
+ * inode number to hold the object ID, which in turn will tell us where
+ * the data is held.  We also save the parent object ID, and with these
+ * two, we can locate the metadata.
+ *
+ * This does mean that we rely on an objects parent remaining the same at
+ * all times - we cannot cope with a cross-directory rename (yet).
+ */
+struct inode *
+adfs_iget(struct super_block *sb, struct object_info *obj)
 {
-	int omode = 0;
+	struct inode *inode;
 
-	if (filetype == 0xfc0 /* LinkFS */) {
-		omode = S_IFLNK|S_IRUSR|S_IWUSR|S_IXUSR|
-				S_IRGRP|S_IWGRP|S_IXGRP|
-				S_IROTH|S_IWOTH|S_IXOTH;
-	} else {
-		if (mode & ADFS_NDA_DIRECTORY) {
-			omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask;
-			omode |= S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH;
-		} else
-			omode |= S_IFREG;
+	inode = get_empty_inode();
+	if (!inode)
+		goto out;
 
-		if (mode & ADFS_NDA_OWNER_READ) {
-			omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask;
-			if (filetype == 0xfe6 /* UnixExec */)
-				omode |= S_IXUGO & sb->u.adfs_sb.s_owner_mask;
-		}
+	inode->i_version = ++global_event;
+	inode->i_sb	 = sb;
+	inode->i_dev	 = sb->s_dev;
+	inode->i_uid	 = sb->u.adfs_sb.s_uid;
+	inode->i_gid	 = sb->u.adfs_sb.s_gid;
+	inode->i_ino	 = obj->file_id;
+	inode->i_size	 = obj->size;
+	inode->i_nlink	 = 2;
+	inode->i_blksize = PAGE_SIZE;
+	inode->i_blocks	 = (inode->i_size + sb->s_blocksize - 1) >>
+			    sb->s_blocksize_bits;
+
+	/*
+	 * we need to save the parent directory ID so that
+	 * write_inode can update the directory information
+	 * for this file.  This will need special handling
+	 * for cross-directory renames.
+	 */
+	inode->u.adfs_i.parent_id = obj->parent_id;
+	inode->u.adfs_i.loadaddr  = obj->loadaddr;
+	inode->u.adfs_i.execaddr  = obj->execaddr;
+	inode->u.adfs_i.attr      = obj->attr;
+	inode->u.adfs_i.stamped	  = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
+
+	inode->i_mode	 = adfs_atts2mode(sb, inode);
+	inode->i_mtime	 =
+	inode->i_atime	 =
+	inode->i_ctime	 = adfs_adfs2unix_time(inode);
+
+	if (S_ISDIR(inode->i_mode)) {
+		inode->i_op	= &adfs_dir_inode_operations;
+	} else if (S_ISREG(inode->i_mode)) {
+		inode->i_op	= &adfs_file_inode_operations;
+	}
 
-		if (mode & ADFS_NDA_OWNER_WRITE)
-			omode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask;
+	insert_inode_hash(inode);
 
-		if (mode & ADFS_NDA_PUBLIC_READ) {
-			omode |= S_IRUGO & sb->u.adfs_sb.s_other_mask;
-			if (filetype == 0xfe6 /* UnixExec */)
-				omode |= S_IXUGO & sb->u.adfs_sb.s_other_mask;
-		}
+out:
+	return inode;
+}
 
-		if (mode & ADFS_NDA_PUBLIC_WRITE)
-			omode |= S_IWUGO & sb->u.adfs_sb.s_other_mask;
-	}
-	return omode;
+/*
+ * This is no longer a valid way to obtain the metadata associated with the
+ * inode number on this filesystem.  This means that this filesystem cannot
+ * be shared via NFS.
+ */
+void adfs_read_inode(struct inode *inode)
+{
+	adfs_error(inode->i_sb, "unsupported method of reading inode");
+	make_bad_inode(inode);
 }
 
-void adfs_read_inode (struct inode *inode)
+/*
+ * Validate and convert a changed access mode/time to their ADFS equivalents.
+ * adfs_write_inode will actually write the information back to the directory
+ * later.
+ */
+int
+adfs_notify_change(struct dentry *dentry, struct iattr *attr)
 {
-	struct super_block *sb;
-	struct buffer_head *bh[4];
-	struct adfs_idir_entry ide;
-	int buffers;
+	struct inode *inode = dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	unsigned int ia_valid = attr->ia_valid;
+	int error;
 
-	sb = inode->i_sb;
-	inode->i_uid = sb->u.adfs_sb.s_uid;
-	inode->i_gid = sb->u.adfs_sb.s_gid;
-	inode->i_version = ++global_event;
+	error = inode_change_ok(inode, attr);
 
-	if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
-		adfs_error (sb, "adfs_read_inode",
-			    "bad inode number: %lu", inode->i_ino);
-		goto bad;
-	}
-
-	if (frag_id(inode_frag (inode->i_ino)) == ADFS_ROOT_FRAG &&
-	    inode_idx (inode->i_ino) == 0) {
-		/* root dir */
-		inode->i_mode	 = S_IRWXUGO | S_IFDIR;
-		inode->i_nlink	 = 2;
-		inode->i_size	 = ADFS_NEWDIR_SIZE;
-		inode->i_blksize = PAGE_SIZE;
-		inode->i_blocks  = inode->i_size / sb->s_blocksize;
-		inode->i_mtime   =
-		inode->i_atime   =
-		inode->i_ctime   = 0;
-		inode->u.adfs_i.file_id = inode_frag (inode->i_ino);
-	} else {
-		if (!(buffers = adfs_dir_read_parent (inode, bh)))
-			goto bad;
-
-		if (adfs_dir_check (inode, bh, buffers, NULL)) {
-			adfs_dir_free (bh, buffers);
-			goto bad;
-		}
-
-		if (!adfs_dir_find_entry (sb, bh, buffers, inode_dirindex (inode->i_ino), &ide)) {
-			adfs_dir_free (bh, buffers);
-			goto bad;
-		}
-		adfs_dir_free (bh, buffers);
-		inode->i_mode	 = adfs_atts2mode(sb, ide.mode, ide.filetype);
-		inode->i_nlink	 = 2;
-		inode->i_size    = ide.size;
-		inode->i_blksize = PAGE_SIZE;
-		inode->i_blocks	 = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
-		inode->i_mtime	 =
-		inode->i_atime   =
-		inode->i_ctime   = ide.mtime;
-		inode->u.adfs_i.file_id = ide.file_id;
+	/*
+	 * we can't change the UID or GID of any file -
+	 * we have a global UID/GID in the superblock
+	 */
+	if ((ia_valid & ATTR_UID && attr->ia_uid != sb->u.adfs_sb.s_uid) ||
+	    (ia_valid & ATTR_GID && attr->ia_gid != sb->u.adfs_sb.s_gid))
+		error = -EPERM;
+
+	if (error)
+		goto out;
+
+	if (ia_valid & ATTR_SIZE)
+		inode->i_size = attr->ia_size;
+	if (ia_valid & ATTR_MTIME) {
+		inode->i_mtime = attr->ia_mtime;
+		adfs_unix2adfs_time(inode, attr->ia_mtime);
+	}
+	/*
+	 * FIXME: should we make these == to i_mtime since we don't
+	 * have the ability to represent them in our filesystem?
+	 */
+	if (ia_valid & ATTR_ATIME)
+		inode->i_atime = attr->ia_atime;
+	if (ia_valid & ATTR_CTIME)
+		inode->i_ctime = attr->ia_ctime;
+	if (ia_valid & ATTR_MODE) {
+		inode->u.adfs_i.attr = adfs_mode2atts(sb, inode);
+		inode->i_mode = adfs_atts2mode(sb, inode);
 	}
 
-	if (S_ISDIR(inode->i_mode))
-		inode->i_op	 = &adfs_dir_inode_operations;
-	else if (S_ISREG(inode->i_mode))
-		inode->i_op	 = &adfs_file_inode_operations;
-	return;
+	/*
+	 * FIXME: should we be marking this inode dirty even if
+	 * we don't have any metadata to write back?
+	 */
+	if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE))
+		mark_inode_dirty(inode);
+out:
+	return error;
+}
 
-bad:
-	make_bad_inode(inode);
+/*
+ * write an existing inode back to the directory, and therefore the disk.
+ * The adfs-specific inode data has already been updated by
+ * adfs_notify_change()
+ */
+void adfs_write_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct object_info obj;
+
+	obj.file_id	= inode->i_ino;
+	obj.name_len	= 0;
+	obj.parent_id	= inode->u.adfs_i.parent_id;
+	obj.loadaddr	= inode->u.adfs_i.loadaddr;
+	obj.execaddr	= inode->u.adfs_i.execaddr;
+	obj.attr	= inode->u.adfs_i.attr;
+	obj.size	= inode->i_size;
+
+	adfs_dir_update(sb, &obj);
 }
diff -urN linux-2.2.18-pre8/fs/adfs/map.c linux/fs/adfs/map.c
--- linux-2.2.18-pre8/fs/adfs/map.c	Tue Jan 20 16:44:58 1998
+++ linux/fs/adfs/map.c	Mon Dec  3 20:25:20 2001
@@ -1,108 +1,288 @@
 /*
  *  linux/fs/adfs/map.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
 
-static inline unsigned int
-adfs_convert_map_to_sector (const struct super_block *sb, unsigned int mapoff)
-{
-	if (sb->u.adfs_sb.s_map2blk >= 0)
-		mapoff <<= sb->u.adfs_sb.s_map2blk;
-	else
-		mapoff >>= -sb->u.adfs_sb.s_map2blk;
-	return mapoff;
-}
+#include <asm/spinlock.h>
 
-static inline unsigned int
-adfs_convert_sector_to_map (const struct super_block *sb, unsigned int secoff)
-{
-	if (sb->u.adfs_sb.s_map2blk >= 0)
-		secoff >>= sb->u.adfs_sb.s_map2blk;
-	else
-		secoff <<= -sb->u.adfs_sb.s_map2blk;
-	return secoff;
-}
+#include "adfs.h"
 
-static int lookup_zone (struct super_block *sb, int zone, int frag_id, int *offset)
-{
-	unsigned int mapptr, idlen, mapsize;
-	unsigned long *map;
+/*
+ * For the future...
+ */
+static rwlock_t adfs_map_lock;
 
-	map     = ((unsigned long *)sb->u.adfs_sb.s_map[zone]->b_data) + 1;
-	zone    =
-	mapptr  = zone == 0 ? (ADFS_DR_SIZE << 3) : 0;
-	idlen   = sb->u.adfs_sb.s_idlen;
-	mapsize = sb->u.adfs_sb.s_zonesize;
+/*
+ * return the map bit offset of the fragment frag_id in
+ * the zone dm.
+ * Note that the loop is optimised for best asm code -
+ * look at the output of:
+ *  gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
+ */
+static int
+lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
+	    const unsigned int frag_id, unsigned int *offset)
+{
+	const unsigned int mapsize = dm->dm_endbit;
+	const unsigned int idmask = (1 << idlen) - 1;
+	unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
+	unsigned int start = dm->dm_startbit;
+	unsigned int mapptr;
 
 	do {
-		unsigned long v1, v2;
-		unsigned int start;
+		unsigned long frag;
 
-		v1 = map[mapptr>>5];
-		v2 = map[(mapptr>>5)+1];
+		/*
+		 * get fragment id
+		 */
+		{
+			unsigned long v2;
+			unsigned int tmp;
 
-		v1 = (v1 >> (mapptr & 31)) | (v2 << (32 - (mapptr & 31)));
-		start = mapptr;
-		mapptr += idlen;
+			tmp = start >> 5;
+
+			frag = le32_to_cpu(map[tmp]);
+			v2   = le32_to_cpu(map[tmp + 1]);
+
+			tmp  = start & 31;
 
-		v2 = map[mapptr >> 5] >> (mapptr & 31);
-		if (!v2) {
-			mapptr = (mapptr + 32) & ~31;
-			for (; (v2 = map[mapptr >> 5]) == 0 && mapptr < mapsize; mapptr += 32);
+			frag = (frag >> tmp) | (v2 << (32 - tmp));
+
+			frag &= idmask;
 		}
-		for (; (v2 & 255) == 0; v2 >>= 8, mapptr += 8);
-		for (; (v2 & 1) == 0; v2 >>= 1, mapptr += 1);
-		mapptr += 1;
-
-		if ((v1 & ((1 << idlen) - 1)) == frag_id) {
-			int length = mapptr - start;
-			if (*offset >= length)
-				*offset -= length;
-			else
-				return start + *offset - zone;
+
+		mapptr = start + idlen;
+
+		/*
+		 * find end of fragment
+		 */
+		{
+			unsigned long v2;
+
+			while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+				mapptr = (mapptr & ~31) + 32;
+				if (mapptr >= mapsize)
+					goto error;
+			}
+
+			mapptr += 1 + ffz(~v2);
 		}
+
+		if (frag == frag_id)
+			goto found;
+again:
+		start = mapptr;
 	} while (mapptr < mapsize);
+
+error:
 	return -1;
+
+found:
+	{
+		int length = mapptr - start;
+		if (*offset >= length) {
+			*offset -= length;
+			goto again;
+		}
+	}
+	return start + *offset;
 }
 
-int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+/*
+ * Scan the free space map, for this zone, calculating the total
+ * number of map bits in each free space fragment.
+ *
+ * Note: idmask is limited to 15 bits [3.2]
+ */
+static unsigned int
+scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 {
-	unsigned int start_zone, zone, max_zone, mapoff, secoff;
+	const unsigned int mapsize = dm->dm_endbit + 32;
+	const unsigned int idlen  = asb->s_idlen;
+	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
+	const unsigned int idmask = (1 << frag_idlen) - 1;
+	unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
+	unsigned int start = 8, mapptr;
+	unsigned long frag;
+	unsigned long total = 0;
+
+	/*
+	 * get fragment id
+	 */
+	{
+		unsigned long v2;
+		unsigned int tmp;
+
+		tmp = start >> 5;
 
-	zone = start_zone = frag_id / sb->u.adfs_sb.s_ids_per_zone;
-	max_zone = sb->u.adfs_sb.s_map_size;
+		frag = le32_to_cpu(map[tmp]);
+		v2   = le32_to_cpu(map[tmp + 1]);
 
-	if (start_zone >= max_zone) {
-		adfs_error (sb, "adfs_map_lookup", "fragment %X is invalid (zone = %d, max = %d)",
-			    frag_id, start_zone, max_zone);
-		return 0;
+		tmp  = start & 31;
+
+		frag = (frag >> tmp) | (v2 << (32 - tmp));
+
+		frag &= idmask;
 	}
 
-	/* Convert sector offset to map offset */
-	mapoff = adfs_convert_sector_to_map (sb, offset);
-	/* Calculate sector offset into map block */
-	secoff = offset - adfs_convert_map_to_sector (sb, mapoff);
+	/*
+	 * If the freelink is null, then no free fragments
+	 * exist in this zone.
+	 */
+	if (frag == 0)
+		return 0;
 
 	do {
-		int result = lookup_zone (sb, zone, frag_id, &mapoff);
+		start += frag;
+
+		/*
+		 * get fragment id
+		 */
+		{
+			unsigned long v2;
+			unsigned int tmp;
+
+			tmp = start >> 5;
+
+			frag = le32_to_cpu(map[tmp]);
+			v2   = le32_to_cpu(map[tmp + 1]);
+
+			tmp  = start & 31;
 
-		if (result != -1) {
-			result += zone ? (zone * sb->u.adfs_sb.s_zonesize) - (ADFS_DR_SIZE << 3): 0;
-			return adfs_convert_map_to_sector (sb, result) + secoff;
+			frag = (frag >> tmp) | (v2 << (32 - tmp));
+
+			frag &= idmask;
+		}
+
+		mapptr = start + idlen;
+
+		/*
+		 * find end of fragment
+		 */
+		{
+			unsigned long v2;
+
+			while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+				mapptr = (mapptr & ~31) + 32;
+				if (mapptr >= mapsize)
+					goto error;
+			}
+
+			mapptr += 1 + ffz(~v2);
 		}
 
-		zone ++;
-		if (zone >= max_zone)
-			zone = 0;
+		total += mapptr - start;
+	} while (frag >= idlen + 1);
+
+	if (frag != 0)
+		printk(KERN_ERR "adfs: undersized free fragment\n");
+
+	return total;
+error:
+	printk(KERN_ERR "adfs: oversized free fragment\n");
+	return 0;
+}
+
+static int
+scan_map(struct adfs_sb_info *asb, unsigned int zone,
+	 const unsigned int frag_id, unsigned int mapoff)
+{
+	const unsigned int idlen = asb->s_idlen;
+	struct adfs_discmap *dm, *dm_end;
+	int result;
+
+	dm	= asb->s_map + zone;
+	zone	= asb->s_map_size;
+	dm_end	= asb->s_map + zone;
+
+	do {
+		result = lookup_zone(dm, idlen, frag_id, &mapoff);
+
+		if (result != -1)
+			goto found;
+
+		dm ++;
+		if (dm == dm_end)
+			dm = asb->s_map;
+	} while (--zone > 0);
+
+	return -1;
+found:
+	result -= dm->dm_startbit;
+	result += dm->dm_startblk;
+
+	return result;
+}
+
+/*
+ * calculate the amount of free blocks in the map.
+ *
+ *              n=1
+ *  total_free = E(free_in_zone_n)
+ *              nzones
+ */
+unsigned int
+adfs_map_free(struct super_block *sb)
+{
+	struct adfs_sb_info *asb = &sb->u.adfs_sb;
+	struct adfs_discmap *dm;
+	unsigned int total = 0;
+	unsigned int zone;
+
+	dm   = asb->s_map;
+	zone = asb->s_map_size;
+
+	do {
+		total += scan_free_map(asb, dm++);
+	} while (--zone > 0);
+
+	return signed_asl(total, asb->s_map2blk);
+}
 
-	} while (zone != start_zone);
+int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+{
+	struct adfs_sb_info *asb = &sb->u.adfs_sb;
+	unsigned int zone, mapoff;
+	int result;
+
+	/*
+	 * map & root fragment is special - it starts in the center of the
+	 * disk.  The other fragments start at zone (frag / ids_per_zone)
+	 */
+	if (frag_id == ADFS_ROOT_FRAG)
+		zone = asb->s_map_size >> 1;
+	else
+		zone = frag_id / asb->s_ids_per_zone;
+
+	if (zone >= asb->s_map_size)
+		goto bad_fragment;
+
+	/* Convert sector offset to map offset */
+	mapoff = signed_asl(offset, -asb->s_map2blk);
+
+	read_lock(&adfs_map_lock);
+	result = scan_map(asb, zone, frag_id, mapoff);
+	read_unlock(&adfs_map_lock);
+
+	if (result > 0) {
+		unsigned int secoff;
+
+		/* Calculate sector offset into map block */
+		secoff = offset - signed_asl(mapoff, asb->s_map2blk);
+		return secoff + signed_asl(result, asb->s_map2blk);
+	}
 
-	adfs_error (sb, "adfs_map_lookup", "fragment %X at offset %d not found in map (start zone %d)",
-		    frag_id, offset, start_zone);
+	adfs_error(sb, "fragment %04X at offset %d not found in map",
+		   frag_id, offset);
+	return 0;
+
+bad_fragment:
+	adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)",
+		   frag_id, zone, asb->s_map_size);
 	return 0;
 }
diff -urN linux-2.2.18-pre8/fs/adfs/namei.c linux/fs/adfs/namei.c
--- linux-2.2.18-pre8/fs/adfs/namei.c	Thu May 13 23:25:58 1999
+++ linux/fs/adfs/namei.c	Wed Dec 31 16:00:00 1969
@@ -1,113 +0,0 @@
-/*
- *  linux/fs/adfs/namei.c
- *
- * Copyright (C) 1997 Russell King
- */
-
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/adfs_fs.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/locks.h>
-
-/*
- * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure
- */
-static int adfs_match (int len, const char * const name, struct adfs_idir_entry *de)
-{
-	int i;
-
-	if (!de || len > ADFS_NAME_LEN)
-		return 0;
-	/*
-	 * "" means "." ---> so paths like "/usr/lib//libc.a" work
-	 */
-	if (!len && de->name_len == 1 && de->name[0] == '.' &&
-	    de->name[1] == '\0')
-		return 1;
-	if (len != de->name_len)
-		return 0;
-
-	for (i = 0; i < len; i++)
-		if ((de->name[i] ^ name[i]) & 0x5f)
-			return 0;
-	return 1;
-}
-
-static int adfs_find_entry (struct inode *dir, const char * const name, int namelen,
-			    struct adfs_idir_entry *ide)
-{
-	struct super_block *sb;
-	struct buffer_head *bh[4];
-	union  adfs_dirtail dt;
-	unsigned long parent_object_id, dir_object_id;
-	int buffers, pos;
-
-	sb = dir->i_sb;
-
-	if (adfs_inode_validate (dir)) {
-		adfs_error (sb, "adfs_find_entry",
-			    "invalid inode number: %lu", dir->i_ino);
-		return 0;
-	}
-
-	if (!(buffers = adfs_dir_read (dir, bh))) {
-		adfs_error (sb, "adfs_find_entry", "unable to read directory");
-		return 0;
-	}
-
-	if (adfs_dir_check (dir, bh, buffers, &dt)) {
-		adfs_dir_free (bh, buffers);
-		return 0;
-	}
-
-	parent_object_id = adfs_val (dt.new.dirparent, 3);
-	dir_object_id = adfs_inode_objid (dir);
-
-	if (namelen == 2 && name[0] == '.' && name[1] == '.') {
-		ide->name_len = 2;
-		ide->name[0] = ide->name[1] = '.';
-		ide->name[2] = '\0';
-		ide->inode_no = adfs_inode_generate (parent_object_id, 0);
-		adfs_dir_free (bh, buffers);
-		return 1;
-	}
-
-	pos = 5;
-
-	do {
-		if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, ide))
-			break;
-
-		if (adfs_match (namelen, name, ide)) {
-			adfs_dir_free (bh, buffers);
-			return pos;
-		}
-		pos += 26;
-	} while (1);
-	adfs_dir_free (bh, buffers);
-	return 0;
-}
-
-struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry)
-{
-	struct inode *inode = NULL;
-	struct adfs_idir_entry de;
-	unsigned long ino;
-
-	if (dentry->d_name.len > ADFS_NAME_LEN)
-		return ERR_PTR(-ENAMETOOLONG);
-
-	if (adfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de)) {
-		ino = de.inode_no;
-		inode = iget (dir->i_sb, ino);
-
-		if (!inode)
-			return ERR_PTR(-EACCES);
-	}
-	d_add(dentry, inode);
-	return NULL;
-}
diff -urN linux-2.2.18-pre8/fs/adfs/super.c linux/fs/adfs/super.c
--- linux-2.2.18-pre8/fs/adfs/super.c	Fri Mar 26 13:57:41 1999
+++ linux/fs/adfs/super.c	Mon Dec  3 20:25:20 2001
@@ -1,9 +1,9 @@
 /*
  *  linux/fs/adfs/super.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
-
+#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -21,12 +21,11 @@
 
 #include <stdarg.h>
 
-static void adfs_put_super(struct super_block *sb);
-static int adfs_remount(struct super_block *sb, int *flags, char *data);
-static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
-void adfs_read_inode(struct inode *inode);
+#include "adfs.h"
+#include "dir_f.h"
+#include "dir_fplus.h"
 
-void adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
+void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
 {
 	char error_buf[128];
 	va_list args;
@@ -40,7 +39,74 @@
 		function ? function : "", error_buf);
 }
 
-static unsigned char adfs_calczonecheck(struct super_block *sb, char *map)
+static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
+{
+	int i;
+
+	/* sector size must be 256, 512 or 1024 bytes */
+	if (dr->log2secsize != 8 &&
+	    dr->log2secsize != 9 &&
+	    dr->log2secsize != 10)
+		return 1;
+
+	/* idlen must be at least log2secsize + 3 */
+	if (dr->idlen < dr->log2secsize + 3)
+		return 1;
+
+	/* we cannot have such a large disc that we
+	 * are unable to represent sector offsets in
+	 * 32 bits.  This works out at 2.0 TB.
+	 */
+	if (dr->disc_size_high >> dr->log2secsize)
+		return 1;
+
+	/*
+	 * The following checks are not required for F+
+	 * stage 1.
+	 */
+#if 0
+	/* idlen must be smaller be no greater than 15 */
+	if (dr->idlen > 15)
+		return 1;
+
+	/* nzones must be less than 128 for the root
+	 * directory to be addressable
+	 */
+	if (dr->nzones >= 128 && dr->nzones_high == 0)
+		return 1;
+
+	/* root must be of the form 0x2.. */
+	if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
+		return 1;
+#else
+	/*
+	 * Stage 2 F+ does not require the following check
+	 */
+#if 0
+	/* idlen must be no greater than 16 v2 [1.0] */
+	if (dr->idlen > 16)
+		return 1;
+
+	/* we can't handle F+ discs yet */
+	if (dr->format_version || dr->root_size)
+		return 1;
+
+#else
+	/* idlen must be no greater than 19 v2 [1.0] */
+	if (dr->idlen > 19)
+		return 1;
+#endif
+#endif
+
+	/* reserved bytes should be zero */
+	for (i = 0; i < sizeof(dr->unused52); i++)
+		if (dr->unused52[i] != 0)
+			return 1;
+
+	return 0;
+}
+
+static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
 {
 	unsigned int v0, v1, v2, v3;
 	int i;
@@ -64,46 +130,34 @@
 	return v0 ^ v1 ^ v2 ^ v3;
 }
 
-static int adfs_checkmap(struct super_block *sb)
+static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
 {
 	unsigned char crosscheck = 0, zonecheck = 1;
 	int i;
 
 	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
-		char *map;
+		unsigned char *map;
+
+		map = dm[i].dm_bh->b_data;
 
-		map = sb->u.adfs_sb.s_map[i]->b_data;
 		if (adfs_calczonecheck(sb, map) != map[0]) {
-			adfs_error(sb, "adfs_checkmap", "zone %d fails zonecheck", i);
+			adfs_error(sb, "zone %d fails zonecheck", i);
 			zonecheck = 0;
 		}
 		crosscheck ^= map[3];
 	}
 	if (crosscheck != 0xff)
-		adfs_error(sb, "adfs_checkmap", "crosscheck != 0xff");
+		adfs_error(sb, "crosscheck != 0xff");
 	return crosscheck == 0xff && zonecheck;
 }
 
-static struct super_operations adfs_sops = {
-	adfs_read_inode,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	adfs_put_super,
-	NULL,
-	adfs_statfs,
-	adfs_remount
-};
-
 static void adfs_put_super(struct super_block *sb)
 {
 	int i;
 
 	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-		brelse(sb->u.adfs_sb.s_map[i]);
+		brelse(sb->u.adfs_sb.s_map[i].dm_bh);
 	kfree(sb->u.adfs_sb.s_map);
-	brelse(sb->u.adfs_sb.s_sbh);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -159,13 +213,104 @@
 	return parse_options(sb, data);
 }
 
+static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+	struct adfs_sb_info *asb = &sb->u.adfs_sb;
+	struct statfs tmp;
+
+	tmp.f_type    = ADFS_SUPER_MAGIC;
+	tmp.f_namelen = asb->s_namelen;
+	tmp.f_bsize   = sb->s_blocksize;
+	tmp.f_blocks  = asb->s_size;
+	tmp.f_files   = asb->s_ids_per_zone * asb->s_map_size;
+	tmp.f_bavail  =
+	tmp.f_bfree   = adfs_map_free(sb);
+	tmp.f_ffree   = tmp.f_bfree * tmp.f_files / tmp.f_blocks;
+
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+static struct super_operations adfs_sops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+	read_inode:	adfs_read_inode,
+	notify_change:	adfs_notify_change,
+#endif
+	write_inode:	adfs_write_inode,
+	put_super:	adfs_put_super,
+	statfs:		adfs_statfs,
+	remount_fs:	adfs_remount,
+};
+
+static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
+{
+	struct adfs_discmap *dm;
+	unsigned int map_addr, zone_size, nzones;
+	int i, zone;
+
+	nzones    = sb->u.adfs_sb.s_map_size;
+	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
+	map_addr  = (nzones >> 1) * zone_size -
+		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
+	map_addr  = signed_asl(map_addr, sb->u.adfs_sb.s_map2blk);
+
+	sb->u.adfs_sb.s_ids_per_zone = zone_size / (sb->u.adfs_sb.s_idlen + 1);
+
+	dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL);
+	if (dm == NULL) {
+		adfs_error(sb, "not enough memory");
+		return NULL;
+	}
+
+	for (zone = 0; zone < nzones; zone++, map_addr++) {
+		dm[zone].dm_startbit = 0;
+		dm[zone].dm_endbit   = zone_size;
+		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
+		dm[zone].dm_bh       = bread(sb->s_dev, map_addr, sb->s_blocksize);
+
+		if (!dm[zone].dm_bh) {
+			adfs_error(sb, "unable to read map");
+			goto error_free;
+		}
+	}
+
+	/* adjust the limits for the first and last map zones */
+	i = zone - 1;
+	dm[0].dm_startblk = 0;
+	dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
+	dm[i].dm_endbit   = (dr->disc_size_high << (32 - dr->log2bpmb)) +
+			    (dr->disc_size >> dr->log2bpmb) +
+			    (ADFS_DR_SIZE_BITS - i * zone_size);
+
+	if (adfs_checkmap(sb, dm))
+		return dm;
+
+	adfs_error(sb, NULL, "map corrupted");
+
+error_free:
+	while (--zone >= 0)
+		brelse(dm[zone].dm_bh);
+
+	kfree(dm);
+	return NULL;
+}
+
+static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
+{
+	unsigned long discsize;
+
+	discsize  = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
+	discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
+
+	return discsize;
+}
+
 struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent)
 {
 	struct adfs_discrecord *dr;
 	struct buffer_head *bh;
+	struct object_info root_obj;
 	unsigned char *b_data;
 	kdev_t dev = sb->s_dev;
-	int i, j;
 
 	/* set default options */
 	sb->u.adfs_sb.s_uid = 0;
@@ -180,7 +325,7 @@
 	lock_super(sb);
 	set_blocksize(dev, BLOCK_SIZE);
 	if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) {
-		adfs_error(sb, NULL, "unable to read superblock");
+		adfs_error(sb, "unable to read superblock");
 		goto error_unlock;
 	}
 
@@ -192,8 +337,19 @@
 				"%s.\n", kdevname(dev));
 		goto error_free_bh;
 	}
+
 	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
 
+	/*
+	 * Do some sanity checks on the ADFS disc record
+	 */
+	if (adfs_checkdiscrecord(dr)) {
+		if (!silent)
+			printk("VPS: Can't find an adfs filesystem on dev "
+				"%s.\n", kdevname(dev));
+		goto error_free_bh;
+	}
+
 	sb->s_blocksize_bits = dr->log2secsize;
 	sb->s_blocksize = 1 << sb->s_blocksize_bits;
 	if (sb->s_blocksize != BLOCK_SIZE &&
@@ -204,13 +360,13 @@
 		set_blocksize(dev, sb->s_blocksize);
 		bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize);
 		if (!bh) {
-			adfs_error(sb, NULL, "couldn't read superblock on "
+			adfs_error(sb, "couldn't read superblock on "
 				"2nd try.");
 			goto error_unlock;
 		}
 		b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
 		if (adfs_checkbblk(b_data)) {
-			adfs_error(sb, NULL, "disc record mismatch, very weird!");
+			adfs_error(sb, "disc record mismatch, very weird!");
 			goto error_free_bh;
 		}
 		dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
@@ -221,99 +377,72 @@
 				"%s.\n", kdevname(dev));
 		goto error_free_bh;
 	}
-	/* blocksize on this device should now be set to the adfs log2secsize */
-
-	sb->u.adfs_sb.s_sbh		= bh;
-	sb->u.adfs_sb.s_dr		= dr;
 
-	/* s_zone_size = size of 1 zone (1 sector) * bits_in_byte - zone_spare =>
-	 * number of map bits in a zone
-	 */
-	sb->u.adfs_sb.s_zone_size	= (8 << dr->log2secsize) - dr->zone_spare;
-
-	/* s_ids_per_zone = bit size of 1 zone / min. length of fragment block =>
-	 * number of ids in one zone
+	/*
+	 * blocksize on this device should now be set to the ADFS log2secsize
 	 */
-	sb->u.adfs_sb.s_ids_per_zone	= sb->u.adfs_sb.s_zone_size / (dr->idlen + 1);
 
-	/* s_idlen = length of 1 id */
-	sb->u.adfs_sb.s_idlen		= dr->idlen;
+	sb->s_magic		 = ADFS_SUPER_MAGIC;
+	sb->u.adfs_sb.s_idlen	 = dr->idlen;
+	sb->u.adfs_sb.s_map_size = dr->nzones | (dr->nzones_high << 8);
+	sb->u.adfs_sb.s_map2blk	 = dr->log2bpmb - dr->log2secsize;
+	sb->u.adfs_sb.s_size     = adfs_discsize(dr, sb->s_blocksize_bits);
+	sb->u.adfs_sb.s_version  = dr->format_version;
+	sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
 
-	/* map size (in sectors) = number of zones */
-	sb->u.adfs_sb.s_map_size	= dr->nzones;
-
-	/* zonesize = size of sector - zonespare */
-	sb->u.adfs_sb.s_zonesize	= (sb->s_blocksize << 3) - dr->zone_spare;
-
-	/* map start (in sectors) = start of zone (number of zones) / 2 */
-	sb->u.adfs_sb.s_map_block	= (dr->nzones >> 1) * sb->u.adfs_sb.s_zone_size -
-					   ((dr->nzones > 1) ? 8 * ADFS_DR_SIZE : 0);
-
-	/* (signed) number of bits to shift left a map address to a sector address */
-	sb->u.adfs_sb.s_map2blk		= dr->log2bpmb - dr->log2secsize;
-
-	if (sb->u.adfs_sb.s_map2blk >= 0)
-		sb->u.adfs_sb.s_map_block <<= sb->u.adfs_sb.s_map2blk;
-	else
-		sb->u.adfs_sb.s_map_block >>= -sb->u.adfs_sb.s_map2blk;
-
-	printk(KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n",
-		sb->u.adfs_sb.s_zone_size, sb->u.adfs_sb.s_ids_per_zone,
-		sb->u.adfs_sb.s_map_block, sb->u.adfs_sb.s_map_size);
-	printk(KERN_DEBUG "ADFS: sector size %d, map bit size %d, share size %d\n",
-		1 << dr->log2secsize, 1 << dr->log2bpmb,
-		1 << (dr->log2secsize + dr->log2sharesize));
-
-	sb->s_magic = ADFS_SUPER_MAGIC;
-
-	sb->u.adfs_sb.s_map = kmalloc(sb->u.adfs_sb.s_map_size *
-				sizeof(struct buffer_head *), GFP_KERNEL);
-	if (sb->u.adfs_sb.s_map == NULL) {
-		adfs_error(sb, NULL, "not enough memory");
+	sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
+	if (!sb->u.adfs_sb.s_map)
 		goto error_free_bh;
-	}
 
-	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
-		sb->u.adfs_sb.s_map[i] = bread(dev,
-						sb->u.adfs_sb.s_map_block + i,
-						sb->s_blocksize);
-		if (!sb->u.adfs_sb.s_map[i]) {
-			for (j = 0; j < i; j++)
-				brelse(sb->u.adfs_sb.s_map[j]);
-			kfree(sb->u.adfs_sb.s_map);
-			adfs_error(sb, NULL, "unable to read map");
-			goto error_free_bh;
-		}
-	}
-	if (!adfs_checkmap(sb)) {
-		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-			brelse(sb->u.adfs_sb.s_map[i]);
-		adfs_error(sb, NULL, "map corrupted");
-		goto error_free_bh;
-	}
+	brelse(bh);
 
-	dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0]->b_data + 4);
+	/*
+	 * set up enough so that we can read an inode
+	 */
+	sb->s_op = &adfs_sops;
 	unlock_super(sb);
 
+	dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0].dm_bh->b_data + 4);
+
+	root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
+	root_obj.name_len  = 0;
+	root_obj.loadaddr  = 0;
+	root_obj.execaddr  = 0;
+	root_obj.size	   = ADFS_NEWDIR_SIZE;
+	root_obj.attr	   = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ |
+			     ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
+
 	/*
-	 * set up enough so that it can read an inode
+	 * If this is a F+ disk with variable length directories,
+	 * get the root_size from the disc record.
 	 */
-	sb->s_op = &adfs_sops;
-	sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0);
-	sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL);
+	if (sb->u.adfs_sb.s_version) {
+		root_obj.size = dr->root_size;
+		sb->u.adfs_sb.s_dir     = &adfs_fplus_dir_ops;
+		sb->u.adfs_sb.s_namelen = ADFS_FPLUS_NAME_LEN;
+	} else {
+		sb->u.adfs_sb.s_dir     = &adfs_f_dir_ops;
+		sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN;
+	}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj));
+#else
+	sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj), NULL);
+#endif
 	if (!sb->s_root) {
+		int i;
+
 		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-			brelse(sb->u.adfs_sb.s_map[i]);
-		brelse(bh);
-		adfs_error(sb, NULL, "get root inode failed\n");
+			brelse(sb->u.adfs_sb.s_map[i].dm_bh);
+		kfree(sb->u.adfs_sb.s_map);
+		adfs_error(sb, "get root inode failed\n");
 		goto error_dec_use;
 	}
 	return sb;
 
 error_free_bh:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 error_unlock:
 	unlock_super(sb);
 error_dec_use:
@@ -323,70 +452,11 @@
 	return NULL;
 }
 
-static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
-{
-	struct statfs tmp;
-	const unsigned int nidlen = sb->u.adfs_sb.s_idlen + 1;
-
-	tmp.f_type = ADFS_SUPER_MAGIC;
-	tmp.f_bsize = sb->s_blocksize;
-	tmp.f_blocks = sb->u.adfs_sb.s_dr->disc_size_high << (32 - sb->s_blocksize_bits) |
-		       sb->u.adfs_sb.s_dr->disc_size >> sb->s_blocksize_bits;
-	tmp.f_files = tmp.f_blocks >> nidlen;
-	{
-		unsigned int i, j = 0;
-		const unsigned mask = (1 << (nidlen - 1)) - 1;
-		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
-			const char *map = sb->u.adfs_sb.s_map[i]->b_data;
-			unsigned freelink, mapindex = 24;
-			j -= nidlen;
-			do {
-				unsigned char k, l, m;
-				unsigned off = (mapindex - nidlen) >> 3;
-				unsigned rem;
-				const unsigned boff = mapindex & 7;
-
-				/* get next freelink */
-
-				k = map[off++];
-				l = map[off++];
-				m = map[off++];
-				freelink = (m << 16) | (l << 8) | k;
-				rem = freelink >> (boff + nidlen - 1);
-				freelink = (freelink >> boff) & mask;
-				mapindex += freelink;
-
-				/* find its length and add it to running total */
-
-				while (rem == 0) {
-					j += 8;
-					rem = map[off++];
-				}
-				if ((rem & 0xff) == 0) j+=8, rem>>=8;
-				if ((rem & 0xf) == 0) j+=4, rem>>=4;
-				if ((rem & 0x3) == 0) j+=2, rem>>=2;
-				if ((rem & 0x1) == 0) j+=1;
-				j += nidlen - boff;
-				if (freelink <= nidlen) break;
-			} while (mapindex < 8 * sb->s_blocksize);
-			if (mapindex > 8 * sb->s_blocksize)
-				adfs_error(sb, NULL, "oversized free fragment\n");
-			else if (freelink)
-				adfs_error(sb, NULL, "undersized free fragment\n");
-		}
-		tmp.f_bfree = tmp.f_bavail = j <<
-			(sb->u.adfs_sb.s_dr->log2bpmb - sb->s_blocksize_bits);
-	}
-	tmp.f_ffree = tmp.f_bfree >> nidlen;
-	tmp.f_namelen = ADFS_NAME_LEN;
-	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
-}
-
 static struct file_system_type adfs_fs_type = {
 	"adfs", FS_REQUIRES_DEV, adfs_read_super, NULL
 };
 
-__initfunc(int init_adfs_fs(void))
+int __init init_adfs_fs(void)
 {
 	return register_filesystem(&adfs_fs_type);
 }
diff -urN linux-2.2.18-pre8/fs/inode.c linux/fs/inode.c
--- linux-2.2.18-pre8/fs/inode.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/inode.c	Mon Dec  3 20:25:21 2001
@@ -529,7 +529,7 @@
 /*
  * Called with the inode lock held.
  */
-static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head)
+static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
 {
 	struct list_head *tmp;
 	struct inode * inode;
@@ -545,6 +545,8 @@
 			continue;
 		if (inode->i_ino != ino)
 			continue;
+		if (find_actor && !find_actor(inode, ino, opaque))
+			continue;
 		inode->i_count++;
 		break;
 	}
@@ -619,7 +621,7 @@
  * We no longer cache the sb_flags in i_flags - see fs.h
  *	-- rmk@arm.uk.linux.org
  */
-static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head)
+static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
 {
 	struct inode * inode;
 	struct list_head * tmp = inode_unused.next;
@@ -664,7 +666,7 @@
 	inode = grow_inodes();
 	if (inode) {
 		/* We released the lock, so.. */
-		struct inode * old = find_inode(sb, ino, head);
+		struct inode * old = find_inode(sb, ino, head, find_actor, opaque);
 		if (!old)
 			goto add_new_inode;
 		list_add(&inode->i_list, &inode_unused);
@@ -694,7 +696,7 @@
 retry:
 	if (counter > max_reserved) {
 		head = inode_hashtable + hash(sb,counter);
-		inode = find_inode(sb, res = counter++, head);
+		inode = find_inode(sb, res = counter++, head, NULL, NULL);
 		if (!inode) {
 			spin_unlock(&inode_lock);
 			return res;
@@ -720,13 +722,13 @@
 	return inode;
 }
 
-struct inode *iget(struct super_block *sb, unsigned long ino)
+struct inode *iget4(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque)
 {
 	struct list_head * head = inode_hashtable + hash(sb,ino);
 	struct inode * inode;
 
 	spin_lock(&inode_lock);
-	inode = find_inode(sb, ino, head);
+	inode = find_inode(sb, ino, head, find_actor, opaque);
 	if (inode) {
 		spin_unlock(&inode_lock);
 		wait_on_inode(inode);
@@ -737,7 +739,12 @@
 	 * the inode lock and re-trying the search in case it
 	 * had to block at any point.
 	 */
-	return get_new_inode(sb, ino, head);
+	return get_new_inode(sb, ino, head, find_actor, opaque);
+}
+
+struct inode *iget(struct super_block *sb, unsigned long ino)
+{
+	return iget4(sb, ino, NULL, NULL);
 }
 
 void insert_inode_hash(struct inode *inode)
@@ -898,13 +905,13 @@
 	struct inode * inode;
 
 	spin_lock(&inode_lock);
-	inode = find_inode(sb, ino, head);
+	inode = find_inode(sb, ino, head, NULL, NULL);
 	if (inode) {
 		spin_unlock(&inode_lock);
 		wait_on_inode(inode);
 	}
 	else
-		inode = get_new_inode (sb, ino, head);
+		inode = get_new_inode (sb, ino, head, NULL, NULL);
 
 	/* When we get the inode, we have to check if it is in use. We
 	   have to release it if it is not. */
diff -urN linux-2.2.18-pre8/fs/lockd/Makefile linux/fs/lockd/Makefile
--- linux-2.2.18-pre8/fs/lockd/Makefile	Mon Apr  7 11:35:30 1997
+++ linux/fs/lockd/Makefile	Mon Dec  3 20:25:21 2001
@@ -10,6 +10,11 @@
 O_TARGET := lockd.o
 O_OBJS   := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
 	    svcproc.o svcsubs.o mon.o xdr.o
+
+ifdef CONFIG_NFS_V3
+  O_OBJS += xdr4.o
+endif
+
 OX_OBJS  := lockd_syms.o
 M_OBJS   := $(O_TARGET)
 
diff -urN linux-2.2.18-pre8/fs/lockd/clntlock.c linux/fs/lockd/clntlock.c
--- linux-2.2.18-pre8/fs/lockd/clntlock.c	Mon Aug  9 12:05:02 1999
+++ linux/fs/lockd/clntlock.c	Mon Dec  3 20:25:21 2001
@@ -71,7 +71,7 @@
 	 * a 1 minute timeout would do. See the comment before
 	 * nlmclnt_lock for an explanation.
 	 */
-	sleep_on_timeout(&block.b_wait, 30*HZ);
+	interruptible_sleep_on_timeout(&block.b_wait, 30*HZ);
 
 	for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
 		if (*head == &block) {
@@ -152,7 +152,7 @@
 		host->h_monitored = 0;
 		host->h_nsmstate = newstate;
 		host->h_state++;
-		host->h_count++;
+		nlm_get_host(host);
 		kernel_thread(reclaimer, host, 0);
 	}
 }
diff -urN linux-2.2.18-pre8/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c
--- linux-2.2.18-pre8/fs/lockd/clntproc.c	Wed Feb 17 09:44:33 1999
+++ linux/fs/lockd/clntproc.c	Mon Dec  3 20:25:21 2001
@@ -46,11 +46,13 @@
 {
 	struct nlm_args	*argp = &req->a_args;
 	struct nlm_lock	*lock = &argp->lock;
+	struct dentry   *dentry = fl->fl_file->f_dentry;
+	struct nfs_fh   *fh = NFS_FH(dentry);
 
 	memset(argp, 0, sizeof(*argp));
 	nlmclnt_next_cookie(&argp->cookie);
 	argp->state   = nsm_local_state;
-	lock->fh      = *NFS_FH(fl->fl_file->f_dentry);
+	memcpy(&lock->fh, fh, sizeof(*fh));
 	lock->caller  = system_utsname.nodename;
 	lock->oh.data = req->a_owner;
 	lock->oh.len  = sprintf(req->a_owner, "%d@%s",
@@ -100,15 +102,22 @@
 int
 nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
 {
-	struct nfs_server	*nfssrv = NFS_SERVER(inode);
 	struct nlm_host		*host;
 	struct nlm_rqst		reqst, *call = &reqst;
 	sigset_t		oldset;
 	unsigned long		flags;
-	int			status;
+	int			status, proto, vers;
 
-	/* Always use NLM version 1 over UDP for now... */
-	if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), IPPROTO_UDP, 1)))
+	vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
+	if (NFS_PROTO(inode)->version > 3) {
+		printk(KERN_NOTICE "NFSv4 file locking not implemented!\n");
+		return -ENOLCK;
+	}
+
+	/* Retrieve transport protocol from NFS client */
+	proto = NFS_CLIENT(inode)->cl_xprt->prot;
+
+	if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers)))
 		return -ENOLCK;
 
 	/* Create RPC client handle if not there, and copy soft
@@ -122,9 +131,9 @@
 			status = -ENOLCK;
 			goto done;
 		}
-		clnt->cl_softrtry = nfssrv->client->cl_softrtry;
-		clnt->cl_intr     = nfssrv->client->cl_intr;
-		clnt->cl_chatty   = nfssrv->client->cl_chatty;
+		clnt->cl_softrtry = NFS_CLIENT(inode)->cl_softrtry;
+		clnt->cl_intr     = NFS_CLIENT(inode)->cl_intr;
+		clnt->cl_chatty   = NFS_CLIENT(inode)->cl_chatty;
 	}
 
 	/* Keep the old signal mask */
@@ -141,6 +150,10 @@
 		spin_unlock_irqrestore(&current->sigmask_lock, flags);
 
 		call = nlmclnt_alloc_call();
+		if (!call) {
+			status = -ENOMEM;
+			goto out_restore;
+		}
 		call->a_flags = RPC_TASK_ASYNC;
 	} else {
 		spin_unlock_irqrestore(&current->sigmask_lock, flags);
@@ -164,8 +177,9 @@
 	}
 
 	if (status < 0 && (call->a_flags & RPC_TASK_ASYNC))
-		rpc_free(call);
+		kfree(call);
 
+out_restore:
 	spin_lock_irqsave(&current->sigmask_lock, flags);
 	current->blocked = oldset;
 	recalc_sigpending(current);
@@ -199,8 +213,7 @@
 	struct nlm_rqst	*call;
 
 	while (!signalled()) {
-		call = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
-					sizeof(struct nlm_rqst));
+		call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL);
 		if (call)
 			return call;
 		printk("nlmclnt_alloc_call: failed, waiting for memory\n");
@@ -220,11 +233,21 @@
 	struct rpc_clnt	*clnt;
 	struct nlm_args	*argp = &req->a_args;
 	struct nlm_res	*resp = &req->a_res;
+	struct file	*filp = argp->lock.fl.fl_file;
+	struct rpc_message msg;
 	int		status;
 
 	dprintk("lockd: call procedure %s on %s\n",
 			nlm_procname(proc), host->h_name);
 
+	msg.rpc_proc = proc;
+	msg.rpc_argp = argp;
+	msg.rpc_resp = resp;
+	if (filp)
+		msg.rpc_cred = nfs_file_cred(filp);
+	else
+		msg.rpc_cred = NULL;
+
 	do {
 		if (host->h_reclaiming && !argp->reclaim) {
 			interruptible_sleep_on(&host->h_gracewait);
@@ -236,7 +259,7 @@
 			return -ENOLCK;
 
 		/* Perform the RPC call. If an error occurs, try again */
-		if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) {
+		if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
 			dprintk("lockd: rpc_call returned error %d\n", -status);
 			switch (status) {
 			case -EPROTONOSUPPORT:
@@ -245,6 +268,7 @@
 			case -ECONNREFUSED:
 			case -ETIMEDOUT:
 			case -ENOTCONN:
+				nlm_rebind_host(host);
 				status = -EAGAIN;
 				break;
 			case -ERESTARTSYS:
@@ -252,10 +276,7 @@
 			default:
 				break;
 			}
-			if (req->a_args.block)
-				nlm_rebind_host(host);
-			else
-				break;
+			break;
 		} else
 		if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) {
 			dprintk("lockd: server in grace period\n");
@@ -288,13 +309,15 @@
 /*
  * Generic NLM call, async version.
  */
-int
-nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+static int
+_nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback,
+		    struct rpc_cred *cred)
 {
 	struct nlm_host	*host = req->a_host;
 	struct rpc_clnt	*clnt;
 	struct nlm_args	*argp = &req->a_args;
 	struct nlm_res	*resp = &req->a_res;
+	struct rpc_message msg;
 	int		status;
 
 	dprintk("lockd: call procedure %s on %s (async)\n",
@@ -304,16 +327,42 @@
 	if ((clnt = nlm_bind_host(host)) == NULL)
 		return -ENOLCK;
 
+	/* Increment host refcount */
+        nlm_get_host(host);
+
         /* bootstrap and kick off the async RPC call */
-        status = rpc_do_call(clnt, proc, argp, resp, RPC_TASK_ASYNC,
-					callback, req);
+	msg.rpc_proc = proc;
+	msg.rpc_argp = argp;
+	msg.rpc_resp =resp;
+	msg.rpc_cred = cred;
+        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
 
-	/* If the async call is proceeding, increment host refcount */
-        if (status >= 0 && (req->a_flags & RPC_TASK_ASYNC))
-                host->h_count++;
+	if (status < 0)
+		nlm_release_host(host);
 	return status;
 }
 
+
+int
+nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+	struct nlm_args	*argp = &req->a_args;
+	struct file	*filp = argp->lock.fl.fl_file;
+	struct rpc_cred *cred = NULL;
+
+	if (filp)
+		cred = nfs_file_cred(filp);
+
+	return _nlmclnt_async_call(req, proc, callback, cred);
+}
+
+int
+nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+	return _nlmclnt_async_call(req, proc, callback, NULL);
+}
+
+
 /*
  * TEST for the presence of a conflicting lock
  */
@@ -328,7 +377,7 @@
 	status = req->a_res.status;
 	if (status == NLM_LCK_GRANTED) {
 		fl->fl_type = F_UNLCK;
-	} if (status == NLM_LCK_DENIED) {
+	} else if (status == NLM_LCK_DENIED) {
 		/*
 		 * Report the conflicting lock back to the application.
 		 * FIXME: Is it OK to report the pid back as well?
@@ -342,6 +391,21 @@
 	return 0;
 }
 
+static
+void nlmclnt_insert_lock_callback(struct file_lock *fl)
+{
+	nlm_get_host(fl->fl_u.nfs_fl.host);
+}
+static
+void nlmclnt_remove_lock_callback(struct file_lock *fl)
+{
+	if (fl->fl_u.nfs_fl.host) {
+		nlm_release_host(fl->fl_u.nfs_fl.host);
+		fl->fl_u.nfs_fl.host = NULL;
+	}
+}
+
+
 /*
  * LOCK: Try to create a lock
  *
@@ -375,7 +439,7 @@
 		return -ENOLCK;
 	}
 
-	while (1) {
+	do {
 		if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
 			if (resp->status != NLM_LCK_BLOCKED)
 				break;
@@ -383,11 +447,14 @@
 		}
 		if (status < 0)
 			return status;
-	}
+	} while (resp->status == NLM_LCK_BLOCKED);
 
 	if (resp->status == NLM_LCK_GRANTED) {
 		fl->fl_u.nfs_fl.state = host->h_state;
 		fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
+		fl->fl_u.nfs_fl.host = host;
+		fl->fl_insert = nlmclnt_insert_lock_callback;
+		fl->fl_remove = nlmclnt_remove_lock_callback;
 	}
 
 	return nlm_stat_to_errno(resp->status);
@@ -439,15 +506,9 @@
 static int
 nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
 {
-	struct nlm_host	*host = req->a_host;
 	struct nlm_res	*resp = &req->a_res;
 	int		status;
 
-	/* No monitor, no lock: see nlmclnt_lock().
-	 * Since this is an UNLOCK, don't try to setup monitoring here. */
-	if (!host->h_monitored)
-		return -ENOLCK;
-
 	/* Clean the GRANTED flag now so the lock doesn't get
 	 * reclaimed while we're stuck in the unlock call. */
 	fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
@@ -482,17 +543,20 @@
 
 	if (task->tk_status < 0) {
 		dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
-		nlm_rebind_host(req->a_host);
-		rpc_restart_call(task);
-		return;
+		goto retry_unlock;
 	}
 	if (status != NLM_LCK_GRANTED
 	 && status != NLM_LCK_DENIED_GRACE_PERIOD) {
 		printk("lockd: unexpected unlock status: %d\n", status);
 	}
-
-die:
-	rpc_release_task(task);
+ die:
+	nlm_release_host(req->a_host);
+	kfree(req);
+	return;
+ retry_unlock:
+	nlm_rebind_host(req->a_host);
+	rpc_restart_call(task);
+	return;
 }
 
 /*
@@ -515,10 +579,9 @@
 	recalc_sigpending(current);
 	spin_unlock_irqrestore(&current->sigmask_lock, flags);
 
-	do {
-		req = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
-							sizeof(*req));
-	} while (req == NULL);
+	req = nlmclnt_alloc_call();
+	if (!req)
+		return -ENOMEM;
 	req->a_host  = host;
 	req->a_flags = RPC_TASK_ASYNC;
 
@@ -527,7 +590,7 @@
 	status = nlmclnt_async_call(req, NLMPROC_CANCEL,
 					nlmclnt_cancel_callback);
 	if (status < 0)
-		rpc_free(req);
+		kfree(req);
 
 	spin_lock_irqsave(&current->sigmask_lock, flags);
 	current->blocked = oldset;
@@ -568,7 +631,6 @@
 	}
 
 die:
-	rpc_release_task(task);
 	nlm_release_host(req->a_host);
 	kfree(req);
 	return;
diff -urN linux-2.2.18-pre8/fs/lockd/host.c linux/fs/lockd/host.c
--- linux-2.2.18-pre8/fs/lockd/host.c	Mon Mar  1 10:34:56 1999
+++ linux/fs/lockd/host.c	Mon Dec  3 20:25:21 2001
@@ -15,6 +15,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
 
 
 #define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
@@ -105,8 +106,7 @@
 				host->h_next = nlm_hosts[hash];
 				nlm_hosts[hash] = host;
 			}
-			host->h_expires = jiffies + NLM_HOST_EXPIRE;
-			host->h_count++;
+			nlm_get_host(host);
 			up(&nlm_host_sema);
 			return host;
 		}
@@ -171,9 +171,12 @@
 	down(&host->h_sema);
 
 	/* If we've already created an RPC client, check whether
-	 * RPC rebind is required */
+	 * RPC rebind is required
+	 * Note: why keep rebinding if we're on a tcp connection?
+	 */
 	if ((clnt = host->h_rpcclnt) != NULL) {
-		if (time_after_eq(jiffies, host->h_nextrebind)) {
+		xprt = clnt->cl_xprt;
+		if (!xprt->stream && time_after_eq(jiffies, host->h_nextrebind)) {
 			clnt->cl_port = 0;
 			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
 			dprintk("lockd: next rebind in %ld jiffies\n",
@@ -229,13 +232,27 @@
 }
 
 /*
+ * Increment NLM host count
+ */
+struct nlm_host * nlm_get_host(struct nlm_host *host)
+{
+	if (host) {
+		dprintk("lockd: get host %s\n", host->h_name);
+		host->h_count ++;
+		host->h_expires = jiffies + NLM_HOST_EXPIRE;
+	}
+	return host;
+}
+
+/*
  * Release NLM host after use
  */
-void
-nlm_release_host(struct nlm_host *host)
+void nlm_release_host(struct nlm_host *host)
 {
-	dprintk("lockd: release host %s\n", host->h_name);
-	host->h_count -= 1;
+	if (host && host->h_count) {
+		dprintk("lockd: release host %s\n", host->h_name);
+		host->h_count --;
+	}
 }
 
 /*
@@ -307,8 +324,10 @@
 			}
 			dprintk("lockd: delete host %s\n", host->h_name);
 			*q = host->h_next;
+			if (host->h_monitored)
+				nsm_unmonitor(host);
 			if ((clnt = host->h_rpcclnt) != NULL) {
-				if (clnt->cl_users) {
+				if (atomic_read(&clnt->cl_users)) {
 					printk(KERN_WARNING
 						"lockd: active RPC handle\n");
 					clnt->cl_dead = 1;
diff -urN linux-2.2.18-pre8/fs/lockd/mon.c linux/fs/lockd/mon.c
--- linux-2.2.18-pre8/fs/lockd/mon.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/lockd/mon.c	Mon Dec  3 20:25:21 2001
@@ -4,6 +4,10 @@
  * The kernel statd client.
  *
  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Note: In a future release, we should fold all NSM activity into
+ * rpc.mountd and the mount program, respectively. Stuff like this
+ * really doesn't belong in the kernel.	--okir
  */
 
 #include <linux/types.h>
@@ -30,14 +34,12 @@
  * Common procedure for SM_MON/SM_UNMON calls
  */
 static int
-nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc)
+nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res)
 {
 	struct rpc_clnt	*clnt;
 	int		status;
 	struct nsm_args	args;
-	struct nsm_res	res;
 
-	dprintk("lockd: nsm_%s(%s)\n", what, host->h_name);
 	status = -EACCES;
 	clnt = nsm_create();
 	if (!clnt)
@@ -47,23 +49,15 @@
 	args.prog = NLM_PROGRAM;
 	args.vers = 1;
 	args.proc = NLMPROC_NSM_NOTIFY;
+	memset(res, 0, sizeof(*res));
 
-	status = rpc_call(clnt, proc, &args, &res, 0);
-	if (status < 0) {
+	status = rpc_call(clnt, proc, &args, res, 0);
+	if (status < 0)
 		printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
 			status);
-		goto out;
-	}
-
-	status = -EACCES;
-	if (res.status != 0) {
-		printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name);
-		goto out;
-	}
-
-	nsm_local_state = res.state;
-	status = 0;
-out:
+	else
+		status = 0;
+ out:
 	return status;
 }
 
@@ -73,10 +67,16 @@
 int
 nsm_monitor(struct nlm_host *host)
 {
+	struct nsm_res	res;
 	int		status;
 
-	status = nsm_mon_unmon(host, "monitor", SM_MON);
-	if (status >= 0)
+	dprintk("lockd: nsm_monitor(%s)\n", host->h_name);
+
+	status = nsm_mon_unmon(host, SM_MON, &res);
+
+	if (status < 0 || res.status != 0)
+		printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name);
+	else
 		host->h_monitored = 1;
 	return status;
 }
@@ -87,9 +87,15 @@
 int
 nsm_unmonitor(struct nlm_host *host)
 {
+	struct nsm_res	res;
 	int		status;
 
-	if ((status = nsm_mon_unmon(host, "unmonitor", SM_UNMON)) >= 0)
+	dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name);
+
+	status = nsm_mon_unmon(host, SM_UNMON, &res);
+	if (status < 0)
+		printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", host->h_name);
+	else
 		host->h_monitored = 0;
 	return status;
 }
@@ -155,15 +161,15 @@
 	 */
 	sprintf(buffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
 				 	(addr>>8) & 0xff,  (addr) & 0xff);
-	if (!(p = xdr_encode_string(p, buffer))
-	 || !(p = xdr_encode_string(p, system_utsname.nodename)))
+	if (!(p = xdr_encode_string(p, buffer, -1))
+	 || !(p = xdr_encode_string(p, system_utsname.nodename, -1)))
 		return -EIO;
 	*p++ = htonl(argp->prog);
 	*p++ = htonl(argp->vers);
 	*p++ = htonl(argp->proc);
 
 	/* This is the private part. Needed only for SM_MON call */
-	if (rqstp->rq_task->tk_proc == SM_MON) {
+	if (rqstp->rq_task->tk_msg.rpc_proc == SM_MON) {
 		*p++ = argp->addr;
 		*p++ = 0;
 		*p++ = 0;
@@ -187,7 +193,7 @@
 static int
 xdr_decode_stat(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp)
 {
-	resp->status = ntohl(*p++);
+	resp->state = ntohl(*p++);
 	return 0;
 }
 
diff -urN linux-2.2.18-pre8/fs/lockd/svclock.c linux/fs/lockd/svclock.c
--- linux-2.2.18-pre8/fs/lockd/svclock.c	Mon Aug  9 12:05:02 1999
+++ linux/fs/lockd/svclock.c	Mon Dec  3 20:25:21 2001
@@ -520,7 +520,7 @@
 	nlmsvc_insert_block(block, jiffies + 30 * HZ);
 
 	/* Call the client */
-	nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
+	nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
 						nlmsvc_grant_callback);
 	up(&file->f_sema);
 }
@@ -564,7 +564,6 @@
 	block->b_incall = 0;
 
 	nlm_release_host(call->a_host);
-	rpc_release_task(task);
 }
 
 /*
diff -urN linux-2.2.18-pre8/fs/lockd/svcproc.c linux/fs/lockd/svcproc.c
--- linux-2.2.18-pre8/fs/lockd/svcproc.c	Tue Oct 26 17:53:42 1999
+++ linux/fs/lockd/svcproc.c	Mon Dec  3 20:25:21 2001
@@ -476,7 +476,7 @@
 	call->a_host  = host;
 	memcpy(&call->a_args, resp, sizeof(*resp));
 
-	if (nlmclnt_async_call(call, proc, nlmsvc_callback_exit) < 0)
+	if (nlmsvc_async_call(call, proc, nlmsvc_callback_exit) < 0)
 		return rpc_system_err;
 
 	return rpc_success;
@@ -492,7 +492,6 @@
 					task->tk_pid, -task->tk_status);
 	}
 	nlm_release_host(call->a_host);
-	rpc_release_task(task);
 	kfree(call);
 }
 
diff -urN linux-2.2.18-pre8/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c
--- linux-2.2.18-pre8/fs/lockd/svcsubs.c	Tue Oct 26 17:53:42 1999
+++ linux/fs/lockd/svcsubs.c	Mon Dec  3 20:25:21 2001
@@ -48,7 +48,7 @@
 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
 					struct nfs_fh *f)
 {
-	struct knfs_fh	*fh = (struct knfs_fh *) f;
+	struct knfs_fh	*fh = (struct knfs_fh *) f->data;
 	struct nlm_file	*file;
 	unsigned int	hash;
 	u32		nfserr;
diff -urN linux-2.2.18-pre8/fs/lockd/xdr.c linux/fs/lockd/xdr.c
--- linux-2.2.18-pre8/fs/lockd/xdr.c	Sat Feb  6 12:46:21 1999
+++ linux/fs/lockd/xdr.c	Mon Dec  3 20:25:21 2001
@@ -6,6 +6,7 @@
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/utsname.h>
@@ -20,6 +21,7 @@
 
 #define NLMDBG_FACILITY		NLMDBG_XDR
 #define NLM_MAXSTRLEN		1024
+#define OFFSET_MAX		LONG_MAX
 
 #define QUADLEN(len)		(((len) + 3) >> 2)
 
@@ -29,6 +31,8 @@
 
 
 typedef struct nlm_args	nlm_args;
+static void nlm_register_stats(void);
+static void nlm_unregister_stats(void);
 
 /*
  * Initialization of NFS status variables
@@ -48,6 +52,14 @@
 	nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
 
 	inited = 1;
+
+	nlm_register_stats();
+}
+
+void
+nlmxdr_shutdown(void)
+{
+	nlm_unregister_stats();
 }
 
 /*
@@ -93,22 +105,23 @@
 {
 	unsigned int	len;
 
-	if ((len = ntohl(*p++)) != sizeof(*f)) {
+	if ((len = ntohl(*p++)) != NFS2_FHSIZE) {
 		printk(KERN_NOTICE
 			"lockd: bad fhandle size %x (should be %d)\n",
-			len, sizeof(*f));
+			len, NFS2_FHSIZE);
 		return NULL;
 	}
-	memcpy(f, p, sizeof(*f));
-	return p + XDR_QUADLEN(sizeof(*f));
+	f->size = NFS2_FHSIZE;
+	memcpy(f->data, p, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 static inline u32 *
 nlm_encode_fh(u32 *p, struct nfs_fh *f)
 {
-	*p++ = htonl(sizeof(*f));
-	memcpy(p, f, sizeof(*f));
-	return p + XDR_QUADLEN(sizeof(*f));
+	*p++ = htonl(NFS2_FHSIZE);
+	memcpy(p, f->data, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 /*
@@ -145,7 +158,7 @@
 	fl->fl_start = ntohl(*p++);
 	len = ntohl(*p++);
 	if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
-		fl->fl_end = NLM_OFFSET_MAX;
+		fl->fl_end = OFFSET_MAX;
 	return p;
 }
 
@@ -157,17 +170,21 @@
 {
 	struct file_lock	*fl = &lock->fl;
 
-	if (!(p = xdr_encode_string(p, lock->caller))
+	if (!(p = xdr_encode_string(p, lock->caller, -1))
 	 || !(p = nlm_encode_fh(p, &lock->fh))
 	 || !(p = nlm_encode_oh(p, &lock->oh)))
 		return NULL;
 
+	if (fl->fl_start > NLM_OFFSET_MAX
+	 || (fl->fl_end > NLM_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
+		return NULL;
+
 	*p++ = htonl(fl->fl_pid);
-	*p++ = htonl(lock->fl.fl_start);
-	if (lock->fl.fl_end == NLM_OFFSET_MAX)
+	*p++ = htonl(fl->fl_start);
+	if (fl->fl_end == OFFSET_MAX)
 		*p++ = xdr_zero;
 	else
-		*p++ = htonl(lock->fl.fl_end - lock->fl.fl_start + 1);
+		*p++ = htonl(fl->fl_end - fl->fl_start + 1);
 
 	return p;
 }
@@ -193,7 +210,7 @@
 			return 0;
 
 		*p++ = htonl(fl->fl_start);
-		if (fl->fl_end == NLM_OFFSET_MAX)
+		if (fl->fl_end == OFFSET_MAX)
 			*p++ = xdr_zero;
 		else
 			*p++ = htonl(fl->fl_end - fl->fl_start + 1);
@@ -428,7 +445,7 @@
 		fl->fl_start = ntohl(*p++);
 		len = ntohl(*p++);
 		if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
-			fl->fl_end = NLM_OFFSET_MAX;
+			fl->fl_end = OFFSET_MAX;
 	}
 	return 0;
 }
@@ -512,10 +529,10 @@
  */
 #define NLM_void_sz		0
 #define NLM_cookie_sz		3	/* 1 len , 2 data */
-#define NLM_caller_sz		1+QUADLEN(sizeof(system_utsname.nodename))
+#define NLM_caller_sz		1+QUADLEN(NLM_MAXSTRLEN)
 #define NLM_netobj_sz		1+QUADLEN(XDR_MAX_NETOBJ)
 /* #define NLM_owner_sz		1+QUADLEN(NLM_MAXOWNER) */
-#define NLM_fhandle_sz		1+QUADLEN(NFS_FHSIZE)
+#define NLM_fhandle_sz		1+QUADLEN(NFS2_FHSIZE)
 #define NLM_lock_sz		3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz
 #define NLM_holder_sz		4+NLM_netobj_sz
 
@@ -541,7 +558,8 @@
     { "nlm_" #proc,						\
       (kxdrproc_t) nlmclt_encode_##argtype,			\
       (kxdrproc_t) nlmclt_decode_##restype,			\
-      MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2		\
+      MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2,		\
+      0								\
     }
 
 static struct rpc_procinfo	nlm_procedures[] = {
@@ -586,14 +604,21 @@
 	3, 24, nlm_procedures,
 };
 
+#ifdef CONFIG_NFS_V3
+extern struct rpc_version nlm_version4;
+#endif
+
 static struct rpc_version *	nlm_versions[] = {
 	NULL,
 	&nlm_version1,
 	NULL,
 	&nlm_version3,
+#ifdef CONFIG_NFS_V3
+	&nlm_version4,
+#endif
 };
 
-static struct rpc_stat		nlm_stats;
+static struct rpc_stat		nlm_stats = { &nlm_program };
 
 struct rpc_program		nlm_program = {
 	"lockd",
@@ -613,3 +638,13 @@
 }
 #endif
 
+static void nlm_register_stats(void) {
+#ifdef CONFIG_PROC_FS
+	rpc_proc_register(&nlm_stats);
+#endif
+}
+static void nlm_unregister_stats(void) {
+#ifdef CONFIG_PROC_FS
+	rpc_proc_unregister("lockd");
+#endif
+}
diff -urN linux-2.2.18-pre8/fs/lockd/xdr4.c linux/fs/lockd/xdr4.c
--- linux-2.2.18-pre8/fs/lockd/xdr4.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/xdr4.c	Mon Dec  3 20:25:21 2001
@@ -0,0 +1,582 @@
+/*
+ * linux/fs/lockd/xdr.c
+ *
+ * XDR support for lockd and the lock client.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1999, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/utsname.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY		NLMDBG_XDR
+#define NLM_MAXSTRLEN		1024
+#define OFFSET_MAX		((off_t)LONG_MAX)
+
+#define QUADLEN(len)		(((len) + 3) >> 2)
+
+
+typedef struct nlm_args	nlm_args;
+
+static inline off_t
+size_to_off_t(__s64 size)
+{
+        size = (size > (__s64)LONG_MAX) ? (off_t)LONG_MAX : (off_t) size;
+        return (size < (__s64)-LONG_MAX) ? (off_t)-LONG_MAX : (off_t) size;
+}
+
+/*
+ * XDR functions for basic NLM types
+ */
+static u32 *
+nlm4_decode_cookie(u32 *p, struct nlm_cookie *c)
+{
+	unsigned int	len;
+
+	len = ntohl(*p++);
+	
+	if(len==0)
+	{
+		c->len=4;
+		memset(c->data, 0, 4);	/* hockeypux brain damage */
+	}
+	else if(len<=8)
+	{
+		c->len=len;
+		memcpy(c->data, p, len);
+		p+=(len+3)>>2;
+	}
+	else 
+	{
+		printk(KERN_NOTICE
+			"lockd: bad cookie size %d (only cookies under 8 bytes are supported.)\n", len);
+		return NULL;
+	}
+	return p;
+}
+
+static u32 *
+nlm4_encode_cookie(u32 *p, struct nlm_cookie *c)
+{
+	*p++ = htonl(c->len);
+	memcpy(p, c->data, c->len);
+	p+=(c->len+3)>>2;
+	return p;
+}
+
+static u32 *
+nlm4_decode_fh(u32 *p, struct nfs_fh *f)
+{
+	memset(f->data, 0, sizeof(f->data));
+	f->size = ntohl(*p++);
+	if (f->size > NFS_MAXFHSIZE) {
+		printk(KERN_NOTICE
+			"lockd: bad fhandle size %x (should be %d)\n",
+			f->size, NFS_MAXFHSIZE);
+		return NULL;
+	}
+      	memcpy(f->data, p, f->size);
+	return p + XDR_QUADLEN(f->size);
+}
+
+static u32 *
+nlm4_encode_fh(u32 *p, struct nfs_fh *f)
+{
+	*p++ = htonl(f->size);
+	memcpy(p, f->data, f->size);
+	return p + XDR_QUADLEN(f->size);
+}
+
+/*
+ * Encode and decode owner handle
+ */
+static u32 *
+nlm4_decode_oh(u32 *p, struct xdr_netobj *oh)
+{
+	return xdr_decode_netobj(p, oh);
+}
+
+static u32 *
+nlm4_encode_oh(u32 *p, struct xdr_netobj *oh)
+{
+	return xdr_encode_netobj(p, oh);
+}
+
+static u32 *
+nlm4_decode_lock(u32 *p, struct nlm_lock *lock)
+{
+	struct file_lock	*fl = &lock->fl;
+	__s64			len, start, end;
+	int			tmp;
+
+	if (!(p = xdr_decode_string(p, &lock->caller, &tmp, NLM_MAXSTRLEN))
+	 || !(p = nlm4_decode_fh(p, &lock->fh))
+	 || !(p = nlm4_decode_oh(p, &lock->oh)))
+		return NULL;
+
+	memset(fl, 0, sizeof(*fl));
+	fl->fl_owner = current->files;
+	fl->fl_pid   = ntohl(*p++);
+	fl->fl_flags = FL_POSIX;
+	fl->fl_type  = F_RDLCK;		/* as good as anything else */
+	p = xdr_decode_hyper(p, &start);
+	p = xdr_decode_hyper(p, &len);
+	end = start + len - 1;
+
+	fl->fl_start = size_to_off_t(start);
+	fl->fl_end = size_to_off_t(end);
+
+	if (len == 0 || fl->fl_end < 0)
+		fl->fl_end = OFFSET_MAX;
+	return p;
+}
+
+/*
+ * Encode a lock as part of an NLM call
+ */
+static u32 *
+nlm4_encode_lock(u32 *p, struct nlm_lock *lock)
+{
+	struct file_lock	*fl = &lock->fl;
+
+	if (!(p = xdr_encode_string(p, lock->caller, -1))
+	 || !(p = nlm4_encode_fh(p, &lock->fh))
+	 || !(p = nlm4_encode_oh(p, &lock->oh)))
+		return NULL;
+
+	*p++ = htonl(fl->fl_pid);
+	p = xdr_encode_hyper(p, fl->fl_start);
+	if (fl->fl_end == OFFSET_MAX)
+		p = xdr_encode_hyper(p, 0);
+	else
+		p = xdr_encode_hyper(p, fl->fl_end - fl->fl_start + 1);
+
+	return p;
+}
+
+/*
+ * Encode result of a TEST/TEST_MSG call
+ */
+static u32 *
+nlm4_encode_testres(u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+
+	if (resp->status == nlm_lck_denied) {
+		struct file_lock	*fl = &resp->lock.fl;
+
+		*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
+		*p++ = htonl(fl->fl_pid);
+
+		/* Encode owner handle. */
+		if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
+			return 0;
+
+		p = xdr_encode_hyper(p, fl->fl_start);
+		if (fl->fl_end == OFFSET_MAX)
+			p = xdr_encode_hyper(p, 0);
+		else
+			p = xdr_encode_hyper(p, fl->fl_end - fl->fl_start + 1);
+	}
+
+	return p;
+}
+
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_argbuf;
+
+	return p - buf->base <= buf->buflen;
+}
+
+static int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_resbuf;
+
+	buf->len = p - buf->base;
+	return (buf->len <= buf->buflen);
+}
+
+/*
+ * First, the server side XDR functions
+ */
+int
+nlm4svc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+		return 0;
+
+	exclusive = ntohl(*p++);
+	if (!(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_testres(p, resp)))
+		return 0;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+		return 0;
+	argp->block  = ntohl(*p++);
+	exclusive    = ntohl(*p++);
+	if (!(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+	argp->reclaim = ntohl(*p++);
+	argp->state   = ntohl(*p++);
+	argp->monitor = 1;		/* monitor client by default */
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+		return 0;
+	argp->block = ntohl(*p++);
+	exclusive = ntohl(*p++);
+	if (!(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie))
+	 || !(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	argp->lock.fl.fl_type = F_UNLCK;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+	int		len;
+
+	memset(lock, 0, sizeof(*lock));
+	lock->fl.fl_pid = ~(u32) 0;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie))
+	 || !(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))
+	 || !(p = nlm4_decode_fh(p, &lock->fh))
+	 || !(p = nlm4_decode_oh(p, &lock->oh)))
+		return 0;
+	argp->fsm_mode = ntohl(*p++);
+	argp->fsm_access = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+	*p++ = xdr_zero;		/* sequence argument */
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+	int		len;
+
+	if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN)))
+		return 0;
+	argp->state = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp)
+{
+	if (!(p = xdr_decode_string(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
+		return 0;
+	argp->state = ntohl(*p++);
+	argp->addr = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+		return 0;
+	resp->status = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * Now, the client side XDR functions
+ */
+static int
+nlm4clt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+	return 0;
+}
+
+static int
+nlm4clt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+		return -EIO;
+	resp->status = ntohl(*p++);
+	if (resp->status == NLM_LCK_DENIED) {
+		struct file_lock	*fl = &resp->lock.fl;
+		u32			excl;
+		s64			start, end, len;
+
+		memset(&resp->lock, 0, sizeof(resp->lock));
+		excl = ntohl(*p++);
+		fl->fl_pid = ntohl(*p++);
+		if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
+			return -EIO;
+
+		fl->fl_flags = FL_POSIX;
+		fl->fl_type  = excl? F_WRLCK : F_RDLCK;
+		p = xdr_decode_hyper(p, &start);
+		p = xdr_decode_hyper(p, &len);
+		end = start + len - 1;
+
+		fl->fl_start = size_to_off_t(start);
+		fl->fl_end = size_to_off_t(end);
+		if (len == 0 || fl->fl_end < 0)
+			fl->fl_end = OFFSET_MAX;
+	}
+	return 0;
+}
+
+
+static int
+nlm4clt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	*p++ = argp->block? xdr_one : xdr_zero;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	*p++ = argp->reclaim? xdr_one : xdr_zero;
+	*p++ = htonl(argp->state);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	*p++ = argp->block? xdr_one : xdr_zero;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+		return -EIO;
+	*p++ = resp->status;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_testres(p, resp)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+		return -EIO;
+	resp->status = ntohl(*p++);
+	return 0;
+}
+
+/*
+ * Buffer requirements for NLM
+ */
+#define NLM4_void_sz		0
+#define NLM4_cookie_sz		3	/* 1 len , 2 data */
+#define NLM4_caller_sz		1+XDR_QUADLEN(NLM_MAXSTRLEN)
+#define NLM4_netobj_sz		1+XDR_QUADLEN(XDR_MAX_NETOBJ)
+/* #define NLM4_owner_sz		1+XDR_QUADLEN(NLM4_MAXOWNER) */
+#define NLM4_fhandle_sz		1+XDR_QUADLEN(NFS3_FHSIZE)
+#define NLM4_lock_sz		5+NLM4_caller_sz+NLM4_netobj_sz+NLM4_fhandle_sz
+#define NLM4_holder_sz		6+NLM4_netobj_sz
+
+#define NLM4_testargs_sz	NLM4_cookie_sz+1+NLM4_lock_sz
+#define NLM4_lockargs_sz	NLM4_cookie_sz+4+NLM4_lock_sz
+#define NLM4_cancargs_sz	NLM4_cookie_sz+2+NLM4_lock_sz
+#define NLM4_unlockargs_sz	NLM4_cookie_sz+NLM4_lock_sz
+
+#define NLM4_testres_sz		NLM4_cookie_sz+1+NLM4_holder_sz
+#define NLM4_res_sz		NLM4_cookie_sz+1
+#define NLM4_norep_sz		0
+
+#ifndef MAX
+# define MAX(a,b)		(((a) > (b))? (a) : (b))
+#endif
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlm4clt_decode_norep	NULL
+
+#define PROC(proc, argtype, restype)				\
+    { "nlm4_" #proc,						\
+      (kxdrproc_t) nlm4clt_encode_##argtype,			\
+      (kxdrproc_t) nlm4clt_decode_##restype,			\
+      MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2,	\
+      0								\
+    }
+
+static struct rpc_procinfo	nlm4_procedures[] = {
+    PROC(null,		void,		void),
+    PROC(test,		testargs,	testres),
+    PROC(lock,		lockargs,	res),
+    PROC(canc,		cancargs,	res),
+    PROC(unlock,	unlockargs,	res),
+    PROC(granted,	testargs,	res),
+    PROC(test_msg,	testargs,	norep),
+    PROC(lock_msg,	lockargs,	norep),
+    PROC(canc_msg,	cancargs,	norep),
+    PROC(unlock_msg,	unlockargs,	norep),
+    PROC(granted_msg,	testargs,	norep),
+    PROC(test_res,	testres,	norep),
+    PROC(lock_res,	res,		norep),
+    PROC(canc_res,	res,		norep),
+    PROC(unlock_res,	res,		norep),
+    PROC(granted_res,	res,		norep),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+#ifdef NLMCLNT_SUPPORT_SHARES
+    PROC(share,		shareargs,	shareres),
+    PROC(unshare,	shareargs,	shareres),
+    PROC(nm_lock,	lockargs,	res),
+    PROC(free_all,	notify,		void),
+#else
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+#endif
+};
+
+struct rpc_version	nlm_version4 = {
+	4, 24, nlm4_procedures,
+};
diff -urN linux-2.2.18-pre8/fs/locks.c linux/fs/locks.c
--- linux-2.2.18-pre8/fs/locks.c	Mon Dec  3 20:24:43 2001
+++ linux/fs/locks.c	Mon Dec  3 20:25:21 2001
@@ -1130,6 +1130,8 @@
 		new->fl_start = fl->fl_start;
 		new->fl_end = fl->fl_end;
 		new->fl_notify = fl->fl_notify;
+		new->fl_insert = fl->fl_insert;
+		new->fl_remove = fl->fl_remove;
 		new->fl_u = fl->fl_u;
 	}
 	return new;
@@ -1148,6 +1150,9 @@
 	fl->fl_next = *pos;	/* insert into file's list */
 	*pos = fl;
 
+	if (fl->fl_insert)
+		fl->fl_insert(fl);
+
 	return;
 }
 
@@ -1175,6 +1180,9 @@
 		prevfl->fl_nextlink = nextfl;
 	else
 		file_lock_table = nextfl;
+
+	if (thisfl->fl_remove)
+		thisfl->fl_remove(thisfl);
 	
 	locks_wake_up_blocks(thisfl, wait);
 	locks_free_lock(thisfl);
diff -urN linux-2.2.18-pre8/fs/namei.c linux/fs/namei.c
--- linux-2.2.18-pre8/fs/namei.c	Mon Sep  4 10:39:26 2000
+++ linux/fs/namei.c	Mon Dec  3 20:25:21 2001
@@ -136,13 +136,11 @@
  * for filesystem access without changing the "normal" uids which
  * are used for other things..
  */
-int permission(struct inode * inode,int mask)
+int vfs_permission(struct inode * inode,int mask)
 {
 	int mode = inode->i_mode;
 
-	if (inode->i_op && inode->i_op->permission)
-		return inode->i_op->permission(inode, mask);
-	else if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
+	if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
 		 (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
 		return -EROFS; /* Nobody gets write access to a read-only fs */
 	else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
@@ -159,6 +157,13 @@
 		if (capable(CAP_DAC_READ_SEARCH))
 			return 0;
 	return -EACCES;
+}
+
+int permission(struct inode * inode,int mask)
+{
+	if (inode->i_op && inode->i_op->permission)
+		return inode->i_op->permission(inode, mask);
+	return vfs_permission(inode, mask);
 }
 
 /*
diff -urN linux-2.2.18-pre8/fs/nfs/Makefile linux/fs/nfs/Makefile
--- linux-2.2.18-pre8/fs/nfs/Makefile	Wed Jun 24 14:30:10 1998
+++ linux/fs/nfs/Makefile	Mon Dec  3 20:25:21 2001
@@ -9,10 +9,13 @@
 
 O_TARGET := nfs.o
 O_OBJS   := inode.o file.o read.o write.o dir.o symlink.o proc.o \
-	    nfs2xdr.o
+	    nfs2xdr.o flushd.o unlink.o
 
 ifdef CONFIG_ROOT_NFS
   O_OBJS += nfsroot.o mount_clnt.o
+endif
+ifdef CONFIG_NFS_V3
+  O_OBJS += nfs3proc.o nfs3xdr.o
 endif
 
 M_OBJS   := $(O_TARGET)
diff -urN linux-2.2.18-pre8/fs/nfs/dir.c linux/fs/nfs/dir.c
--- linux-2.2.18-pre8/fs/nfs/dir.c	Mon Dec  3 20:24:43 2001
+++ linux/fs/nfs/dir.c	Mon Dec  3 20:25:21 2001
@@ -14,6 +14,9 @@
  *              Following Linus comments on my original hack, this version
  *              depends only on the dcache stuff and doesn't touch the inode
  *              layer (iput() and friends).
+ *  6 Jun 1999  Cache readdir lookups in the page cache. -DaveM
+ *  7 Oct 1999  Rewrite of Dave's readdir stuff for NFSv3 support, and in order
+ *              to simplify cookie handling. -Trond
  */
 
 #include <linux/sched.h>
@@ -23,32 +26,22 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
+#include <asm/pgtable.h>
 #include <linux/mm.h>
 #include <linux/sunrpc/types.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
 
 #include <asm/segment.h>	/* for fs functions */
 
 #define NFS_PARANOIA 1
 /* #define NFS_DEBUG_VERBOSE 1 */
 
-/*
- * Head for a dircache entry. Currently still very simple; when
- * the cache grows larger, we will need a LRU list.
- */
-struct nfs_dirent {
-	dev_t			dev;		/* device number */
-	ino_t			ino;		/* inode number */
-	u32			cookie;		/* cookie of first entry */
-	unsigned short		valid  : 1,	/* data is valid */
-				locked : 1;	/* entry locked */
-	unsigned int		size;		/* # of entries */
-	unsigned long		age;		/* last used */
-	unsigned long		mtime;		/* last attr stamp */
-	struct wait_queue *	wait;
-	__u32 *			entry;		/* three __u32's per entry */
-};
-
 static int nfs_safe_remove(struct dentry *);
 
 static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
@@ -92,278 +85,382 @@
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
 	NULL,			/* readpage */
-	NULL,			/* writepage */
-	NULL,			/* bmap */
+ 	NULL,			/* writepage */
+ 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL,			/* permission */
+	nfs_permission,		/* permission */
 	NULL,			/* smap */
 	NULL,			/* updatepage */
 	nfs_revalidate,		/* revalidate */
 };
 
 static ssize_t
-nfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+nfs_dir_read(struct file *file, char *buf, size_t count, loff_t *ppos)
 {
 	return -EISDIR;
 }
 
-static struct nfs_dirent	dircache[NFS_MAX_DIRCACHE];
+typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
+typedef struct {
+	struct file	*file;
+	struct page	*page;
+	unsigned long	page_index;
+	unsigned	page_offset;
+	u64		target;
+	struct nfs_entry *entry;
+	decode_dirent_t	decode;
+	int		plus;
+	int		error;
+} nfs_readdir_descriptor_t;
 
-/*
- * We need to do caching of directory entries to prevent an
- * incredible amount of RPC traffic.  Only the most recent open
- * directory is cached.  This seems sufficient for most purposes.
- * Technically, we ought to flush the cache on close but this is
- * not a problem in practice.
+/* Now we cache directories properly, by stuffing the dirent
+ * data directly in the page cache.
+ *
+ * Inode invalidation due to refresh etc. takes care of
+ * _everything_, no sloppy entry flushing logic, no extraneous
+ * copying, network direct to page cache, the way it was meant
+ * to be.
  *
- * XXX: Do proper directory caching by stuffing data into the
- * page cache (may require some fiddling for rsize < PAGE_SIZE).
+ * NOTE: Dirent information verification is done always by the
+ *	 page-in of the RPC reply, nowhere else, this simplies
+ *	 things substantially.
  */
-
-static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static
+int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
 {
-	struct dentry 		*dentry = filp->f_dentry;
-	struct inode 		*inode = dentry->d_inode;
-	static struct wait_queue *readdir_wait = NULL;
-	struct wait_queue	**waitp = NULL;
-	struct nfs_dirent	*cache, *free;
-	unsigned long		age, dead;
-	u32			cookie;
-	int			ismydir, result;
-	int			i, j, index = 0;
-	__u32			*entry;
-	char			*name, *start;
-
-	dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
-	if (result < 0)
-		goto out;
-
-	/*
-	 * Try to find the entry in the cache
+	struct file	*file = desc->file;
+	struct dentry	*dir = file->f_dentry;
+	struct inode	*inode = dir->d_inode;
+	struct nfs_fattr dir_attr;
+	void		*buffer = (void *)page_address(page);
+	int		plus = NFS_USE_READDIRPLUS(inode);
+	int		error;
+
+	dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->offset);
+
+ again:
+	error = NFS_CALL(readdir, inode, (dir, &dir_attr,
+					  nfs_file_cred(file),
+					  desc->entry->cookie, buffer,
+					  NFS_SERVER(inode)->dtsize, plus));
+	nfs_refresh_inode(inode, &dir_attr);
+	/* We requested READDIRPLUS, but the server doesn't grok it */
+	if (desc->plus && error == -ENOTSUPP) {
+		NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
+		plus = 0;
+		goto again;
+	}
+	if (error < 0)
+		goto error;
+	flush_dcache_page(page_address(page)); /* Is this correct? */
+	set_bit(PG_uptodate, &page->flags);
+
+	/* Ensure consistent page alignment of the data.
+	 * Note: assumes we have exclusive access to this inode either
+	 *	 throught inode->i_sem or some other mechanism.
 	 */
-again:
-	if (waitp) {
-		interruptible_sleep_on(waitp);
-		if (signal_pending(current))
-			return -ERESTARTSYS;
-		waitp = NULL;
-	}
-
-	cookie = filp->f_pos;
-	entry  = NULL;
-	free   = NULL;
-	age    = ~(unsigned long) 0;
-	dead   = jiffies - NFS_ATTRTIMEO(inode);
-
-	for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
-		/*
-		dprintk("NFS: dircache[%d] valid %d locked %d\n",
-					i, cache->valid, cache->locked);
-		 */
-		ismydir = (cache->dev == inode->i_dev
-				&& cache->ino == inode->i_ino);
-		if (cache->locked) {
-			if (!ismydir || cache->cookie != cookie)
-				continue;
-			dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n");
-			waitp = &cache->wait;
-			goto again;
-		}
-
-		if (ismydir && cache->mtime != inode->i_mtime)
-			cache->valid = 0;
-
-		if (!cache->valid || cache->age < dead) {
-			free = cache;
-			age  = 0;
-		} else if (cache->age < age) {
-			free = cache;
-			age  = cache->age;
-		}
-
-		if (!ismydir || !cache->valid)
-			continue;
-
-		if (cache->cookie == cookie && cache->size > 0) {
-			entry = cache->entry + (index = 0);
-			cache->locked = 1;
-			break;
-		}
-		for (j = 0; j < cache->size; j++) {
-			__u32 *this_ent = cache->entry + j*3;
+	if (page->offset == 0)
+		invalidate_inode_pages(inode);
+	nfs_unlock_page(page);
+	return 0;
+ error:
+	set_bit(PG_error, &page->flags);
+	nfs_unlock_page(page);
+	desc->error = error;
+	return -EIO;
+}
 
-			if (*(this_ent+1) != cookie)
-				continue;
-			if (j < cache->size - 1) {
-				index = j + 1;
-				entry = this_ent + 3;
-			} else if (*(this_ent+2) & (1 << 15)) {
-				/* eof */
-				return 0;
-			}
+/*
+ * Given a pointer to a buffer that has already been filled by a call
+ * to readdir, find the next entry.
+ *
+ * If the end of the buffer has been reached, return -EAGAIN, if not,
+ * return the offset within the buffer of the next entry to be
+ * read.
+ */
+static inline
+int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
+{
+	struct nfs_entry *entry = desc->entry;
+	char		*start = (char *)page_address(page),
+			*p = start;
+	int		loop_count = 0,
+			status = 0;
+
+	for(;;) {
+		p = (char *)desc->decode((u32*)p, entry, desc->plus);
+		if (IS_ERR(p)) {
+			status = PTR_ERR(p);
 			break;
 		}
-		if (entry) {
-			dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
-						(int)(cache - dircache));
-			cache->locked = 1;
+		desc->page_offset = p - start;
+		dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie);
+		if (entry->prev_cookie == desc->target)
 			break;
+		if (loop_count++ > 200) {
+			loop_count = 0;
+			schedule();
 		}
 	}
+	dfprintk(VFS, "NFS: find_dirent() returns %d\n", status);
+	return status;
+}
 
-	/*
-	 * Okay, entry not present in cache, or locked and inaccessible.
-	 * Set up the cache entry and attempt a READDIR call.
-	 */
-	if (entry == NULL) {
-		if ((cache = free) == NULL) {
-			dfprintk(DIRCACHE, "NFS: dircache contention\n");
-			waitp = &readdir_wait;
-			goto again;
-		}
-		dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n",
-				(int)(free - dircache));
-		cache->cookie = cookie;
-		cache->locked = 1;
-		cache->valid  = 0;
-		cache->dev    = inode->i_dev;
-		cache->ino    = inode->i_ino;
-		if (!cache->entry) {
-			result = -ENOMEM;
-			cache->entry = (__u32 *) get_free_page(GFP_KERNEL);
-			if (!cache->entry)
-				goto done;
-		}
-
-		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
-					cookie, PAGE_SIZE, cache->entry);
-		if (result <= 0)
-			goto done;
-		cache->size  = result;
-		cache->valid = 1;
-		entry = cache->entry + (index = 0);
-	}
-	cache->mtime = inode->i_mtime;
-	cache->age = jiffies;
-
-	/*
-	 * Yowza! We have a cache entry...
-	 */
-	start = (char *) cache->entry;
-	while (index < cache->size) {
-		__u32	fileid  = *entry++;
-		__u32	nextpos = *entry++; /* cookie */
-		__u32	length  = *entry++;
+/*
+ * Find the given page, and call find_dirent() in order to try to
+ * return the next entry.
+ */
+static inline
+int find_dirent_page(nfs_readdir_descriptor_t *desc)
+{
+	struct inode	*inode = desc->file->f_dentry->d_inode;
+	struct page	*page;
+	unsigned long	index = desc->page_index;
+	int		status;
 
-		/*
-		 * Unpack the eof flag, offset, and length
-		 */
-		result = length & (1 << 15); /* eof flag */
-		name = start + ((length >> 16) & 0xFFFF);
-		length &= 0x7FFF;
-		/*
-		dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
-				(int) length, name, length,
-				(unsigned int) filp->f_pos,
-				fileid, result);
-		 */
+	dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index);
 
-		if (filldir(dirent, name, length, cookie, fileid) < 0)
-			break;
-		cookie = nextpos;
-		index++;
+	if (desc->page) {
+		page_cache_release(desc->page);
+		desc->page = NULL;
 	}
-	filp->f_pos = cookie;
-	result = 0;
 
-	/* XXX: May want to kick async readdir-ahead here. Not too hard
-	 * to do. */
-
-done:
-	dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n");
-	cache->locked = 0;
-	wake_up(&cache->wait);
-	wake_up(&readdir_wait);
+	page = read_cache_page(inode, index,
+			       (filler_t *)nfs_readdir_filler, desc);
+	if (IS_ERR(page)) {
+		status = PTR_ERR(page);
+		goto out;
+	}
 
-out:
-	return result;
+	/* NOTE: Someone else may have changed the READDIRPLUS flag */
+	desc->plus = NFS_USE_READDIRPLUS(inode);
+	status = find_dirent(desc, page);
+	if (status >= 0)
+		desc->page = page;
+	else
+		page_cache_release(page);
+ out:
+	dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status);
+	return status;
 }
 
 /*
- * Invalidate dircache entries for an inode.
+ * Recurse through the page cache pages, and return a
+ * filled nfs_entry structure of the next directory entry if possible.
+ *
+ * The target for the search is 'desc->target'.
  */
-void
-nfs_invalidate_dircache(struct inode *inode)
+static inline
+int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
 {
-	struct nfs_dirent *cache = dircache;
-	dev_t		dev = inode->i_dev;
-	ino_t		ino = inode->i_ino;
-	int		i;
-
-	dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
-	for (i = NFS_MAX_DIRCACHE; i--; cache++) {
-		if (cache->ino != ino)
-			continue;
-		if (cache->dev != dev)
-			continue;
-		if (!cache->valid)
-			continue;
-		if (cache->locked) {
-			printk("NFS: cache locked for %s/%ld\n",
-				kdevname(dev), (long) ino);
-			continue;
+	int		res = 0;
+	int		loop_count = 0;
+
+	dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target);
+	for (;;) {
+		res = find_dirent_page(desc);
+		if (res != -EAGAIN)
+			break;
+		/* Align to beginning of next page */
+		desc->page_offset = 0;
+		desc->page_index += PAGE_CACHE_SIZE;
+		if (loop_count++ > 200) {
+			loop_count = 0;
+			schedule();
 		}
-		cache->valid = 0;	/* brute force */
 	}
+	dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res);
+	return res;
 }
 
 /*
- * Invalidate the dircache for a super block (or all caches),
- * and release the cache memory.
+ * Once we've found the start of the dirent within a page: fill 'er up...
  */
-void
-nfs_invalidate_dircache_sb(struct super_block *sb)
-{
-	struct nfs_dirent *cache = dircache;
-	int		i;
-
-	for (i = NFS_MAX_DIRCACHE; i--; cache++) {
-		if (sb && sb->s_dev != cache->dev)
-			continue;
-		if (cache->locked) {
-			printk("NFS: cache locked at umount %s\n",
-				(cache->entry ? "(lost a page!)" : ""));
-			continue;
+static 
+int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
+		   filldir_t filldir)
+{
+	struct file	*file = desc->file;
+	struct nfs_entry *entry = desc->entry;
+	char		*start = (char *)page_address(desc->page),
+			*p = start + desc->page_offset;
+	unsigned long	fileid;
+	int		loop_count = 0,
+			res = 0;
+
+	dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target);
+
+	for(;;) {
+		/* Note: entry->prev_cookie contains the cookie for
+		 *	 retrieving the current dirent on the server */
+		fileid = nfs_fileid_to_ino_t(entry->ino);
+		res = filldir(dirent, entry->name, entry->len, 
+			      entry->prev_cookie, fileid);
+		if (res < 0)
+			break;
+		file->f_pos = desc->target = entry->cookie;
+		p = (char *)desc->decode((u32 *)p, entry, desc->plus);
+		if (IS_ERR(p)) {
+			if (PTR_ERR(p) == -EAGAIN) {
+				desc->page_offset = 0;
+				desc->page_index += PAGE_CACHE_SIZE;
+			}
+			break;
 		}
-		cache->valid = 0;	/* brute force */
-		if (cache->entry) {
-			free_page((unsigned long) cache->entry);
-			cache->entry = NULL;
+		desc->page_offset = p - start;
+		if (loop_count++ > 200) {
+			loop_count = 0;
+			schedule();
 		}
 	}
+	page_cache_release(desc->page);
+	desc->page = NULL;
+
+	dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res);
+	return res;
 }
 
 /*
- * Free directory cache memory
- * Called from cleanup_module
+ * If we cannot find a cookie in our cache, we suspect that this is
+ * because it points to a deleted file, so we ask the server to return
+ * whatever it thinks is the next entry. We then feed this to filldir.
+ * If all goes well, we should then be able to find our way round the
+ * cache on the next call to readdir_search_pagecache();
+ *
+ * NOTE: we cannot add the anonymous page to the pagecache because
+ *	 the data it contains might not be page aligned. Besides,
+ *	 we should already have a complete representation of the
+ *	 directory in the page cache by the time we get here.
  */
-void
-nfs_free_dircache(void)
+static inline
+int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
+		     filldir_t filldir)
+{
+	struct file	*file = desc->file;
+	struct dentry	*dir = file->f_dentry;
+	struct inode	*inode = dir->d_inode;
+	struct nfs_fattr dir_attr;
+	struct page	*page = NULL;
+	unsigned long	cache_page;
+	u32		*p;
+	int		status = -EIO;
+
+	dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target);
+	if (desc->page) {
+		page_cache_release(desc->page);
+		desc->page = NULL;
+	}
+
+	cache_page = page_cache_alloc();
+	if (!cache_page) {
+		status = -ENOMEM;
+		goto out;
+	}
+	page = page_cache_entry(cache_page);
+	p = (u32 *)page_address(page);
+	status = NFS_CALL(readdir, inode, (dir, &dir_attr,
+					   nfs_file_cred(file),
+					   desc->target, p,
+					   NFS_SERVER(inode)->dtsize, 0));
+	nfs_refresh_inode(inode, &dir_attr);
+	if (status >= 0) {
+		p = desc->decode(p, desc->entry, 0);
+		if (IS_ERR(p))
+			status = PTR_ERR(p);
+		else
+			desc->entry->prev_cookie = desc->target;
+	}
+	if (status < 0)
+		goto out_release;
+
+	desc->page_index = 0;
+	desc->page_offset = 0;
+	desc->page = page;
+	status = nfs_do_filldir(desc, dirent, filldir);
+
+	/* Reset read descriptor so it searches the page cache from
+	 * the start upon the next call to readdir_search_pagecache() */
+	desc->page_index = 0;
+	desc->page_offset = 0;
+	memset(desc->entry, 0, sizeof(*desc->entry));
+ out:
+	dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status);
+	return status;
+ out_release:
+	page_cache_release(page);
+	goto out;
+}
+
+/* The file offset position is now represented as a true offset into the
+ * page cache as is the case in most of the other filesystems.
+ */
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	dfprintk(DIRCACHE, "NFS: freeing dircache\n");
-	nfs_invalidate_dircache_sb(NULL);
+	struct dentry	*dentry = filp->f_dentry;
+	struct inode	*inode = dentry->d_inode;
+	nfs_readdir_descriptor_t my_desc,
+			*desc = &my_desc;
+	struct nfs_entry my_entry;
+	long		res;
+
+	res = nfs_revalidate(dentry);
+	if (res < 0)
+		return res;
+
+	/*
+	 * filp->f_pos points to the file offset in the page cache.
+	 * but if the cache has meanwhile been zapped, we need to
+	 * read from the last dirent to revalidate f_pos
+	 * itself.
+	 */
+	memset(desc, 0, sizeof(*desc));
+	memset(&my_entry, 0, sizeof(my_entry));
+
+	desc->file = filp;
+	desc->target = filp->f_pos;
+	desc->entry = &my_entry;
+	desc->decode = NFS_PROTO(inode)->decode_dirent;
+
+	while(!desc->entry->eof) {
+		res = readdir_search_pagecache(desc);
+		if (res == -EBADCOOKIE) {
+			/* This means either end of directory */
+			if (desc->entry->cookie == desc->target) {
+				res = 0;
+				break;
+			}
+			/* Or that the server has 'lost' a cookie */
+			res = uncached_readdir(desc, dirent, filldir);
+			if (res >= 0)
+				continue;
+		}
+		if (res < 0)
+			break;
+
+		res = nfs_do_filldir(desc, dirent, filldir);
+		if (res < 0) {
+			res = 0;
+			break;
+		}
+	}
+	if (desc->page)
+		page_cache_release(desc->page);
+	if (desc->error < 0)
+		return desc->error;
+	if (res < 0)
+		return res;
+	return 0;
 }
 
+
 /*
  * Whenever an NFS operation succeeds, we know that the dentry
  * is valid, so we update the revalidation timestamp.
  */
-static inline void nfs_renew_times(struct dentry * dentry)
+static inline void
+nfs_renew_times(struct dentry * dentry)
 {
-	dentry->d_time = jiffies;
+		dentry->d_time = jiffies;
 }
 
 static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags)
@@ -386,7 +483,7 @@
 		if (diff < 15*60)
 			timeout = 0;
 	}
-	
+
 	return time_after(jiffies,dentry->d_time + timeout);
 }
 
@@ -400,8 +497,9 @@
 #define NFS_REVALIDATE_NEGATIVE (1 * HZ)
 static inline int nfs_neg_need_reval(struct dentry *dentry)
 {
-	unsigned long timeout = NFS_ATTRTIMEO(dentry->d_parent->d_inode);
-	long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime;
+	struct inode *dir = dentry->d_parent->d_inode;
+	unsigned long timeout = NFS_ATTRTIMEO(dir);
+	long diff = CURRENT_TIME - dir->i_mtime;
 
 	if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE)
 		timeout = NFS_REVALIDATE_NEGATIVE;
@@ -423,11 +521,12 @@
  */
 static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
 {
-	struct dentry * parent = dentry->d_parent;
-	struct inode * inode = dentry->d_inode;
+	struct dentry		*dir = dentry->d_parent;
+	struct inode		*inode = dentry->d_inode,
+				*dir_i = dir->d_inode;
+	struct nfs_fh		fhandle;
+	struct nfs_fattr	fattr, dir_attr;
 	int error;
-	struct nfs_fh fhandle;
-	struct nfs_fattr fattr;
 
 	/*
 	 * If we don't have an inode, let's look at the parent
@@ -442,39 +541,52 @@
 
 	if (is_bad_inode(inode)) {
 		dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
-			parent->d_name.name, dentry->d_name.name);
+			dir->d_name.name, dentry->d_name.name);
 		goto out_bad;
 	}
 
-	if (IS_ROOT(dentry))
-		goto out_valid;
-
 	if (!nfs_dentry_force_reval(dentry, flags))
 		goto out_valid;
 
+	if (IS_ROOT(dentry)) {
+		__nfs_revalidate_inode(dentry);
+		goto out_valid_renew;
+	}
+
+	if (NFS_FLAGS(inode) & NFS_INO_STALE)
+		goto out_bad;
+
 	/*
 	 * Do a new lookup and check the dentry attributes.
 	 */
-	error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent),
-				dentry->d_name.name, &fhandle, &fattr);
-	if (error)
+	error = NFS_CALL(lookup, dir_i, (dir, &dir_attr,
+				  &dentry->d_name, &fhandle, &fattr));
+	if (error < 0)
 		goto out_bad;
 
 	/* Inode number matches? */
-	if (fattr.fileid != inode->i_ino)
+	if (!(fattr.valid & NFS_ATTR_FATTR) ||
+	    NFS_FSID(inode) != fattr.fsid ||
+	    NFS_FILEID(inode) != fattr.fileid)
 		goto out_bad;
 
 	/* Filehandle matches? */
-	if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh)))
+	if (NFS_FH(dentry)->size == 0)
+		goto out_bad;
+
+	if (NFS_FH(dentry)->size != fhandle.size ||
+	    memcmp(NFS_FH(dentry)->data, fhandle.data, fhandle.size))
 		goto out_bad;
 
 	/* Ok, remeber that we successfully checked it.. */
-	nfs_renew_times(dentry);
 	nfs_refresh_inode(inode, &fattr);
+	nfs_refresh_inode(dir_i, &dir_attr);
 
-out_valid:
+ out_valid_renew:
+	nfs_renew_times(dentry);
+ out_valid:
 	return 1;
-out_bad:
+ out_bad:
 	if (!list_empty(&dentry->d_subdirs))
 		shrink_dcache_parent(dentry);
 	/* If we have submounts, don't unhash ! */
@@ -482,35 +594,37 @@
 		goto out_valid;
 	d_drop(dentry);
 	if (dentry->d_parent->d_inode)
-		nfs_invalidate_dircache(dentry->d_parent->d_inode);
+		NFS_CACHEINV(dentry->d_parent->d_inode);
 	if (inode && S_ISDIR(inode->i_mode))
-		nfs_invalidate_dircache(inode);
+		NFS_CACHEINV(inode);
 	return 0;
 }
 
 /*
  * This is called from dput() when d_count is going to 0.
- * We use it to clean up silly-renamed files.
  */
 static void nfs_dentry_delete(struct dentry *dentry)
 {
-	dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name,
-		dentry->d_flags);
-
 	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
-		int error;
-		
-		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-		/* Unhash it first */
+		/* Unhash it, so that ->d_iput() would be called */
 		d_drop(dentry);
-		error = nfs_safe_remove(dentry);
-		if (error)
-			printk("NFS: can't silly-delete %s/%s, error=%d\n",
-				dentry->d_parent->d_name.name,
-				dentry->d_name.name, error);
 	}
+}
+
+__inline__ struct nfs_fh *nfs_fh_alloc(void)
+{
+	struct nfs_fh *p;
 
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return NULL;
+	memset(p, 0, sizeof(*p));
+	return p;
+}
+
+__inline__ void nfs_fh_free(struct nfs_fh *p)
+{
+	kfree(p);
 }
 
 /*
@@ -518,8 +632,21 @@
  */
 static void nfs_dentry_release(struct dentry *dentry)
 {
-	if (dentry->d_fsdata)
-		kfree(dentry->d_fsdata);
+	if (dentry->d_fsdata) {
+		nfs_fh_free(dentry->d_fsdata);
+		dentry->d_fsdata = NULL;
+	}
+}
+
+/*
+ * Called when the dentry loses inode.
+ * We use it to clean up silly-renamed files.
+ */
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+		nfs_complete_unlink(dentry);
+	iput(inode);
 }
 
 struct dentry_operations nfs_dentry_operations = {
@@ -528,36 +655,47 @@
 	NULL,			/* d_compare */
 	nfs_dentry_delete,	/* d_delete(struct dentry *) */
 	nfs_dentry_release,	/* d_release(struct dentry *) */
-	NULL			/* d_iput */
+	nfs_dentry_iput		/* d_iput */
 };
 
-static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
+static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry)
 {
+	struct dentry *dir = dentry->d_parent;
 	struct inode *inode;
 	int error;
 	struct nfs_fh fhandle;
-	struct nfs_fattr fattr;
+	struct nfs_fattr fattr, dir_attr;
 
 	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	error = -ENAMETOOLONG;
-	if (dentry->d_name.len > NFS_MAXNAMLEN)
+	if (dentry->d_name.len > NFS_SERVER(dir_i)->namelen)
 		goto out;
 
-	error = -ENOMEM;
+	dentry->d_op = &nfs_dentry_operations;
+
 	if (!dentry->d_fsdata) {
-		dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
-		if (!dentry->d_fsdata)
+		dentry->d_fsdata = nfs_fh_alloc();
+		if (!dentry->d_fsdata) {
+			error = -ENOMEM;
 			goto out;
+		}
 	}
-	dentry->d_op = &nfs_dentry_operations;
 
-	error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dentry->d_parent), 
-				dentry->d_name.name, &fhandle, &fattr);
+#if NFS_FIXME
+	inode = nfs_dircache_lookup(dir_i, dentry);
+	if (inode)
+		goto no_entry;
+#endif
+
+	error = NFS_CALL(lookup, dir_i, (dir, &dir_attr,
+				 &dentry->d_name, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
 	inode = NULL;
 	if (error == -ENOENT)
 		goto no_entry;
+
 	if (!error) {
 		error = -EACCES;
 		inode = nfs_fhget(dentry, &fhandle, &fattr);
@@ -596,29 +734,46 @@
  * that the operation succeeded on the server, but an error in the
  * reply path made it appear to have failed.
  */
-static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
+static int nfs_create(struct inode *dir_i, struct dentry *dentry, int mode)
 {
-	int error;
-	struct nfs_sattr sattr;
-	struct nfs_fattr fattr;
-	struct nfs_fh fhandle;
+	struct dentry	*dir = dentry->d_parent;
+	struct iattr	 attr;
+	struct nfs_fattr fattr, dir_attr;
+	struct nfs_fh	 fhandle;
+	int		 error;
 
 	dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
 
-	sattr.mode = mode;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+#ifdef NFSD_BROKEN_UID
+	/* We set uid/gid in the request because IBM's broken nfsd
+	 * uses the root uid/gid otherwise. Argh!
+	 * (Hopefully the server will override the gid when the directory
+	 * has the sticky bit set. Irix may have a problem here...)
+	 */
+	attr.ia_mode = mode;
+	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_mode = mode;
+	attr.ia_valid = ATTR_MODE;
+#endif
 
 	/*
 	 * Invalidate the dir cache before the operation to avoid a race.
+	 * The 0 argument passed into the create function should one day
+	 * contain the O_EXCL flag if requested. This allows NFSv3 to
+	 * select the appropriate create strategy. Currently open_namei
+	 * does not pass the create flags.
 	 */
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-			dentry->d_name.name, &sattr, &fhandle, &fattr);
-	if (!error)
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(create, dir_i, (dir, &dir_attr, &dentry->d_name,
+			&attr, 0, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && fhandle.size != 0)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
-	if (error)
+	if (error || fhandle.size == 0)
 		d_drop(dentry);
 	return error;
 }
@@ -626,28 +781,35 @@
 /*
  * See comments for nfs_proc_create regarding failed operations.
  */
-static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+static int nfs_mknod(struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
 {
-	int error;
-	struct nfs_sattr sattr;
-	struct nfs_fattr fattr;
-	struct nfs_fh fhandle;
+	struct dentry	*dir = dentry->d_parent;
+	struct iattr	 attr;
+	struct nfs_fattr fattr, dir_attr;
+	struct nfs_fh	 fhandle;
+	int		 error;
 
 	dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
 
-	sattr.mode = mode;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	if (S_ISCHR(mode) || S_ISBLK(mode))
-		sattr.size = rdev; /* get out your barf bag */
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
-
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name, &sattr, &fhandle, &fattr);
-	if (!error)
+#ifdef NFSD_BROKEN_UID
+	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+	attr.ia_mode = mode;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_valid = ATTR_MODE;
+	attr.ia_mode = mode;
+#endif
+
+
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(mknod, dir_i, (dir, &dir_attr, &dentry->d_name,
+				&attr, rdev, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && fhandle.size != 0)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
-	if (error)
+	if (error || fhandle.size == 0)
 		d_drop(dentry);
 	return error;
 }
@@ -655,52 +817,55 @@
 /*
  * See comments for nfs_proc_create regarding failed operations.
  */
-static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode)
 {
-	int error;
-	struct nfs_sattr sattr;
-	struct nfs_fattr fattr;
-	struct nfs_fh fhandle;
-
-	dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+	struct dentry   *dir = dentry->d_parent;
+	struct iattr	 attr;
+	struct nfs_fattr fattr, dir_attr;
+	struct nfs_fh	 fhandle;
+	int		 error;
+
+	dfprintk(VFS, "NFS: mkdir(%x/%ld, %s)\n",
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
+
+#ifdef NFSD_BROKEN_UID
+	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+	attr.ia_mode = mode | S_IFDIR;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_valid = ATTR_MODE;
+	attr.ia_mode = mode | S_IFDIR;
+#endif
 
-	sattr.mode = mode | S_IFDIR;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(mkdir, dir_i, (dir, &dir_attr,
+				&dentry->d_name, &attr, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && fhandle.size != 0)
+		error = nfs_instantiate(dentry, &fhandle, &fattr);
 
-	/*
-	 * Always drop the dentry, we can't always depend on
-	 * the fattr returned by the server (AIX seems to be
-	 * broken). We're better off doing another lookup than
-	 * depending on potentially bogus information.
-	 */
-	d_drop(dentry);
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
-				dentry->d_name.name, &sattr, &fhandle, &fattr);
-	if (!error)
-		dir->i_nlink++;
+	if (error || fhandle.size == 0)
+		d_drop(dentry);
 	return error;
 }
 
-static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
+static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry)
 {
-	int error;
+	struct dentry	*dir = dentry->d_parent;
+	struct nfs_fattr dir_attr;
+	int		 error;
 
 	dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
 
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name);
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(rmdir, dir_i, (dir, &dir_attr, &dentry->d_name));
+	nfs_refresh_inode(dir_i, &dir_attr);
 
-	/* Update i_nlink and invalidate dentry. */
-	if (!error) {
-		d_drop(dentry);
-		if (dir->i_nlink)
-			dir->i_nlink--;
-	}
+	/* Free the inode */
+	if (!error)
+		d_delete(dentry);
 
 	return error;
 }
@@ -760,15 +925,18 @@
 	return sdentry;
 }
 
-static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
+static int nfs_sillyrename(struct inode *dir_i, struct dentry *dentry)
 {
+	struct dentry	*dir = dentry->d_parent;
 	static unsigned int sillycounter = 0;
-	const int      i_inosize  = sizeof(dir->i_ino)*2;
-	const int      countersize = sizeof(sillycounter)*2;
-	const int      slen       = strlen(".nfs") + i_inosize + countersize;
-	char           silly[slen+1];
-	struct dentry *sdentry;
-	int            error = -EIO;
+	struct nfs_fattr dir_attr;
+	const int        i_inosize  = sizeof(dir_i->i_ino)*2;
+	const int        countersize = sizeof(sillycounter)*2;
+	const int        slen       = strlen(".nfs") + i_inosize + countersize;
+	struct qstr      qsilly;
+	char             silly[slen+1];
+	struct dentry *  sdentry;
+	int              error = -EIO;
 
 	dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name, 
@@ -778,15 +946,13 @@
 	 * Note that a silly-renamed file can be deleted once it's
 	 * no longer in use -- it's just an ordinary file now.
 	 */
-	if (dentry->d_count == 1) {
-		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
+	if (dentry->d_count == 1)
 		goto out;  /* No need to silly rename. */
-	}
 
 #ifdef NFS_PARANOIA
-if (!dentry->d_inode)
-printk("NFS: silly-renaming %s/%s, negative dentry??\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+	if (!dentry->d_inode)
+		printk(KERN_ERR "NFS: silly-renaming %s/%s, negative dentry??\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 	/*
 	 * We don't allow a dentry to be silly-renamed twice.
@@ -802,7 +968,8 @@
 	do {
 		char *suffix = silly + slen - countersize;
 
-		dput(sdentry);
+		if (sdentry)
+			dput(sdentry);
 		sillycounter++;
 		sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);
 
@@ -818,14 +985,16 @@
 			goto out;
 	} while(sdentry->d_inode != NULL); /* need negative lookup */
 
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_rename(NFS_SERVER(dir),
-				NFS_FH(dentry->d_parent), dentry->d_name.name,
-				NFS_FH(dentry->d_parent), silly);
+	nfs_zap_caches(dir_i);
+	qsilly.name = silly;
+	qsilly.len  = strlen(silly);
+	error = NFS_CALL(rename, dir_i, (dir, &dir_attr, &dentry->d_name,
+				  dir, &dir_attr, &qsilly));
+	nfs_refresh_inode(dir_i, &dir_attr);
 	if (!error) {
 		nfs_renew_times(dentry);
 		d_move(dentry, sdentry);
-		dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+		error = nfs_async_unlink(dentry);
  		/* If we return 0 we don't unlink */
 	}
 	dput(sdentry);
@@ -842,54 +1011,63 @@
  */
 static int nfs_safe_remove(struct dentry *dentry)
 {
-	struct inode *dir = dentry->d_parent->d_inode;
-	struct inode *inode = dentry->d_inode;
-	int error, rehash = 0;
+	struct nfs_fattr dir_attr;
+	struct dentry	*dir = dentry->d_parent;
+	struct inode	*dir_i = dir->d_inode,   
+			*inode = dentry->d_inode;
+	int		 error = 0, rehash = 0;
 		
-	dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name,
-		inode->i_ino);
+	/*
+	 * Unhash the dentry while we remove the file ...
+	 */
+	if (!list_empty(&dentry->d_hash)) {
+		d_drop(dentry);
+		rehash = 1;
+	}
+
+	/* If the dentry was sillyrenamed, we simply call d_delete() */
+	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+		goto out_delete;
+
+	dfprintk(VFS, "NFS: safe_remove(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	/* N.B. not needed now that d_delete is done in advance? */
 	error = -EBUSY;
 	if (!inode) {
 #ifdef NFS_PARANOIA
-printk("nfs_safe_remove: %s/%s already negative??\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+		printk(KERN_ERR "nfs_safe_remove: %s/%s already negative??\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 	}
 
 	if (dentry->d_count > 1) {
 #ifdef NFS_PARANOIA
-printk("nfs_safe_remove: %s/%s busy, d_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+		printk(KERN_INFO "nfs_safe_remove: %s/%s busy, d_count=%d\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
 #endif
 		goto out;
 	}
+
+	nfs_zap_caches(dir_i);
+	if (inode)
+		NFS_CACHEINV(inode);
+	error = NFS_CALL(remove, dir_i, (dir, &dir_attr, &dentry->d_name));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (error < 0)
+		goto out;
+
+ out_delete:
 	/*
-	 * Unhash the dentry while we remove the file ...
-	 */
-	if (!list_empty(&dentry->d_hash)) {
-		d_drop(dentry);
-		rehash = 1;
-	}
-	/*
-	 * Update i_nlink and free the inode before unlinking.
+	 * Free the inode
 	 */
-	if (inode) {
-		if (inode->i_nlink)
-			inode->i_nlink --;
-		d_delete(dentry);
-	}
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name);
+	d_delete(dentry);
+out:
 	/*
 	 * Rehash the negative dentry if the operation succeeded.
 	 */
-	if (!error && rehash)
-		d_add(dentry, NULL);
-out:
+	if (rehash)
+		d_rehash(dentry);
 	return error;
 }
 
@@ -905,6 +1083,8 @@
 	dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name);
 
+	if (dentry->d_inode)
+		nfs_wb_all(dentry->d_inode);
 	error = nfs_sillyrename(dir, dentry);
 	if (error && error != -EBUSY) {
 		error = nfs_safe_remove(dentry);
@@ -916,45 +1096,60 @@
 }
 
 static int
-nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+nfs_symlink(struct inode *dir_i, struct dentry *dentry, const char *symname)
 {
-	struct nfs_sattr sattr;
-	int error;
+	struct dentry	*dir = dentry->d_parent;
+	struct nfs_fattr dir_attr, sym_attr;
+	struct nfs_fh    sym_fh;
+	struct iattr     attr;
+	struct qstr      qsymname;
+	int              error, mode, maxlen;
 
 	dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name, symname);
 
 	error = -ENAMETOOLONG;
-	if (strlen(symname) > NFS_MAXPATHLEN)
+	maxlen = (NFS_PROTO(dir_i)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
+	if (strlen(symname) > maxlen)
 		goto out;
 
 #ifdef NFS_PARANOIA
-if (dentry->d_inode)
-printk("nfs_proc_symlink: %s/%s not negative!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+	if (dentry->d_inode)
+		printk(KERN_WARNING "nfs_proc_symlink: %s/%s not negative!\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 	/*
 	 * Fill in the sattr for the call.
+
  	 * Note: SunOS 4.1.2 crashes if the mode isn't initialized!
 	 */
-	sattr.mode = S_IFLNK | S_IRWXUGO;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+#ifdef NFSD_BROKEN_UID
+	attr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
+	attr.ia_mode = mode = S_IFLNK | S_IRWXUGO;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_valid = ATTR_MODE;
+	attr.ia_mode = mode = S_IFLNK | S_IRWXUGO;
+#endif
 
-	/*
-	 * Drop the dentry in advance to force a new lookup.
-	 * Since nfs_proc_symlink doesn't return a fattr, we
-	 * can't instantiate the new inode.
-	 */
-	d_drop(dentry);
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name, symname, &sattr);
-	if (!error) {
-		nfs_renew_times(dentry->d_parent);
-	} else if (error == -EEXIST) {
-		printk("nfs_proc_symlink: %s/%s already exists??\n",
-			dentry->d_parent->d_name.name, dentry->d_name.name);
+
+	qsymname.name = symname;
+	qsymname.len  = strlen(symname);
+
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(symlink, dir_i, (dir, &dir_attr,
+				&dentry->d_name, &qsymname, &attr,
+				&sym_fh, &sym_attr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && sym_fh.size != 0 && (sym_attr.valid & NFS_ATTR_FATTR)) {
+		error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
+	} else {
+		if (error == -EEXIST)
+			printk(KERN_INFO "nfs_proc_symlink: %s/%s already exists??\n",
+			       dentry->d_parent->d_name.name,
+			       dentry->d_name.name);
+		d_drop(dentry);
 	}
 
 out:
@@ -962,10 +1157,12 @@
 }
 
 static int 
-nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
 {
-	struct inode *inode = old_dentry->d_inode;
-	int error;
+	struct dentry	*dir = dentry->d_parent;
+	struct inode	*inode = old_dentry->d_inode;
+	struct nfs_fattr old_attr, dir_attr;
+	int		 error;
 
 	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
 		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
@@ -977,16 +1174,12 @@
 	 * we can't use the existing dentry.
 	 */
 	d_drop(dentry);
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
-				NFS_FH(dentry->d_parent), dentry->d_name.name);
-	if (!error) {
- 		/*
-		 * Update the link count immediately, as some apps
-		 * (e.g. pine) test this after making a link.
-		 */
-		inode->i_nlink++;
-	}
+	nfs_zap_caches(dir_i);
+	NFS_CACHEINV(inode);
+	error = NFS_CALL(link, inode, (old_dentry, &old_attr,
+				       dir, &dir_attr, &dentry->d_name));
+	nfs_refresh_inode(inode, &old_attr);
+	nfs_refresh_inode(dir_i, &dir_attr);
 	return error;
 }
 
@@ -1013,14 +1206,28 @@
  * no pending writes (if it's a file), and the use count must be 1.
  * If these conditions are met, we can drop the dentries before doing
  * the rename.
+ *
+ * FIXME: Sun seems to take this even one step further. The connectathon
+ * test suite has a file that renames open file A to open file B,
+ * and expects a silly rename to happen for B.
  */
 static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		      struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct inode *old_inode = old_dentry->d_inode;
-	struct inode *new_inode = new_dentry->d_inode;
-	struct dentry *dentry = NULL;
-	int error, rehash = 0;
+	struct nfs_fattr old_attr, new_attr;
+	struct inode *   old_inode = old_dentry->d_inode;
+	struct inode *   new_inode = new_dentry->d_inode;
+	struct dentry *  dentry = NULL, *rehash = NULL;
+	int              error;
+
+	/*
+	 * To prevent any new references to the target during the rename,
+	 * we unhash the dentry and free the inode in advance.
+	 */
+	if (!list_empty(&new_dentry->d_hash)) {
+		d_drop(new_dentry);
+		rehash = new_dentry;
+	}
 
 	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
 		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
@@ -1038,6 +1245,8 @@
 	 * With directories check is done in VFS.
 	 */
 	error = -EBUSY;
+	if (new_inode)
+		nfs_wb_all(new_inode);
 	if (new_dentry->d_count > 1 && new_inode) {
 		int err;
 		/* copy the target dentry's name */
@@ -1049,17 +1258,18 @@
 		/* silly-rename the existing target ... */
 		err = nfs_sillyrename(new_dir, new_dentry);
 		if (!err) {
-			new_dentry = dentry;
+			new_dentry = rehash = dentry;
 			new_inode = NULL;
 			/* hash the replacement target */
-			d_add(new_dentry, NULL);
+			d_instantiate(new_dentry, NULL);
 		}
 
 		/* dentry still busy? */
 		if (new_dentry->d_count > 1) {
 #ifdef NFS_PARANOIA
-printk("nfs_rename: target %s/%s busy, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+		printk(KERN_INFO "nfs_rename: target %s/%s busy, d_count=%d\n",
+			new_dentry->d_parent->d_name.name,
+			new_dentry->d_name.name,new_dentry->d_count);
 #endif
 			goto out;
 		}
@@ -1075,40 +1285,75 @@
 
 	if (new_dentry->d_count > 1 && new_inode) {
 #ifdef NFS_PARANOIA
-printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+		printk(KERN_INFO "nfs_rename: new dentry %s/%s busy, d_count=%d\n",
+			new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
 #endif
 		goto out;
 	}
 
-	/*
-	 * To prevent any new references to the target during the rename,
-	 * we unhash the dentry and free the inode in advance.
-	 */
-	if (!list_empty(&new_dentry->d_hash)) {
-		d_drop(new_dentry);
-		rehash = 1;
-	}
 	if (new_inode)
 		d_delete(new_dentry);
 
-	nfs_invalidate_dircache(new_dir);
-	nfs_invalidate_dircache(old_dir);
-	error = nfs_proc_rename(NFS_DSERVER(old_dentry),
-			NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
-			NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
+	nfs_zap_caches(new_dir);
+	nfs_zap_caches(old_dir);
+	error = NFS_CALL(rename, old_dir,
+			 (old_dentry->d_parent, &old_attr, &old_dentry->d_name,
+			  new_dentry->d_parent, &new_attr, &new_dentry->d_name));
+	nfs_refresh_inode(old_dir, &old_attr);
+	nfs_refresh_inode(new_dir, &new_attr);
 
+out:
 	/* Update the dcache if needed */
 	if (rehash)
-		d_add(new_dentry, NULL);
+		d_rehash(rehash);
 	if (!error && !S_ISDIR(old_inode->i_mode))
 		d_move(old_dentry, new_dentry);
-
-out:
 	/* new dentry created? */
 	if (dentry)
 		dput(dentry);
 	return error;
+}
+
+int
+nfs_permission(struct inode *i, int msk)
+{
+	struct nfs_fattr	fattr;
+	struct dentry		*de = NULL;
+	int			err = vfs_permission(i, msk);
+	struct list_head	*start, *tmp;
+
+	if (!NFS_PROTO(i)->access)
+		goto out;
+	/*
+	 * Trust UNIX mode bits except:
+	 *
+	 * 1) When override capabilities may have been invoked
+	 * 2) When root squashing may be involved
+	 * 3) When ACLs may overturn a negative answer */
+	if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH)
+	    && (current->fsuid != 0) && (current->fsgid != 0)
+	    && err != -EACCES)
+		goto out;
+
+	tmp = start = &i->i_dentry;
+	while ((tmp = tmp->next) != start) {
+		de = list_entry(tmp, struct dentry, d_alias);
+		if (de->d_inode == i)
+			break;
+	}
+	if (!de || de->d_inode != i)
+		return 0;
+
+	err = NFS_CALL(access, i, (de, msk, &fattr, 0));
+
+	if (err == -EACCES && NFS_CLIENT(i)->cl_droppriv &&
+	    current->uid != 0 && current->gid != 0 &&
+	    (current->fsuid != current->uid || current->fsgid != current->gid))
+		err = NFS_CALL(access, i, (de, msk, &fattr, 1));
+
+	nfs_refresh_inode(i, &fattr);
+ out:
+	return err;
 }
 
 /*
diff -urN linux-2.2.18-pre8/fs/nfs/file.c linux/fs/nfs/file.c
--- linux-2.2.18-pre8/fs/nfs/file.c	Tue Oct 26 17:53:42 1999
+++ linux/fs/nfs/file.c	Mon Dec  3 20:25:21 2001
@@ -1,6 +1,8 @@
 /*
  *  linux/fs/nfs/file.c
  *
+ *  NFS regular file handling.
+ *
  *  Copyright (C) 1992  Rick Sladkey
  *
  *  Changes Copyright (C) 1994 by Florian La Roche
@@ -12,8 +14,6 @@
  *  Expire cache on write to a file by Wai S Kok (Oct 1994).
  *
  *  Total rewrite of read side for new NFS buffer cache.. Linus.
- *
- *  nfs regular file handling functions
  */
 
 #include <linux/sched.h>
@@ -21,11 +21,16 @@
 #include <linux/errno.h>
 #include <linux/fcntl.h>
 #include <linux/stat.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
 #include <linux/lockd/bind.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -37,6 +42,8 @@
 static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *);
 static int  nfs_file_flush(struct file *);
 static int  nfs_fsync(struct file *, struct dentry *dentry);
+static int  nfs_prepare_write(struct file *, struct page *, unsigned, unsigned);
+static int  nfs_sync_page(struct page *page);
 
 static struct file_operations nfs_file_operations = {
 	NULL,			/* lseek - default */
@@ -73,10 +80,12 @@
 	nfs_writepage,		/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL,			/* permission */
+	nfs_permission,		/* permission */
 	NULL,			/* smap */
 	nfs_updatepage,		/* updatepage */
 	nfs_revalidate,		/* revalidate */
+	nfs_prepare_write,	/* prepare_write */
+	nfs_sync_page,		/* sync_page */
 };
 
 /* Hack for future NFS swap support */
@@ -91,17 +100,11 @@
 static int
 nfs_file_flush(struct file *file)
 {
-	struct inode	*inode = file->f_dentry->d_inode;
-	int		status;
+	struct dentry	*dentry = file->f_dentry;
+	struct inode	*inode = dentry->d_inode;
 
 	dfprintk(VFS, "nfs: flush(%x/%ld)\n", inode->i_dev, inode->i_ino);
-
-	status = nfs_wb_file(inode, file);
-	if (!status) {
-		status = file->f_error;
-		file->f_error = 0;
-	}
-	return status;
+	return nfs_fsync(file, dentry);
 }
 
 static ssize_t
@@ -114,7 +117,7 @@
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		(unsigned long) count, (unsigned long) *ppos);
 
-	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	result = nfs_revalidate_inode(dentry);
 	if (!result)
 		result = generic_file_read(file, buf, count, ppos);
 	return result;
@@ -129,7 +132,7 @@
 	dfprintk(VFS, "nfs: mmap(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 
-	status = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	status = nfs_revalidate_inode(dentry);
 	if (!status)
 		status = generic_file_mmap(file, vma);
 	return status;
@@ -157,6 +160,34 @@
 }
 
 
+static int nfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+	return nfs_flush_incompatible(file, page);
+}
+
+/*
+ * The following is used by wait_on_page(), generic_file_readahead()
+ * to initiate the completion of any page readahead operations.
+ */
+static int nfs_sync_page(struct page *page)
+{
+	struct inode	*inode = page->inode;
+	unsigned long	index = page_index(page);
+	unsigned int	rpages;
+	int		result;
+
+	if (!inode)
+		return 0;
+
+	rpages = NFS_SERVER(inode)->rpages;
+	result = nfs_pagein_inode(inode, index, rpages);
+	if (result < 0)
+		goto out_bad;
+	return 0;
+ out_bad:
+	return result;
+}
+
 /* 
  * Write to a file (through the page cache).
  */
@@ -174,7 +205,7 @@
 	result = -EBUSY;
 	if (IS_SWAPFILE(inode))
 		goto out_swapfile;
-	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	result = nfs_revalidate_inode(dentry);
 	if (result)
 		goto out;
 
@@ -197,7 +228,8 @@
 int
 nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
-	struct inode * inode = filp->f_dentry->d_inode;
+	struct dentry * dentry = filp->f_dentry;
+	struct inode * inode = dentry->d_inode;
 	int	status = 0;
 
 	dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
@@ -235,17 +267,17 @@
 	 */
 	status = nfs_wb_all(inode);
 	if (status < 0)
-		return status;
+		goto out_unlock;
 
-	if ((status = nlmclnt_proc(inode, cmd, fl)) < 0)
-		return status;
-	else
+	status = nlmclnt_proc(inode, cmd, fl);
+	if (status > 0)
 		status = 0;
 
 	/*
 	 * Make sure we re-validate anything we've got cached.
 	 * This makes locking act as a cache coherency point.
 	 */
+ out_unlock:
  out_ok:
 	NFS_CACHEINV(inode);
 	return status;
diff -urN linux-2.2.18-pre8/fs/nfs/flushd.c linux/fs/nfs/flushd.c
--- linux-2.2.18-pre8/fs/nfs/flushd.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/flushd.c	Mon Dec  3 20:25:21 2001
@@ -0,0 +1,276 @@
+/*
+ * linux/fs/nfs/flushd.c
+ *
+ * For each NFS mount, there is a separate cache object that contains
+ * a hash table of all clusters. With this cache, an async RPC task
+ * (`flushd') is associated, which wakes up occasionally to inspect
+ * its list of dirty buffers.
+ * (Note that RPC tasks aren't kernel threads. Take a look at the
+ * rpciod code to understand what they are).
+ *
+ * Inside the cache object, we also maintain a count of the current number
+ * of dirty pages, which may not exceed a certain threshold.
+ * (FIXME: This threshold should be configurable).
+ *
+ * The code is streamlined for what I think is the prevalent case for
+ * NFS traffic, which is sequential write access without concurrent
+ * access by different processes.
+ *
+ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Rewritten 6/3/2000 by Trond Myklebust
+ * Copyright (C) 1999, 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+
+#include <linux/sched.h>
+
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/sched.h>
+
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_flushd.h>
+#include <linux/nfs_mount.h>
+
+/*
+ * Various constants
+ */
+#define NFSDBG_FACILITY         NFSDBG_PAGECACHE
+
+/*
+ * This is the wait queue all cluster daemons sleep on
+ */
+static struct rpc_wait_queue    flushd_queue = RPC_INIT_WAITQ("nfs_flushd");
+
+/*
+ * Local function declarations.
+ */
+static void	nfs_flushd(struct rpc_task *);
+static void	nfs_flushd_exit(struct rpc_task *);
+
+
+int nfs_reqlist_init(struct nfs_server *server)
+{
+	struct nfs_reqlist	*cache;
+	struct rpc_task		*task;
+	int			status = 0;
+
+	dprintk("NFS: writecache_init\n");
+	cache = server->rw_requests;
+
+	if (cache->task)
+		return 0;
+
+	/* Create the RPC task */
+	status = -ENOMEM;
+	task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC);
+	if (!task)
+		goto out_err;
+
+	task->tk_calldata = server;
+
+	cache->task = task;
+
+	/* Run the task */
+	cache->runat = jiffies;
+
+	cache->auth = server->client->cl_auth;
+	task->tk_action   = nfs_flushd;
+	task->tk_exit   = nfs_flushd_exit;
+
+	rpc_execute(task);
+	return 0;
+ out_err:
+	return status;
+}
+
+void nfs_reqlist_exit(struct nfs_server *server)
+{
+	struct nfs_reqlist      *cache;
+
+	cache = server->rw_requests;
+	if (!cache)
+		return;
+
+	dprintk("NFS: reqlist_exit (ptr %p rpc %p)\n", cache, cache->task);
+	while (cache->task || cache->inodes) {
+		if (!cache->task) {
+			nfs_reqlist_init(server);
+		} else {
+			cache->task->tk_status = -ENOMEM;
+			rpc_wake_up_task(cache->task);
+		}
+		interruptible_sleep_on_timeout(&cache->request_wait, 1 * HZ);
+	}
+}
+
+int nfs_reqlist_alloc(struct nfs_server *server)
+{
+	struct nfs_reqlist	*cache;
+	if (server->rw_requests)
+		return 0;
+
+	cache = (struct nfs_reqlist *)kmalloc(sizeof(*cache), GFP_KERNEL);
+	if (!cache)
+		return -ENOMEM;
+
+	memset(cache, 0, sizeof(*cache));
+	server->rw_requests = cache;
+
+	return 0;
+}
+
+void nfs_reqlist_free(struct nfs_server *server)
+{
+	if (server->rw_requests) {
+		kfree(server->rw_requests);
+		server->rw_requests = NULL;
+	}
+}
+
+void nfs_wake_flushd()
+{
+	rpc_wake_up_status(&flushd_queue, -ENOMEM);
+}
+
+static void inode_append_flushd(struct inode *inode)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct inode		**q;
+
+	if (NFS_FLAGS(inode) & NFS_INO_FLUSH)
+		return;
+	inode->u.nfs_i.hash_next = NULL;
+
+	q = &cache->inodes;
+	while (*q)
+		q = &(*q)->u.nfs_i.hash_next;
+	*q = inode;
+
+	/* Note: we increase the inode i_count in order to prevent
+	 *	 it from disappearing when on the flush list
+	 */
+	NFS_FLAGS(inode) |= NFS_INO_FLUSH;
+	inode->i_count++;
+}
+
+void inode_remove_flushd(struct inode *inode)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct inode		**q;
+
+	if (!(NFS_FLAGS(inode) & NFS_INO_FLUSH))
+		return;
+
+	q = &cache->inodes;
+	while (*q && *q != inode)
+		q = &(*q)->u.nfs_i.hash_next;
+	if (*q) {
+		*q = inode->u.nfs_i.hash_next;
+		NFS_FLAGS(inode) &= ~NFS_INO_FLUSH;
+		iput(inode);
+	}
+}
+
+void inode_schedule_scan(struct inode *inode, unsigned long time)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct rpc_task		*task;
+	unsigned long		mintimeout;
+
+	if (time_after(NFS_NEXTSCAN(inode), time))
+		NFS_NEXTSCAN(inode) = time;
+	mintimeout = jiffies + 1 * HZ;
+	if (time_before(mintimeout, NFS_NEXTSCAN(inode)))
+		mintimeout = NFS_NEXTSCAN(inode);
+	inode_append_flushd(inode);
+
+	task = cache->task;
+	if (!task) {
+		nfs_reqlist_init(NFS_SERVER(inode));
+	} else {
+		if (time_after(cache->runat, mintimeout))
+			rpc_wake_up_task(task);
+	}
+}
+
+
+static void
+nfs_flushd(struct rpc_task *task)
+{
+	struct nfs_server	*server;
+	struct nfs_reqlist	*cache;
+	struct inode		*inode, *next;
+	unsigned long		delay = jiffies + NFS_WRITEBACK_LOCKDELAY;
+	int			flush = (task->tk_status == -ENOMEM);
+
+        dprintk("NFS: %4d flushd starting\n", task->tk_pid);
+	server = (struct nfs_server *) task->tk_calldata;
+        cache = server->rw_requests;
+
+	next = cache->inodes;
+	cache->inodes = NULL;
+
+	while ((inode = next) != NULL) {
+		next = next->u.nfs_i.hash_next;
+		inode->u.nfs_i.hash_next = NULL;
+		NFS_FLAGS(inode) &= ~NFS_INO_FLUSH;
+
+		if (flush) {
+			nfs_pagein_inode(inode, 0, 0);
+			nfs_sync_file(inode, NULL, 0, 0, FLUSH_AGING);
+		} else if (time_after(jiffies, NFS_NEXTSCAN(inode))) {
+			NFS_NEXTSCAN(inode) = jiffies + NFS_WRITEBACK_LOCKDELAY;
+			nfs_pagein_timeout(inode);
+			nfs_flush_timeout(inode, FLUSH_AGING);
+#ifdef CONFIG_NFS_V3
+			nfs_commit_timeout(inode, FLUSH_AGING);
+#endif
+		}
+
+		if (nfs_have_writebacks(inode) || nfs_have_read(inode)) {
+			inode_append_flushd(inode);
+			if (time_after(delay, NFS_NEXTSCAN(inode)))
+				delay = NFS_NEXTSCAN(inode);
+		}
+		iput(inode);
+	}
+
+	dprintk("NFS: %4d flushd back to sleep\n", task->tk_pid);
+	if (time_after(jiffies + 1 * HZ, delay))
+		delay = 1 * HZ;
+	else
+		delay = delay - jiffies;
+	task->tk_status = 0;
+	task->tk_action = nfs_flushd;
+	task->tk_timeout = delay;
+	cache->runat = jiffies + task->tk_timeout;
+
+	if (!cache->nr_requests && !cache->inodes) {
+		cache->task = NULL;
+		task->tk_action = NULL;
+	} else
+		rpc_sleep_on(&flushd_queue, task, NULL, NULL);
+}
+
+static void
+nfs_flushd_exit(struct rpc_task *task)
+{
+	struct nfs_server	*server;
+	struct nfs_reqlist	*cache;
+	server = (struct nfs_server *) task->tk_calldata;
+	cache = server->rw_requests;
+
+	if (cache->task == task)
+		cache->task = NULL;
+	wake_up(&cache->request_wait);
+}
+
diff -urN linux-2.2.18-pre8/fs/nfs/inode.c linux/fs/nfs/inode.c
--- linux-2.2.18-pre8/fs/nfs/inode.c	Wed Jun  7 14:26:43 2000
+++ linux/fs/nfs/inode.c	Mon Dec  3 20:25:21 2001
@@ -24,11 +24,19 @@
 #include <linux/errno.h>
 #include <linux/locks.h>
 #include <linux/unistd.h>
+#include <linux/major.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/stats.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs_flushd.h>
 #include <linux/lockd/bind.h>
 
+#include <asm/spinlock.h>
+
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
@@ -37,16 +45,16 @@
 #define NFS_PARANOIA 1
 
 static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *);
-static void nfs_zap_caches(struct inode *);
-static void nfs_invalidate_inode(struct inode *);
 
 static void nfs_read_inode(struct inode *);
 static void nfs_put_inode(struct inode *);
 static void nfs_delete_inode(struct inode *);
 static int  nfs_notify_change(struct dentry *, struct iattr *);
 static void nfs_put_super(struct super_block *);
-static void nfs_umount_begin(struct super_block *);
 static int  nfs_statfs(struct super_block *, struct statfs *, int);
+static void nfs_umount_begin(struct super_block *);
+static struct nfs_file *nfs_file_alloc(void);
+static void nfs_file_free(struct nfs_file *p);
 
 static struct super_operations nfs_sops = { 
 	nfs_read_inode,		/* read inode */
@@ -57,12 +65,44 @@
 	nfs_put_super,		/* put superblock */
 	NULL,			/* write superblock */
 	nfs_statfs,		/* stat filesystem */
-	NULL,			/* no remount */
-	NULL,			/* no clear inode */
+	NULL,			/* remount */
+	NULL,			/* clear inode */
 	nfs_umount_begin	/* umount attempt begin */
 };
 
-struct rpc_stat			nfs_rpcstat = { &nfs_program };
+
+/*
+ * RPC crutft for NFS
+ */
+static struct rpc_stat		nfs_rpcstat = { &nfs_program };
+static struct rpc_version *	nfs_version[] = {
+	NULL,
+	NULL,
+	&nfs_version2,
+#ifdef CONFIG_NFS_V3
+	&nfs_version3,
+#endif
+};
+
+struct rpc_program		nfs_program = {
+	"nfs",
+	NFS_PROGRAM,
+	sizeof(nfs_version) / sizeof(nfs_version[0]),
+	nfs_version,
+	&nfs_rpcstat,
+};
+
+static inline unsigned long
+nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
+{
+	return nfs_fileid_to_ino_t(fattr->fileid);
+}
+
+/*
+ * We don't keep the file handle in the inode anymore to avoid bloating
+ * struct inode and use a pointer to external memory instead.
+ */
+#define NFS_SB_FHSIZE(sb)	((sb)->u.nfs_sb.s_fhsize)
 
 /*
  * The "read_inode" function doesn't actually do anything:
@@ -78,8 +118,19 @@
 	inode->i_mode = 0;
 	inode->i_rdev = 0;
 	inode->i_op = NULL;
+	NFS_FILEID(inode) = 0;
+	NFS_FSID(inode) = 0;
+	INIT_LIST_HEAD(&inode->u.nfs_i.read);
+	INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
+	INIT_LIST_HEAD(&inode->u.nfs_i.commit);
+	INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
+	inode->u.nfs_i.nread = 0;
+	inode->u.nfs_i.ndirty = 0;
+	inode->u.nfs_i.ncommit = 0;
+	inode->u.nfs_i.npages = 0;
 	NFS_CACHEINV(inode);
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 }
 
 static void
@@ -96,32 +147,15 @@
 static void
 nfs_delete_inode(struct inode * inode)
 {
-	int failed;
-
 	dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
+#ifdef NFS_DEBUG_VERBOSE
 	/*
 	 * Flush out any pending write requests ...
 	 */
-	if (NFS_WRITEBACK(inode) != NULL) {
-		unsigned long timeout = jiffies + 5*HZ;
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
-#endif
-		nfs_inval(inode);
-		while (NFS_WRITEBACK(inode) != NULL &&
-		       time_before(jiffies, timeout)) {
-			current->state = TASK_INTERRUPTIBLE;
-			schedule_timeout(HZ/10);
-		}
-		current->state = TASK_RUNNING;
-		if (NFS_WRITEBACK(inode) != NULL)
-			printk("NFS: Arghhh, stuck RPC requests!\n");
+	if (nfs_have_writebacks(inode) || nfs_have_read(inode)) {
+		printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
 	}
-
-	failed = nfs_check_failed_request(inode);
-	if (failed)
-		printk("NFS: inode %ld had %d failed requests\n",
-			inode->i_ino, failed);
+#endif
 	clear_inode(inode);
 }
 
@@ -131,18 +165,23 @@
 	struct nfs_server *server = &sb->u.nfs_sb.s_server;
 	struct rpc_clnt	*rpc;
 
+	/*
+	 * First get rid of the request flushing daemon.
+	 * Relies on rpc_shutdown_client() waiting on all
+	 * client tasks to finish.
+	 */
+	nfs_reqlist_exit(server);
+
 	if ((rpc = server->client) != NULL)
 		rpc_shutdown_client(rpc);
 
+	nfs_reqlist_free(server);
+
 #if 0
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_down();	/* release rpc.lockd */
 #endif
 	rpciod_down();		/* release rpciod */
-	/*
-	 * Invalidate the dircache for this superblock.
-	 */
-	nfs_invalidate_dircache_sb(sb);
 
 	kfree(server->hostname);
 
@@ -160,17 +199,10 @@
 		rpc_killall_tasks(rpc);
 }
 
-/*
- * Compute and set NFS server blocksize
- */
-static unsigned int
-nfs_block_size(unsigned int bsize, unsigned char *nrbitsp)
-{
-	if (bsize < 1024)
-		bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
-		bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
 
+static inline unsigned long
+nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp)
+{
 	/* make sure blocksize is a power of two */
 	if ((bsize & (bsize - 1)) || nrbitsp) {
 		unsigned int	nrbits;
@@ -180,14 +212,56 @@
 		bsize = 1 << nrbits;
 		if (nrbitsp)
 			*nrbitsp = nrbits;
-		if (bsize < NFS_DEF_FILE_IO_BUFFER_SIZE)
-			bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
 	}
 
 	return bsize;
 }
 
 /*
+ * Calculate the number of 512byte blocks used.
+ */
+static inline unsigned long
+nfs_calc_block_size(u64 tsize)
+{
+	off_t used = nfs_size_to_off_t(tsize);
+	return (used + 511) >> 9;
+}
+
+/*
+ * Compute and set NFS server blocksize
+ */
+static inline unsigned long
+nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
+{
+	if (bsize < 1024)
+		bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+	else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
+		bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+
+	return nfs_block_bits(bsize, nrbitsp);
+}
+
+/*
+ * Obtain the root inode of the file system.
+ */
+static struct inode *
+nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh)
+{
+	struct nfs_server	*server = &sb->u.nfs_sb.s_server;
+	struct nfs_fattr	fattr;
+	struct inode		*inode;
+	int			error;
+
+	if ((error = server->rpc_ops->getroot(server, rootfh, &fattr)) < 0) {
+		printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error);
+		return NULL;
+	}
+
+	inode = __nfs_fhget(sb, &fattr);
+	return inode;
+}
+
+/*
  * The way this works is that the mount process passes a structure
  * in the data argument which contains the server's IP address
  * and the root file handle obtained from the server's mount
@@ -198,38 +272,47 @@
 {
 	struct nfs_mount_data	*data = (struct nfs_mount_data *) raw_data;
 	struct nfs_server	*server;
-	struct rpc_xprt		*xprt;
-	struct rpc_clnt		*clnt;
-	struct nfs_fh		*root_fh;
-	struct inode		*root_inode;
+	struct rpc_xprt		*xprt = 0;
+	struct rpc_clnt		*clnt = 0;
+	struct nfs_fh		*root_fh = NULL,
+				*root = &data->root,
+				fh;
+	struct inode		*root_inode = NULL;
 	unsigned int		authflavor;
-	int			tcp;
 	struct sockaddr_in	srvaddr;
 	struct rpc_timeout	timeparms;
-	struct nfs_fattr	fattr;
+	struct nfs_fsinfo       fsinfo;
+	int                     tcp, version, maxlen;
 
 	MOD_INC_USE_COUNT;
-	if (!data)
-		goto out_miss_args;
+	memset(&sb->u.nfs_sb, 0, sizeof(sb->u.nfs_sb));
+	if (!data) {
+		printk(KERN_NOTICE "nfs_read_super: missing data argument\n");
+		goto failure;
+	}
 
-	/* No NFS V3. */
-	if (data->flags & NFS_MOUNT_VER3)
-		goto out_fail;
-
-	/* Don't complain if "mount" is newer. */
-	if (data->version < NFS_MOUNT_VERSION) {
-		printk("nfs warning: mount version %s than kernel\n",
+	memset(&fh, 0, sizeof(fh));
+	if (data->version != NFS_MOUNT_VERSION) {
+		printk(KERN_WARNING "nfs warning: mount version %s than kernel\n",
 			data->version < NFS_MOUNT_VERSION ? "older" : "newer");
 		if (data->version < 2)
 			data->namlen = 0;
 		if (data->version < 3)
 			data->bsize  = 0;
+		if (data->version < 4) {
+			data->flags &= ~NFS_MOUNT_VER3;
+			root = &fh;
+			root->size = NFS2_FHSIZE;
+			memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
+		}
 	}
 
 	/* We now require that the mount process passes the remote address */
 	memcpy(&srvaddr, &data->addr, sizeof(srvaddr));
-	if (srvaddr.sin_addr.s_addr == INADDR_ANY)
-		goto out_no_remote;
+	if (srvaddr.sin_addr.s_addr == INADDR_ANY) {
+		printk(KERN_WARNING "NFS: mount program didn't pass remote address!\n");
+		goto failure;
+	}
 
 	lock_super(sb);
 
@@ -237,12 +320,16 @@
 
 	sb->s_magic      = NFS_SUPER_MAGIC;
 	sb->s_op         = &nfs_sops;
-	sb->s_blocksize  = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
-	sb->u.nfs_sb.s_root = data->root;
+
+	sb->s_blocksize_bits = 0;
+	sb->s_blocksize = nfs_block_bits(data->bsize, &sb->s_blocksize_bits);
+
 	server           = &sb->u.nfs_sb.s_server;
+	memset(server, 0, sizeof(*server));
+
 	server->rsize    = nfs_block_size(data->rsize, NULL);
 	server->wsize    = nfs_block_size(data->wsize, NULL);
-	server->flags    = data->flags;
+	server->flags    = data->flags & NFS_MOUNT_FLAGMASK;
 
 	if (data->flags & NFS_MOUNT_NOAC) {
 		data->acregmin = data->acregmax = 0;
@@ -253,11 +340,34 @@
 	server->acdirmin = data->acdirmin*HZ;
 	server->acdirmax = data->acdirmax*HZ;
 
+	server->namelen  = data->namlen;
 	server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
 	if (!server->hostname)
-		goto out_unlock;
+		goto failure_unlock;
 	strcpy(server->hostname, data->hostname);
 
+ nfsv3_try_again:
+	/* Check NFS protocol revision and initialize RPC op vector
+	 * and file handle pool. */
+	if (data->flags & NFS_MOUNT_VER3) {
+#ifdef CONFIG_NFS_V3
+		server->rpc_ops = &nfs_v3_clientops;
+		NFS_SB_FHSIZE(sb) = sizeof(unsigned short) + NFS3_FHSIZE;
+		version = 3;
+		if (data->version < 4) {
+			printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
+			goto failure_unlock;
+		}
+#else
+		printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
+		goto failure_unlock;
+#endif
+	} else {
+		server->rpc_ops = &nfs_v2_clientops;
+		NFS_SB_FHSIZE(sb) = sizeof(unsigned short) + NFS2_FHSIZE;
+		version = 2;
+	}
+
 	/* Which protocol do we use? */
 	tcp   = (data->flags & NFS_MOUNT_TCP);
 
@@ -267,11 +377,18 @@
 	timeparms.to_maxval  = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
 	timeparms.to_exponential = 1;
 
+	if (!timeparms.to_initval)
+		timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10;
+	if (!timeparms.to_retries)
+		timeparms.to_retries = 5;
+
 	/* Now create transport and client */
 	xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
 						&srvaddr, &timeparms);
-	if (xprt == NULL)
-		goto out_no_xprt;
+	if (xprt == NULL) {
+		printk(KERN_NOTICE "NFS: cannot create RPC transport. \n");
+		goto failure_unlock;
+	}
 
 	/* Choose authentication flavor */
 	authflavor = RPC_AUTH_UNIX;
@@ -281,39 +398,111 @@
 		authflavor = RPC_AUTH_KRB;
 
 	clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
-						NFS_VERSION, authflavor);
-	if (clnt == NULL)
-		goto out_no_client;
+						version, authflavor);
+	if (clnt == NULL) {
+		printk(KERN_NOTICE "NFS: cannot create RPC client \n");
+		goto failure_unlock;
+	}
 
 	clnt->cl_intr     = (data->flags & NFS_MOUNT_INTR)? 1 : 0;
 	clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0;
+	clnt->cl_droppriv = (data->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0;
 	clnt->cl_chatty   = 1;
 	server->client    = clnt;
 
 	/* Fire up rpciod if not yet running */
-	if (rpciod_up() != 0)
-		goto out_no_iod;
+	if (rpciod_up() != 0) {
+		printk(KERN_NOTICE "NFS: cannot start rpciod!\n");
+		goto failure_unlock;
+	}
 
 	/*
 	 * Keep the super block locked while we try to get 
 	 * the root fh attributes.
 	 */
-	root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+	root_fh = nfs_fh_alloc();
 	if (!root_fh)
 		goto out_no_fh;
-	*root_fh = data->root;
+	memcpy((u8*)root_fh, (u8*)root, sizeof(*root_fh));
 
-	if (nfs_proc_getattr(server, root_fh, &fattr) != 0)
-		goto out_no_fattr;
-
-	root_inode = __nfs_fhget(sb, &fattr);
+	/* Did getting the root inode fail? */
+	if ((root->size > NFS_SB_FHSIZE(sb)
+	     || ! (root_inode = nfs_get_root(sb, root)))
+	    && (data->flags & NFS_MOUNT_VER3)) {
+		data->flags &= ~NFS_MOUNT_VER3;
+		nfs_fh_free(root_fh);
+		rpciod_down();
+		rpc_shutdown_client(server->client);
+		goto nfsv3_try_again;
+	}
 	if (!root_inode)
-		goto out_no_root;
-	sb->s_root = d_alloc_root(root_inode, NULL);
-	if (!sb->s_root)
-		goto out_no_root;
+		goto failure_put_root;
+
+	if (! (sb->s_root = d_alloc_root(root_inode, NULL)))
+		goto failure_put_root;
+
 	sb->s_root->d_op = &nfs_dentry_operations;
 	sb->s_root->d_fsdata = root_fh;
+	sb->u.nfs_sb.s_root = root_fh;
+
+	/* Get some general file system info */
+	if (server->rpc_ops->statfs(server, root, &fsinfo) >= 0) {
+		if (server->namelen == 0)
+			server->namelen = fsinfo.namelen;
+	} else {
+		printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n");
+		goto failure_put_root;
+	}
+
+	/* Fire up the writeback cache */
+	if (nfs_reqlist_alloc(server) < 0) {
+		printk(KERN_NOTICE "NFS: cannot initialize writeback cache.\n");
+                goto failure_kill_reqlist;
+	}
+
+	if (data->rsize == 0 && tcp)
+		server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
+	if (data->wsize == 0 && tcp)
+		server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
+
+	/* NFSv3: we don't have bsize, but rather rtmult and wtmult... */
+	if (!fsinfo.bsize)
+		fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult;
+	/* Also make sure we don't go below rsize/wsize since
+	 * RPC calls are expensive */
+	if (fsinfo.bsize < server->rsize)
+		fsinfo.bsize = server->rsize;
+	if (fsinfo.bsize < server->wsize)
+		fsinfo.bsize = server->wsize;
+
+	if (data->bsize == 0)
+		sb->s_blocksize = nfs_block_bits(fsinfo.bsize, &sb->s_blocksize_bits);
+	if (server->rsize > fsinfo.rtmax)
+		server->rsize = fsinfo.rtmax;
+	server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (server->rpages > NFS_READ_MAXIOV) {
+		server->rpages = NFS_READ_MAXIOV;
+		server->rsize = server->rpages << PAGE_CACHE_SHIFT;
+	}
+
+	if (server->wsize > fsinfo.wtmax)
+		server->wsize = fsinfo.wtmax;
+	server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (server->wpages > NFS_WRITE_MAXIOV) {
+		server->wpages = NFS_WRITE_MAXIOV;
+		server->wsize = server->wpages << PAGE_CACHE_SHIFT;
+	}
+
+	server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
+	if (server->dtsize > PAGE_CACHE_SIZE)
+		server->dtsize = PAGE_CACHE_SIZE;
+	if (server->dtsize > server->rsize)
+		server->dtsize = server->rsize;
+
+	maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN;
+
+	if (server->namelen == 0 || server->namelen > maxlen)
+		server->namelen = maxlen;
 
 	/* We're airborne */
 	unlock_super(sb);
@@ -326,47 +515,29 @@
 	return sb;
 
 	/* Yargs. It didn't work out. */
-out_no_root:
-	printk("nfs_read_super: get root inode failed\n");
-	iput(root_inode);
-	goto out_free_fh;
-
-out_no_fattr:
-	printk("nfs_read_super: get root fattr failed\n");
-out_free_fh:
-	kfree(root_fh);
-out_no_fh:
+ failure_kill_reqlist:
+	nfs_reqlist_exit(server);
+ failure_put_root:
+	if (root_inode)
+		iput(root_inode);
+	if (root_fh)
+		nfs_fh_free(root_fh);
+ out_no_fh:
 	rpciod_down();
-	goto out_shutdown;
-
-out_no_iod:
-	printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
-out_shutdown:
-	rpc_shutdown_client(server->client);
-	goto out_free_host;
-
-out_no_client:
-	printk(KERN_WARNING "NFS: cannot create RPC client.\n");
-	xprt_destroy(xprt);
-	goto out_free_host;
-
-out_no_xprt:
-	printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
 
-out_free_host:
-	kfree(server->hostname);
-out_unlock:
+ failure_unlock:
+	/* Yargs. It didn't work out. */
+	if (clnt)
+		rpc_shutdown_client(server->client);
+	else if (xprt)
+		xprt_destroy(xprt);
 	unlock_super(sb);
-	goto out_fail;
-
-out_no_remote:
-	printk("NFS: mount program didn't pass remote address!\n");
-	goto out_fail;
-
-out_miss_args:
-	printk("nfs_read_super: missing data argument\n");
+	nfs_reqlist_free(server);
+	if (server->hostname)
+		kfree(server->hostname);
+	printk(KERN_NOTICE "NFS: cannot create RPC transport.\n");
 
-out_fail:
+failure:
 	sb->s_dev = 0;
 	MOD_DEC_USE_COUNT;
 	return NULL;
@@ -375,27 +546,51 @@
 static int
 nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
-	int error;
-	struct nfs_fsinfo res;
-	struct statfs tmp;
+	struct nfs_sb_info	*si = &sb->u.nfs_sb;
+	struct nfs_server	*server = &si->s_server;
+	unsigned char		blockbits;
+	unsigned long		blockres;
+	int			error;
+	struct nfs_fsinfo	res;
+	struct statfs		tmp;
 
-	error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root,
-		&res);
+	error = server->rpc_ops->statfs(server, NFS_FH(sb->s_root), &res);
 	if (error) {
-		printk("nfs_statfs: statfs error = %d\n", -error);
-		res.bsize = res.blocks = res.bfree = res.bavail = 0;
+		printk(KERN_NOTICE "nfs_statfs: statfs error = %d\n", -error);
+		memset(&res, 0, sizeof(res));
 	}
 	tmp.f_type = NFS_SUPER_MAGIC;
-	tmp.f_bsize = res.bsize;
-	tmp.f_blocks = res.blocks;
-	tmp.f_bfree = res.bfree;
-	tmp.f_bavail = res.bavail;
-	tmp.f_files = 0;
-	tmp.f_ffree = 0;
-	tmp.f_namelen = NAME_MAX;
+	if (res.bsize == 0)
+		res.bsize = sb->s_blocksize;
+	if (res.namelen == 0)
+		res.namelen = server->namelen;
+	tmp.f_bsize   = nfs_block_bits(res.bsize, &blockbits);
+	blockres = (1 << blockbits) - 1;
+	tmp.f_blocks  = (res.tbytes + blockres) >> blockbits;
+	tmp.f_bfree   = (res.fbytes + blockres) >> blockbits;
+	tmp.f_bavail  = (res.abytes + blockres) >> blockbits;
+	tmp.f_files   = res.tfiles;
+	tmp.f_ffree   = res.ffiles;
+	tmp.f_namelen = res.namelen;
 	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
+#if 0
+int nfs_remountfs(struct super_block *sb, int *flags, char *data)
+{
+	struct nfs_server *server = &sb->u.nfs_sb.s_server;
+
+	if (*flags & ~(NFS_MOUNT_NONLM|MS_RDONLY))
+		return -EINVAL;
+
+	if (*flags & ~NFS_MOUNT_NONLM)
+		return 0;
+
+	if ((*flags & NFS_MOUNT_NONLM) == (server->flags & NFS_MOUNT_NONLM))
+		return 0;
+}
+#endif
+
 /*
  * Free all unused dentries in an inode's alias list.
  *
@@ -437,23 +632,19 @@
 }
 
 /*
- * Invalidate the local caches
+ * Zap the caches.
  */
-static void
-nfs_zap_caches(struct inode *inode)
+void nfs_zap_caches(struct inode *inode)
 {
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-	NFS_CACHEINV(inode);
+	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 
-	if (S_ISDIR(inode->i_mode))
-		nfs_invalidate_dircache(inode);
-	else
-		invalidate_inode_pages(inode);
+	invalidate_inode_pages(inode);
+
+	memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+	NFS_CACHEINV(inode);
 }
 
-/*
- * Invalidate, but do not unhash, the inode
- */
 static void
 nfs_invalidate_inode(struct inode *inode)
 {
@@ -461,7 +652,6 @@
 
 	make_bad_inode(inode);
 	inode->i_mode = save_mode;
-	nfs_inval(inode);
 	nfs_zap_caches(inode);
 }
 
@@ -476,6 +666,8 @@
 	 * do this once. (We don't allow inodes to change types.)
 	 */
 	if (inode->i_mode == 0) {
+		NFS_FILEID(inode) = fattr->fileid;
+		NFS_FSID(inode) = fattr->fsid;
 		inode->i_mode = fattr->mode;
 		if (S_ISREG(inode->i_mode))
 			inode->i_op = &nfs_file_inode_operations;
@@ -496,26 +688,62 @@
 		/*
 		 * Preset the size and mtime, as there's no need
 		 * to invalidate the caches.
-		 */ 
-		inode->i_size  = fattr->size;
-		inode->i_mtime = fattr->mtime.seconds;
-		NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+		 */
+		inode->i_size  = nfs_size_to_off_t(fattr->size);
+		inode->i_mtime = nfs_time_to_secs(fattr->mtime);
+		inode->i_atime = nfs_time_to_secs(fattr->atime);
+		inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+		NFS_CACHE_CTIME(inode) = fattr->ctime;
+		NFS_CACHE_MTIME(inode) = fattr->mtime;
+		NFS_CACHE_ATIME(inode) = fattr->atime;
+		NFS_CACHE_ISIZE(inode) = fattr->size;
+		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 	}
 	nfs_refresh_inode(inode, fattr);
 }
 
+static struct inode *
+nfs_make_new_inode(struct super_block *sb, struct nfs_fattr *fattr)
+{
+	struct inode *inode = get_empty_inode();
+
+	if (!inode)
+		return NULL;	
+	inode->i_sb = sb;
+	inode->i_dev = sb->s_dev;
+	inode->i_flags = 0;
+	inode->i_ino = nfs_fattr_to_ino_t(fattr);
+	nfs_read_inode(inode);
+	nfs_fill_inode(inode, fattr);
+	return inode;
+}
+
 /*
- * The following may seem pretty minimal, but the stateless nature
- * of NFS means that we can't do too much more. Previous attempts to use
- * fattr->nlink to determine how well the cached state matches the
- * server suffer from races with stale dentries. You also risk killing
- * off processes by just doing 'mv file newdir' on the server.
- *
- * FIXME: Of course, if 2 exported files have the same fileid (but
- * different fsid which makes it legal) you're still buggered...
- *                                      Trond, August 1999.
+ * In NFSv3 we can have 64bit inode numbers. In order to support
+ * this, and re-exported directories (also seen in NFSv2)
+ * we are forced to allow 2 different inodes to have the same
+ * i_ino.
  */
 static int
+nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque)
+{
+	struct nfs_fattr *fattr = (struct nfs_fattr *)opaque;
+	if (NFS_FSID(inode) != fattr->fsid)
+		return 0;
+	if (NFS_FILEID(inode) != fattr->fileid)
+		return 0;
+	if (inode->i_mode &&
+	    (fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT))
+		return 0;
+	if (is_bad_inode(inode))
+		return 0;
+	if (NFS_FLAGS(inode) & NFS_INO_STALE)
+		return 0;
+	return 1;
+}
+
+static int
 nfs_inode_is_stale(struct inode *inode, struct nfs_fattr *fattr)
 {
 	int unhashed;
@@ -529,16 +757,16 @@
 		is_stale = 1;
 
 	/*
-	 * Free up unused cached dentries to see if it's wise to unhash
-	 * the inode (which we can do if all the dentries have been unhashed).
+	 * If the inode seems stale, free up cached dentries.
 	 */
 	unhashed = nfs_free_dentries(inode);
 
-	/* Assume we're holding 1 lock on the inode from 'iget'
+	/* Assume we're holding an i_count
 	 *
 	 * NB: sockets sometimes have volatile file handles
 	 *     don't invalidate their inodes even if all dentries are
-	 *     unhashed. */
+	 *     unhashed.
+	 */
 	if (unhashed && inode->i_count == unhashed + 1
 	    && !S_ISSOCK(inode->i_mode) && !S_ISFIFO(inode->i_mode))
 		is_stale = 1;
@@ -561,12 +789,12 @@
 {
 	struct super_block *sb = dentry->d_sb;
 
-	dprintk("NFS: nfs_fhget(%s/%s fileid=%d)\n",
+	dprintk("NFS: nfs_fhget(%s/%s fileid=%Lu)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		fattr->fileid);
+		(long long) fattr->fileid);
 
 	/* Install the file handle in the dentry */
-	*((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
+	memcpy(NFS_FH(dentry), (u8*)fhandle, sizeof(*fhandle));
 
 #ifdef CONFIG_NFS_SNAPSHOT
 	/*
@@ -576,15 +804,9 @@
 	if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) ||
 	    (dentry->d_name.len == 9 &&
 	     memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) {
-		struct inode *inode = get_empty_inode();
+		struct inode *inode = nfs_make_new_inode(sb, fattr);
 		if (!inode)
-			goto out;	
-		inode->i_sb = sb;
-		inode->i_dev = sb->s_dev;
-		inode->i_flags = 0;
-		inode->i_ino = fattr->fileid;
-		nfs_read_inode(inode);
-		nfs_fill_inode(inode, fattr);
+			goto out;
 		inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
 		dprintk("NFS: nfs_fhget(snapshot ino=%ld)\n", inode->i_ino);
 	out:
@@ -605,27 +827,39 @@
 static struct inode *
 __nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr)
 {
-	struct inode *inode = NULL;
+	struct inode	*inode = NULL;
+	unsigned long	ino;
 
-	if (!fattr)
+	if ((fattr->valid & NFS_ATTR_FATTR) == 0)
 		goto out_no_inode;
 
-	while (!inode) {
-		inode = iget(sb, fattr->fileid);
-		if (!inode)
-			goto out_no_inode;
-		/* N.B. This should be impossible ... */
-		if (inode->i_ino != fattr->fileid)
-			goto out_bad_id;
+	ino = nfs_fattr_to_ino_t(fattr);
+
+	while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) {
 
+		/*
+		 * Check for busy inodes, and attempt to get rid of any
+		 * unused local references. If successful, we release the
+		 * inode and try again.
+		 *
+		 * Note that the busy test uses the values in the fattr,
+		 * as the inode may have become a different object.
+		 * (We can probably handle modes changes here, too.)
+		 */
 		if (!nfs_inode_is_stale(inode,fattr))
 			break;
 
-		remove_inode_hash(inode);
-		nfs_invalidate_inode(inode);
+		dprintk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
+		       inode->i_ino, inode->i_count);
+		/* Mark the inode as being stale */
+		NFS_FLAGS(inode) |= NFS_INO_STALE;
+		nfs_zap_caches(inode);
 		iput(inode);
-		inode = NULL;
 	}
+
+	if (!inode)
+		goto out_no_inode;
+
 	nfs_fill_inode(inode, fattr);
 	dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n",
 		inode->i_dev, inode->i_ino, inode->i_count);
@@ -634,10 +868,7 @@
 	return inode;
 
 out_no_inode:
-	printk("__nfs_fhget: iget failed\n");
-	goto out;
-out_bad_id:
-	printk("__nfs_fhget: unexpected inode from iget\n");
+	printk(KERN_NOTICE "__nfs_fhget: iget failed\n");
 	goto out;
 }
 
@@ -645,9 +876,8 @@
 nfs_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
-	int error;
-	struct nfs_sattr sattr;
 	struct nfs_fattr fattr;
+	int              error;
 
 	/*
 	 * Make sure the inode is up-to-date.
@@ -655,87 +885,123 @@
 	error = nfs_revalidate(dentry);
 	if (error) {
 #ifdef NFS_PARANOIA
-printk("nfs_notify_change: revalidate failed, error=%d\n", error);
+		printk(KERN_DEBUG "nfs_notify_change: revalidate failed, error=%d\n", error);
 #endif
 		goto out;
 	}
 
-	sattr.mode = (u32) -1;
-	if (attr->ia_valid & ATTR_MODE) 
-		sattr.mode = attr->ia_mode;
-
-	sattr.uid = (u32) -1;
-	if (attr->ia_valid & ATTR_UID)
-		sattr.uid = attr->ia_uid;
-
-	sattr.gid = (u32) -1;
-	if (attr->ia_valid & ATTR_GID)
-		sattr.gid = attr->ia_gid;
-
-	sattr.size = (u32) -1;
-	if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode))
-		sattr.size = attr->ia_size;
-
-	sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1;
-	if (attr->ia_valid & ATTR_MTIME) {
-		sattr.mtime.seconds = attr->ia_mtime;
-		sattr.mtime.useconds = 0;
-	}
-
-	sattr.atime.seconds = sattr.atime.useconds = (u32) -1;
-	if (attr->ia_valid & ATTR_ATIME) {
-		sattr.atime.seconds = attr->ia_atime;
-		sattr.atime.useconds = 0;
-	}
+	if (!S_ISREG(inode->i_mode))
+		attr->ia_valid &= ~ATTR_SIZE;
 
 	error = nfs_wb_all(inode);
-	if (error)
+	if (error < 0)
 		goto out;
 
-	error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
-				&sattr, &fattr);
-	if (error)
+	/* Now perform the setattr call */
+	error = NFS_CALL(setattr, inode, (dentry, &fattr, attr));
+	if (error || !(fattr.valid & NFS_ATTR_FATTR)) {
+		nfs_zap_caches(inode);
 		goto out;
+	}
 	/*
 	 * If we changed the size or mtime, update the inode
 	 * now to avoid invalidating the page cache.
 	 */
-	if (sattr.size != (u32) -1) {
-		if (sattr.size != fattr.size)
-			printk("nfs_notify_change: sattr=%d, fattr=%d??\n",
-				sattr.size, fattr.size);
-		inode->i_size  = sattr.size;
-		inode->i_mtime = fattr.mtime.seconds;
+	if (!(fattr.valid & NFS_ATTR_WCC)) {
+		fattr.pre_size = NFS_CACHE_ISIZE(inode);
+		fattr.pre_mtime = NFS_CACHE_MTIME(inode);
+		fattr.pre_ctime = NFS_CACHE_CTIME(inode);
+		fattr.valid |= NFS_ATTR_WCC;
 	}
-	if (sattr.mtime.seconds != (u32) -1)
-		inode->i_mtime = fattr.mtime.seconds;
 	error = nfs_refresh_inode(inode, &fattr);
 out:
 	return error;
 }
 
+int
+nfs_update_atime(struct dentry *dentry)
+{
+	struct iattr attr;
+	struct inode *inode = dentry->d_inode;
+
+	nfs_revalidate(dentry);
+	if (!inode || time_before(inode->i_atime,nfs_time_to_secs(NFS_CACHE_ATIME(inode))))
+		return 0;
+
+	attr.ia_valid = ATTR_ATIME|ATTR_ATIME_SET;
+	attr.ia_atime = inode->i_atime;
+	return nfs_notify_change(dentry, &attr);
+}
+
 /*
- * Externally visible revalidation function
+ * Wait for the inode to get unlocked.
+ * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING).
  */
 int
-nfs_revalidate(struct dentry *dentry)
+nfs_wait_on_inode(struct inode *inode, int flag)
 {
-	return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	struct rpc_clnt		*clnt = NFS_CLIENT(inode);
+	int error;
+	if (!(NFS_FLAGS(inode) & flag))
+		return 0;
+	inode->i_count++;
+	error = nfs_wait_event(clnt, inode->i_wait, !(NFS_FLAGS(inode) & flag));
+	iput(inode);
+	return error;
 }
 
 /*
- * These are probably going to contain hooks for
- * allocating and releasing RPC credentials for
- * the file. I'll have to think about Tronds patch
- * a bit more..
+ * Externally visible revalidation function
  */
+int
+nfs_revalidate(struct dentry *dentry)
+{
+	return nfs_revalidate_inode(dentry);
+}
+
+static __inline__ struct nfs_file *nfs_file_alloc(void)
+{
+	struct nfs_file	*p;
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		p->magic = NFS_FILE_MAGIC;
+	}
+	return p;
+}
+
+static __inline__ void nfs_file_free(struct nfs_file *p)
+{
+	if (p->magic == NFS_FILE_MAGIC) {
+		p->magic = 0;
+		kfree(p);
+	} else
+		printk(KERN_ERR "NFS: extra file info corrupted!\n");
+}
+
 int nfs_open(struct inode *inode, struct file *filp)
 {
+	struct rpc_auth	*auth = NFS_CLIENT(inode)->cl_auth;
+	struct nfs_file	*data;
+
+	data = nfs_file_alloc();
+	if (!data)
+		return -ENOMEM;
+	data->cred = rpcauth_lookupcred(auth, 0);
+	filp->private_data = data;
 	return 0;
 }
 
 int nfs_release(struct inode *inode, struct file *filp)
 {
+	struct nfs_file	*data = NFS_FILE(filp);
+	struct rpc_auth	*auth = NFS_CLIENT(inode)->cl_auth;
+	struct rpc_cred	*cred;
+
+	cred = nfs_file_cred(filp);
+	if (cred)
+		rpcauth_releasecred(auth, cred);
+	nfs_file_free(data);
 	return 0;
 }
 
@@ -744,25 +1010,44 @@
  * the cached attributes have to be refreshed.
  */
 int
-_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
+__nfs_revalidate_inode(struct dentry *dentry)
 {
 	struct inode	*inode = dentry->d_inode;
-	int		 status = 0;
 	struct nfs_fattr fattr;
+	int		 status = 0;
 
 	dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		inode->i_ino);
-	status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
+
+	if (!inode || is_bad_inode(inode))
+		return -ESTALE;
+
+	while (NFS_REVALIDATING(inode)) {
+		status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
+		if (status < 0)
+			return status;
+		if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
+			return 0;
+	}
+	NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
+
+	status = NFS_CALL(getattr, inode, (dentry, &fattr));
 	if (status) {
 		int error;
 		u32 *fh;
+		struct dentry *dir = dentry->d_parent;
 		struct nfs_fh fhandle;
+		struct nfs_fattr dir_attr;
+
 		dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
-			dentry->d_parent->d_name.name,
-			dentry->d_name.name, inode->i_ino, status);
+		       dentry->d_parent->d_name.name, dentry->d_name.name,
+		       inode->i_ino, status);
+		nfs_zap_caches(inode);
+
 		if (status != -ESTALE)
 			goto out;
+
 		/*
 		 * A "stale filehandle" error ... show the current fh
 		 * and find out what the filehandle should be.
@@ -770,8 +1055,9 @@
 		fh = (u32 *) NFS_FH(dentry);
 		dfprintk(PAGECACHE, "NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n",
 			fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
-		error = nfs_proc_lookup(server, NFS_FH(dentry->d_parent), 
-					dentry->d_name.name, &fhandle, &fattr);
+		error = NFS_CALL(lookup, dir->d_inode, (dir, &dir_attr, 
+					&dentry->d_name, &fhandle, &fattr));
+		nfs_refresh_inode(dir->d_inode, &dir_attr);
 		if (error) {
 			dfprintk(PAGECACHE, "NFS: lookup failed, error=%d\n", error);
 			goto out;
@@ -787,13 +1073,16 @@
 	status = nfs_refresh_inode(inode, &fattr);
 	if (status) {
 		dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
-			dentry->d_parent->d_name.name,
-			dentry->d_name.name, inode->i_ino, status);
+			 dentry->d_parent->d_name.name, dentry->d_name.name,
+			 inode->i_ino, status);
 		goto out;
 	}
+
 	dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 out:
+	NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
+	wake_up(&inode->i_wait);
 	return status;
 }
 
@@ -802,29 +1091,54 @@
  * an operation.  Here we update the inode to reflect the state
  * of the server's inode.
  *
- * This is a bit tricky because we have to make sure all dirty pages
- * have been sent off to the server before calling invalidate_inode_pages.
- * To make sure no other process adds more write requests while we try
- * our best to flush them, we make them sleep during the attribute refresh.
+ * If we have reason to believe that any data we cached has become
+ * invalid, we schedule it to be flushed on the next occasion
+ * (i.e. when nfs_revalidate_inode is called).
  *
- * A very similar scenario holds for the dir cache.
+ * The reason we don't do it here is because nfs_refresh_inode can
+ * be called outside of the process context, e.g. from nfs_readpage_result,
+ * which is invoked by rpciod.
  */
 int
 nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
-	int invalid = 0;
-	int error = -EIO;
-
-	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
-		 inode->i_dev, inode->i_ino, inode->i_count);
+	off_t		new_size, new_isize;
+	__u64		new_mtime;
+	int		invalid = 0;
+	int		error = -EIO;
 
 	if (!inode || !fattr) {
-		printk("nfs_refresh_inode: inode or fattr is NULL\n");
+		printk(KERN_ERR "nfs_refresh_inode: inode or fattr is NULL\n");
 		goto out;
 	}
-	if (inode->i_ino != fattr->fileid) {
-		printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
-			inode->i_ino, fattr->fileid);
+	if (inode->i_mode == 0) {
+		printk(KERN_ERR "nfs_refresh_inode: empty inode\n");
+		goto out;
+	}
+
+	if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+		goto out;
+
+	if (is_bad_inode(inode))
+		goto out;
+
+	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
+			inode->i_dev, inode->i_ino, inode->i_count,
+			fattr->valid);
+
+
+	if (NFS_FSID(inode) != fattr->fsid ||
+	    NFS_FILEID(inode) != fattr->fileid) {
+		printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n"
+		       "expected (0x%lx%08lx/0x%lx%08lx), got (0x%lx%08lx/0x%lx%08lx)\n",
+		       (unsigned long) (NFS_FSID(inode)>>32),
+		       (unsigned long) (NFS_FSID(inode) & 0xFFFFFFFFUL),
+		       (unsigned long) (NFS_FILEID(inode)>>32),
+		       (unsigned long) (NFS_FILEID(inode) & 0xFFFFFFFFUL),
+		       (unsigned long) (fattr->fsid >> 32),
+		       (unsigned long) (fattr->fsid & 0xFFFFFFFFUL),
+		       (unsigned long) (fattr->fileid >> 32),
+		       (unsigned long) (fattr->fileid & 0xFFFFFFFFUL));
 		goto out;
 	}
 
@@ -834,54 +1148,101 @@
 	if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
 		goto out_changed;
 
-	inode->i_mode = fattr->mode;
-	inode->i_nlink = fattr->nlink;
-	inode->i_uid = fattr->uid;
-	inode->i_gid = fattr->gid;
+ 	new_mtime = fattr->mtime;
+	new_size = fattr->size;
+ 	new_isize = nfs_size_to_off_t(fattr->size);
 
-	inode->i_blocks = fattr->blocks;
-	inode->i_atime = fattr->atime.seconds;
-	inode->i_ctime = fattr->ctime.seconds;
+	error = 0;
 
 	/*
 	 * Update the read time so we don't revalidate too often.
 	 */
 	NFS_READTIME(inode) = jiffies;
-	error = 0;
 
 	/*
-	 * If we have pending write-back entries, we don't want
-	 * to look at the size or the mtime the server sends us
-	 * too closely, as we're in the middle of modifying them.
+	 * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
+	 *       NOT inode->i_size!!!
 	 */
-	if (NFS_WRITEBACK(inode))
-		goto out;
-
-	if (inode->i_size != fattr->size) {
+	if (NFS_CACHE_ISIZE(inode) != new_size) {
 #ifdef NFS_DEBUG_VERBOSE
-printk("NFS: size change on %x/%ld\n", inode->i_dev, inode->i_ino);
+		printk(KERN_DEBUG "NFS: isize change on %x/%ld\n", inode->i_dev, inode->i_ino);
 #endif
-		inode->i_size = fattr->size;
 		invalid = 1;
 	}
 
-	if (inode->i_mtime != fattr->mtime.seconds) {
+	/*
+	 * Note: we don't check inode->i_mtime since pipes etc.
+	 *       can change this value in VFS without requiring a
+	 *	 cache revalidation.
+	 */
+	if (NFS_CACHE_MTIME(inode) != new_mtime) {
 #ifdef NFS_DEBUG_VERBOSE
-printk("NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+		printk(KERN_DEBUG "NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
 #endif
-		inode->i_mtime = fattr->mtime.seconds;
 		invalid = 1;
 	}
 
-	if (invalid)
-		goto out_invalid;
+	/* Check Weak Cache Consistency data.
+	 * If size and mtime match the pre-operation values, we can
+	 * assume that any attribute changes were caused by our NFS
+         * operation, so there's no need to invalidate the caches.
+         */
+        if ((fattr->valid & NFS_ATTR_WCC)
+	    && NFS_CACHE_ISIZE(inode) == fattr->pre_size
+	    && NFS_CACHE_MTIME(inode) == fattr->pre_mtime) {
+		invalid = 0;
+	}
+
+	/*
+	 * If we have pending writebacks, things can get
+	 * messy.
+	 */
+	if (nfs_have_writebacks(inode) && new_isize < inode->i_size)
+		new_isize = inode->i_size;
+
+	NFS_CACHE_CTIME(inode) = fattr->ctime;
+	inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+	/* If we've been messing around with atime, don't
+	 * update it. Save the server value in NFS_CACHE_ATIME.
+	 */
+	NFS_CACHE_ATIME(inode) = fattr->atime;
+	if (time_before(inode->i_atime, nfs_time_to_secs(fattr->atime)))
+		inode->i_atime = nfs_time_to_secs(fattr->atime);
+
+	NFS_CACHE_MTIME(inode) = new_mtime;
+	inode->i_mtime = nfs_time_to_secs(new_mtime);
+
+	NFS_CACHE_ISIZE(inode) = new_size;
+	inode->i_size = new_isize;
 
+	inode->i_mode = fattr->mode;
+	inode->i_nlink = fattr->nlink;
+	inode->i_uid = fattr->uid;
+	inode->i_gid = fattr->gid;
+
+	if (fattr->valid & NFS_ATTR_FATTR_V3) {
+		/*
+		 * report the blocks in 512byte units
+		 */
+		inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
+		inode->i_blksize = inode->i_sb->s_blocksize;
+ 	} else {
+ 		inode->i_blocks = fattr->du.nfs2.blocks;
+ 		inode->i_blksize = fattr->du.nfs2.blocksize;
+ 	}
+ 	inode->i_rdev = 0;
+ 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ 		inode->i_rdev = to_kdev_t(fattr->rdev);
+ 
 	/* Update attrtimeo value */
-	if (fattr->mtime.seconds == NFS_OLDMTIME(inode)) {
+	if (!invalid && time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) {
 		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
 			NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
+		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 	}
-	NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+
+	if (invalid)
+		nfs_zap_caches(inode);
 
 out:
 	return error;
@@ -891,22 +1252,16 @@
 	 * Big trouble! The inode has become a different object.
 	 */
 #ifdef NFS_PARANOIA
-printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
-inode->i_ino, inode->i_mode, fattr->mode);
+	printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
+	       inode->i_ino, inode->i_mode, fattr->mode);
 #endif
 	/*
 	 * No need to worry about unhashing the dentry, as the
 	 * lookup validation will know that the inode is bad.
+	 * (But we fall through to invalidate the caches.)
 	 */
 	nfs_invalidate_inode(inode);
 	goto out;
-
-out_invalid:
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
-#endif
-	nfs_zap_caches(inode);
-	goto out;
 }
 
 /*
@@ -926,8 +1281,6 @@
 init_nfs_fs(void)
 {
 #ifdef CONFIG_PROC_FS
-	rpc_register_sysctl();
-	rpc_proc_init();
 	rpc_proc_register(&nfs_rpcstat);
 #endif
         return register_filesystem(&nfs_fs_type);
@@ -955,6 +1308,5 @@
 	rpc_proc_unregister("nfs");
 #endif
 	unregister_filesystem(&nfs_fs_type);
-	nfs_free_dircache();
 }
 #endif
diff -urN linux-2.2.18-pre8/fs/nfs/mount_clnt.c linux/fs/nfs/mount_clnt.c
--- linux-2.2.18-pre8/fs/nfs/mount_clnt.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/nfs/mount_clnt.c	Mon Dec  3 20:25:21 2001
@@ -17,17 +17,14 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/xprt.h>
 #include <linux/sunrpc/sched.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
 
 #ifdef RPC_DEBUG
 # define NFSDBG_FACILITY	NFSDBG_ROOT
 #endif
 
-#ifndef MAX
-# define MAX(a, b)	(((a) > (b))? (a) : (b))
-#endif
-
-
 /*
 #define MOUNT_PROGRAM		100005
 #define MOUNT_VERSION		1
@@ -35,7 +32,9 @@
 #define MOUNT_UMNT		3
  */
 
-static struct rpc_clnt *	mnt_create(char *, struct sockaddr_in *);
+static int			nfs_gen_mount(struct sockaddr_in *, char *,
+					      struct nfs_fh *, int);
+static struct rpc_clnt *	mnt_create(char *, struct sockaddr_in *, int);
 extern struct rpc_program	mnt_program;
 
 struct mnt_fhstatus {
@@ -43,30 +42,44 @@
 	struct nfs_fh *		fh;
 };
 
+int
+nfs_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
+{
+	return nfs_gen_mount(addr, path, fh, NFS_MNT_VERSION);
+}
+
+int
+nfs3_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
+{
+	return nfs_gen_mount(addr, path, fh, NFS_MNT3_VERSION);
+}
+
 /*
  * Obtain an NFS file handle for the given host and path
  */
-int
-nfs_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
+static int
+nfs_gen_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, int version)
 {
 	struct rpc_clnt		*mnt_clnt;
 	struct mnt_fhstatus	result = { 0, fh };
 	char			hostname[32];
 	int			status;
+	int			call;
 
 	dprintk("NFS:      nfs_mount(%08x:%s)\n",
 			(unsigned)ntohl(addr->sin_addr.s_addr), path);
 
 	strcpy(hostname, in_ntoa(addr->sin_addr.s_addr));
-	if (!(mnt_clnt = mnt_create(hostname, addr)))
+	if (!(mnt_clnt = mnt_create(hostname, addr, version)))
 		return -EACCES;
 
-	status = rpc_call(mnt_clnt, NFS_MNTPROC_MNT, path, &result, 0);
+	call = (version == 3) ? MOUNTPROC3_MNT : MNTPROC_MNT;
+	status = rpc_call(mnt_clnt, call, path, &result, 0);
 	return status < 0? status : (result.status? -EACCES : 0);
 }
 
 static struct rpc_clnt *
-mnt_create(char *hostname, struct sockaddr_in *srvaddr)
+mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version)
 {
 	struct rpc_xprt	*xprt;
 	struct rpc_clnt	*clnt;
@@ -75,7 +88,7 @@
 		return NULL;
 
 	clnt = rpc_create_client(xprt, hostname,
-				&mnt_program, NFS_MNT_VERSION,
+				&mnt_program, version,
 				RPC_AUTH_NULL);
 	if (!clnt) {
 		xprt_destroy(xprt);
@@ -100,7 +113,7 @@
 static int
 xdr_encode_dirpath(struct rpc_rqst *req, u32 *p, const char *path)
 {
-	p = xdr_encode_string(p, path);
+	p = xdr_encode_string(p, path, -1);
 
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -109,14 +122,39 @@
 static int
 xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
 {
-	if ((res->status = ntohl(*p++)) == 0)
-		memcpy(res->fh, p, sizeof(*res->fh));
+	struct nfs_fh *fh = res->fh;
+
+	memset((void *)fh, 0, sizeof(*fh));
+	if ((res->status = ntohl(*p++)) == 0) {
+		res->fh->size = NFS2_FHSIZE;
+		memcpy(res->fh->data, p, NFS2_FHSIZE);
+	}
+	return 0;
+}
+
+static int
+xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
+{
+	struct nfs_fh *fh = res->fh;
+
+	memset((void *)fh, 0, sizeof(*fh));
+	if ((res->status = ntohl(*p++)) == 0) {
+		unsigned int size = ntohl(*p++);
+		if (size <= NFS3_FHSIZE) {
+			res->fh->size = size;
+			memcpy(res->fh->data, p, size);
+		}
+	}
 	return 0;
 }
 
 #define MNT_dirpath_sz		(1 + 256)
 #define MNT_fhstatus_sz		(1 + 8)
 
+#ifndef MAX
+# define MAX(a, b)      (((a) > (b))? (a) : (b))
+#endif
+
 static struct rpc_procinfo	mnt_procedures[2] = {
 	{ "mnt_null",
 		(kxdrproc_t) xdr_error,	
@@ -124,16 +162,32 @@
 	{ "mnt_mount",
 		(kxdrproc_t) xdr_encode_dirpath,	
 		(kxdrproc_t) xdr_decode_fhstatus,
-		MAX(MNT_dirpath_sz, MNT_fhstatus_sz)<<2, 0},
+		MAX(MNT_dirpath_sz, MNT_fhstatus_sz) << 2, 0 },
+};
+
+static struct rpc_procinfo mnt3_procedures[2] = {
+	{ "mnt3_null",
+		(kxdrproc_t) xdr_error,
+		(kxdrproc_t) xdr_error, 0, 0 },
+	{ "mnt3_mount",
+		(kxdrproc_t) xdr_encode_dirpath,
+		(kxdrproc_t) xdr_decode_fhstatus3,
+		MAX(MNT_dirpath_sz, MNT_fhstatus_sz) << 2, 0 },
 };
 
 static struct rpc_version	mnt_version1 = {
 	1, 2, mnt_procedures
 };
 
+static struct rpc_version	mnt_version3 = {
+	3, 2, mnt3_procedures
+};
+
 static struct rpc_version *	mnt_version[] = {
 	NULL,
 	&mnt_version1,
+	NULL,
+	&mnt_version3,
 };
 
 static struct rpc_stat		mnt_stats;
diff -urN linux-2.2.18-pre8/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- linux-2.2.18-pre8/fs/nfs/nfs2xdr.c	Sat Mar  6 14:21:13 1999
+++ linux/fs/nfs/nfs2xdr.c	Mon Dec  3 20:25:21 2001
@@ -1,13 +1,15 @@
 /*
- * linux/fs/nfs/xdr.c
+ * linux/fs/nfs/nfs2xdr.c
  *
  * XDR functions to encode/decode NFS RPC arguments and results.
  *
  * Copyright (C) 1992, 1993, 1994  Rick Sladkey
  * Copyright (C) 1996 Olaf Kirch
+ * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
+ * 		FIFO's need special handling in NFSv2
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS2_XDR_TYPES
 
 #include <linux/param.h>
 #include <linux/sched.h>
@@ -20,6 +22,8 @@
 #include <linux/pagemap.h>
 #include <linux/proc_fs.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
 #include <linux/nfs_fs.h>
 
 /* Uncomment this to support servers requiring longword lengths */
@@ -28,8 +32,7 @@
 #define NFSDBG_FACILITY		NFSDBG_XDR
 /* #define NFS_PARANOIA 1 */
 
-#define QUADLEN(len)		(((len) + 3) >> 2)
-static int			nfs_stat_to_errno(int stat);
+extern int			nfs_stat_to_errno(int stat);
 
 /* Mapping from NFS error code to "errno" error code. */
 #define errno_NFSERR_IO		EIO
@@ -40,8 +43,8 @@
  */
 #define NFS_fhandle_sz		8
 #define NFS_sattr_sz		8
-#define NFS_filename_sz		1+(NFS_MAXNAMLEN>>2)
-#define NFS_path_sz		1+(NFS_MAXPATHLEN>>2)
+#define NFS_filename_sz		1+(NFS2_MAXNAMLEN>>2)
+#define NFS_path_sz		1+(NFS2_MAXPATHLEN>>2)
 #define NFS_fattr_sz		17
 #define NFS_info_sz		5
 #define NFS_entry_sz		NFS_filename_sz+3
@@ -49,6 +52,7 @@
 #define NFS_enc_void_sz		0
 #define NFS_diropargs_sz	NFS_fhandle_sz+NFS_filename_sz
 #define NFS_sattrargs_sz	NFS_fhandle_sz+NFS_sattr_sz
+#define NFS_readlinkargs_sz	NFS_fhandle_sz
 #define NFS_readargs_sz		NFS_fhandle_sz+3
 #define NFS_writeargs_sz	NFS_fhandle_sz+4
 #define NFS_createargs_sz	NFS_diropargs_sz+NFS_sattr_sz
@@ -60,8 +64,9 @@
 #define NFS_dec_void_sz		0
 #define NFS_attrstat_sz		1+NFS_fattr_sz
 #define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz	1+NFS_path_sz
+#define NFS_readlinkres_sz	1
 #define NFS_readres_sz		1+NFS_fattr_sz+1
+#define NFS_writeres_sz         NFS_attrstat_sz
 #define NFS_stat_sz		1
 #define NFS_readdirres_sz	1
 #define NFS_statfsres_sz	1+NFS_info_sz
@@ -72,15 +77,19 @@
 static inline u32 *
 xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
 {
-	*((struct nfs_fh *) p) = *fhandle;
-	return p + QUADLEN(sizeof(*fhandle));
+	memcpy(p, fhandle->data, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 static inline u32 *
 xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
 {
-	*fhandle = *((struct nfs_fh *) p);
-	return p + QUADLEN(sizeof(*fhandle));
+	/* Zero handle first to allow comparisons */
+	memset(fhandle, 0, sizeof(*fhandle));
+	/* NFSv2 handles have a fixed length */
+	fhandle->size = NFS2_FHSIZE;
+	memcpy(fhandle->data, p, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 static inline u32 *
@@ -91,7 +100,14 @@
 	if (*len > maxlen)
 		return NULL;
 	*string = (char *) p;
-	return p + QUADLEN(*len);
+	return p + XDR_QUADLEN(*len);
+}
+
+static inline u32*
+xdr_decode_time(u32 *p, u64 *timep)
+{
+	*timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+	return p;
 }
 
 static inline u32 *
@@ -103,33 +119,51 @@
 	fattr->uid = ntohl(*p++);
 	fattr->gid = ntohl(*p++);
 	fattr->size = ntohl(*p++);
-	fattr->blocksize = ntohl(*p++);
+	fattr->du.nfs2.blocksize = ntohl(*p++);
 	fattr->rdev = ntohl(*p++);
-	fattr->blocks = ntohl(*p++);
+	fattr->du.nfs2.blocks = ntohl(*p++);
 	fattr->fsid = ntohl(*p++);
 	fattr->fileid = ntohl(*p++);
-	fattr->atime.seconds = ntohl(*p++);
-	fattr->atime.useconds = ntohl(*p++);
-	fattr->mtime.seconds = ntohl(*p++);
-	fattr->mtime.useconds = ntohl(*p++);
-	fattr->ctime.seconds = ntohl(*p++);
-	fattr->ctime.useconds = ntohl(*p++);
+	p = xdr_decode_time(p, &fattr->atime);
+	p = xdr_decode_time(p, &fattr->mtime);
+	p = xdr_decode_time(p, &fattr->ctime);
+	fattr->valid |= NFS_ATTR_FATTR;
+	if (fattr->type == NFCHR && fattr->rdev == NFS2_FIFO_DEV) {
+		fattr->type = NFFIFO;
+		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
+		fattr->rdev = 0;
+	}
 	return p;
 }
 
+#define SATTR(p, attr, flag, field) \
+        *p++ = (attr->ia_valid & flag) ? htonl(attr->field) : ~(u32) 0
 static inline u32 *
-xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
+xdr_encode_sattr(u32 *p, struct iattr *attr)
 {
-	*p++ = htonl(sattr->mode);
-	*p++ = htonl(sattr->uid);
-	*p++ = htonl(sattr->gid);
-	*p++ = htonl(sattr->size);
-	*p++ = htonl(sattr->atime.seconds);
-	*p++ = htonl(sattr->atime.useconds);
-	*p++ = htonl(sattr->mtime.seconds);
-	*p++ = htonl(sattr->mtime.useconds);
-	return p;
+	SATTR(p, attr, ATTR_MODE, ia_mode);
+	SATTR(p, attr, ATTR_UID, ia_uid);
+	SATTR(p, attr, ATTR_GID, ia_gid);
+	SATTR(p, attr, ATTR_SIZE, ia_size);
+
+	if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) {
+		*p++ = htonl(attr->ia_atime);
+		*p++ = 0;
+	} else {
+		*p++ = ~(u32) 0;
+		*p++ = ~(u32) 0;
+	}
+
+	if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) {
+		*p++ = htonl(attr->ia_mtime);
+		*p++ = 0;
+	} else {
+		*p++ = ~(u32) 0;	
+		*p++ = ~(u32) 0;
+	}
+  	return p;
 }
+#undef SATTR;
 
 /*
  * NFS encode functions
@@ -176,7 +210,7 @@
 nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_string(p, args->name, args->len);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -190,7 +224,8 @@
 nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen, buflen;
+	size_t		replen;
+	unsigned int	nr;
 
 	p = xdr_encode_fhandle(p, args->fh);
 	*p++ = htonl(args->offset);
@@ -198,21 +233,22 @@
 	*p++ = htonl(args->count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-#if 1
+	/* Get the number of buffers in the receive iovec */
+        nr = args->nriov;
+
+        if (nr+1 > MAX_IOVEC) {
+                printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
+                return -EINVAL;
+        }
+
 	/* set up reply iovec */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
 	req->rq_rvec[0].iov_len  = replen;
-	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->count;
-	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[2].iov_len  = buflen - replen;
-	req->rq_rlen = args->count + buflen;
-	req->rq_rnr = 3;
-#else
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	req->rq_rvec[0].iov_len  = replen;
-#endif
+        /* Copy the iovec */
+        memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
+
+	req->rq_rlen = args->count + replen;
+	req->rq_rnr += nr;
 
 	return 0;
 }
@@ -224,9 +260,9 @@
 nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
 	struct iovec *iov = req->rq_rvec;
-	int	status, count, recvd, hdrlen;
+	u32	count, recvd, hdrlen;
+	int	status;
 
-	dprintk("RPC:      readres OK status %lx\n", (long)ntohl(*p));
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 	p = xdr_decode_fattr(p, res->fattr);
@@ -234,22 +270,22 @@
 	count = ntohl(*p++);
 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
 	recvd = req->rq_rlen - hdrlen;
-	if (p != iov[2].iov_base) {
-		/* Unexpected reply header size. Punt.
-		 * XXX: Move iovec contents to align data on page
-		 * boundary and adjust RPC header size guess */
-		printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
 		return -errno_NFSERR_IO;
 	}
-	if (count > recvd) {
-		printk("NFS: server cheating in read reply: "
-			"count %d > recvd %d\n", count, recvd);
-		count = recvd;
+	if (hdrlen != iov[0].iov_len) {
+		dprintk("NFS: Short READ header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
 	}
 
 	dprintk("RPC:      readres OK count %d\n", count);
-	if (count < res->count)
-		memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+	if (count < res->count) {
+		xdr_zero_iovec(iov+1, req->rq_rnr-1, res->count - count);
+		res->count = count;
+		res->eof = 1;  /* Silly NFSv3ism which can't be helped */
+	} else
+		res->eof = 0;
 
 	return count;
 }
@@ -261,6 +297,7 @@
 static int
 nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
+	unsigned int nr;
 	u32 count = args->count;
 
 	p = xdr_encode_fhandle(p, args->fh);
@@ -270,28 +307,35 @@
 	*p++ = htonl(count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	req->rq_svec[1].iov_base = (void *) args->buffer;
-	req->rq_svec[1].iov_len = count;
-	req->rq_slen += count;
-	req->rq_snr = 2;
+	/* Get the number of buffers in the send iovec */
+	nr = args->nriov;
+
+	if (nr+2 > MAX_IOVEC) {
+                printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs "
+                        "(nr %d max %d)\n", nr, MAX_IOVEC);
+                return -EINVAL;
+        }
+
+	/* Copy the iovec */
+        memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
 
 #ifdef NFS_PAD_WRITES
 	/*
 	 * Some old servers require that the message length
 	 * be a multiple of 4, so we pad it here if needed.
 	 */
-	count = ((count + 3) & ~3) - count;
-	if (count) {
-#if 0
-printk("nfs_writeargs: padding write, len=%d, slen=%d, pad=%d\n",
-req->rq_svec[1].iov_len, req->rq_slen, count);
-#endif
-		req->rq_svec[2].iov_base = (void *) "\0\0\0";
-		req->rq_svec[2].iov_len  = count;
-		req->rq_slen += count;
-		req->rq_snr = 3;
+	if (count & 3) {
+		struct iovec	*iov = req->rq_svec + nr + 1;
+		int		pad = 4 - (count & 3);
+
+		iov->iov_base = (void *) "\0\0\0";
+		iov->iov_len  = pad;
+		count += pad;
+		nr++;
 	}
 #endif
+	req->rq_slen += count;
+	req->rq_snr += nr;
 
 	return 0;
 }
@@ -304,7 +348,7 @@
 nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_string(p, args->name, args->len);
 	p = xdr_encode_sattr(p, args->sattr);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -317,9 +361,9 @@
 nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
 	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -332,7 +376,7 @@
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
 	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -344,8 +388,8 @@
 nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_string(p, args->topath);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
+	p = xdr_encode_string(p, args->topath, args->tolen);
 	p = xdr_encode_sattr(p, args->sattr);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -360,7 +404,7 @@
 	struct rpc_task	*task = req->rq_task;
 	struct rpc_auth	*auth = task->tk_auth;
 	u32		bufsiz = args->bufsiz;
-	int		replen;
+	size_t		replen;
 
 	/*
 	 * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
@@ -376,119 +420,95 @@
 
 	/* set up reply iovec */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-	/*
-	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
-		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
-	 */
 	req->rq_rvec[0].iov_len  = replen;
 	req->rq_rvec[1].iov_base = args->buffer;
 	req->rq_rvec[1].iov_len  = args->bufsiz;
 	req->rq_rlen = replen + args->bufsiz;
-	req->rq_rnr = 2;
-
-	/*
-	dprintk("RPC:      readdirargs set up reply vec:\n");
-	dprintk("          rvec[0] = %p/%d\n",
-			req->rq_rvec[0].iov_base,
-			req->rq_rvec[0].iov_len);
-	dprintk("          rvec[1] = %p/%d\n",
-			req->rq_rvec[1].iov_base,
-			req->rq_rvec[1].iov_len);
-	 */
+	req->rq_rnr ++;
 
 	return 0;
 }
 
 /*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- *	entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
+ * Decode the result of a readdir call.
+ * We're not really decoding anymore, we just leave the buffer untouched
+ * and only check that it is syntactically correct.
+ * The real decoding happens in nfs_decode_entry below, called directly
+ * from nfs_readdir for each entry.
  */
 static int
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
 {
 	struct iovec		*iov = req->rq_rvec;
+	u32			hdrlen;
 	int			 status, nr;
-	char			*string, *start;
-	u32			*end, *entry, len, fileid, cookie;
+	u32			*end, *entry, len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
-		/* Unexpected reply header size. Punt. */
-		printk("NFS: Odd RPC header size in readdirres reply\n");
+	hdrlen = (u8*)p - (u8*) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READDIR reply: %d\n", hdrlen);
 		return -errno_NFSERR_IO;
 	}
+	if (hdrlen != iov[0].iov_len) {
+		dprintk("NFS: Short READDIR header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
 
 	/* Get start and end address of XDR data */
 	p   = (u32 *) iov[1].iov_base;
 	end = (u32 *) ((u8 *) p + iov[1].iov_len);
 
 	/* Get start and end of dirent buffer */
-	entry  = (u32 *) res->buffer;
-	start  = (char *) res->buffer;
-	string = (char *) res->buffer + res->bufsiz;
-	for (nr = 0; *p++; nr++) {
-		fileid = ntohl(*p++);
+	if (res->buffer != p) {
+		printk(KERN_ERR "NFS: Bad result buffer in readdir\n");
+		return -errno_NFSERR_IO;
+	}
 
+	for (nr = 0; *p++; nr++) {
+		entry = p - 1;
+		if (p + 2 > end)
+			goto short_pkt;
+		p++; /* fileid */
 		len = ntohl(*p++);
-		/*
-		 * Check whether the server has exceeded our reply buffer,
-		 * and set a flag to convert the size to longwords.
-		 */
-		if ((p + QUADLEN(len) + 3) > end) {
-			struct rpc_clnt *clnt = req->rq_task->tk_client;
-			printk(KERN_WARNING
-				"NFS: server %s, readdir reply truncated\n",
-				clnt->cl_server);
-			printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n",
-				nr, (end - p), len);
-			clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
-			break;
-		}
-		if (len > NFS_MAXNAMLEN) {
-			printk("NFS: giant filename in readdir (len %x)!\n",
+		p += XDR_QUADLEN(len) + 1;	/* name plus cookie */
+		if (len > NFS2_MAXNAMLEN) {
+			printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n",
 						len);
 			return -errno_NFSERR_IO;
 		}
-		string -= len;
-		if ((void *) (entry+3) > (void *) string) {
-			/* 
-			 * This error is impossible as long as the temp
-			 * buffer is no larger than the user buffer. The 
-			 * current packing algorithm uses the same amount
-			 * of space in the user buffer as in the XDR data,
-			 * so it's guaranteed to fit.
-			 */
-			printk("NFS: incorrect buffer size in %s!\n",
-				__FUNCTION__);
-			break;
-		}
-
-		memmove(string, p, len);
-		p += QUADLEN(len);
-		cookie = ntohl(*p++);
-		/*
-		 * To make everything fit, we encode the length, offset,
-		 * and eof flag into 32 bits. This works for filenames
-		 * up to 32K and PAGE_SIZE up to 64K.
-		 */
-		status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
-		*entry++ = fileid;
-		*entry++ = cookie;
-		*entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
-	}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
+		if (p + 2 > end)
+			goto short_pkt;
+	}
+	return nr;
+ short_pkt:
+	printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
+	entry[0] = entry[1] = 0;
 	return nr;
 }
 
+u32 *
+nfs_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+	if (!*p++) {
+		if (!*p)
+			return ERR_PTR(-EAGAIN);
+		entry->eof = 1;
+		return ERR_PTR(-EBADCOOKIE);
+	}
+
+	entry->ino	  = ntohl(*p++);
+	entry->len	  = ntohl(*p++);
+	entry->name	  = (const char *) p;
+	p		 += XDR_QUADLEN(entry->len);
+	entry->prev_cookie	  = entry->cookie;
+	entry->cookie	  = ntohl(*p++);
+	entry->eof	  = !p[0] && p[1];
+
+	return p;
+}
+
 /*
  * NFS XDR decode functions
  */
@@ -523,12 +543,9 @@
 {
 	int	status;
 
-	dprintk("RPC:      attrstat status %lx\n", (long)ntohl(*p));
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 	xdr_decode_fattr(p, fattr);
-	dprintk("RPC:      attrstat OK type %d mode %o dev %x ino %x\n",
-		fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
 	return 0;
 }
 
@@ -541,14 +558,31 @@
 {
 	int	status;
 
-	dprintk("RPC:      diropres status %lx\n", (long)ntohl(*p));
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 	p = xdr_decode_fhandle(p, res->fh);
 	xdr_decode_fattr(p, res->fattr);
-	dprintk("RPC:      diropres OK type %x mode %o dev %x ino %x\n",
-		res->fattr->type, res->fattr->mode,
-		res->fattr->fsid, res->fattr->fileid);
+	return 0;
+}
+
+/*
+ * Encode READLINK args
+ */
+static int
+nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
+{
+	struct rpc_task *task = req->rq_task;
+	struct rpc_auth *auth = task->tk_auth;
+	size_t		replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->bufsiz;
+	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rnr ++;
 	return 0;
 }
 
@@ -558,33 +592,81 @@
 static int
 nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
 {
+	struct iovec *iov = req->rq_rvec;
+	u32	hdrlen;
+	u32	*strlen;
+	char	*string;
 	int	status;
+	unsigned int len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
-
-	/* Caller takes over the buffer here to avoid extra copy */
-	res->buffer = req->rq_task->tk_buffer;
-	req->rq_task->tk_buffer = NULL;
+	hdrlen = (u8*)p - (u8*) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READLINK reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov[0].iov_len) {
+		dprintk("NFS: Short READLINK header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+	strlen = (u32*)res->buffer;
+	/* Convert length of symlink */
+	len = ntohl(*strlen);
+	if (len > res->bufsiz - 5)
+		len = res->bufsiz - 5;
+	*strlen = len;
+	/* NULL terminate the string we got */
+	string = (char *)(strlen + 1);
+	string[len] = 0;
 	return 0;
 }
 
 /*
+ * Decode WRITE reply
+ */
+static int
+nfs_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	res->verf->committed = NFS_FILE_SYNC;
+	return nfs_xdr_attrstat(req, p, res->fattr);
+}
+
+/*
  * Decode STATFS reply
  */
 static int
 nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
 {
 	int	status;
+	u32	xfer_size;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	res->tsize = ntohl(*p++);
-	res->bsize = ntohl(*p++);
-	res->blocks = ntohl(*p++);
-	res->bfree = ntohl(*p++);
-	res->bavail = ntohl(*p++);
+
+	/* For NFSv2, we more or less have to guess the preferred
+	 * read/write/readdir sizes from the single 'transfer size'
+	 * value.
+	 */
+	xfer_size = ntohl(*p++);	/* tsize */
+	res->rtmax  = 8 * 1024;
+	res->rtpref = xfer_size;
+	res->rtmult = xfer_size;
+	res->wtmax  = 8 * 1024;
+	res->wtpref = xfer_size;
+	res->wtmult = xfer_size;
+	res->dtpref = PAGE_CACHE_SIZE;
+	res->maxfilesize = 0x7FFFFFFF;	/* just a guess */
+	res->bsize  = ntohl(*p++);
+
+	res->tbytes = ntohl(*p++) * res->bsize;
+	res->fbytes = ntohl(*p++) * res->bsize;
+	res->abytes = ntohl(*p++) * res->bsize;
+	res->tfiles = 0;
+	res->ffiles = 0;
+	res->afiles = 0;
+	res->namelen = 0;
+
 	return 0;
 }
 
@@ -601,7 +683,7 @@
 	{ NFSERR_NOENT,		ENOENT		},
 	{ NFSERR_IO,		errno_NFSERR_IO	},
 	{ NFSERR_NXIO,		ENXIO		},
-	{ NFSERR_EAGAIN,	EAGAIN		},
+/*	{ NFSERR_EAGAIN,	EAGAIN		}, */
 	{ NFSERR_ACCES,		EACCES		},
 	{ NFSERR_EXIST,		EEXIST		},
 	{ NFSERR_XDEV,		EXDEV		},
@@ -612,18 +694,31 @@
 	{ NFSERR_FBIG,		EFBIG		},
 	{ NFSERR_NOSPC,		ENOSPC		},
 	{ NFSERR_ROFS,		EROFS		},
-	{ NFSERR_OPNOTSUPP,	EOPNOTSUPP	},
+	{ NFSERR_MLINK,		EMLINK		},
 	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
 	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
 	{ NFSERR_DQUOT,		EDQUOT		},
 	{ NFSERR_STALE,		ESTALE		},
+	{ NFSERR_REMOTE,	EREMOTE		},
 #ifdef EWFLUSH
 	{ NFSERR_WFLUSH,	EWFLUSH		},
 #endif
+	{ NFSERR_BADHANDLE,	EBADHANDLE	},
+	{ NFSERR_NOT_SYNC,	ENOTSYNC	},
+	{ NFSERR_BAD_COOKIE,	EBADCOOKIE	},
+	{ NFSERR_NOTSUPP,	ENOTSUPP	},
+	{ NFSERR_TOOSMALL,	ETOOSMALL	},
+	{ NFSERR_SERVERFAULT,	ESERVERFAULT	},
+	{ NFSERR_BADTYPE,	EBADTYPE	},
+	{ NFSERR_JUKEBOX,	EJUKEBOX	},
 	{ -1,			EIO		}
 };
 
-static int
+/*
+ * Convert an NFS error code to a local one.
+ * This one is used jointly by NFSv2 and NFSv3.
+ */
+int
 nfs_stat_to_errno(int stat)
 {
 	int i;
@@ -632,7 +727,7 @@
 		if (nfs_errtbl[i].stat == stat)
 			return nfs_errtbl[i].errno;
 	}
-	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
+	printk(KERN_ERR "nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
 	return nfs_errtbl[i].errno;
 }
 
@@ -644,7 +739,8 @@
     { "nfs_" #proc,					\
       (kxdrproc_t) nfs_xdr_##argtype,			\
       (kxdrproc_t) nfs_xdr_##restype,			\
-      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2	\
+      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2,	\
+      0							\
     }
 
 static struct rpc_procinfo	nfs_procedures[18] = {
@@ -653,10 +749,10 @@
     PROC(setattr,	sattrargs,	attrstat),
     PROC(root,		enc_void,	dec_void),
     PROC(lookup,	diropargs,	diropres),
-    PROC(readlink,	fhandle,	readlinkres),
+    PROC(readlink,	readlinkargs,	readlinkres),
     PROC(read,		readargs,	readres),
     PROC(writecache,	enc_void,	dec_void),
-    PROC(write,		writeargs,	attrstat),
+    PROC(write,		writeargs,	writeres),
     PROC(create,	createargs,	diropres),
     PROC(remove,	diropargs,	stat),
     PROC(rename,	renameargs,	stat),
@@ -668,22 +764,8 @@
     PROC(statfs,	fhandle,	statfsres),
 };
 
-static struct rpc_version	nfs_version2 = {
+struct rpc_version		nfs_version2 = {
 	2,
 	sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
 	nfs_procedures
-};
-
-static struct rpc_version *	nfs_version[] = {
-	NULL,
-	NULL,
-	&nfs_version2
-};
-
-struct rpc_program	nfs_program = {
-	"nfs",
-	NFS_PROGRAM,
-	sizeof(nfs_version) / sizeof(nfs_version[0]),
-	nfs_version,
-	&nfs_rpcstat,
 };
diff -urN linux-2.2.18-pre8/fs/nfs/nfs3proc.c linux/fs/nfs/nfs3proc.c
--- linux-2.2.18-pre8/fs/nfs/nfs3proc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/nfs3proc.c	Mon Dec  3 20:25:21 2001
@@ -0,0 +1,509 @@
+/*
+ *  linux/fs/nfs/nfs3proc.c
+ *
+ *  Client-side NFSv3 procedures stubs.
+ *
+ *  Copyright (C) 1997, Olaf Kirch
+ */
+
+#define NFS_NEED_NFS3_XDR_TYPES
+
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+
+#include <asm/segment.h>
+
+#define NFSDBG_FACILITY		NFSDBG_PROC
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+				struct nfs_fattr *fattr)
+{
+	int	status;
+
+	dprintk("NFS call  getroot\n");
+	fattr->valid = 0;
+	status = rpc_call(server->client, NFS3PROC_GETATTR, fhandle, fattr, 0);
+	dprintk("NFS reply getroot\n");
+	return status;
+}
+
+/*
+ * One function for each procedure in the NFS protocol.
+ */
+static int
+nfs3_proc_getattr(struct dentry *dentry, struct nfs_fattr *fattr)
+{
+	int	status;
+
+	dprintk("NFS call  getattr\n");
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_GETATTR,
+			  NFS_FH(dentry), fattr, 0);
+	dprintk("NFS reply getattr\n");
+	return status;
+}
+
+static int
+nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
+			struct iattr *sattr)
+{
+	struct nfs3_sattrargs	arg = { NFS_FH(dentry), sattr, 0, 0 };
+	int	status;
+
+	dprintk("NFS call  setattr\n");
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_SETATTR, &arg, fattr, 0);
+	dprintk("NFS reply setattr\n");
+	return status;
+}
+
+static int
+nfs3_proc_lookup(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  lookup %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_LOOKUP, &arg, &res, 0);
+	if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR))
+		status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_GETATTR,
+			 fhandle, fattr, 0);
+	dprintk("NFS reply lookup: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_access(struct dentry *dentry, int mode, struct nfs_fattr *fattr, int ruid)
+{
+	struct nfs3_accessargs	arg = { NFS_FH(dentry), 0 };
+	struct nfs3_accessres	res = { fattr, 0 };
+	int	status, flags;
+
+	dprintk("NFS call  access\n");
+	fattr->valid = 0;
+
+	if (mode & MAY_READ)
+		arg.access |= NFS3_ACCESS_READ;
+	if (S_ISDIR(dentry->d_inode->i_mode)) {
+		if (mode & MAY_WRITE)
+			arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE;
+		if (mode & MAY_EXEC)
+			arg.access |= NFS3_ACCESS_LOOKUP;
+	} else {
+		if (mode & MAY_WRITE)
+			arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND;
+		if (mode & MAY_EXEC)
+			arg.access |= NFS3_ACCESS_EXECUTE;
+	}
+	flags = (ruid) ? RPC_CALL_REALUID : 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_ACCESS, &arg, &res, flags);
+	dprintk("NFS reply access\n");
+
+	if (status == 0 && (arg.access & res.access) != arg.access)
+		status = -EACCES;
+	return status;
+}
+
+static int
+nfs3_proc_readlink(struct dentry *dentry, struct nfs_fattr *fattr,
+			void *buffer, unsigned int buflen)
+{
+	struct nfs3_readlinkargs args = { NFS_FH(dentry), buffer, buflen };
+	struct nfs3_readlinkres	res = { fattr, buffer, buflen };
+	int			status;
+
+	dprintk("NFS call  readlink\n");
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_READLINK,
+					&args, &res, 0);
+	dprintk("NFS reply readlink: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_read(struct dentry *dentry, struct nfs_fattr *fattr,
+	       struct rpc_cred *cred, int flags,
+	       unsigned long offset, unsigned int count,
+	       void *buffer, int *eofp)
+{
+	struct nfs_readargs	arg = { NFS_FH(dentry), offset, count, 1,
+					{{buffer, count}, {0,0}, {0,0}, {0,0},
+					 {0,0}, {0,0}, {0,0}, {0,0}} };
+	struct nfs_readres	res = { fattr, count, 0 };
+	struct rpc_message	msg = { NFS3PROC_READ, &arg, &res, cred };
+	int			status;
+
+	dprintk("NFS call  read %d @ %ld\n", count, offset);
+	fattr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+	dprintk("NFS reply read: %d\n", status);
+	*eofp = res.eof;
+	return status;
+}
+
+static int
+nfs3_proc_write(struct dentry *dentry, struct nfs_fattr *fattr,
+		struct rpc_cred *cred, int flags,
+		unsigned long offset, unsigned int count,
+		void *buffer, struct nfs_writeverf *verf)
+{
+	struct nfs_writeargs	arg = { NFS_FH(dentry), offset, count,
+					NFS_FILE_SYNC, 1,
+					{{buffer, count}, {0,0}, {0,0}, {0,0},
+					 {0,0}, {0,0}, {0,0}, {0,0}} };
+	struct nfs_writeres	res = { fattr, verf, 0 };
+	struct rpc_message	msg = { NFS3PROC_WRITE, &arg, &res, cred };
+	int			status, rpcflags = 0;
+
+	dprintk("NFS call  write %d @ %ld\n", count, offset);
+	fattr->valid = 0;
+	if (flags & NFS_RW_SWAP)
+		rpcflags |= NFS_RPC_SWAPFLAGS;
+	arg.stable = (flags & NFS_RW_SYNC) ? NFS_FILE_SYNC : NFS_UNSTABLE;
+
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, rpcflags);
+
+	dprintk("NFS reply read: %d\n", status);
+	return status < 0? status : res.count;
+}
+
+/*
+ * Create a regular file.
+ * For now, we don't implement O_EXCL.
+ */
+static int
+nfs3_proc_create(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct iattr *sattr, int flags,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_createargs	arg = { NFS_FH(dir), name->name, name->len,
+					sattr, 0, { 0, 0 } };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  create %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+
+	arg.createmode = NFS3_CREATE_UNCHECKED;
+	if (flags & O_EXCL) {
+		arg.createmode  = NFS3_CREATE_EXCLUSIVE;
+		arg.verifier[0] = jiffies;
+		arg.verifier[1] = current->pid;
+	}
+
+again:
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_CREATE, &arg, &res, 0);
+
+	/* If the server doesn't support the exclusive creation semantics,
+	 * try again with simple 'guarded' mode. */
+	if (status == NFSERR_NOTSUPP) {
+		switch (arg.createmode) {
+			case NFS3_CREATE_EXCLUSIVE:
+				arg.createmode = NFS3_CREATE_GUARDED;
+				break;
+
+			case NFS3_CREATE_GUARDED:
+				arg.createmode = NFS3_CREATE_UNCHECKED;
+				break;
+
+			case NFS3_CREATE_UNCHECKED:
+				goto exit;
+		}
+		goto again;
+	}
+
+exit:
+	dprintk("NFS reply create: %d\n", status);
+
+	/* When we created the file with exclusive semantics, make
+	 * sure we set the attributes afterwards. */
+	if (status == 0 && arg.createmode == NFS3_CREATE_EXCLUSIVE) {
+		struct nfs3_sattrargs	arg = { fhandle, sattr, 0, 0 };
+		dprintk("NFS call  setattr (post-create)\n");
+
+		/* Note: we could use a guarded setattr here, but I'm
+		 * not sure this buys us anything (and I'd have
+		 * to revamp the NFSv3 XDR code) */
+		fattr->valid = 0;
+		status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_SETATTR,
+						&arg, fattr, 0);
+		dprintk("NFS reply setattr (post-create): %d\n", status);
+	}
+
+	return status;
+}
+
+static int
+nfs3_proc_remove(struct dentry *dir, struct nfs_fattr *dir_attr,
+				struct qstr *name)
+{
+	struct nfs3_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	struct rpc_message	msg = {NFS3PROC_REMOVE, &arg, dir_attr, NULL };
+	int			status;
+
+	dprintk("NFS call  remove %s\n", name->name);
+	dir_attr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+	dprintk("NFS reply remove: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name)
+{
+	struct nfs3_diropargs	*arg;
+	struct nfs_fattr	*res;
+	unsigned		size = sizeof(*arg)+sizeof(*res);
+
+	arg = (struct nfs3_diropargs *)kmalloc(size, GFP_KERNEL);
+	if (!arg)
+		return -ENOMEM;
+	memset(arg, 0, size);
+	res = (struct nfs_fattr*)(arg + 1);
+	arg->fh = NFS_FH(dir);
+	arg->name = name->name;
+	arg->len = name->len;
+	msg->rpc_proc = NFS3PROC_REMOVE;
+	msg->rpc_argp = arg;
+	msg->rpc_resp = res;
+	return 0;
+}
+
+static void
+nfs3_proc_unlink_done(struct dentry *dir, struct rpc_message *msg)
+{
+	struct nfs_fattr	*dir_attr = (struct nfs_fattr*)msg->rpc_resp;
+
+	if (msg->rpc_argp) {
+		nfs_refresh_inode(dir->d_inode, dir_attr);
+		kfree(msg->rpc_argp);
+	}
+}
+
+
+static int
+nfs3_proc_rename(struct dentry *old_dir, struct nfs_fattr *old_attr,
+			struct qstr *old_name,
+			struct dentry *new_dir, struct nfs_fattr *new_attr,
+			struct qstr *new_name)
+{
+	struct nfs3_renameargs	arg = { NFS_FH(old_dir),
+					old_name->name, old_name->len,
+					NFS_FH(new_dir),
+					new_name->name, new_name->len };
+	struct nfs3_renameres	res = { old_attr, new_attr };
+	int			status;
+
+	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
+	old_attr->valid = 0;
+	new_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(old_dir->d_inode), NFS3PROC_RENAME, &arg, &res, 0);
+	dprintk("NFS reply rename: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_link(struct dentry *dentry, struct nfs_fattr *fattr,
+			struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name)
+{
+	struct nfs3_linkargs	arg = { NFS_FH(dentry), NFS_FH(dir),
+					name->name, name->len };
+	struct nfs3_linkres	res = { dir_attr, fattr };
+	int			status;
+
+	dprintk("NFS call  link %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_LINK, &arg, &res, 0);
+	dprintk("NFS reply link: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_symlink(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct qstr *path,
+			struct iattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_symlinkargs	arg = { NFS_FH(dir), name->name, name->len,
+					path->name, path->len, sattr };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_SYMLINK, &arg, &res, 0);
+	dprintk("NFS reply symlink: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_mkdir(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct iattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_createargs	arg = { NFS_FH(dir), name->name, name->len,
+					sattr, 0, { 0, 0 } };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  mkdir %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_MKDIR, &arg, &res, 0);
+	dprintk("NFS reply mkdir: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_rmdir(struct dentry *dir, struct nfs_fattr *dir_attr,
+				struct qstr *name)
+{
+	struct nfs3_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	int			status;
+
+	dprintk("NFS call  rmdir %s\n", name->name);
+	dir_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_RMDIR, &arg, dir_attr, 0);
+	dprintk("NFS reply rmdir: %d\n", status);
+	return status;
+}
+
+/*
+ * The READDIR implementation is somewhat hackish - we pass the user buffer
+ * to the encode function, which installs it in the receive iovec.
+ * The decode function itself doesn't perform any decoding, it just makes
+ * sure the reply is syntactically correct.
+ *
+ * Also note that this implementation handles both plain readdir and
+ * readdirplus.
+ */
+static int
+nfs3_proc_readdir(struct dentry *dir, struct nfs_fattr *dir_attr,
+		  struct rpc_cred *cred,
+		  u64 cookie, void *entry, unsigned int size, int plus)
+{
+	struct nfs3_readdirargs	arg = { NFS_FH(dir), cookie, {0, 0}, 0, 0, 0 };
+	struct nfs3_readdirres	res = { dir_attr, 0, 0, 0, 0 };
+	struct rpc_message	msg = { NFS3PROC_READDIR, &arg, &res, cred };
+	u32			*verf = NFS_COOKIEVERF(dir->d_inode);
+	int			status;
+
+	arg.buffer  = entry;
+	arg.bufsiz  = size;
+	arg.verf[0] = verf[0];
+	arg.verf[1] = verf[1];
+	arg.plus    = plus;
+	res.buffer  = entry;
+	res.bufsiz  = size;
+	res.verf    = verf;
+	res.plus    = plus;
+
+	if (plus)
+		msg.rpc_proc = NFS3PROC_READDIRPLUS;
+
+	dprintk("NFS call  readdir%s %d\n",
+			plus? "plus" : "", (unsigned int) cookie);
+
+	dir_attr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+	dprintk("NFS reply readdir: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_mknod(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct iattr *sattr,
+			dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+	struct nfs3_mknodargs	arg = { NFS_FH(dir), name->name, name->len, 0,
+					sattr, rdev };
+	struct nfs3_diropres	res = { dir_attr, fh, fattr };
+	int			status;
+
+	switch (sattr->ia_mode & S_IFMT) {
+	case S_IFBLK:	arg.type = NF3BLK;  break;
+	case S_IFCHR:	arg.type = NF3CHR;  break;
+	case S_IFIFO:	arg.type = NF3FIFO; break;
+	case S_IFSOCK:	arg.type = NF3SOCK; break;
+	default:	return -EINVAL;
+	}
+
+	dprintk("NFS call  mknod %s %x\n", name->name, rdev);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_MKNOD, &arg, &res, 0);
+	dprintk("NFS reply mknod: %d\n", status);
+	return status;
+}
+
+/*
+ * This is a combo call of fsstat and fsinfo
+ */
+static int
+nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+			struct nfs_fsinfo *info)
+{
+	int	status;
+
+	dprintk("NFS call  fsstat\n");
+	memset((char *)info, 0, sizeof(*info));
+	status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, info, 0);
+	if (status < 0)
+		goto error;
+	status = rpc_call(server->client, NFS3PROC_FSINFO, fhandle, info, 0);
+
+error:
+	dprintk("NFS reply statfs: %d\n", status);
+	return status;
+}
+
+struct nfs_rpc_ops	nfs_v3_clientops = {
+	3,			/* protocol version */
+	nfs3_proc_get_root,
+	nfs3_proc_getattr,
+	nfs3_proc_setattr,
+	nfs3_proc_lookup,
+	nfs3_proc_access,
+	nfs3_proc_readlink,
+	nfs3_proc_read,
+	nfs3_proc_write,
+	NULL,			/* commit */
+	nfs3_proc_create,
+	nfs3_proc_remove,
+	nfs3_proc_unlink_setup,
+	nfs3_proc_unlink_done,
+	nfs3_proc_rename,
+	nfs3_proc_link,
+	nfs3_proc_symlink,
+	nfs3_proc_mkdir,
+	nfs3_proc_rmdir,
+	nfs3_proc_readdir,
+	nfs3_proc_mknod,
+	nfs3_proc_statfs,
+
+	nfs3_decode_dirent,
+};
diff -urN linux-2.2.18-pre8/fs/nfs/nfs3xdr.c linux/fs/nfs/nfs3xdr.c
--- linux-2.2.18-pre8/fs/nfs/nfs3xdr.c	Sat Dec 20 21:06:01 1997
+++ linux/fs/nfs/nfs3xdr.c	Mon Dec  3 20:25:21 2001
@@ -1,94 +1,160 @@
 /*
- * linux/fs/nfs/nfs2xdr.c
+ * linux/fs/nfs/nfs3xdr.c
  *
  * XDR functions to encode/decode NFSv3 RPC arguments and results.
- * Note: this is incomplete!
  *
- * Copyright (C) 1996 Olaf Kirch
+ * Copyright (C) 1996, 1997 Olaf Kirch
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS3_XDR_TYPES
 
 #include <linux/param.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
-#include <linux/nfs_fs.h>
 #include <linux/utsname.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/in.h>
 #include <linux/pagemap.h>
 #include <linux/proc_fs.h>
+#include <linux/kdev_t.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
 
-#ifdef RPC_DEBUG
-# define RPC_FACILITY		RPCDBG_NFS
-#endif
+/* Uncomment this to support servers requiring longword lengths */
+#define NFS_PAD_WRITES		1
 
-#define QUADLEN(len)		(((len) + 3) >> 2)
-static int			nfs_stat_to_errno(int stat);
+#define NFSDBG_FACILITY		NFSDBG_XDR
 
 /* Mapping from NFS error code to "errno" error code. */
 #define errno_NFSERR_IO		EIO
 
+extern int			nfs_stat_to_errno(int);
+
 /*
  * Declare the space requirements for NFS arguments and replies as
  * number of 32bit-words
  */
-#define NFS_fhandle_sz		(1+16)
-#define NFS_sattr_sz		8
-#define NFS_filename_sz		1+(NFS_MAXNAMLEN>>2)
-#define NFS_path_sz		1+(NFS_MAXPATHLEN>>2)
-#define NFS_fattr_sz		17
-#define NFS_info_sz		5
-#define NFS_entry_sz		NFS_filename_sz+3
-
-#define NFS_enc_void_sz		0
-#define NFS_diropargs_sz	NFS_fhandle_sz+NFS_filename_sz
-#define NFS_sattrargs_sz	NFS_fhandle_sz+NFS_sattr_sz
-#define NFS_readargs_sz		NFS_fhandle_sz+3
-#define NFS_writeargs_sz	NFS_fhandle_sz+4
-#define NFS_createargs_sz	NFS_diropargs_sz+NFS_sattr_sz
-#define NFS_renameargs_sz	NFS_diropargs_sz+NFS_diropargs_sz
-#define NFS_linkargs_sz		NFS_fhandle_sz+NFS_diropargs_sz
-#define NFS_symlinkargs_sz	NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
-#define NFS_readdirargs_sz	NFS_fhandle_sz+2
-
-#define NFS_dec_void_sz		0
-#define NFS_attrstat_sz		1+NFS_fattr_sz
-#define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz	1+NFS_path_sz
-#define NFS_readres_sz		1+NFS_fattr_sz+1
-#define NFS_stat_sz		1
-#define NFS_readdirres_sz	1
-#define NFS_statfsres_sz	1+NFS_info_sz
+#define NFS3_fhandle_sz		1+16
+#define NFS3_fh_sz		NFS3_fhandle_sz	/* shorthand */
+#define NFS3_sattr_sz		15
+#define NFS3_filename_sz	1+(NFS3_MAXNAMLEN>>2)
+#define NFS3_path_sz		1+(NFS3_MAXPATHLEN>>2)
+#define NFS3_fattr_sz		21
+#define NFS3_wcc_attr_sz		6
+#define NFS3_pre_op_attr_sz	1+NFS3_wcc_attr_sz
+#define NFS3_post_op_attr_sz	1+NFS3_fattr_sz
+#define NFS3_wcc_data_sz		NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz
+#define NFS3_fsstat_sz		
+#define NFS3_fsinfo_sz		
+#define NFS3_pathconf_sz		
+#define NFS3_entry_sz		NFS3_filename_sz+3
+
+#define NFS3_enc_void_sz	0
+#define NFS3_sattrargs_sz	NFS3_fh_sz+NFS3_sattr_sz+3
+#define NFS3_diropargs_sz	NFS3_fh_sz+NFS3_filename_sz
+#define NFS3_accessargs_sz	NFS3_fh_sz+1
+#define NFS3_readlinkargs_sz	NFS3_fh_sz
+#define NFS3_readargs_sz	NFS3_fh_sz+3
+#define NFS3_writeargs_sz	NFS3_fh_sz+5
+#define NFS3_createargs_sz	NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_mkdirargs_sz	NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_symlinkargs_sz	NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz
+#define NFS3_mknodargs_sz	NFS3_diropargs_sz+2+NFS3_sattr_sz
+#define NFS3_renameargs_sz	NFS3_diropargs_sz+NFS3_diropargs_sz
+#define NFS3_linkargs_sz		NFS3_fh_sz+NFS3_diropargs_sz
+#define NFS3_readdirargs_sz	NFS3_fh_sz+2
+#define NFS3_commitargs_sz	NFS3_fh_sz+3
+
+#define NFS3_dec_void_sz	0
+#define NFS3_attrstat_sz	1+NFS3_fattr_sz
+#define NFS3_wccstat_sz		1+NFS3_wcc_data_sz
+#define NFS3_lookupres_sz	1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)
+#define NFS3_accessres_sz	1+NFS3_post_op_attr_sz+1
+#define NFS3_readlinkres_sz	1+NFS3_post_op_attr_sz
+#define NFS3_readres_sz		1+NFS3_post_op_attr_sz+3
+#define NFS3_writeres_sz	1+NFS3_wcc_data_sz+4
+#define NFS3_createres_sz	1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_renameres_sz	1+(2 * NFS3_wcc_data_sz)
+#define NFS3_linkres_sz		1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_readdirres_sz	1+NFS3_post_op_attr_sz+2
+#define NFS3_fsstatres_sz	1+NFS3_post_op_attr_sz+13
+#define NFS3_fsinfores_sz	1+NFS3_post_op_attr_sz+12
+#define NFS3_pathconfres_sz	1+NFS3_post_op_attr_sz+6
+#define NFS3_commitres_sz	1+NFS3_wcc_data_sz+2
+
+/*
+ * Map file type to S_IFMT bits
+ */
+static struct {
+	unsigned int	mode;
+	unsigned int	nfs2type;
+} nfs_type2fmt[] = {
+      { 0,		NFNON	},
+      { S_IFREG,	NFREG	},
+      { S_IFDIR,	NFDIR	},
+      { S_IFBLK,	NFBLK	},
+      { S_IFCHR,	NFCHR	},
+      { S_IFLNK,	NFLNK	},
+      { S_IFSOCK,	NFSOCK	},
+      { S_IFIFO,	NFFIFO	},
+      { 0,		NFBAD	}
+};
 
 /*
  * Common NFS XDR functions as inlines
  */
 static inline u32 *
-xdr_encode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
 {
 	*p++ = htonl(fh->size);
 	memcpy(p, fh->data, fh->size);
-	return p + QUADLEN(fh->size);
+	return p + XDR_QUADLEN(fh->size);
 }
 
 static inline u32 *
-xdr_decode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
 {
+	/*
+	 * Zero all nonused bytes
+	 */
+	memset((u8 *)fh, 0, sizeof(*fh));
 	if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
 		memcpy(fh->data, p, fh->size);
-		return p + QUADLEN(fh->size);
+		return p + XDR_QUADLEN(fh->size);
 	}
 	return NULL;
 }
 
-static inline enum nfs_ftype
-xdr_decode_ftype(u32 type)
+/*
+ * Encode/decode time.
+ * Since the VFS doesn't care for fractional times, we ignore the
+ * nanosecond field.
+ */
+static inline u32 *
+xdr_encode_time(u32 *p, time_t time)
+{
+	*p++ = htonl(time);
+	*p++ = 0;
+	return p;
+}
+
+static inline u32 *
+xdr_decode_time3(u32 *p, u64 *timep)
+{
+	*timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_time3(u32 *p, u64 time)
 {
-	return (type == NF3FIFO)? NFFIFO : (enum nfs_ftype) type;
+	*p++ = htonl(time >> 32);
+	*p++ = htonl(time & 0xFFFFFFFF);
+	return p;
 }
 
 static inline u32 *
@@ -99,47 +165,119 @@
 	if (*len > maxlen)
 		return NULL;
 	*string = (char *) p;
-	return p + QUADLEN(*len);
+	return p + XDR_QUADLEN(*len);
 }
 
 static inline u32 *
-xdr_decode_fattr(u32 *p, struct nfs3_fattr *fattr)
+xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
 {
-	fattr->type = xdr_decode_ftype(ntohl(*p++));
-	fattr->mode = ntohl(*p++);
+	unsigned int	type;
+	int		fmode;
+
+	type = ntohl(*p++);
+	if (type >= NF3BAD)
+		type = NF3BAD;
+	fmode = nfs_type2fmt[type].mode;
+	fattr->type = nfs_type2fmt[type].nfs2type;
+	fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
 	fattr->nlink = ntohl(*p++);
 	fattr->uid = ntohl(*p++);
 	fattr->gid = ntohl(*p++);
-	fattr->size = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
-	fattr->used = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
-	fattr->rdev_maj = ntohl(*p++);
-	fattr->rdev_min = ntohl(*p++);
-	fattr->fsid = ntohl(*p++);
-	fattr->fileid = ntohl(*p++);
-	fattr->atime.seconds = ntohl(*p++);
-	fattr->atime.useconds = ntohl(*p++);
-	fattr->mtime.seconds = ntohl(*p++);
-	fattr->mtime.useconds = ntohl(*p++);
-	fattr->ctime.seconds = ntohl(*p++);
-	fattr->ctime.useconds = ntohl(*p++);
+	p = xdr_decode_hyper(p, &fattr->size);
+	p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
+	/* Turn remote device info into Linux-specific dev_t */
+	fattr->rdev = (ntohl(*p++) << MINORBITS) | (ntohl(*p++) & MINORMASK);
+	p = xdr_decode_hyper(p, &fattr->fsid);
+	p = xdr_decode_hyper(p, &fattr->fileid);
+	p = xdr_decode_time3(p, &fattr->atime);
+	p = xdr_decode_time3(p, &fattr->mtime);
+	p = xdr_decode_time3(p, &fattr->ctime);
+
+	/* Update the mode bits */
+	fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct iattr *attr)
+{
+	if (attr->ia_valid & ATTR_MODE) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_mode);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_UID) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_uid);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_GID) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_gid);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_SIZE) {
+		*p++ = xdr_one;
+		p = xdr_encode_hyper(p, (__u64) attr->ia_size);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_ATIME_SET) {
+		*p++ = xdr_two;
+		p = xdr_encode_time(p, attr->ia_atime);
+	} else if (attr->ia_valid & ATTR_ATIME) {
+		*p++ = xdr_one;
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_MTIME_SET) {
+		*p++ = xdr_two;
+		p = xdr_encode_time(p, attr->ia_mtime);
+	} else if (attr->ia_valid & ATTR_MTIME) {
+		*p++ = xdr_one;
+	} else {
+		*p++ = xdr_zero;
+	}
+	return p;
+}
+
+static inline u32 *
+xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr)
+{
+	p = xdr_decode_hyper(p, &fattr->pre_size);
+	p = xdr_decode_time3(p, &fattr->pre_mtime);
+	p = xdr_decode_time3(p, &fattr->pre_ctime);
+	fattr->valid |= NFS_ATTR_WCC;
+	return p;
+}
+
+static inline u32 *
+xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr)
+{
+	if (*p++)
+		p = xdr_decode_fattr(p, fattr);
 	return p;
 }
 
 static inline u32 *
-xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
+xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr)
 {
-	*p++ = htonl(sattr->mode);
-	*p++ = htonl(sattr->uid);
-	*p++ = htonl(sattr->gid);
-	*p++ = htonl(sattr->size >> 32);
-	*p++ = htonl(sattr->size & 0xFFFFFFFF);
-	*p++ = htonl(sattr->atime.seconds);
-	*p++ = htonl(sattr->atime.useconds);
-	*p++ = htonl(sattr->mtime.seconds);
-	*p++ = htonl(sattr->mtime.useconds);
+	if (*p++)
+		return xdr_decode_wcc_attr(p, fattr);
 	return p;
 }
 
+
+static inline u32 *
+xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr)
+{
+	p = xdr_decode_pre_op_attr(p, fattr);
+	return xdr_decode_post_op_attr(p, fattr);
+}
+
 /*
  * NFS encode functions
  */
@@ -147,7 +285,7 @@
  * Encode void argument
  */
 static int
-nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
 {
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -155,10 +293,9 @@
 
 /*
  * Encode file handle argument
- * GETATTR, READLINK, STATFS
  */
 static int
-nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh)
+nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
 {
 	p = xdr_encode_fhandle(p, fh);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
@@ -169,23 +306,37 @@
  * Encode SETATTR arguments
  */
 static int
-nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
+nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
 	p = xdr_encode_sattr(p, args->sattr);
+	*p++ = htonl(args->guard);
+	if (args->guard)
+		p = xdr_encode_time3(p, args->guardtime);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
  * Encode directory ops argument
- * LOOKUP, REMOVE, RMDIR
  */
 static int
-nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
+nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name, args->len);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode access() argument
+ */
+static int
+nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	*p++ = htonl(args->access);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -193,149 +344,178 @@
 /*
  * Arguments to a READ call. Since we read data directly into the page
  * cache, we also set up the reply iovec here so that iov[1] points
- * exactly to the page wewant to fetch.
+ * exactly to the page we want to fetch.
  */
 static int
-nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen, buflen;
+	size_t		replen;
+	unsigned int	nr;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
+	p = xdr_encode_hyper(p, args->offset);
 	*p++ = htonl(args->count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-#if 1
+	/* Get the number of buffers in the receive iovec */
+	nr = args->nriov;
+
+	if (nr+1 > MAX_IOVEC) {
+		printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
+		return -EINVAL;
+	}
+
 	/* set up reply iovec */
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
-	req->rq_rvec[0].iov_len  = replen;
-	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->count;
-	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[2].iov_len  = buflen - replen;
-	req->rq_rlen = args->count + buflen;
-	req->rq_rnr = 3;
-#else
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
 	req->rq_rvec[0].iov_len  = replen;
-#endif
+
+	/* Copy the iovec */
+	memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
+
+	req->rq_rlen = args->count + replen;
+	req->rq_rnr += nr;
 
 	return 0;
 }
 
 /*
- * Decode READ reply
+ * Write arguments. Splice the buffer to be written into the iovec.
  */
 static int
-nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
-	struct iovec *iov = req->rq_rvec;
-	int	status, count, recvd, hdrlen;
+	unsigned int	nr;
+	u32 count = args->count;
 
-	dprintk("RPC:      readres OK status %lx\n", ntohl(*p));
-	if ((status = ntohl(*p++)))
-		return -nfs_stat_to_errno(status);
-	p = xdr_decode_fattr(p, res->fattr);
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_hyper(p, args->offset);
+	*p++ = htonl(count);
+	*p++ = htonl(args->stable);
+	*p++ = htonl(count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	count = ntohl(*p++);
-	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-	recvd = req->rq_rlen - hdrlen;
-	if (p != iov[2].iov_base) {
-		/* Unexpected reply header size. Punt.
-		 * XXX: Move iovec contents to align data on page
-		 * boundary and adjust RPC header size guess */
-		printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
-		return -errno_NFSERR_IO;
-	}
-	if (count > recvd) {
-		printk("NFS: server cheating in read reply: "
-			"count %d > recvd %d\n", count, recvd);
-		count = recvd;
+	/* Get the number of buffers in the send iovec */
+	nr = args->nriov;
+
+	if (nr+2 > MAX_IOVEC) {
+		printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs\n");
+		return -EINVAL;
 	}
 
-	dprintk("RPC:      readres OK count %d\n", count);
-	if (count < res->count)
-		memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+	/* Copy the iovec */
+	memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
 
-	return count;
-}
+#ifdef NFS_PAD_WRITES
+	/*
+	 * Some old servers require that the message length
+	 * be a multiple of 4, so we pad it here if needed.
+	 */
+	if (count & 3) {
+		struct iovec	*iov = req->rq_svec + nr + 1;
+		int		pad = 4 - (count & 3);
+
+		iov->iov_base = (void *) "\0\0\0";
+		iov->iov_len  = pad;
+		count += pad;
+		nr++;
+	}
+#endif
+	req->rq_slen += count;
+	req->rq_snr  += nr;
 
+	return 0;
+}
 
 /*
- * Write arguments. Splice the buffer to be written into the iovec.
+ * Encode CREATE arguments
  */
 static int
-nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
-	*p++ = htonl(args->count);
-	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	p = xdr_encode_string(p, args->name, args->len);
 
-	req->rq_svec[1].iov_base = (void *) args->buffer;
-	req->rq_svec[1].iov_len = args->count;
-	req->rq_slen += args->count;
-	req->rq_snr = 2;
+	*p++ = htonl(args->createmode);
+	if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
+		*p++ = args->verifier[0];
+		*p++ = args->verifier[1];
+	} else
+		p = xdr_encode_sattr(p, args->sattr);
 
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode create arguments
- * CREATE, MKDIR
+ * Encode MKDIR arguments
  */
 static int
-nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
+nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_string(p, args->name, args->len);
 	p = xdr_encode_sattr(p, args->sattr);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode RENAME arguments
+ * Encode SYMLINK arguments
  */
 static int
-nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
+nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
+	p = xdr_encode_sattr(p, args->sattr);
+	p = xdr_encode_string(p, args->topath, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode LINK arguments
+ * Encode MKNOD arguments
+ */
+static int
+nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name, args->len);
+	*p++ = htonl(args->type);
+	p = xdr_encode_sattr(p, args->sattr);
+	if (args->type == NF3CHR || args->type == NF3BLK) {
+		*p++ = htonl(args->rdev >> MINORBITS);
+		*p++ = htonl(args->rdev & MINORMASK);
+	}
+
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode RENAME arguments
  */
 static int
-nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
+nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
 	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode SYMLINK arguments
+ * Encode LINK arguments
  */
 static int
-nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
+nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_string(p, args->topath);
-	p = xdr_encode_sattr(p, args->sattr);
+	p = xdr_encode_fhandle(p, args->tofh);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -344,129 +524,168 @@
  * Encode arguments to readdir call
  */
 static int
-nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
+nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen;
+	size_t		replen;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->cookie);
+	p = xdr_encode_hyper(p, args->cookie);
+	*p++ = args->verf[0];
+	*p++ = args->verf[1];
+	if (args->plus) {
+		/* readdirplus: need dircount + buffer size.
+		 * We just make sure we make dircount big enough */
+		*p++ = htonl(args->bufsiz >> 3);
+	}
 	*p++ = htonl(args->bufsiz);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
 	/* set up reply iovec */
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-	/*
-	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
-		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
-	 */
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
 	req->rq_rvec[0].iov_len  = replen;
 	req->rq_rvec[1].iov_base = args->buffer;
 	req->rq_rvec[1].iov_len  = args->bufsiz;
 	req->rq_rlen = replen + args->bufsiz;
-	req->rq_rnr = 2;
-
-	/*
-	dprintk("RPC:      readdirargs set up reply vec:\n");
-	dprintk("          rvec[0] = %p/%d\n",
-			req->rq_rvec[0].iov_base,
-			req->rq_rvec[0].iov_len);
-	dprintk("          rvec[1] = %p/%d\n",
-			req->rq_rvec[1].iov_base,
-			req->rq_rvec[1].iov_len);
-	 */
+	req->rq_rnr ++;
 
 	return 0;
 }
 
 /*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- *	entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
- */
-static int
-nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
-{
-	struct iovec		*iov = req->rq_rvec;
-	int			status, nr, len;
-	char			*string, *start;
-	u32			*end;
-	__u32			fileid, cookie, *entry;
-
-	if ((status = ntohl(*p++)))
+ * Decode the result of a readdir call.
+ * We just check for syntactical correctness.
+ */
+static int
+nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
+{
+	struct iovec	*iov = req->rq_rvec;
+	u32		hdrlen;
+	int		status, nr;
+	unsigned int	len;
+	u32		*entry, *end;
+
+	status = ntohl(*p++);
+	/* Decode post_op_attrs */
+	p = xdr_decode_post_op_attr(p, res->dir_attr);
+	if (status)
 		return -nfs_stat_to_errno(status);
-	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
-		/* Unexpected reply header size. Punt. */
-		printk("NFS: Odd RPC header size in readdirres reply\n");
-		return -errno_NFSERR_IO;
+	/* Decode verifier cookie */
+	if (res->verf) {
+		res->verf[0] = *p++;
+		res->verf[1] = *p++;
+	} else {
+		p += 2;
 	}
 
-	p = (u32 *) iov[1].iov_base;
-	end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
-	if (p != res->buffer) {
-		printk("NFS: p != res->buffer in %s:%d!!!\n",
-					__FILE__, __LINE__);
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READDIR reply: %d\n", hdrlen);
 		return -errno_NFSERR_IO;
 	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READDIR header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
 
-	entry  = (__u32 *) res->buffer;
-	start  = (char *) res->buffer;
-	string = start + res->bufsiz;
+	p   = (u32 *) iov[1].iov_base;
+	end = (u32 *) ((u8 *) p + iov[1].iov_len);
 	for (nr = 0; *p++; nr++) {
-		fileid = ntohl(*p++);
-
-		len = ntohl(*p++);
-		if ((p + QUADLEN(len) + 3) > end) {
-			printk(KERN_NOTICE
-				"NFS: short packet in readdir reply!\n");
-			break;
-		}
-		if (len > NFS_MAXNAMLEN) {
-			printk("NFS: giant filename in readdir (len %x)!\n",
+		entry = p - 1;
+		if (p + 3 > end)
+			goto short_pkt;
+		p += 2;				/* inode # */
+		len = ntohl(*p++);		/* string length */
+		p += XDR_QUADLEN(len) + 2;	/* name + cookie */
+		if (len > NFS3_MAXNAMLEN) {
+			printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
 						len);
 			return -errno_NFSERR_IO;
 		}
-		string -= len;
-		if ((void *) (entry+3) > (void *) string) {
-			/* 
-			 * This error is impossible as long as the temp
-			 * buffer is no larger than the user buffer. The 
-			 * current packing algorithm uses the same amount
-			 * of space in the user buffer as in the XDR data,
-			 * so it's guaranteed to fit.
-			 */
-			printk("NFS: incorrect buffer size in %s!\n",
-				__FUNCTION__);
-			break;
+
+		if (res->plus) {
+			/* post_op_attr */
+			if (p > end)
+				goto short_pkt;
+			if (*p++)
+				p += 21;
+			if (p > end)
+				goto short_pkt;
+			/* post_op_fh3 */
+			if (*p++) {
+				len = ntohl(*p++);
+				if (len > NFS3_FHSIZE) {
+					printk(KERN_WARNING "NFS: giant filehandle in "
+						"readdir (len %x)!\n", len);
+					return -errno_NFSERR_IO;
+				}
+				p += XDR_QUADLEN(len);
+			}
 		}
+		if (p + 2 > end)
+			goto short_pkt;
+	}
 
-		memmove(string, p, len);
-		p += QUADLEN(len);
-		cookie = ntohl(*p++);
-		/*
-		 * To make everything fit, we encode the length, offset,
-		 * and eof flag into 32 bits. This works for filenames
-		 * up to 32K and PAGE_SIZE up to 64K.
-		 */
-		status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
-		*entry++ = fileid;
-		*entry++ = cookie;
-		*entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
-		/*
-		dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n",
-			len, string, cookie, status);
-		 */
-	}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
 	return nr;
+ short_pkt:
+	printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
+	/* truncate listing */
+	entry[0] = entry[1] = 0;
+	return nr;
+}
+
+u32 *
+nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+	struct nfs_entry old = *entry;
+
+	if (!*p++) {
+		if (!*p)
+			return ERR_PTR(-EAGAIN);
+		entry->eof = 1;
+		return ERR_PTR(-EBADCOOKIE);
+	}
+
+	p = xdr_decode_hyper(p, &entry->ino);
+	entry->len  = ntohl(*p++);
+	entry->name = (const char *) p;
+	p += XDR_QUADLEN(entry->len);
+	entry->prev_cookie = entry->cookie;
+	p = xdr_decode_hyper(p, &entry->cookie);
+
+	if (plus) {
+		p = xdr_decode_post_op_attr(p, &entry->fattr);
+		/* In fact, a post_op_fh3: */
+		if (*p++) {
+			p = xdr_decode_fhandle(p, &entry->fh);
+			/* Ugh -- server reply was truncated */
+			if (p == NULL) {
+				dprintk("NFS: FH truncated\n");
+				*entry = old;
+				return ERR_PTR(-EAGAIN);
+			}
+		} else {
+			/* If we don't get a file handle, the attrs
+			 * aren't worth a lot. */
+			entry->fattr.valid = 0;
+		}
+	}
+
+	entry->eof = !p[0] && p[1];
+	return p;
+}
+
+/*
+ * Encode COMMIT arguments
+ */
+static int
+nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_hyper(p, args->offset);
+	*p++ = htonl(args->count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
 }
 
 /*
@@ -476,59 +695,89 @@
  * Decode void reply
  */
 static int
-nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
 {
 	return 0;
 }
 
 /*
- * Decode simple status reply
+ * Decode attrstat reply.
  */
 static int
-nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 {
 	int	status;
 
-	if ((status = ntohl(*p++)) != 0)
-		status = -nfs_stat_to_errno(status);
-	return status;
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	xdr_decode_fattr(p, fattr);
+	return 0;
 }
 
 /*
- * Decode attrstat reply
- * GETATTR, SETATTR, WRITE
+ * Decode status+wcc_data reply
+ * SATTR, REMOVE, RMDIR
  */
 static int
-nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 {
 	int	status;
 
-	dprintk("RPC:      attrstat status %lx\n", ntohl(*p));
 	if ((status = ntohl(*p++)))
-		return -nfs_stat_to_errno(status);
-	xdr_decode_fattr(p, fattr);
-	dprintk("RPC:      attrstat OK type %d mode %o dev %x ino %x\n",
-		fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
-	return 0;
+		status = -nfs_stat_to_errno(status);
+	xdr_decode_wcc_data(p, fattr);
+	return status;
 }
 
 /*
- * Decode diropres reply
- * LOOKUP, CREATE, MKDIR
+ * Decode LOOKUP reply
  */
 static int
-nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
+nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
 {
 	int	status;
 
-	dprintk("RPC:      diropres status %lx\n", ntohl(*p));
-	if ((status = ntohl(*p++)))
+	if ((status = ntohl(*p++))) {
+		status = -nfs_stat_to_errno(status);
+	} else {
+		if (!(p = xdr_decode_fhandle(p, res->fh)))
+			return -errno_NFSERR_IO;
+		p = xdr_decode_post_op_attr(p, res->fattr);
+	}
+	xdr_decode_post_op_attr(p, res->dir_attr);
+	return status;
+}
+
+/*
+ * Decode ACCESS reply
+ */
+static int
+nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
+{
+	int	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, res->fattr);
+	if (status)
 		return -nfs_stat_to_errno(status);
-	p = xdr_decode_fhandle(p, res->fh);
-	xdr_decode_fattr(p, res->fattr);
-	dprintk("RPC:      diropres OK type %x mode %o dev %x ino %x\n",
-		res->fattr->type, res->fattr->mode,
-		res->fattr->fsid, res->fattr->fileid);
+	res->access = ntohl(*p++);
+	return 0;
+}
+
+static int
+nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
+{
+	struct rpc_task *task = req->rq_task;
+	struct rpc_auth *auth = task->tk_auth;
+	size_t		replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->bufsiz;
+	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rnr ++;
 	return 0;
 }
 
@@ -536,155 +785,308 @@
  * Decode READLINK reply
  */
 static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkres *res)
 {
+	struct iovec	*iov = req->rq_rvec;
+	u32		hdrlen;
+	u32	*strlen;
+	char	*string;
 	int	status;
+	unsigned int len;
 
-	if ((status = ntohl(*p++)))
+	status = ntohl(*p++);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+
+	if (status != 0)
 		return -nfs_stat_to_errno(status);
-	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
 
-	/* Caller takes over the buffer here to avoid extra copy */
-	res->buffer = req->rq_task->tk_buffer;
-	req->rq_task->tk_buffer = NULL;
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READLINK reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READLINK header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
+	strlen = (u32*)res->buffer;
+	/* Convert length of symlink */
+	len = ntohl(*strlen);
+	if (len > res->bufsiz - 5)
+		len = res->bufsiz - 5;
+	*strlen = len;
+	/* NULL terminate the string we got */
+	string = (char *)(strlen + 1);
+	string[len] = 0;
 	return 0;
 }
 
 /*
- * Decode STATFS reply
+ * Decode READ reply
  */
 static int
-nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
+	struct iovec *iov = req->rq_rvec;
+	u32	count, ocount, recvd, hdrlen;
 	int	status;
 
-	if ((status = ntohl(*p++)))
+	status = ntohl(*p++);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+
+	if (status != 0)
 		return -nfs_stat_to_errno(status);
-	res->tsize = ntohl(*p++);
-	res->bsize = ntohl(*p++);
-	res->blocks = ntohl(*p++);
-	res->bfree = ntohl(*p++);
-	res->bavail = ntohl(*p++);
-	return 0;
+
+	/* Decode reply could and EOF flag. NFSv3 is somewhat redundant
+	 * in that it puts the count both in the res struct and in the
+	 * opaque data count. */
+	count    = ntohl(*p++);
+	res->eof = ntohl(*p++);
+	ocount   = ntohl(*p++);
+
+	if (ocount != count) {
+		printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
+		return -errno_NFSERR_IO;
+	}
+
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READ header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
+	recvd = req->rq_rlen - hdrlen;
+	if (count > recvd) {
+		printk(KERN_WARNING "NFS: server cheating in read reply: "
+			"count %d > recvd %d\n", count, recvd);
+		count = recvd;
+	}
+
+	if (count < res->count) {
+		xdr_zero_iovec(iov+1, req->rq_rnr-1, res->count - count);
+		res->count = count;
+	}
+
+	return count;
 }
 
 /*
- * We need to translate between nfs status return values and
- * the local errno values which may not be the same.
+ * Decode WRITE response
  */
-static struct {
-	int stat;
-	int errno;
-} nfs_errtbl[] = {
-	{ NFS_OK,		0		},
-	{ NFSERR_PERM,		EPERM		},
-	{ NFSERR_NOENT,		ENOENT		},
-	{ NFSERR_IO,		errno_NFSERR_IO	},
-	{ NFSERR_NXIO,		ENXIO		},
-	{ NFSERR_EAGAIN,	EAGAIN		},
-	{ NFSERR_ACCES,		EACCES		},
-	{ NFSERR_EXIST,		EEXIST		},
-	{ NFSERR_XDEV,		EXDEV		},
-	{ NFSERR_NODEV,		ENODEV		},
-	{ NFSERR_NOTDIR,	ENOTDIR		},
-	{ NFSERR_ISDIR,		EISDIR		},
-	{ NFSERR_INVAL,		EINVAL		},
-	{ NFSERR_FBIG,		EFBIG		},
-	{ NFSERR_NOSPC,		ENOSPC		},
-	{ NFSERR_ROFS,		EROFS		},
-	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
-	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
-	{ NFSERR_DQUOT,		EDQUOT		},
-	{ NFSERR_STALE,		ESTALE		},
-#ifdef EWFLUSH
-	{ NFSERR_WFLUSH,	EWFLUSH		},
-#endif
-	{ -1,			EIO		}
-};
+static int
+nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	int	status;
+
+	status = ntohl(*p++);
+	p = xdr_decode_wcc_data(p, res->fattr);
+
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
 
+	res->count = ntohl(*p++);
+	res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
+	res->verf->verifier[0] = *p++;
+	res->verf->verifier[1] = *p++;
+
+	return res->count;
+}
+
+/*
+ * Decode a CREATE response
+ */
 static int
-nfs_stat_to_errno(int stat)
+nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
 {
-	int i;
+	int	status;
 
-	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
-		if (nfs_errtbl[i].stat == stat)
-			return nfs_errtbl[i].errno;
+	status = ntohl(*p++);
+	if (status == 0) {
+		if (*p++) {
+			if (!(p = xdr_decode_fhandle(p, res->fh)))
+				return -errno_NFSERR_IO;
+			p = xdr_decode_post_op_attr(p, res->fattr);
+		} else {
+			memset(res->fh, 0, sizeof(*res->fh));
+			/* Do decode post_op_attr but set it to NULL */
+			p = xdr_decode_post_op_attr(p, res->fattr);
+			res->fattr->valid = 0;
+		}
+	} else {
+		status = -nfs_stat_to_errno(status);
 	}
-	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
-	return nfs_errtbl[i].errno;
+	p = xdr_decode_wcc_data(p, res->dir_attr);
+	return status;
 }
 
-#ifndef MAX
-# define MAX(a, b)	(((a) > (b))? (a) : (b))
-#endif
+/*
+ * Decode RENAME reply
+ */
+static int
+nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
+{
+	int	status;
 
-#define PROC(proc, argtype, restype)	\
-    { "nfs_" #proc,					\
-      (kxdrproc_t) nfs_xdr_##argtype,			\
-      (kxdrproc_t) nfs_xdr_##restype,			\
-      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2	\
-    }
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	p = xdr_decode_wcc_data(p, res->fromattr);
+	p = xdr_decode_wcc_data(p, res->toattr);
+	return status;
+}
 
-static struct rpc_procinfo	nfs_procedures[18] = {
-    PROC(null,		enc_void,	dec_void),
-    PROC(getattr,	fhandle,	attrstat),
-    PROC(setattr,	sattrargs,	attrstat),
-    PROC(root,		enc_void,	dec_void),
-    PROC(lookup,	diropargs,	diropres),
-    PROC(readlink,	fhandle,	readlinkres),
-    PROC(read,		readargs,	readres),
-    PROC(writecache,	enc_void,	dec_void),
-    PROC(write,		writeargs,	attrstat),
-    PROC(create,	createargs,	diropres),
-    PROC(remove,	diropargs,	stat),
-    PROC(rename,	renameargs,	stat),
-    PROC(link,		linkargs,	stat),
-    PROC(symlink,	symlinkargs,	stat),
-    PROC(mkdir,		createargs,	diropres),
-    PROC(rmdir,		diropargs,	stat),
-    PROC(readdir,	readdirargs,	readdirres),
-    PROC(statfs,	fhandle,	statfsres),
-};
+/*
+ * Decode LINK reply
+ */
+static int
+nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
+{
+	int	status;
 
-static struct rpc_version	nfs_version2 = {
-	2,
-	sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
-	nfs_procedures
-};
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+	p = xdr_decode_wcc_data(p, res->dir_attr);
+	return status;
+}
 
-static struct rpc_version *	nfs_version[] = {
-	NULL,
-	NULL,
-	&nfs_version2
-};
+/*
+ * Decode FSSTAT reply
+ */
+static int
+nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	struct nfs_fattr dummy;
+	int		status;
 
-struct rpc_program	nfs_program = {
-	"nfs",
-	NFS_PROGRAM,
-	sizeof(nfs_version) / sizeof(nfs_version[0]),
-	nfs_version,
-	&nfs_rpcstat,
-};
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	p = xdr_decode_hyper(p, &res->tbytes);
+	p = xdr_decode_hyper(p, &res->fbytes);
+	p = xdr_decode_hyper(p, &res->abytes);
+	p = xdr_decode_hyper(p, &res->tfiles);
+	p = xdr_decode_hyper(p, &res->ffiles);
+	p = xdr_decode_hyper(p, &res->afiles);
+
+	/* ignore invarsec */
+	return 0;
+}
 
 /*
- * RPC stats support
+ * Decode FSINFO reply
  */
 static int
-nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
 {
-	return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
+	struct nfs_fattr dummy;
+	int		status;
+
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	res->rtmax  = ntohl(*p++);
+	res->rtpref = ntohl(*p++);
+	res->rtmult = ntohl(*p++);
+	res->wtmax  = ntohl(*p++);
+	res->wtpref = ntohl(*p++);
+	res->wtmult = ntohl(*p++);
+	res->dtpref = ntohl(*p++);
+	p = xdr_decode_hyper(p, &res->maxfilesize);
+
+	/* ignore time_delta and properties */
+	return 0;
 }
 
-static struct proc_dir_entry	proc_nfsclnt = {
-	0, 3, "nfs",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	6, &proc_net_inode_operations,
-	nfs_get_info
+/*
+ * Decode PATHCONF reply
+ */
+static int
+nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	struct nfs_fattr dummy;
+	int		status;
+
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+	res->linkmax = ntohl(*p++);
+	res->namelen = ntohl(*p++);
+
+	/* ignore remaining fields */
+	return 0;
+}
+
+/*
+ * Decode COMMIT reply
+ */
+static int
+nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	int		status;
+
+	status = ntohl(*p++);
+	p = xdr_decode_wcc_data(p, res->fattr);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	res->verf->verifier[0] = *p++;
+	res->verf->verifier[1] = *p++;
+	return 0;
+}
+
+#ifndef MAX
+# define MAX(a, b)	(((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype)				\
+    { "nfs3_" #proc,						\
+      (kxdrproc_t) nfs3_xdr_##argtype,				\
+      (kxdrproc_t) nfs3_xdr_##restype,				\
+      MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2,	\
+      0							\
+    }
+
+static struct rpc_procinfo	nfs3_procedures[22] = {
+  PROC(null,		enc_void,	dec_void),
+  PROC(getattr,		fhandle,	attrstat),
+  PROC(setattr, 	sattrargs,	wccstat),
+  PROC(lookup,		diropargs,	lookupres),
+  PROC(access,		accessargs,	accessres),
+  PROC(readlink,	readlinkargs,	readlinkres),
+  PROC(read,		readargs,	readres),
+  PROC(write,		writeargs,	writeres),
+  PROC(create,		createargs,	createres),
+  PROC(mkdir,		mkdirargs,	createres),
+  PROC(symlink,		symlinkargs,	createres),
+  PROC(mknod,		mknodargs,	createres),
+  PROC(remove,		diropargs,	wccstat),
+  PROC(rmdir,		diropargs,	wccstat),
+  PROC(rename,		renameargs,	renameres),
+  PROC(link,		linkargs,	linkres),
+  PROC(readdir,		readdirargs,	readdirres),
+  PROC(readdirplus,	readdirargs,	readdirres),
+  PROC(fsstat,		fhandle,	fsstatres),
+  PROC(fsinfo,  	fhandle,	fsinfores),
+  PROC(pathconf,	fhandle,	pathconfres),
+  PROC(commit,		commitargs,	commitres),
 };
 
-struct rpc_stat			nfs_rpcstat = {
-	NULL,			/* next */
-	&proc_nfsclnt,		/* /proc/net directory entry */
-	&nfs_program,		/* RPC program */
+struct rpc_version		nfs_version3 = {
+	3,
+	sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
+	nfs3_procedures
 };
+
diff -urN linux-2.2.18-pre8/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- linux-2.2.18-pre8/fs/nfs/nfsroot.c	Thu Apr 23 18:12:21 1998
+++ linux/fs/nfs/nfsroot.c	Mon Dec  3 20:25:21 2001
@@ -61,8 +61,12 @@
  *	Martin Mares	:	Use root_server_addr appropriately during setup.
  *	Martin Mares	:	Rewrote parameter parsing, now hopefully giving
  *				correct overriding.
+ *	Trond Myklebust :	Add in preliminary support for NFSv3 and TCP.
+ *				Fix bug in root_nfs_addr(). nfs_data.namlen
+ *				is NOT for the length of the hostname.
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
@@ -70,7 +74,8 @@
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/sunrpc/clnt.h>
-#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/in.h>
@@ -85,6 +90,7 @@
 
 /* Default path we try to mount. "%s" gets replaced by our IP address */
 #define NFS_ROOT		"/tftpboot/%s"
+#define NFS_PORT		NFS2_PORT
 
 /* Parameters passed from the kernel command line */
 static char nfs_root_name[256] __initdata = "";
@@ -93,7 +99,7 @@
 static __u32 servaddr __initdata = 0;
 
 /* Name of directory to mount */
-static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };
+static char nfs_path[PATH_MAX] __initdata = { 0, };
 
 /* NFS-related data */
 static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
@@ -145,6 +151,14 @@
 	{ "nocto",	~NFS_MOUNT_NOCTO,	NFS_MOUNT_NOCTO },
 	{ "ac",		~NFS_MOUNT_NOAC,	0 },
 	{ "noac",	~NFS_MOUNT_NOAC,	NFS_MOUNT_NOAC },
+	{ "lock",	~NFS_MOUNT_NONLM,	0 },
+	{ "nolock",	~NFS_MOUNT_NONLM,	NFS_MOUNT_NONLM },
+#ifdef CONFIG_NFS_V3
+	{ "v2",		~NFS_MOUNT_VER3,	0 },
+	{ "v3",		~NFS_MOUNT_VER3,	NFS_MOUNT_VER3 },
+#endif
+	{ "udp",	~NFS_MOUNT_TCP,		0 },
+	{ "tcp",	~NFS_MOUNT_TCP,		NFS_MOUNT_TCP },
 	{ NULL,		0,			0 }
 };
 
@@ -211,8 +225,8 @@
 		}
 	}
 	if (name[0] && strcmp(name, "default")) {
-		strncpy(buf, name, NFS_MAXPATHLEN-1);
-		buf[NFS_MAXPATHLEN-1] = 0;
+		strncpy(buf, name, PATH_MAX-1);
+		buf[PATH_MAX-1] = 0;
 	}
 }
 
@@ -222,17 +236,16 @@
  */
 __initfunc(static int root_nfs_name(char *name))
 {
-	char buf[NFS_MAXPATHLEN];
+	char buf[PATH_MAX];
 	char *cp;
 
 	/* Set some default values */
 	memset(&nfs_data, 0, sizeof(nfs_data));
 	nfs_port          = -1;
 	nfs_data.version  = NFS_MOUNT_VERSION;
-	nfs_data.flags    = NFS_MOUNT_NONLM;	/* No lockd in nfs root yet */
-	nfs_data.rsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	nfs_data.wsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	nfs_data.bsize	  = 0;
+	/* It is ok to have lockd in nfs root since it will be started
+	   later manually in the rc script. */
+	nfs_data.flags    = 0;
 	nfs_data.timeo    = 7;
 	nfs_data.retrans  = 3;
 	nfs_data.acregmin = 3;
@@ -248,7 +261,7 @@
 	root_nfs_parse(name, buf);
 
 	cp = system_utsname.nodename;
-	if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
+	if (strlen(buf) + strlen(cp) > PATH_MAX) {
 		printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
 		return -1;
 	}
@@ -269,7 +282,6 @@
 	}
 
 	strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1);
-	nfs_data.namlen = strlen(nfs_data.hostname);
 	return 0;
 }
 
@@ -356,14 +368,14 @@
 /*
  *  Query server portmapper for the port of a daemon program.
  */
-__initfunc(static int root_nfs_getport(int program, int version))
+__initfunc(static int root_nfs_getport(int program, int version, int proto))
 {
 	struct sockaddr_in sin;
 
 	printk(KERN_NOTICE "Looking up port of RPC %d/%d on %s\n",
 		program, version, in_ntoa(servaddr));
 	set_sockaddr(&sin, servaddr, 0);
-	return rpc_getport_external(&sin, program, version, IPPROTO_UDP);
+	return rpc_getport_external(&sin, program, version, proto);
 }
 
 
@@ -375,22 +387,39 @@
 __initfunc(static int root_nfs_ports(void))
 {
 	int port;
+	int nfsd_ver, mountd_ver;
+	int nfsd_port, mountd_port;
+	int proto;
+
+	if (nfs_data.flags & NFS_MOUNT_VER3) {
+		nfsd_ver = NFS3_VERSION;
+		mountd_ver = NFS_MNT3_VERSION;
+		nfsd_port = NFS_PORT;
+		mountd_port = NFS_MNT_PORT;
+	} else {
+		nfsd_ver = NFS2_VERSION;
+		mountd_ver = NFS_MNT_VERSION;
+		nfsd_port = NFS_PORT;
+		mountd_port = NFS_MNT_PORT;
+	}
+
+	proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
 
 	if (nfs_port < 0) {
-		if ((port = root_nfs_getport(NFS_PROGRAM, NFS_VERSION)) < 0) {
+		if ((port = root_nfs_getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
 			printk(KERN_ERR "Root-NFS: Unable to get nfsd port "
 					"number from server, using default\n");
-			port = NFS_PORT;
+			port = nfsd_port;
 		}
 		nfs_port = htons(port);
 		dprintk("Root-NFS: Portmapper on server returned %d "
 			"as nfsd port\n", port);
 	}
 
-	if ((port = root_nfs_getport(NFS_MNT_PROGRAM, NFS_MNT_VERSION)) < 0) {
+	if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) {
 		printk(KERN_ERR "Root-NFS: Unable to get mountd port "
 				"number from server, using default\n");
-		port = NFS_MNT_PORT;
+		port = mountd_port;
 	}
 	mount_port = htons(port);
 	dprintk("Root-NFS: mountd port is %d\n", port);
@@ -409,7 +438,10 @@
 	int status;
 
 	set_sockaddr(&sin, servaddr, mount_port);
-	status = nfs_mount(&sin, nfs_path, &nfs_data.root);
+	if (nfs_data.flags & NFS_MOUNT_VER3)
+		status = nfs3_mount(&sin, nfs_path, &nfs_data.root);
+	else
+		status = nfs_mount(&sin, nfs_path, &nfs_data.root);
 	if (status < 0)
 		printk(KERN_ERR "Root-NFS: Server returned error %d "
 				"while mounting %s\n", status, nfs_path);
diff -urN linux-2.2.18-pre8/fs/nfs/proc.c linux/fs/nfs/proc.c
--- linux-2.2.18-pre8/fs/nfs/proc.c	Tue Jan 13 10:03:40 1998
+++ linux/fs/nfs/proc.c	Mon Dec  3 20:25:21 2001
@@ -13,10 +13,6 @@
  *  Note: Error returns are optimized for NFS_OK, which isn't translated via
  *  nfs_stat_to_errno(), but happens to be already the right return code.
  *
- *  FixMe: We ought to define a sensible small max size for
- *  things like getattr that are tiny packets and use the
- *  old get_free_page stuff with it.
- *
  *  Also, the code currently doesn't check the size of the packet, when
  *  it decodes the packet.
  *
@@ -25,211 +21,342 @@
  *  Completely rewritten to support the new RPC call interface;
  *  rewrote and moved the entire XDR stuff to xdr.c
  *  --Olaf Kirch June 1996
+ *
+ *  The code below initializes all auto variables explicitly, otherwise
+ *  it will fail to work as a module (gcc generates a memset call for an
+ *  incomplete struct).
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS2_XDR_TYPES
 
+#include <linux/types.h>
 #include <linux/param.h>
+#include <linux/malloc.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
-#include <linux/malloc.h>
 #include <linux/utsname.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/in.h>
 #include <linux/pagemap.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
 #include <linux/nfs_fs.h>
 
 #include <asm/segment.h>
 
-#ifdef NFS_DEBUG
-# define NFSDBG_FACILITY	NFSDBG_PROC
-#endif
 
 
 /*
+ * Typing short-hands
+ */
+typedef struct nfs_fattr	fattr;
+typedef struct qstr		qstr;
+
+#define NFSDBG_FACILITY		NFSDBG_PROC
+
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+			fattr *fattr)
+{
+	int		status;
+
+	dprintk("NFS call  getroot\n");
+	fattr->valid = 0;
+	status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+	dprintk("NFS reply getroot\n");
+	return status;
+}
+
+/*
  * One function for each procedure in the NFS protocol.
  */
-int
-nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fattr *fattr)
+static int
+nfs_proc_getattr(struct dentry *dentry, fattr *fattr)
 {
 	int	status;
 
 	dprintk("NFS call  getattr\n");
-	status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_GETATTR,
+				NFS_FH(dentry), fattr, 0);
 	dprintk("NFS reply getattr\n");
 	return status;
 }
 
-int
-nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_sattr *sattr, struct nfs_fattr *fattr)
+static int
+nfs_proc_setattr(struct dentry *dentry, fattr *fattr,
+				struct iattr *sattr)
 {
-	struct nfs_sattrargs	arg = { fhandle, sattr };
+	struct nfs_sattrargs	arg = { NFS_FH(dentry), sattr };
 	int	status;
 
 	dprintk("NFS call  setattr\n");
-	status = rpc_call(server->client, NFSPROC_SETATTR, &arg, fattr, 0);
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_SETATTR, &arg, fattr, 0);
 	dprintk("NFS reply setattr\n");
 	return status;
 }
 
-int
-nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
-		    struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_lookup(struct dentry *dir, fattr *dir_attr, qstr *name,
+		struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_diropargs	arg = { dir, name };
+	struct nfs_diropargs	arg = { NFS_FH(dir), name->name, name->len };
 	struct nfs_diropok	res = { fhandle, fattr };
 	int			status;
 
-	dprintk("NFS call  lookup %s\n", name);
-	status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
+	dprintk("NFS call  lookup %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_LOOKUP, &arg, &res, 0);
 	dprintk("NFS reply lookup: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-			void **p0, char **string, unsigned int *len,
-			unsigned int maxlen)
+static int
+nfs_proc_readlink(struct dentry *dentry, fattr *fattr,
+			void *buffer, unsigned int bufsiz)
 {
-	struct nfs_readlinkres	res = { string, len, maxlen, NULL };
+	struct nfs_readlinkargs	args = { NFS_FH(dentry), buffer, bufsiz };
+	struct nfs_readlinkres	res = { buffer, bufsiz };
 	int			status;
 
 	dprintk("NFS call  readlink\n");
-	status = rpc_call(server->client, NFSPROC_READLINK, fhandle, &res, 0);
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_READLINK,
+					&args, &res, 0);
 	dprintk("NFS reply readlink: %d\n", status);
-	if (!status)
-		*p0 = res.buffer;
-	else if (res.buffer)
-		kfree(res.buffer);
 	return status;
 }
 
-int
-nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
-			  unsigned long offset, unsigned int count,
-			  void *buffer, struct nfs_fattr *fattr)
-{
-	struct nfs_readargs	arg = { fhandle, offset, count, buffer };
-	struct nfs_readres	res = { fattr, count };
+static int
+nfs_proc_read(struct dentry *dentry, fattr *fattr,
+	      struct rpc_cred *cred, int flags,
+	      unsigned long offset, unsigned int count,
+	      void *buffer, int *eofp)
+{
+	struct nfs_readargs	arg = { NFS_FH(dentry), offset, count, 1,
+				       {{ buffer, count }, {0,0}, {0,0}, {0,0},
+					{0,0}, {0,0}, {0,0}, {0,0}} };
+	struct nfs_readres	res = { fattr, count, 0};
+	struct rpc_message	msg = { NFSPROC_READ, &arg, &res, cred };
 	int			status;
 
 	dprintk("NFS call  read %d @ %ld\n", count, offset);
-	status = rpc_call(server->client, NFSPROC_READ, &arg, &res,
-			swap? NFS_RPC_SWAPFLAGS : 0);
+	fattr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+
 	dprintk("NFS reply read: %d\n", status);
+	*eofp = res.eof;
 	return status;
 }
 
-int
-nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
-			unsigned long offset, unsigned int count,
-			const void *buffer, struct nfs_fattr *fattr)
-{
-	struct nfs_writeargs	arg = { fhandle, offset, count, buffer };
-	int			status;
+static int
+nfs_proc_write(struct dentry *dentry, fattr *fattr,
+	       struct rpc_cred *cred, int how,
+	       unsigned long offset, unsigned int count,
+	       void *buffer, struct nfs_writeverf *verf)
+{
+	struct nfs_writeargs	arg = {NFS_FH(dentry), offset, count,
+					NFS_FILE_SYNC, 1,
+					{{buffer, count}, {0,0}, {0,0}, {0,0},
+					 {0,0}, {0,0}, {0,0}, {0,0}}};
+	struct nfs_writeres     res = {fattr, verf, count};
+	struct rpc_message	msg = { NFSPROC_WRITE, &arg, &res, cred };
+	int			status, flags = 0;
 
 	dprintk("NFS call  write %d @ %ld\n", count, offset);
-	status = rpc_call(server->client, NFSPROC_WRITE, &arg, fattr,
-			swap? (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) : 0);
-	dprintk("NFS reply read: %d\n", status);
+	fattr->valid = 0;
+	if (how & NFS_RW_SWAP)
+		flags |= NFS_RPC_SWAPFLAGS;
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+
+	dprintk("NFS reply write: %d\n", status);
+	verf->committed = NFS_FILE_SYNC;      /* NFSv2 always syncs data */
 	return status < 0? status : count;
 }
 
-int
-nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_create(struct dentry *dir, fattr *dir_attr,
+		qstr *name, struct iattr *sattr, int flags,
+		struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_createargs	arg = { dir, name, sattr };
+	struct nfs_createargs	arg = { NFS_FH(dir), name->name,
+					name->len, sattr };
 	struct nfs_diropok	res = { fhandle, fattr };
 	int			status;
 
-	dprintk("NFS call  create %s\n", name);
-	status = rpc_call(server->client, NFSPROC_CREATE, &arg, &res, 0);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	dprintk("NFS call  create %s\n", name->name);
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
 	dprintk("NFS reply create: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
+/*
+ * In NFSv2, mknod is grafted onto the create call.
+ */
+static int
+nfs_proc_mknod(struct dentry *dir, fattr *dir_attr,
+			qstr *name, struct iattr *sattr, dev_t rdev,
+			struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_diropargs	arg = { dir, name };
-	int			status;
+	struct nfs_createargs	arg = { NFS_FH(dir), name->name,
+						     name->len, sattr };
+	struct nfs_diropok	res = { fhandle, fattr };
+	int			status, mode;
+
+	dprintk("NFS call  mknod %s\n", name->name);
+
+	mode = sattr->ia_mode;
+	if (S_ISFIFO(mode)) {
+		sattr->ia_mode = (mode & ~S_IFMT) | S_IFCHR;
+		sattr->ia_valid &= ~ATTR_SIZE;
+	} else if (S_ISCHR(mode) || S_ISBLK(mode)) {
+		sattr->ia_valid |= ATTR_SIZE;
+		sattr->ia_size   = rdev;	/* get out your barf bag */
+	}
+
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
+
+	if (status == -EINVAL && S_ISFIFO(mode)) {
+		sattr->ia_mode = mode;
+		dir_attr->valid = 0;
+		fattr->valid = 0;
+		status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
+	}
+	dprintk("NFS reply mknod: %d\n", status);
+	return status;
+}
+  
+static int
+nfs_proc_remove(struct dentry *dir, fattr *dir_attr, qstr *name)
+{
+	struct nfs_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	struct rpc_message	msg = { NFSPROC_REMOVE, &arg, NULL, NULL };
+	int			status;
+
+	dir_attr->valid = 0;
+	dprintk("NFS call  remove %s\n", name->name);
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
 
-	dprintk("NFS call  remove %s\n", name);
-	status = rpc_call(server->client, NFSPROC_REMOVE, &arg, NULL, 0);
 	dprintk("NFS reply remove: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_rename(struct nfs_server *server,
-		struct nfs_fh *old_dir, const char *old_name,
-		struct nfs_fh *new_dir, const char *new_name)
+static int
+nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name)
 {
-	struct nfs_renameargs	arg = { old_dir, old_name, new_dir, new_name };
-	int			status;
+	struct nfs_diropargs	*arg;
 
-	dprintk("NFS call  rename %s -> %s\n", old_name, new_name);
-	status = rpc_call(server->client, NFSPROC_RENAME, &arg, NULL, 0);
+	arg = (struct nfs_diropargs *)kmalloc(sizeof(*arg), GFP_KERNEL);
+	if (!arg)
+		return -ENOMEM;
+	memset(arg, 0, sizeof(*arg));
+	arg->fh = NFS_FH(dir);
+	arg->name = name->name;
+	arg->len = name->len;
+	msg->rpc_proc = NFSPROC_REMOVE;
+	msg->rpc_argp = arg;
+	return 0;
+}
+
+static void
+nfs_proc_unlink_done(struct dentry *dir, struct rpc_message *msg)
+{
+	if (msg->rpc_argp) {
+		NFS_CACHEINV(dir->d_inode);
+		kfree(msg->rpc_argp);
+	}
+}
+
+static int
+nfs_proc_rename(struct dentry *old_dir, fattr *old_attr, qstr *old_name,
+		struct dentry *new_dir, fattr *new_attr, qstr *new_name)
+{
+	struct nfs_renameargs	arg = { NFS_FH(old_dir), old_name->name,
+					old_name->len,
+					NFS_FH(new_dir), new_name->name,
+					new_name->len};
+	int			status;
+
+	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
+	old_attr->valid = 0;
+	new_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(old_dir->d_inode), NFSPROC_RENAME, &arg, NULL, 0);
 	dprintk("NFS reply rename: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fh *dir, const char *name)
-{
-	struct nfs_linkargs	arg = { fhandle, dir, name };
+static int
+nfs_proc_link(struct dentry *dentry, fattr *attr,
+	      struct dentry *dir, fattr *dir_attr, qstr *name)
+{
+	struct nfs_linkargs	arg = { NFS_FH(dentry), NFS_FH(dir),
+					name->name, name->len };
 	int			status;
 
-	dprintk("NFS call  link %s\n", name);
-	status = rpc_call(server->client, NFSPROC_LINK, &arg, NULL, 0);
+	dprintk("NFS call  link %s\n", name->name);
+	dir_attr->valid = 0;
+	attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_LINK, &arg, NULL, 0);
 	dprintk("NFS reply link: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, const char *path,
-			struct nfs_sattr *sattr)
-{
-	struct nfs_symlinkargs	arg = { dir, name, path, sattr };
+static int
+nfs_proc_symlink(struct dentry *dir, fattr *dir_attr, qstr *name,
+		 qstr *path, struct iattr *sattr,
+		 struct nfs_fh *sym_fh, fattr *sym_attr)
+{
+	struct nfs_symlinkargs	arg = { NFS_FH(dir), name->name, name->len,
+					path->name, path->len, sattr };
 	int			status;
 
-	dprintk("NFS call  symlink %s -> %s\n", name, path);
-	status = rpc_call(server->client, NFSPROC_SYMLINK, &arg, NULL, 0);
+	dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
+	dir_attr->valid = 0;
+	sym_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_SYMLINK, &arg, NULL, 0);
 	dprintk("NFS reply symlink: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_mkdir(struct dentry *dir, fattr *dir_attr, qstr *name,
+	       struct iattr *sattr,
+	       struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_createargs	arg = { dir, name, sattr };
+	struct nfs_createargs	arg = { NFS_FH(dir), name->name, name->len,
+					sattr };
 	struct nfs_diropok	res = { fhandle, fattr };
 	int			status;
 
-	dprintk("NFS call  mkdir %s\n", name);
-	status = rpc_call(server->client, NFSPROC_MKDIR, &arg, &res, 0);
+	dprintk("NFS call  mkdir %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_MKDIR, &arg, &res, 0);
 	dprintk("NFS reply mkdir: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
+static int
+nfs_proc_rmdir(struct dentry *dir, fattr *dir_attr, qstr *name)
 {
-	struct nfs_diropargs	arg = { dir, name };
+	struct nfs_diropargs	arg = { NFS_FH(dir), name->name, name->len };
 	int			status;
 
-	dprintk("NFS call  rmdir %s\n", name);
-	status = rpc_call(server->client, NFSPROC_RMDIR, &arg, NULL, 0);
+	dprintk("NFS call  rmdir %s\n", name->name);
+	dir_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_RMDIR, &arg, NULL, 0);
 	dprintk("NFS reply rmdir: %d\n", status);
 	return status;
 }
@@ -237,66 +364,71 @@
 /*
  * The READDIR implementation is somewhat hackish - we pass a temporary
  * buffer to the encode function, which installs it in the receive
- * iovec. The dirent buffer itself is passed in the result struct.
+ * the receive iovec. The decode function just parses the reply to make
+ * sure it is syntactically correct; the entries itself are decoded
+ * from nfs_readdir by calling the decode_entry function directly.
  */
-int
-nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-			u32 cookie, unsigned int size, __u32 *entry)
+static int
+nfs_proc_readdir(struct dentry *dir, fattr *dir_attr,
+		 struct rpc_cred *cred,
+		 __u64 cookie, void *entry, unsigned int size, int plus)
 {
 	struct nfs_readdirargs	arg;
 	struct nfs_readdirres	res;
-	void *			buffer;
-	unsigned int		buf_size = PAGE_SIZE;
+	struct rpc_message	msg = { NFSPROC_READDIR, &arg, &res, cred };
 	int			status;
 
-	/* First get a temp buffer for the readdir reply */
-	/* N.B. does this really need to be cleared? */
-	status = -ENOMEM;
-	buffer = (void *) get_free_page(GFP_KERNEL);
-	if (!buffer)
-		goto out;
-
-	/*
-	 * Calculate the effective size the buffer.  To make sure
-	 * that the returned data will fit into the user's buffer,
-	 * we decrease the buffer size as necessary.
-	 *
-	 * Note: NFS returns three __u32 values for each entry,
-	 * and we assume that the data is packed into the user
-	 * buffer with the same efficiency. 
-	 */
-	if (size < buf_size)
-		buf_size = size;
-	if (server->rsize < buf_size)
-		buf_size = server->rsize;
-#if 0
-printk("nfs_proc_readdir: user size=%d, rsize=%d, buf_size=%d\n",
-size, server->rsize, buf_size);
-#endif
-
-	arg.fh = fhandle;
+	dir_attr->valid = 0;
+	arg.fh = NFS_FH(dir);
 	arg.cookie = cookie;
-	arg.buffer = buffer;
-	arg.bufsiz = buf_size;
+	arg.buffer = entry;
+	arg.bufsiz = size;
 	res.buffer = entry;
 	res.bufsiz = size;
 
-	dprintk("NFS call  readdir %d\n", cookie);
-	status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0);
+	dir_attr->valid = 0;
+	dprintk("NFS call  readdir %d\n", (unsigned int)cookie);
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+
 	dprintk("NFS reply readdir: %d\n", status);
-	free_page((unsigned long) buffer);
-out:
 	return status;
 }
 
-int
+static int
 nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
 			struct nfs_fsinfo *info)
 {
 	int	status;
 
 	dprintk("NFS call  statfs\n");
+	memset((char *)info, 0, sizeof(*info));
 	status = rpc_call(server->client, NFSPROC_STATFS, fhandle, info, 0);
 	dprintk("NFS reply statfs: %d\n", status);
 	return status;
 }
+
+struct nfs_rpc_ops     nfs_v2_clientops = {
+       2,		       /* protocol version */
+       nfs_proc_get_root,
+       nfs_proc_getattr,
+       nfs_proc_setattr,
+       nfs_proc_lookup,
+       NULL,		       /* access */
+       nfs_proc_readlink,
+       nfs_proc_read,
+       nfs_proc_write,
+       NULL,		       /* commit */
+       nfs_proc_create,
+       nfs_proc_remove,
+       nfs_proc_unlink_setup,
+       nfs_proc_unlink_done,
+       nfs_proc_rename,
+       nfs_proc_link,
+       nfs_proc_symlink,
+       nfs_proc_mkdir,
+       nfs_proc_rmdir,
+       nfs_proc_readdir,
+       nfs_proc_mknod,
+       nfs_proc_statfs,
+       nfs_decode_dirent,
+};
diff -urN linux-2.2.18-pre8/fs/nfs/read.c linux/fs/nfs/read.c
--- linux-2.2.18-pre8/fs/nfs/read.c	Wed May  3 17:16:46 2000
+++ linux/fs/nfs/read.c	Mon Dec  3 20:25:21 2001
@@ -15,7 +15,8 @@
  * within the RPC code when root squashing is suspected.
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS2_XDR_TYPES
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -23,43 +24,62 @@
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
+#include <asm/pgtable.h>
 #include <linux/pagemap.h>
+#include <linux/file.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_page.h>
+#include <linux/nfs_flushd.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
-#include <asm/pgtable.h>
 
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
 
-struct nfs_rreq {
-	struct inode *		ra_inode;	/* inode from which to read */
-	struct page *		ra_page;	/* page to be read */
-	struct nfs_readargs	ra_args;	/* XDR argument struct */
-	struct nfs_readres	ra_res;		/* ... and result struct */
-	struct nfs_fattr	ra_fattr;	/* fattr storage */
+struct nfs_read_data {
+	struct rpc_task		task;
+	struct dentry		*dentry;
+	struct rpc_cred		*cred;
+	struct nfs_readargs	args;	/* XDR argument struct */
+	struct nfs_readres	res;	/* ... and result struct */
+	struct nfs_fattr	fattr;	/* fattr storage */
+	struct list_head	pages;	/* Coalesced read requests */
 };
 
+/*
+ * Local function declarations
+ */
+static void	nfs_readpage_result(struct rpc_task *task);
+
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)	(0)
 #endif
 
+static __inline__ struct nfs_read_data *nfs_readdata_alloc(void)
+{
+	struct nfs_read_data	*p;
+	p = (struct nfs_read_data*) kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->pages);
+	}
+	return p;
+}
 
-/*
- * Set up the NFS read request struct
- */
-static inline void
-nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh,
-		  unsigned long offset, void *buffer, unsigned int rsize)
+static __inline__ void nfs_readdata_free(struct nfs_read_data *p)
 {
-	req->ra_args.fh     = fh;
-	req->ra_args.offset = offset;
-	req->ra_args.count  = rsize;
-	req->ra_args.buffer = buffer;
-	req->ra_res.fattr   = &req->ra_fattr;
-	req->ra_res.count   = rsize;
+	kfree(p);
+}
+
+static void nfs_readdata_release(struct rpc_task *task)
+{
+        struct nfs_read_data   *data = (struct nfs_read_data *)task->tk_calldata;
+        nfs_readdata_free(data);
 }
 
 
@@ -67,32 +87,37 @@
  * Read a page synchronously.
  */
 static int
-nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
+nfs_readpage_sync(struct file *file, struct page *page)
 {
-	struct nfs_rreq	rqst;
-	unsigned long	offset = page->offset;
+	struct dentry	*dentry = file->f_dentry;
+	struct inode	*inode = dentry->d_inode;
+	struct rpc_cred	*cred = nfs_file_cred(file);
+	struct nfs_fattr fattr;
+	unsigned long	offset = nfs_page_offset(page);
 	char		*buffer = (char *) page_address(page);
 	int		rsize = NFS_SERVER(inode)->rsize;
 	int		result, refresh = 0;
-	int		count = PAGE_SIZE;
-	int		flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0;
+	int		count = PAGE_CACHE_SIZE, chunk, eof = 0;
+	int		flags = 0;
+
+	if (IS_SWAPFILE(inode))
+		flags |= NFS_RPC_SWAPFLAGS;
 
 	dprintk("NFS: nfs_readpage_sync(%p)\n", page);
 	clear_bit(PG_error, &page->flags);
 
 	do {
-		if (count < rsize)
-			rsize = count;
+		if ((chunk = rsize) > count)
+			chunk = count;
 
 		dprintk("NFS: nfs_proc_read(%s, (%s/%s), %ld, %d, %p)\n",
 			NFS_SERVER(inode)->hostname,
 			dentry->d_parent->d_name.name, dentry->d_name.name,
-			offset, rsize, buffer);
+			offset, chunk, buffer);
 
-		/* Set up arguments and perform rpc call */
-		nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize);
-		result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ,
-					&rqst.ra_args, &rqst.ra_res, flags);
+		result = NFS_CALL(read, inode, (dentry, &fattr, cred, flags,
+						offset, chunk, buffer, &eof));
+		nfs_refresh_inode(inode, &fattr);
 
 		/*
 		 * Even if we had a partial success we can't mark the page
@@ -107,7 +132,7 @@
 		count  -= result;
 		offset += result;
 		buffer += result;
-		if (result < rsize)	/* NFSv2ism */
+		if (eof)
 			break;
 	} while (count);
 
@@ -118,97 +143,311 @@
 
 io_error:
 	/* Note: we don't refresh if the call returned error */
-	if (refresh && result >= 0)
-		nfs_refresh_inode(inode, &rqst.ra_fattr);
-	/* N.B. Use nfs_unlock_page here? */
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
+	if (result < 0)
+		set_bit(PG_error, &page->flags);
 	return result;
 }
 
+static inline struct nfs_page *
+_nfs_find_read(struct inode *inode, struct page *page)
+{
+	struct list_head	*head, *next;
+
+	head = &inode->u.nfs_i.read;
+	next = head->next;
+	while (next != head) {
+		struct nfs_page *req = nfs_list_entry(next);
+		next = next->next;
+		if (page_index(req->wb_page) != page_index(page))
+			continue;
+		req->wb_count++;
+		return req;
+	}
+	return NULL;
+}
+
+static struct nfs_page *
+nfs_find_read(struct inode *inode, struct page *page)
+{
+	struct nfs_page *req;
+	req = _nfs_find_read(inode, page);
+	return req;
+}
+
 /*
- * This is the callback from RPC telling us whether a reply was
- * received or some error occurred (timeout or socket shutdown).
+ * Add a request to the inode's asynchronous read list.
  */
-static void
-nfs_readpage_result(struct rpc_task *task)
+static inline void
+nfs_mark_request_read(struct nfs_page *req)
 {
-	struct nfs_rreq	*req = (struct nfs_rreq *) task->tk_calldata;
-	struct page	*page = req->ra_page;
-	unsigned long	address = page_address(page);
-	int		result = task->tk_status;
-	static int	succ = 0, fail = 0;
-
-	dprintk("NFS: %4d received callback for page %lx, result %d\n",
-			task->tk_pid, address, result);
-
-	if (result >= 0) {
-		result = req->ra_res.count;
-		if (result < PAGE_SIZE) {
-			memset((char *) address + result, 0, PAGE_SIZE - result);
+	struct inode *inode = req->wb_dentry->d_inode;
+
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.read);
+		inode->u.nfs_i.nread++;
+	}
+	inode_schedule_scan(inode, req->wb_timeout);
+}
+
+static int
+nfs_readpage_async(struct file *file, struct page *page)
+{
+	struct inode	*inode = file->f_dentry->d_inode;
+	struct nfs_page	*req, *new = NULL;
+	int		result;
+
+	for (;;) {
+		result = 0;
+		if (PageUptodate(page))
+			break;
+
+		req = nfs_find_read(inode, page);
+		if (req) {
+			if (page != req->wb_page) {
+				nfs_release_request(req);
+				nfs_pagein_inode(inode, page_index(page), 0);
+				continue;
+			}
+			nfs_release_request(req);
+			break;
 		}
-		nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
-		flush_dcache_page(address);
-		set_bit(PG_uptodate, &page->flags);
-		succ++;
-	} else {
-		set_bit(PG_error, &page->flags);
-		fail++;
-		dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
+
+		if (new) {
+			nfs_lock_request(new);
+			new->wb_timeout = jiffies + NFS_READ_DELAY;
+			nfs_mark_request_read(new);
+			nfs_unlock_request(new);
+			new = NULL;
+			break;
+		}
+
+		result = -ENOMEM;
+		new = nfs_create_request(file, page, 0, PAGE_CACHE_SIZE);
+		if (!new)
+			break;
 	}
-	/* N.B. Use nfs_unlock_page here? */
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
 
-	free_page(address);
+	if (inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages ||
+	    page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
+		nfs_pagein_inode(inode, 0, 0);
+	if (new)
+		nfs_release_request(new);
+	return result;
+}
 
-	rpc_release_task(task);
-	kfree(req);
+/*
+ * Set up the NFS read request struct
+ */
+static void
+nfs_read_rpcsetup(struct list_head *head, struct nfs_read_data *data)
+{
+	struct nfs_page		*req;
+	struct iovec		*iov;
+	unsigned int		count;
+
+	iov = data->args.iov;
+	count = 0;
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		iov->iov_base = (void *)(page_address(req->wb_page) + req->wb_offset);
+		iov->iov_len = req->wb_bytes;
+		count += req->wb_bytes;
+		iov++;
+		data->args.nriov++;
+	}
+	req = nfs_list_entry(data->pages.next);
+	data->dentry	  = req->wb_dentry;
+	data->cred	  = req->wb_cred;
+	data->args.fh     = NFS_FH(req->wb_dentry);
+	data->args.offset = nfs_page_offset(req->wb_page) + req->wb_offset;
+	data->args.count  = count;
+	data->res.fattr   = &data->fattr;
+	data->res.count   = count;
+	data->res.eof     = 0;
 }
 
-static inline int
-nfs_readpage_async(struct dentry *dentry, struct inode *inode,
-			struct page *page)
+static void
+nfs_async_read_error(struct list_head *head)
 {
-	unsigned long address = page_address(page);
-	struct nfs_rreq	*req;
-	int		result = -1, flags;
+	struct nfs_page	*req;
+	struct page	*page;
 
-	dprintk("NFS: nfs_readpage_async(%p)\n", page);
-	if (NFS_CONGESTED(inode))
-		goto out_defer;
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		page = req->wb_page;
+		nfs_list_remove_request(req);
+		set_bit(PG_error, &page->flags);
+		nfs_unlock_page(page);
+		nfs_unlock_request(req);
+		nfs_release_request(req);
+	}
+}
+
+static int
+nfs_pagein_one(struct list_head *head, struct dentry *dentry)
+{
+	struct inode		*inode = dentry->d_inode;
+	struct rpc_task		*task;
+	struct rpc_clnt		*clnt = NFS_CLIENT(inode);
+	struct nfs_read_data	*data;
+	struct rpc_message	msg;
+	int			flags;
+	sigset_t		oldset;
+
+	data = nfs_readdata_alloc();
+	if (!data)
+		goto out_bad;
+	task = &data->task;
 
 	/* N.B. Do we need to test? Never called for swapfile inode */
 	flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
-	req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req));
-	if (!req)
-		goto out_defer;
-
-	/* Initialize request */
-	/* N.B. Will the dentry remain valid for life of request? */
-	nfs_readreq_setup(req, NFS_FH(dentry), page->offset,
-				(void *) address, PAGE_SIZE);
-	req->ra_inode = inode;
-	req->ra_page = page; /* count has been incremented by caller */
+
+	nfs_read_rpcsetup(head, data);
+
+	/* Finalize the task. */
+	rpc_init_task(task, clnt, nfs_readpage_result, flags);
+	task->tk_calldata = data;
+	/* Release requests */
+	task->tk_release = nfs_readdata_release;
+
+#ifdef CONFIG_NFS_V3
+	msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ;
+#else
+	msg.rpc_proc = NFSPROC_READ;
+#endif
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
 
 	/* Start the async call */
-	dprintk("NFS: executing async READ request.\n");
-	result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ,
-				&req->ra_args, &req->ra_res, flags,
-				nfs_readpage_result, req);
-	if (result < 0)
-		goto out_free;
-	result = 0;
-out:
-	return result;
+	dprintk("NFS: %4d initiated read call (req %s/%s count %d nriov %d.\n",
+		task->tk_pid,
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		data->args.count, data->args.nriov);
+
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+out_bad:
+	nfs_async_read_error(head);
+	return -ENOMEM;
+}
+
+static int
+nfs_pagein_list(struct inode *inode, struct list_head *head)
+{
+	LIST_HEAD(one_request);
+	struct nfs_page		*req;
+	int			error = 0;
+	unsigned int		pages = 0,
+				rpages = NFS_SERVER(inode)->rpages;
+
+	while (!list_empty(head)) {
+		pages += nfs_coalesce_requests(head, &one_request, rpages);
+		req = nfs_list_entry(one_request.next);
+		error = nfs_pagein_one(&one_request, req->wb_dentry);
+		if (error < 0)
+			break;
+	}
+	if (error >= 0)
+		return pages;
+
+	nfs_async_read_error(head);
+	return error;
+}
 
-out_defer:
-	dprintk("NFS: deferring async READ request.\n");
-	goto out;
-out_free:
-	dprintk("NFS: failed to enqueue async READ request.\n");
-	kfree(req);
-	goto out;
+static int
+nfs_scan_read_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.read, dst, inode);
+	inode->u.nfs_i.nread -= pages;
+	if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n");
+	return pages;
+}
+
+static int
+nfs_scan_read(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+{
+	int	res;
+	res = nfs_scan_list(&inode->u.nfs_i.read, dst, NULL, idx_start, npages);
+	inode->u.nfs_i.nread -= res;
+	if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n");
+	return res;
+}
+
+int nfs_pagein_inode(struct inode *inode, unsigned long idx_start,
+		     unsigned int npages)
+{
+	LIST_HEAD(head);
+	int	res,
+		error = 0;
+
+	res = nfs_scan_read(inode, &head, idx_start, npages);
+	if (res)
+		error = nfs_pagein_list(inode, &head);
+	if (error < 0)
+		return error;
+	return res;
+}
+
+int nfs_pagein_timeout(struct inode *inode)
+{
+	LIST_HEAD(head);
+	int	pages,
+		error = 0;
+
+	pages = nfs_scan_read_timeout(inode, &head);
+	if (pages)
+		error = nfs_pagein_list(inode, &head);
+	if (error < 0)
+		return error;
+	return pages;
+}
+
+/*
+ * This is the callback from RPC telling us whether a reply was
+ * received or some error occurred (timeout or socket shutdown).
+ */
+static void
+nfs_readpage_result(struct rpc_task *task)
+{
+	struct nfs_read_data	*data = (struct nfs_read_data *) task->tk_calldata;
+	struct dentry		*dentry = data->dentry;
+	struct inode		*inode = dentry->d_inode;
+	int			count = data->res.count;
+
+	dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
+		task->tk_pid, task->tk_status);
+
+	nfs_refresh_inode(inode, &data->fattr);
+	while (!list_empty(&data->pages)) {
+		struct nfs_page *req = nfs_list_entry(data->pages.next);
+		struct page *page = req->wb_page;
+		nfs_list_remove_request(req);
+
+		if (task->tk_status >= 0 && count >= 0) {
+			flush_dcache_page(page_address(page)); /* Is this correct? */
+			set_bit(PG_uptodate, &page->flags);
+			count -= PAGE_CACHE_SIZE;
+		} else
+			set_bit(PG_error, &page->flags);
+		nfs_unlock_page(page);
+
+		dprintk("NFS: read (%s/%s %d@%ld)\n",
+                        req->wb_dentry->d_parent->d_name.name,
+                        req->wb_dentry->d_name.name,
+                        req->wb_bytes,
+                        (nfs_page_offset(page) + req->wb_offset));
+		nfs_unlock_request(req);
+		nfs_release_request(req);
+	}
 }
 
 /*
@@ -216,7 +455,7 @@
  * We read the page synchronously in the following cases:
  *  -	The file is a swap file. Swap-ins are always sync operations,
  *	so there's no need bothering to make async reads 100% fail-safe.
- *  -	The NFS rsize is smaller than PAGE_SIZE. We could kludge our way
+ *  -	The NFS rsize is smaller than PAGE_CACHE_SIZE. We could kludge our way
  *	around this by creating several consecutive read requests, but
  *	that's hardly worth it.
  *  -	The error flag is set for this page. This happens only when a
@@ -228,41 +467,35 @@
 {
 	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
-	int		error;
+	int		error = 0,
+			rsize = NFS_SERVER(inode)->rsize;
+
+	while (!nfs_lock_page(page))
+		wait_on_page(page);
 
-	dprintk("NFS: nfs_readpage (%p %ld@%ld)\n",
-		page, PAGE_SIZE, page->offset);
-	atomic_inc(&page->count);
-	set_bit(PG_locked, &page->flags);
+	dprintk("NFS: nfs_readpage (%p %d@%ld)\n",
+		page, rsize, page->offset);
 
 	/*
-	 * Try to flush any pending writes to the file..
-	 *
-	 * NOTE! Because we own the page lock, there cannot
-	 * be any new pending writes generated at this point
-	 * for this page (other pages can be written to).
+	 * Try to flush any pending writes to the file
 	 */
 	error = nfs_wb_page(inode, page);
-	if (error)
-		goto out_error;
+	if (error < 0)
+		goto out_unlock;
 
 	error = -1;
-	if (!IS_SWAPFILE(inode) && !PageError(page) &&
-	    NFS_SERVER(inode)->rsize >= PAGE_SIZE)
-		error = nfs_readpage_async(dentry, inode, page);
+	if (!IS_SWAPFILE(inode) && !PageError(page) && rsize >= PAGE_CACHE_SIZE)
+		error = nfs_readpage_async(file, page);
+
 	if (error >= 0)
 		goto out;
 
-	error = nfs_readpage_sync(dentry, inode, page);
+	error = nfs_readpage_sync(file, page);
 	if (error < 0 && IS_SWAPFILE(inode))
-		printk("Aiee.. nfs swap-in of page failed!\n");
-	goto out_free;
+		printk(KERN_ERR "Aiee.. nfs swap-in of page failed!\n");
 
-out_error:
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
-out_free:
-	free_page(page_address(page));
-out:
+ out_unlock:
+	nfs_unlock_page(page);
+ out:
 	return error;
 }
diff -urN linux-2.2.18-pre8/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- linux-2.2.18-pre8/fs/nfs/symlink.c	Mon Apr 12 09:57:11 1999
+++ linux/fs/nfs/symlink.c	Mon Dec  3 20:25:21 2001
@@ -5,13 +5,20 @@
  *
  *  Optimization changes Copyright (C) 1994 Florian La Roche
  *
+ *  Jun 7 1999, cache symlink lookups in the page cache.  -DaveM
+ *
  *  nfs symlink handling code
  */
 
+#define NFS_NEED_XDR_TYPES
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
 #include <linux/stat.h>
+#include <asm/pgtable.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
@@ -37,70 +44,85 @@
 	NULL,			/* rename */
 	nfs_readlink,		/* readlink */
 	nfs_follow_link,	/* follow_link */
+	NULL,			/* get_block */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
+/* Symlink caching in the page cache is even more simplistic
+ * and straight-forward than readdir caching.
+ */
+static int nfs_symlink_filler(struct dentry *dentry, struct page *page)
+{
+	struct inode *inode = dentry->d_inode;
+	struct nfs_fattr fattr;
+	void * buffer = (void *)page_address(page);
+	unsigned int error;
+
+	/* We place the length at the beginning of the page,
+	 * in client byte order, followed by the string.
+	 */
+	error = NFS_CALL(readlink, inode, (dentry, &fattr, buffer,
+					   PAGE_CACHE_SIZE-sizeof(u32)-4));
+	nfs_refresh_inode(inode, &fattr);
+	if (error < 0)
+		goto error;
+	flush_dcache_page(page_address(page)); /* Is this correct? */
+	set_bit(PG_uptodate, &page->flags);
+	nfs_unlock_page(page);
+	return 0;
+error:
+	set_bit(PG_error, &page->flags);
+	nfs_unlock_page(page);
+	return -EIO;
+}
+
 static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
-	int error;
-	unsigned int len;
-	char *res;
-	void *mem;
-
-	dfprintk(VFS, "nfs: readlink(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
-					&mem, &res, &len, NFS_MAXPATHLEN);
-	if (! error) {
-		if (len > buflen)
-			len = buflen;
-		copy_to_user(buffer, res, len);
-		error = len;
-		kfree(mem);
-	}
-	return error;
+	struct inode *inode = dentry->d_inode;
+	struct page *page;
+	u32 *p, len;
+
+	/* Caller revalidated the directory inode already. */
+	page = read_cache_page(inode, 0,
+				(filler_t *)nfs_symlink_filler, dentry);
+	if (IS_ERR(page))
+		goto read_failed;
+
+	p = (u32 *) page_address(page);
+	len = *p++;
+	if (len > buflen)
+		len = buflen;
+	copy_to_user(buffer, p, len);
+	page_cache_release(page);
+	return len;
+read_failed:
+	return PTR_ERR(page);
 }
 
 static struct dentry *
-nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
+nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
 {
-	int error;
-	unsigned int len;
-	char *res;
-	void *mem;
-	char *path;
 	struct dentry *result;
-
-	dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
-				 &mem, &res, &len, NFS_MAXPATHLEN);
-	result = ERR_PTR(error);
-	if (error)
-		goto out_dput;
-
-	result = ERR_PTR(-ENOMEM);
-	path = kmalloc(len + 1, GFP_KERNEL);
-	if (!path)
-		goto out_mem;
-	memcpy(path, res, len);
-	path[len] = 0;
-	kfree(mem);
-
-	result = lookup_dentry(path, base, follow);
-	kfree(path);
-out:
+	struct inode *inode = dentry->d_inode;
+	struct page *page;
+	u32 *p;
+
+	/* Caller revalidated the directory inode already. */
+	page = read_cache_page(inode, 0,
+				(filler_t *)nfs_symlink_filler, dentry);
+	if (IS_ERR(page))
+		goto read_failed;
+
+	p = (u32 *) page_address(page);
+	result = lookup_dentry((char *) (p + 1), base, follow);
+	page_cache_release(page);
 	return result;
-
-out_mem:
-	kfree(mem);
-out_dput:
-	dput(base);
-	goto out;
+read_failed:
+	return (struct dentry *)page;
 }
diff -urN linux-2.2.18-pre8/fs/nfs/unlink.c linux/fs/nfs/unlink.c
--- linux-2.2.18-pre8/fs/nfs/unlink.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/unlink.c	Mon Dec  3 20:25:21 2001
@@ -0,0 +1,218 @@
+/*
+ *  linux/fs/nfs/unlink.c
+ *
+ * nfs sillydelete handling
+ *
+ * NOTE: we rely on holding the BKL for list manipulation protection.
+ *
+ * Copyright (C) 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/dcache.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+
+
+struct nfs_unlinkdata {
+	struct nfs_unlinkdata	*next;
+	struct dentry	*dir, *dentry;
+	struct qstr	name;
+	struct rpc_task	task;
+	struct rpc_cred	*cred;
+	unsigned int	count;
+};
+
+static struct nfs_unlinkdata	*nfs_deletes = NULL;
+static struct rpc_wait_queue	nfs_delete_queue = RPC_INIT_WAITQ("nfs_delete_queue");
+
+/**
+ * nfs_detach_unlinkdata - Remove asynchronous unlink from global list
+ * @data: pointer to descriptor
+ */
+static inline void
+nfs_detach_unlinkdata(struct nfs_unlinkdata *data)
+{
+	struct nfs_unlinkdata	**q;
+
+	for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) {
+		if (*q == data) {
+			*q = data->next;
+			break;
+		}
+	}
+}
+
+/**
+ * nfs_put_unlinkdata - release data from a sillydelete operation.
+ * @data: pointer to unlink structure.
+ */
+static void
+nfs_put_unlinkdata(struct nfs_unlinkdata *data)
+{
+	if (--data->count == 0) {
+		nfs_detach_unlinkdata(data);
+		if (data->name.name != NULL)
+			kfree(data->name.name);
+		kfree(data);
+	}
+}
+
+#define NAME_ALLOC_LEN(len)	((len+16) & ~15)
+/**
+ * nfs_copy_dname - copy dentry name to data structure
+ * @dentry: pointer to dentry
+ * @data: nfs_unlinkdata
+ */
+static inline void
+nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
+{
+	char		*str;
+	int		len = dentry->d_name.len;
+
+	str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
+	if (!str)
+		return;
+	memcpy(str, dentry->d_name.name, len);
+	if (!data->name.len) {
+		data->name.len = len;
+		data->name.name = str;
+	} else
+		kfree(str);
+}
+
+/**
+ * nfs_async_unlink_init - Initialize the RPC info
+ * @task: rpc_task of the sillydelete
+ *
+ * We delay initializing RPC info until after the call to dentry_iput()
+ * in order to minimize races against rename().
+ */
+static void
+nfs_async_unlink_init(struct rpc_task *task)
+{
+	struct nfs_unlinkdata	*data = (struct nfs_unlinkdata *)task->tk_calldata;
+	struct dentry		*dir = data->dir;
+	struct rpc_message	msg;
+	int			status = -ENOENT;
+
+	if (!data->name.len)
+		goto out_err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.rpc_cred = data->cred;
+	status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name);
+	if (status < 0)
+		goto out_err;
+	rpc_call_setup(task, &msg, 0);
+	return;
+ out_err:
+	rpc_exit(task, status);
+}
+
+/**
+ * nfs_async_unlink_done - Sillydelete post-processing
+ * @task: rpc_task of the sillydelete
+ *
+ * Do the directory attribute update.
+ */
+static void
+nfs_async_unlink_done(struct rpc_task *task)
+{
+	struct nfs_unlinkdata	*data = (struct nfs_unlinkdata *)task->tk_calldata;
+	struct dentry		*dir = data->dir;
+	struct inode		*dir_i;
+
+	if (!dir)
+		return;
+	dir_i = dir->d_inode;
+	nfs_zap_caches(dir_i);
+	NFS_PROTO(dir_i)->unlink_done(dir, &task->tk_msg);
+	rpcauth_releasecred(task->tk_auth, data->cred);
+	data->cred = NULL;
+	dput(dir);
+}
+
+/**
+ * nfs_async_unlink_release - Release the sillydelete data.
+ * @task: rpc_task of the sillydelete
+ *
+ * We need to call nfs_put_unlinkdata as a 'tk_release' task since the
+ * rpc_task would be freed too.
+ */
+static void
+nfs_async_unlink_release(struct rpc_task *task)
+{
+	struct nfs_unlinkdata	*data = (struct nfs_unlinkdata *)task->tk_calldata;
+	nfs_put_unlinkdata(data);
+}
+
+/**
+ * nfs_async_unlink - asynchronous unlinking of a file
+ * @dir: directory in which the file resides.
+ * @name: name of the file to unlink.
+ */
+int
+nfs_async_unlink(struct dentry *dentry)
+{
+	struct dentry	*dir = dentry->d_parent;
+	struct nfs_unlinkdata	*data;
+	struct rpc_task	*task;
+	struct rpc_clnt	*clnt = NFS_CLIENT(dir->d_inode);
+	int		status = -ENOMEM;
+
+	data = kmalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		goto out;
+	memset(data, 0, sizeof(*data));
+
+	data->dir = dget(dir);
+	data->dentry = dentry;
+
+	data->next = nfs_deletes;
+	nfs_deletes = data;
+	data->count = 1;
+
+	task = &data->task;
+	rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC);
+	task->tk_calldata = data;
+	task->tk_action = nfs_async_unlink_init;
+	task->tk_release = nfs_async_unlink_release;
+
+	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+	data->cred = rpcauth_lookupcred(clnt->cl_auth, 0);
+
+	rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL);
+	status = 0;
+ out:
+	return status;
+}
+
+/**
+ * nfs_complete_remove - Initialize completion of the sillydelete
+ * @dentry: dentry to delete
+ *
+ * Since we're most likely to be called by dentry_iput(), we
+ * only use the dentry to find the sillydelete. We then copy the name
+ * into the qstr.
+ */
+void
+nfs_complete_unlink(struct dentry *dentry)
+{
+	struct nfs_unlinkdata	*data;
+
+	for(data = nfs_deletes; data != NULL; data = data->next) {
+		if (dentry == data->dentry)
+			break;
+	}
+	if (!data)
+		return;
+	data->count++;
+	nfs_copy_dname(dentry, data);
+	dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
+	if (data->task.tk_rpcwait == &nfs_delete_queue)
+		rpc_wake_up_task(&data->task);
+	nfs_put_unlinkdata(data);
+}
diff -urN linux-2.2.18-pre8/fs/nfs/write.c linux/fs/nfs/write.c
--- linux-2.2.18-pre8/fs/nfs/write.c	Wed Mar  3 11:17:02 1999
+++ linux/fs/nfs/write.c	Mon Dec  3 20:25:21 2001
@@ -10,26 +10,19 @@
  * RPC call to write the page is scheduled immediately; otherwise, the call
  * is delayed for a few seconds.
  *
- * Just like readahead, no async I/O is performed if wsize < PAGE_SIZE.
+ * Just like readahead, no async I/O is performed if wsize < PAGE_CACHE_SIZE.
  *
- * Write requests are kept on the inode's writeback list. Each entry in
+ * Write requests are kept on the inode's dirty list. Each entry in
  * that list references the page (portion) to be written. When the
- * cache timeout has expired, the RPC task is woken up, and tries to
- * lock the page. As soon as it manages to do so, the request is moved
- * from the writeback list to the writelock list.
+ * cache timeout has expired, the flushd task is woken up, and tries to
+ * lock the request. As soon as it manages to do so, the request is removed
+ * from the dirty list. When the RPC write call completes, the request
+ * is either released or put on the inode's commit list.
  *
  * Note: we must make sure never to confuse the inode passed in the
  * write_page request with the one in page->inode. As far as I understand
  * it, these are different when doing a swap-out.
  *
- * To understand everything that goes on here and in the NFS read code,
- * one should be aware that a page is locked in exactly one of the following
- * cases:
- *
- *  -	A write request is in progress.
- *  -	A user process is in generic_file_write/nfs_update_page
- *  -	A user process is in generic_file_read
- *
  * Also note that because of the way pages are invalidated in
  * nfs_revalidate_inode, the following assertions hold:
  *
@@ -38,75 +31,147 @@
  *  -	If the page is not uptodate, there will be no pending write
  *	requests, and no process will be in nfs_update_page.
  *
- * FIXME: Interaction with the vmscan routines is not optimal yet.
- * Either vmscan must be made nfs-savvy, or we need a different page
- * reclaim concept that supports something like FS-independent
- * buffer_heads with a b_ops-> field.
- *
  * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Rewritten 6/3/2000 by Trond Myklebust.
+ * Copyright (C) 1999, 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/malloc.h>
 #include <linux/swap.h>
+#include <asm/pgtable.h>
 #include <linux/pagemap.h>
-#include <linux/file.h>
 
+#include <linux/sunrpc/auth.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs_flushd.h>
+#include <linux/nfs_page.h>
 #include <asm/uaccess.h>
+#include <linux/file.h>
+
 
 #define NFS_PARANOIA 1
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
 
-static void			nfs_wback_begin(struct rpc_task *task);
-static void			nfs_wback_result(struct rpc_task *task);
-static void			nfs_cancel_request(struct nfs_wreq *req);
+static unsigned int	nfs_nr_requests = 0;
 
 /*
- * Cache parameters
+ * Local structures
+ * This is the struct where the WRITE/COMMIT arguments go.
  */
-#define NFS_WRITEBACK_DELAY	(10 * HZ)
-#define NFS_WRITEBACK_MAX	64
+struct nfs_write_data {
+	struct rpc_task		task;
+	struct dentry		*dentry;
+	struct rpc_cred		*cred;
+	struct nfs_writeargs	args;		/* argument struct */
+	struct nfs_writeres	res;		/* result struct */
+	struct nfs_fattr	fattr;
+	struct nfs_writeverf	verf;
+	struct list_head	pages;		/* Coalesced requests we wish to flush */
+};
 
 /*
- * Limit number of delayed writes
+ * Local function declarations
  */
-static int			nr_write_requests = 0;
-static struct rpc_wait_queue	write_queue = RPC_INIT_WAITQ("write_chain");
+static void	nfs_writeback_done(struct rpc_task *);
+#ifdef CONFIG_NFS_V3
+static void	nfs_commit_done(struct rpc_task *);
+#endif
 
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)	(0)
 #endif
 
+static __inline__ struct nfs_page *nfs_page_alloc(void)
+{
+	struct nfs_page *p;
+	p = (struct nfs_page *) kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->wb_hash);
+		INIT_LIST_HEAD(&p->wb_list);
+	}
+	return p;
+}
+
+static __inline__ void nfs_page_free(struct nfs_page *p)
+{
+	kfree(p);
+}
+
+static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
+{
+	struct nfs_write_data   *p;
+	p = (struct nfs_write_data *) kmalloc(sizeof(*p), GFP_NFS);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->pages);
+	}
+	return p;
+}
+
+static __inline__ void nfs_writedata_free(struct nfs_write_data *p)
+{
+	kfree(p);
+}
+
+static void nfs_writedata_release(struct rpc_task *task)
+{
+	nfs_writedata_free(task->tk_calldata);
+}
+
+static __inline__ int
+nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
+{
+	if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) {
+		fattr->pre_size  = NFS_CACHE_ISIZE(inode);
+		fattr->pre_mtime = NFS_CACHE_MTIME(inode);
+		fattr->pre_ctime = NFS_CACHE_CTIME(inode);
+		fattr->valid |= NFS_ATTR_WCC;
+	}
+	return nfs_refresh_inode(inode, fattr);
+}
+
 /*
  * Write a page synchronously.
  * Offset is the data offset within the page.
  */
 static int
-nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
-		struct page *page, unsigned long offset, unsigned int count)
+nfs_writepage_sync(struct file *file, struct page *page,
+		   unsigned long offset, unsigned int count)
 {
+	struct dentry	*dentry = file->f_dentry;
+	struct inode	*inode = dentry->d_inode;
+	struct rpc_cred	*cred = nfs_file_cred(file);
 	unsigned int	wsize = NFS_SERVER(inode)->wsize;
-	int		result, refresh = 0, written = 0;
+	int		result, refresh = 0, written = 0, flags;
 	u8		*buffer;
 	struct nfs_fattr fattr;
+	struct nfs_writeverf verifier;
 
 	dprintk("NFS:      nfs_writepage_sync(%s/%s %d@%ld)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		count, page->offset + offset);
+		count, nfs_page_offset(page) + offset);
 
 	buffer = (u8 *) page_address(page) + offset;
-	offset += page->offset;
+	offset += nfs_page_offset(page);
+
+	flags = ((IS_SWAPFILE(inode)) ? NFS_RW_SWAP : 0) | NFS_RW_SYNC;
 
 	do {
 		if (count < wsize && !IS_SWAPFILE(inode))
 			wsize = count;
 
-		result = nfs_proc_write(NFS_DSERVER(dentry), NFS_FH(dentry),
-					IS_SWAPFILE(inode), offset, wsize,
-					buffer, &fattr);
+		result = NFS_PROTO(inode)->write(dentry, &fattr, cred, flags,
+						 offset, wsize, buffer,
+						 &verifier);
+		nfs_write_attributes(inode, &fattr);
 
 		if (result < 0) {
 			/* Must mark the page invalid after I/O error */
@@ -114,7 +179,7 @@
 			goto io_error;
 		}
 		if (result != wsize)
-			printk("NFS: short write, wsize=%u, result=%d\n",
+			printk(KERN_ERR "NFS: short write, size=%u, result=%d\n",
 			wsize, result);
 		refresh = 1;
 		buffer  += wsize;
@@ -130,274 +195,677 @@
 	} while (count);
 
 io_error:
-	/* Note: we don't refresh if the call failed (fattr invalid) */
-	if (refresh && result >= 0) {
-		/* See comments in nfs_wback_result */
-		/* N.B. I don't think this is right -- sync writes in order */
-		if (fattr.size < inode->i_size)
-			fattr.size = inode->i_size;
-		if (fattr.mtime.seconds < inode->i_mtime)
-			printk("nfs_writepage_sync: prior time??\n");
-		/* Solaris 2.5 server seems to send garbled
-		 * fattrs occasionally */
-		if (inode->i_ino == fattr.fileid) {
-			/*
-			 * We expect the mtime value to change, and
-			 * don't want to invalidate the caches.
-			 */
-			inode->i_mtime = fattr.mtime.seconds;
-			nfs_refresh_inode(inode, &fattr);
-		} 
-		else
-			printk("nfs_writepage_sync: inode %ld, got %u?\n",
-				inode->i_ino, fattr.fileid);
-	}
-
 	return written? written : result;
 }
 
 /*
- * Append a writeback request to a list
+ * Write a page to the server. This will be used for NFS swapping only
+ * (for now), and we currently do this synchronously only.
  */
-static inline void
-append_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+int
+nfs_writepage(struct file * file, struct page *page)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	unsigned offset = PAGE_CACHE_SIZE;
+
+	if (page->offset >= inode->i_size)
+		return -EIO;
+	if (page->offset + offset > inode->i_size)
+		offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+	return nfs_writepage_sync(file, page, 0, offset);
+}
+
+/*
+ * Check whether the file range we want to write to is locked by
+ * us.
+ */
+static int
+region_locked(struct inode *inode, struct nfs_page *req)
 {
-	dprintk("NFS:      append_write_request(%p, %p)\n", q, wreq);
-	rpc_append_list(q, wreq);
+	struct file_lock	*fl;
+	unsigned long		rqstart, rqend;
+
+	/* Don't optimize writes if we don't use NLM */
+	if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
+		return 0;
+
+	rqstart = nfs_page_offset(req->wb_page) + req->wb_offset;
+	rqend = rqstart + req->wb_bytes;
+	for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+		if (fl->fl_owner == current->files && (fl->fl_flags & FL_POSIX)
+		    && fl->fl_type == F_WRLCK
+		    && fl->fl_start <= rqstart && rqend <= fl->fl_end) {
+			return 1;
+		}
+	}
+
+	return 0;
 }
 
 /*
- * Remove a writeback request from a list
+ * Insert a write request into an inode
  */
 static inline void
-remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 {
-	dprintk("NFS:      remove_write_request(%p, %p)\n", q, wreq);
-	rpc_remove_list(q, wreq);
+	if (!list_empty(&req->wb_hash))
+		return;
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted hashed!\n");
+	inode->u.nfs_i.npages++;
+	list_add(&req->wb_hash, &inode->u.nfs_i.writeback);
+	req->wb_count++;
 }
 
 /*
- * Find a non-busy write request for a given page to
- * try to combine with.
+ * Insert a write request into an inode
  */
-static inline struct nfs_wreq *
-find_write_request(struct inode *inode, struct page *page)
+static inline void
+nfs_inode_remove_request(struct nfs_page *req)
 {
-	pid_t pid = current->pid;
-	struct nfs_wreq	*head, *req;
+	struct inode *inode;
+	if (list_empty(&req->wb_hash))
+		return;
+
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted unhashed!\n");
+	inode = req->wb_dentry->d_inode;
+	list_del(&req->wb_hash);
+	INIT_LIST_HEAD(&req->wb_hash);
+	inode->u.nfs_i.npages--;
+	if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n");
+	if (!nfs_have_writebacks(inode) && !nfs_have_read(inode))
+		inode_remove_flushd(inode);
+	nfs_release_request(req);
+}
 
-	dprintk("NFS:      find_write_request(%x/%ld, %p)\n",
-				inode->i_dev, inode->i_ino, page);
-	if (!(req = head = NFS_WRITEBACK(inode)))
-		return NULL;
-	do {
-		/*
-		 * We can't combine with canceled requests or
-		 * requests that have already been started..
-		 */
-		if (req->wb_flags & (NFS_WRITE_CANCELLED | NFS_WRITE_INPROGRESS))
+/*
+ * Find a request
+ */
+static inline struct nfs_page *
+_nfs_find_request(struct inode *inode, struct page *page)
+{
+	struct list_head	*head, *next;
+
+	head = &inode->u.nfs_i.writeback;
+	next = head->next;
+	while (next != head) {
+		struct nfs_page *req = nfs_inode_wb_entry(next);
+		next = next->next;
+		if (page_index(req->wb_page) != page_index(page))
 			continue;
+		req->wb_count++;
+		return req;
+	}
+	return NULL;
+}
 
-		if (req->wb_page == page && req->wb_pid == pid)
-			return req;
+static struct nfs_page *
+nfs_find_request(struct inode *inode, struct page *page)
+{
+	struct nfs_page		*req;
 
-		/*
-		 * Ehh, don't keep too many tasks queued..
-		 */
-		rpc_wake_up_task(&req->wb_task);
+	req = _nfs_find_request(inode, page);
+	return req;
+}
 
-	} while ((req = WB_NEXT(req)) != head);
-	return NULL;
+/*
+ * Insert a write request into a sorted list
+ */
+void nfs_list_add_request(struct nfs_page *req, struct list_head *head)
+{
+	struct list_head *prev;
+
+	if (!list_empty(&req->wb_list)) {
+		printk(KERN_ERR "NFS: Add to list failed!\n");
+		return;
+	}
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted added to list!\n");
+	prev = head->prev;
+	while (prev != head) {
+		struct nfs_page	*p = nfs_list_entry(prev);
+		if (page_index(p->wb_page) < page_index(req->wb_page))
+			break;
+		prev = prev->prev;
+	}
+	list_add(&req->wb_list, prev);
+	req->wb_list_head = head;
 }
 
 /*
- * Find and release all failed requests for this inode.
+ * Insert a write request into an inode
  */
-int
-nfs_check_failed_request(struct inode * inode)
+void nfs_list_remove_request(struct nfs_page *req)
 {
-	/* FIXME! */
-	return 0;
+	if (list_empty(&req->wb_list))
+		return;
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted removed from list!\n");
+	list_del(&req->wb_list);
+	INIT_LIST_HEAD(&req->wb_list);
+	req->wb_list_head = NULL;
 }
 
 /*
- * Try to merge adjacent write requests. This works only for requests
- * issued by the same user.
+ * Add a request to the inode's dirty list.
+ */
+static inline void
+nfs_mark_request_dirty(struct nfs_page *req)
+{
+	struct inode *inode = req->wb_dentry->d_inode;
+
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.dirty);
+		inode->u.nfs_i.ndirty++;
+	}
+	inode_schedule_scan(inode, req->wb_timeout);
+}
+
+/*
+ * Check if a request is dirty
  */
 static inline int
-update_write_request(struct nfs_wreq *req, unsigned int first,
-			unsigned int bytes)
+nfs_dirty_request(struct nfs_page *req)
 {
-	unsigned int	rqfirst = req->wb_offset,
-			rqlast = rqfirst + req->wb_bytes,
-			last = first + bytes;
+	struct inode *inode = req->wb_dentry->d_inode;
+	return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty;
+}
 
-	dprintk("nfs:      trying to update write request %p\n", req);
+#ifdef CONFIG_NFS_V3
+/*
+ * Add a request to the inode's commit list.
+ */
+static inline void
+nfs_mark_request_commit(struct nfs_page *req)
+{
+	struct inode *inode = req->wb_dentry->d_inode;
 
-	/* not contiguous? */
-	if (rqlast < first || last < rqfirst)
-		return 0;
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.commit);
+		inode->u.nfs_i.ncommit++;
+	}
+	inode_schedule_scan(inode, req->wb_timeout);
+}
+#endif
 
-	if (first < rqfirst)
-		rqfirst = first;
-	if (rqlast < last)
-		rqlast = last;
+/*
+ * Create a write request.
+ * Page must be locked by the caller. This makes sure we never create
+ * two different requests for the same page, and avoids possible deadlock
+ * when we reach the hard limit on the number of dirty pages.
+ */
+struct nfs_page *nfs_create_request(struct file *file, struct page *page,
+				    unsigned int offset, unsigned int count)
+{
+	struct dentry		*dentry = file->f_dentry;
+	struct inode		*inode = dentry->d_inode;
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct nfs_page		*req = NULL;
+	long			timeout;
 
-	req->wb_offset = rqfirst;
-	req->wb_bytes  = rqlast - rqfirst;
-	req->wb_count++;
+	/* Deal with hard/soft limits.
+	 */
+	do {
+		/* If we're still over the global soft limit, wake up all requests */
+		if (nfs_nr_requests >= MAX_REQUEST_SOFT) {
+			dprintk("NFS:      hit soft limit (%d requests)\n",
+				nfs_nr_requests);
+			if (!cache->task)
+				nfs_reqlist_init(NFS_SERVER(inode));
+			nfs_wake_flushd();
+		}
+
+		/* If we haven't reached the hard limit yet,
+		 * try to allocate the request struct */
+		if (cache->nr_requests < MAX_REQUEST_HARD) {
+			req = nfs_page_alloc();
+			if (req != NULL)
+				break;
+		}
+
+		/* We're over the hard limit. Wait for better times */
+		dprintk("NFS:      create_request sleeping (total %d pid %d)\n",
+			nfs_nr_requests, current->pid);
+
+		timeout = 1 * HZ;
+		if (NFS_SERVER(inode)->flags & NFS_MOUNT_INTR) {
+			interruptible_sleep_on_timeout(&cache->request_wait,
+						       timeout);
+			if (signalled())
+				break;
+		} else
+			sleep_on_timeout(&cache->request_wait, timeout);
+
+		dprintk("NFS:      create_request waking up (tot %d pid %d)\n",
+			nfs_nr_requests, current->pid);
+	} while (!req);
+	if (!req)
+		return NULL;
 
-	return 1;
+	/* Initialize the request struct. Initially, we assume a
+	 * long write-back delay. This will be adjusted in
+	 * update_nfs_request below if the region is not locked. */
+	req->wb_page    = page;
+	atomic_inc(&page->count);
+	req->wb_offset  = offset;
+	req->wb_bytes   = count;
+	req->wb_file    = file;
+	file->f_count++;
+	req->wb_dentry	= dentry;
+	req->wb_cred	= nfs_file_cred(file);
+	req->wb_count   = 1;
+
+	/* register request's existence */
+	cache->nr_requests++;
+	nfs_nr_requests++;
+	return req;
 }
 
-static inline void
-free_write_request(struct nfs_wreq * req)
+
+/*
+ * Release all resources associated with a write request after it
+ * has been committed to stable storage
+ */
+void
+nfs_release_request(struct nfs_page *req)
 {
-	if (!--req->wb_count)
-		kfree(req);
+	struct inode		*inode = req->wb_dentry->d_inode;
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct page		*page = req->wb_page;
+
+	if (--req->wb_count)
+		return;
+
+	if (!list_empty(&req->wb_list)) {
+		printk(KERN_ERR "NFS: Request released while still on a list!\n");
+		nfs_list_remove_request(req);
+	}
+	if (!list_empty(&req->wb_hash)) {
+		printk(KERN_ERR "NFS: Request released while still hashed!\n");
+		nfs_inode_remove_request(req);
+	}
+	if (NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: Request released while still locked!\n");
+
+	fput(req->wb_file);
+	page_cache_release(page);
+	nfs_page_free(req);
+	/* wake up anyone waiting to allocate a request */
+	cache->nr_requests--;
+	nfs_nr_requests--;
+	wake_up(&cache->request_wait);
 }
 
 /*
- * Create and initialize a writeback request
+ * Wait for a request to complete.
+ *
+ * Interruptible by signals only if mounted with intr flag.
  */
-static inline struct nfs_wreq *
-create_write_request(struct file * file, struct page *page, unsigned int offset, unsigned int bytes)
+static int
+nfs_wait_on_request(struct nfs_page *req)
 {
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
-	struct rpc_clnt	*clnt = NFS_CLIENT(inode);
-	struct nfs_wreq *wreq;
-	struct rpc_task	*task;
+	struct inode	*inode = req->wb_dentry->d_inode;
+        struct rpc_clnt	*clnt = NFS_CLIENT(inode);
+        int retval;
 
-	dprintk("NFS:      create_write_request(%s/%s, %ld+%d)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name,
-		page->offset + offset, bytes);
+	if (!NFS_WBACK_BUSY(req))
+		return 0;
+	req->wb_count++;
+	retval = nfs_wait_event(clnt, req->wb_wait, !NFS_WBACK_BUSY(req));
+	nfs_release_request(req);
+        return retval;
+}
 
-	/* FIXME: Enforce hard limit on number of concurrent writes? */
-	wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL);
-	if (!wreq)
-		goto out_fail;
-	memset(wreq, 0, sizeof(*wreq));
-
-	task = &wreq->wb_task;
-	rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE);
-	task->tk_calldata = wreq;
-	task->tk_action = nfs_wback_begin;
-
-	rpcauth_lookupcred(task);	/* Obtain user creds */
-	if (task->tk_status < 0)
-		goto out_req;
-
-	/* Put the task on inode's writeback request list. */
-	wreq->wb_file = file;
-	wreq->wb_pid    = current->pid;
-	wreq->wb_page   = page;
-	wreq->wb_offset = offset;
-	wreq->wb_bytes  = bytes;
-	wreq->wb_count	= 2;		/* One for the IO, one for us */
-
-	append_write_request(&NFS_WRITEBACK(inode), wreq);
-
-	if (nr_write_requests++ > NFS_WRITEBACK_MAX*3/4)
-		rpc_wake_up_next(&write_queue);
-
-	return wreq;
-
-out_req:
-	rpc_release_task(task);
-	kfree(wreq);
-out_fail:
-	return NULL;
+/*
+ * Wait for a request to complete.
+ *
+ * Interruptible by signals only if mounted with intr flag.
+ */
+static int
+nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages)
+{
+	struct list_head	*p, *head;
+	unsigned long		idx_end;
+	unsigned int		res = 0;
+	int			error;
+
+	if (npages == 0)
+		idx_end = ~0;
+	else
+		idx_end = idx_start + npages - 1;
+
+	head = &inode->u.nfs_i.writeback;
+	p = head->next;
+	while (p != head) {
+		unsigned long pg_idx;
+		struct nfs_page *req = nfs_inode_wb_entry(p);
+
+		p = p->next;
+
+		if (file && req->wb_file != file)
+			continue;
+
+		pg_idx = page_index(req->wb_page);
+		if (pg_idx < idx_start || pg_idx > idx_end)
+			continue;
+
+		if (!NFS_WBACK_BUSY(req))
+			continue;
+		req->wb_count++;
+		error = nfs_wait_on_request(req);
+		nfs_release_request(req);
+		if (error < 0)
+			return error;
+		p = head->next;
+		res++;
+	}
+	return res;
 }
 
 /*
- * Schedule a writeback RPC call.
- * If the server is congested, don't add to our backlog of queued
- * requests but call it synchronously.
- * The function returns whether we should wait for the thing or not.
- *
- * FIXME: Here we could walk the inode's lock list to see whether the
- * page we're currently writing to has been write-locked by the caller.
- * If it is, we could schedule an async write request with a long
- * delay in order to avoid writing back the page until the lock is
- * released.
+ * Scan cluster for dirty pages and send as many of them to the
+ * server as possible.
  */
-static inline int
-schedule_write_request(struct nfs_wreq *req, int sync)
+int nfs_scan_list_timeout(struct list_head *head, struct list_head *dst,
+			  struct inode *inode)
 {
-	struct rpc_task	*task = &req->wb_task;
-	struct file	*file = req->wb_file;
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
+	struct list_head	*p;
+        struct nfs_page		*req;
+        int			pages = 0;
 
-	if (NFS_CONGESTED(inode) || nr_write_requests >= NFS_WRITEBACK_MAX)
-		sync = 1;
+	p = head->next;
+        while (p != head) {
+		req = nfs_list_entry(p);
+		p = p->next;
+		if (time_after(req->wb_timeout, jiffies)) {
+			if (time_after(NFS_NEXTSCAN(inode), req->wb_timeout))
+				NFS_NEXTSCAN(inode) = req->wb_timeout;
+			continue;
+		}
+		if (!nfs_lock_request(req))
+			continue;
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		pages++;
+	}
+	return pages;
+}
 
-	if (sync) {
-		sigset_t	oldmask;
-		struct rpc_clnt *clnt = NFS_CLIENT(inode);
-		dprintk("NFS: %4d schedule_write_request (sync)\n",
-					task->tk_pid);
-		/* Page is already locked */
-		rpc_clnt_sigmask(clnt, &oldmask);
-		rpc_execute(task);
-		rpc_clnt_sigunmask(clnt, &oldmask);
-	} else {
-		dprintk("NFS: %4d schedule_write_request (async)\n",
-					task->tk_pid);
-		task->tk_flags |= RPC_TASK_ASYNC;
-		task->tk_timeout = NFS_WRITEBACK_DELAY;
-		rpc_sleep_on(&write_queue, task, NULL, NULL);
+static int
+nfs_scan_dirty_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.dirty, dst, inode);
+	inode->u.nfs_i.ndirty -= pages;
+	if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+	return pages;
+}
+
+#ifdef CONFIG_NFS_V3
+static int
+nfs_scan_commit_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.commit, dst, inode);
+	inode->u.nfs_i.ncommit -= pages;
+	if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+	return pages;
+}
+#endif
+
+int nfs_scan_list(struct list_head *src, struct list_head *dst, 
+		  struct file *file, unsigned long idx_start, 
+		  unsigned int npages)
+{
+	struct list_head	*p;
+	struct nfs_page		*req;
+	unsigned long		idx_end;
+	int			res;
+
+	res = 0;
+	if (npages == 0)
+		idx_end = ~0;
+	else
+		idx_end = idx_start + npages - 1;
+	p = src->next;
+	while (p != src) {
+		unsigned long pg_idx;
+
+		req = nfs_list_entry(p);
+		p = p->next;
+
+		if (file && req->wb_file != file)
+			continue;
+
+		pg_idx = page_index(req->wb_page);
+		if (pg_idx < idx_start || pg_idx > idx_end)
+			continue;
+
+		if (!nfs_lock_request(req))
+			continue;
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		res++;
 	}
+	return res;
+}
 
-	return sync;
+static int
+nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
+{
+	int	res;
+	res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages);
+	inode->u.nfs_i.ndirty -= res;
+	if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+	return res;
+}
+
+#ifdef CONFIG_NFS_V3
+static int
+nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
+{
+	int	res;
+	res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages);
+	inode->u.nfs_i.ncommit -= res;
+	if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+	return res;
+}
+#endif
+
+int nfs_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int maxpages)
+{
+	struct nfs_page		*req = NULL;
+	unsigned int		pages = 0;
+
+	while (!list_empty(src)) {
+		struct nfs_page	*prev = req;
+
+		req = nfs_list_entry(src->next);
+		if (prev) {
+			if (req->wb_file != prev->wb_file)
+				break;
+
+			if (page_index(req->wb_page) != page_index(prev->wb_page)+1)
+				break;
+
+			if (req->wb_offset != 0)
+				break;
+		}
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		pages++;
+		if (req->wb_offset + req->wb_bytes != PAGE_CACHE_SIZE)
+			break;
+		if (pages >= maxpages)
+			break;
+	}
+	return pages;
 }
 
 /*
- * Wait for request to complete.
+ * Try to update any existing write request, or create one if there is none.
+ * In order to match, the request's credentials must match those of
+ * the calling process.
+ *
+ * Note: Should always be called with the Page Lock held!
  */
-static int
-wait_on_write_request(struct nfs_wreq *req)
+static struct nfs_page *
+nfs_update_request(struct file* file, struct page *page,
+		   unsigned int offset, unsigned int bytes)
 {
-	struct file		*file = req->wb_file;
-	struct dentry		*dentry = file->f_dentry;
-	struct inode		*inode = dentry->d_inode;
-	struct rpc_clnt		*clnt = NFS_CLIENT(inode);
-	struct wait_queue	wait = { current, NULL };
-	sigset_t		oldmask;
-	int retval;
-
-	/* Make sure it's started.. */
-	if (!WB_INPROGRESS(req))
-		rpc_wake_up_task(&req->wb_task);
+	struct inode		*inode = file->f_dentry->d_inode;
+	struct nfs_page		*req, *new = NULL;
+	unsigned long		rqend, end;
+
+	end = offset + bytes;
 
-	rpc_clnt_sigmask(clnt, &oldmask);
-	add_wait_queue(&req->wb_wait, &wait);
 	for (;;) {
-		current->state = TASK_INTERRUPTIBLE;
-		retval = 0;
-		if (req->wb_flags & NFS_WRITE_COMPLETE)
+		/* Loop over all inode entries and see if we find
+		 * A request for the page we wish to update
+		 */
+		req = _nfs_find_request(inode, page);
+		if (req) {
+			if (!nfs_lock_request(req)) {
+				nfs_wait_on_request(req);
+				nfs_release_request(req);
+				continue;
+			}
+			if (new)
+				nfs_release_request(new);
 			break;
-		retval = -ERESTARTSYS;
-		if (signalled())
+		}
+
+		req = new;
+		if (req) {
+			nfs_lock_request(req);
+			nfs_inode_add_request(inode, req);
+			nfs_mark_request_dirty(req);
 			break;
-		schedule();
+		}
+
+		/*
+		 * If we're over the soft limit, flush out old requests
+		 */
+		if (inode->u.nfs_i.npages >= MAX_REQUEST_SOFT)
+			nfs_wb_file(inode, file);
+		new = nfs_create_request(file, page, offset, bytes);
+		if (!new)
+			return ERR_PTR(-ENOMEM);
+		/* If the region is locked, adjust the timeout */
+		if (region_locked(inode, new))
+			new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY;
+		else
+			new->wb_timeout = jiffies + NFS_WRITEBACK_DELAY;
+	}
+
+	/* We have a request for our page.
+	 * If the creds don't match, or the
+	 * page addresses don't match,
+	 * tell the caller to wait on the conflicting
+	 * request.
+	 */
+	rqend = req->wb_offset + req->wb_bytes;
+	if (req->wb_file != file
+	    || req->wb_page != page
+	    || !nfs_dirty_request(req)
+	    || offset > rqend || end < req->wb_offset) {
+		nfs_unlock_request(req);
+		nfs_release_request(req);
+		return ERR_PTR(-EBUSY);
+	}
+
+	/* Okay, the request matches. Update the region */
+	if (offset < req->wb_offset) {
+		req->wb_offset = offset;
+		req->wb_bytes = rqend - req->wb_offset;
 	}
-	remove_wait_queue(&req->wb_wait, &wait);
-	current->state = TASK_RUNNING;
-	rpc_clnt_sigunmask(clnt, &oldmask);
-	return retval;
+
+	if (end > rqend)
+		req->wb_bytes = end - req->wb_offset;
+
+	nfs_unlock_request(req);
+
+	return req;
 }
 
 /*
- * Write a page to the server. This will be used for NFS swapping only
- * (for now), and we currently do this synchronously only.
+ * This is the strategy routine for NFS.
+ * It is called by nfs_updatepage whenever the user wrote up to the end
+ * of a page.
+ *
+ * We always try to submit a set of requests in parallel so that the
+ * server's write code can gather writes. This is mainly for the benefit
+ * of NFSv2.
+ *
+ * We never submit more requests than we think the remote can handle.
+ * For UDP sockets, we make sure we don't exceed the congestion window;
+ * for TCP, we limit the number of requests to 8.
+ *
+ * NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that
+ * should be sent out in one go. This is for the benefit of NFSv2 servers
+ * that perform write gathering.
+ *
+ * FIXME: Different servers may have different sweet spots.
+ * Record the average congestion window in server struct?
  */
+#define NFS_STRATEGY_PAGES      8
+static void
+nfs_strategy(struct inode *inode)
+{
+	unsigned int	dirty, wpages;
+
+	dirty  = inode->u.nfs_i.ndirty;
+	wpages = NFS_SERVER(inode)->wpages;
+#ifdef CONFIG_NFS_V3
+	if (NFS_PROTO(inode)->version == 2) {
+		if (dirty >= NFS_STRATEGY_PAGES * wpages)
+			nfs_flush_file(inode, NULL, 0, 0, 0);
+	} else {
+		if (dirty >= wpages)
+			nfs_flush_file(inode, NULL, 0, 0, 0);
+		if (inode->u.nfs_i.ncommit > NFS_STRATEGY_PAGES * wpages &&
+		    nfs_nr_requests > MAX_REQUEST_SOFT)
+			nfs_commit_file(inode, NULL, 0, 0, 0);
+	}
+#else
+	if (dirty >= NFS_STRATEGY_PAGES * wpages)
+		nfs_flush_file(inode, NULL, 0, 0, 0);
+#endif
+	/*
+	 * If we're running out of requests, flush out everything
+	 * in order to reduce memory useage...
+	 */
+	if (inode->u.nfs_i.npages > MAX_REQUEST_SOFT)
+		nfs_wb_all(inode);
+}
+
 int
-nfs_writepage(struct file * file, struct page *page)
+nfs_flush_incompatible(struct file *file, struct page *page)
 {
-	struct dentry *dentry = file->f_dentry;
-	return nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE);
+	struct inode	*inode = file->f_dentry->d_inode;
+	struct nfs_page	*req;
+	int		status = 0;
+	/*
+	 * Look for a request corresponding to this page. If there
+	 * is one, and it belongs to another file, we flush it out
+	 * before we try to copy anything into the page. Do this
+	 * due to the lack of an ACCESS-type call in NFSv2.
+	 * Also do the same if we find a request from an existing
+	 * dropped page.
+	 */
+	req = nfs_find_request(inode,page);
+	if (req) {
+		if (req->wb_file != file || req->wb_page != page)
+			status = nfs_wb_page(inode, page);
+		nfs_release_request(req);
+	}
+	return (status < 0) ? status : 0;
 }
 
 /*
@@ -411,270 +879,532 @@
 {
 	struct dentry	*dentry = file->f_dentry;
 	struct inode	*inode = dentry->d_inode;
-	struct nfs_wreq	*req;
-	int		synchronous = sync;
-	int		retval;
+	struct nfs_page	*req;
+	int		status = 0;
 
 	dprintk("NFS:      nfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		count, page->offset+offset, sync);
-
-	/*
-	 * Try to find a corresponding request on the writeback queue.
-	 * If there is one, we can be sure that this request is not
-	 * yet being processed, because we hold a lock on the page.
-	 *
-	 * If the request was created by us, update it. Otherwise,
-	 * transfer the page lock and flush out the dirty page now.
-	 * After returning, generic_file_write will wait on the
-	 * page and retry the update.
-	 */
-	req = find_write_request(inode, page);
-	if (req && req->wb_file == file && update_write_request(req, offset, count))
-		goto updated;
+		count, nfs_page_offset(page)+offset, sync);
 
 	/*
 	 * If wsize is smaller than page size, update and write
 	 * page synchronously.
 	 */
-	if (NFS_SERVER(inode)->wsize < PAGE_SIZE)
-		return nfs_writepage_sync(dentry, inode, page, offset, count);
+	if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE)
+		return nfs_writepage_sync(file, page, offset, count);
 
-	/* Create the write request. */
-	req = create_write_request(file, page, offset, count);
-	if (!req)
-		return -ENOBUFS;
-
-	/*
-	 * Ok, there's another user of this page with the new request..
-	 * The IO completion will then free the page and the dentry.
-	 */
-	atomic_inc(&page->count);
-	file->f_count++;
-
-	/* Schedule request */
-	synchronous = schedule_write_request(req, sync);
+        /*
+         * Try to find an NFS request corresponding to this page
+         * and update it.
+         * If the existing request cannot be updated, we must flush
+	 * it out now.
+         */
+	do {
+		req = nfs_update_request(file, page, offset, count);
+		if (IS_ERR(req))
+			status = PTR_ERR(req);
+		if (status != -EBUSY)
+			break;
+		/* Request could not be updated. Flush it out and try again */
+		status = nfs_wb_page(inode, page);
+	} while (status >= 0);
+	if (status < 0)
+		goto done;
 
-updated:
-	if (req->wb_bytes == PAGE_SIZE)
+	if (req->wb_bytes == PAGE_CACHE_SIZE)
 		set_bit(PG_uptodate, &page->flags);
 
-	retval = count;
-	if (synchronous) {
-		int status = wait_on_write_request(req);
-		if (status) {
-			nfs_cancel_request(req);
-			retval = status;
-		} else {
-			status = req->wb_status;
-			if (status < 0)
-				retval = status;
-		}
+        status = count;         /* unless we detect an error */
 
-		if (retval < 0)
-			clear_bit(PG_uptodate, &page->flags);
-	}
+	/* If the user requested a sync write, do it now */
+	if (sync) {
+		int     error;
 
-	free_write_request(req);
-	return retval;
+		error = nfs_sync_file(inode, file, page_index(page), 1, FLUSH_SYNC|FLUSH_STABLE);
+		if (error < 0 || (error = file->f_error) < 0)
+			status = error;
+		file->f_error = 0;
+	} else {
+		/* If we wrote past the end of the page.
+		 * Call the strategy routine so it can send out a bunch
+		 * of requests.
+		 */
+		if (req->wb_offset == 0 && req->wb_bytes == PAGE_CACHE_SIZE)
+			nfs_strategy(inode);
+	}
+	nfs_release_request(req);
+done:
+        dprintk("NFS:      nfs_updatepage returns %d (isize %ld)\n",
+                                                status, inode->i_size);
+	if (status < 0)
+		clear_bit(PG_uptodate, &page->flags);
+	return status;
 }
 
 /*
- * Cancel a write request. We always mark it cancelled,
- * but if it's already in progress there's no point in
- * calling rpc_exit, and we don't want to overwrite the
- * tk_status field.
- */ 
+ * Set up the argument/result storage required for the RPC call.
+ */
 static void
-nfs_cancel_request(struct nfs_wreq *req)
+nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data)
 {
-	req->wb_flags |= NFS_WRITE_CANCELLED;
-	if (!WB_INPROGRESS(req)) {
-		rpc_exit(&req->wb_task, 0);
-		rpc_wake_up_task(&req->wb_task);
+	struct nfs_page		*req;
+	struct iovec		*iov;
+	unsigned int		count;
+
+	/* Set up the RPC argument and reply structs
+	 * NB: take care not to mess about with data->commit et al. */
+
+	iov = data->args.iov;
+	count = 0;
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		iov->iov_base = (void *)(page_address(req->wb_page) + req->wb_offset);
+		iov->iov_len = req->wb_bytes;
+		count += req->wb_bytes;
+		iov++;
+		data->args.nriov++;
 	}
+	req = nfs_list_entry(data->pages.next);
+	data->dentry = req->wb_dentry;
+	data->cred = req->wb_cred;
+	data->args.fh     = NFS_FH(req->wb_dentry);
+	data->args.offset = nfs_page_offset(req->wb_page) + req->wb_offset;
+	data->args.count  = count;
+	data->res.fattr   = &data->fattr;
+	data->res.count   = count;
+	data->res.verf    = &data->verf;
 }
 
+
 /*
- * Cancel all writeback requests, both pending and in progress.
+ * Create an RPC task for the given write request and kick it.
+ * The page must have been locked by the caller.
+ *
+ * It may happen that the page we're passed is not marked dirty.
+ * This is the case if nfs_updatepage detects a conflicting request
+ * that has been written but not committed.
  */
-static void
-nfs_cancel_dirty(struct inode *inode, pid_t pid)
+static int
+nfs_flush_one(struct list_head *head, struct dentry *dentry, int how)
 {
-	struct nfs_wreq *head, *req;
+	struct inode            *inode = dentry->d_inode;
+	struct rpc_clnt 	*clnt = NFS_CLIENT(inode);
+	struct nfs_write_data	*data;
+	struct rpc_task		*task;
+	struct rpc_message	msg;
+	int                     flags,
+				async = !(how & FLUSH_SYNC),
+				stable = (how & FLUSH_STABLE);
+	sigset_t		oldset;
+
+
+	data = nfs_writedata_alloc();
+	if (!data)
+		goto out_bad;
+	task = &data->task;
+
+	/* Set the initial flags for the task.  */
+	flags = (async) ? RPC_TASK_ASYNC : 0;
+
+	/* Set up the argument struct */
+	nfs_write_rpcsetup(head, data);
+	if (stable) {
+		if (!inode->u.nfs_i.ncommit)
+			data->args.stable = NFS_FILE_SYNC;
+		else
+			data->args.stable = NFS_DATA_SYNC;
+	} else
+		data->args.stable = NFS_UNSTABLE;
+
+	/* Finalize the task. */
+	rpc_init_task(task, clnt, nfs_writeback_done, flags);
+	task->tk_calldata = data;
+	/* Release requests */
+	task->tk_release = nfs_writedata_release;
+
+#ifdef CONFIG_NFS_V3
+	msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_WRITE : NFSPROC_WRITE;
+#else
+	msg.rpc_proc = NFSPROC_WRITE;
+#endif
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
+
+	dprintk("NFS: %4d initiated write call (req %s/%s count %d nriov %d)\n",
+		task->tk_pid, 
+		dentry->d_parent->d_name.name,
+		dentry->d_name.name,
+		data->args.count, data->args.nriov);
+
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+ out_bad:
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_dirty(req);
+		nfs_unlock_request(req);
+	}
+	return -ENOMEM;
+}
 
-	req = head = NFS_WRITEBACK(inode);
-	while (req != NULL) {
-		if (pid == 0 || req->wb_pid == pid)
-			nfs_cancel_request(req);
-		if ((req = WB_NEXT(req)) == head)
+static int
+nfs_flush_list(struct inode *inode, struct list_head *head, int how)
+{
+	LIST_HEAD(one_request);
+	struct nfs_page		*req;
+	int			error = 0;
+	unsigned int		pages = 0,
+				wpages = NFS_SERVER(inode)->wpages;
+
+	while (!list_empty(head)) {
+		pages += nfs_coalesce_requests(head, &one_request, wpages);
+		req = nfs_list_entry(one_request.next);
+		error = nfs_flush_one(&one_request, req->wb_dentry, how);
+		if (error < 0)
 			break;
 	}
+	if (error >= 0)
+		return pages;
+
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_dirty(req);
+		nfs_unlock_request(req);
+	}
+	return error;
 }
 
+
 /*
- * If we're waiting on somebody else's request
- * we need to increment the counter during the
- * wait so that the request doesn't disappear
- * from under us during the wait..
+ * This function is called when the WRITE call is complete.
  */
-static int FASTCALL(wait_on_other_req(struct nfs_wreq *));
-static int wait_on_other_req(struct nfs_wreq *req)
+static void
+nfs_writeback_done(struct rpc_task *task)
 {
-	int retval;
-	req->wb_count++;
-	retval = wait_on_write_request(req);
-	free_write_request(req);
-	return retval;
-}
+	struct nfs_write_data	*data = (struct nfs_write_data *) task->tk_calldata;
+	struct nfs_writeargs	*argp = &data->args;
+	struct nfs_writeres	*resp = &data->res;
+	struct dentry		*dentry = data->dentry;
+	struct inode		*inode = dentry->d_inode;
+	struct nfs_page		*req;
+	struct page		*page;
 
-/*
- * This writes back a set of requests according to the condition.
- *
- * If this ever gets much more convoluted, use a fn pointer for
- * the condition..
- */
-#define NFS_WB(inode, cond) { int retval = 0 ; \
-	do { \
-		struct nfs_wreq *req = NFS_WRITEBACK(inode); \
-		struct nfs_wreq *head = req; \
-		if (!req) break; \
-		for (;;) { \
-			if (!(req->wb_flags & NFS_WRITE_COMPLETE)) \
-				if (cond) break; \
-			req = WB_NEXT(req); \
-			if (req == head) goto out; \
-		} \
-		retval = wait_on_other_req(req); \
-	} while (!retval); \
-out:	return retval; \
-}
+	dprintk("NFS: %4d nfs_writeback_done (status %d)\n",
+		task->tk_pid, task->tk_status);
 
-int
-nfs_wb_all(struct inode *inode)
-{
-	NFS_WB(inode, 1);
+	/* We can't handle that yet but we check for it nevertheless */
+	if (resp->count < argp->count && task->tk_status >= 0) {
+		static unsigned long    complain = 0;
+		if (time_before(complain, jiffies)) {
+			printk(KERN_WARNING
+			       "NFS: Server wrote less than requested.\n");
+			complain = jiffies + 300 * HZ;
+		}
+		/* Can't do anything about it right now except throw
+		 * an error. */
+		task->tk_status = -EIO;
+	}
+#ifdef CONFIG_NFS_V3
+	if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
+		/* We tried a write call, but the server did not
+		 * commit data to stable storage even though we
+		 * requested it.
+		 */
+		static unsigned long    complain = 0;
+
+		if (time_before(complain, jiffies)) {
+			dprintk("NFS: faulty NFSv3 server %s:"
+				" (committed = %d) != (stable = %d)\n",
+				NFS_SERVER(inode)->hostname,
+				resp->verf->committed, argp->stable);
+			complain = jiffies + 300 * HZ;
+		}
+	}
+#endif
+
+	/* Update attributes as result of writeback. */
+	nfs_write_attributes(inode, resp->fattr);
+
+	while (!list_empty(&data->pages)) {
+		req = nfs_list_entry(data->pages.next);
+		nfs_list_remove_request(req);
+		page = req->wb_page;
+
+		dprintk("NFS: write (%s/%s %d@%Ld)",
+			req->wb_dentry->d_parent->d_name.name,
+			req->wb_dentry->d_name.name,
+			req->wb_bytes,
+			(long long)(nfs_page_offset(page) + req->wb_offset));
+
+		if (task->tk_status < 0) {
+			clear_bit(PG_uptodate, &page->flags);
+			set_bit(PG_error, &page->flags);
+			nfs_inode_remove_request(req);
+			if (req->wb_file)
+				req->wb_file->f_error = task->tk_status;
+			dprintk(", error = %d\n", task->tk_status);
+			goto next;
+		}
+
+#ifdef CONFIG_NFS_V3
+		if (resp->verf->committed != NFS_UNSTABLE) {
+			nfs_inode_remove_request(req);
+			dprintk(" OK\n");
+			goto next;
+		}
+
+		memcpy(&req->wb_verf, resp->verf, sizeof(req->wb_verf));
+		req->wb_timeout = jiffies + NFS_COMMIT_DELAY;
+		nfs_mark_request_commit(req);
+		dprintk(" marked for commit\n");
+#else
+		nfs_inode_remove_request(req);
+#endif
+	next:
+		nfs_unlock_request(req);
+	}
 }
 
+#ifdef CONFIG_NFS_V3
 /*
- * Write back all requests on one page - we do this before reading it.
+ * Set up the argument/result storage required for the RPC call.
  */
-int
-nfs_wb_page(struct inode *inode, struct page *page)
+static void
+nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data)
 {
-	NFS_WB(inode, req->wb_page == page);
+	struct nfs_page		*req;
+	struct dentry		*dentry;
+	struct inode		*inode;
+	unsigned long		start, end, len;
+
+	/* Set up the RPC argument and reply structs
+	 * NB: take care not to mess about with data->commit et al. */
+
+	end = 0;
+	start = ~0;
+	req = nfs_list_entry(head->next);
+	dentry = req->wb_dentry;
+	data->dentry = dentry;
+	data->cred = req->wb_cred;
+	inode = dentry->d_inode;
+	while (!list_empty(head)) {
+		struct nfs_page	*req;
+		unsigned long	rqstart, rqend;
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		rqstart = nfs_page_offset(req->wb_page) + req->wb_offset;
+		rqend = rqstart + req->wb_bytes;
+		if (rqstart < start)
+			start = rqstart;
+		if (rqend > end)
+			end = rqend;
+	}
+	data->args.fh     = NFS_FH(dentry);
+	data->args.offset = start;
+	len = end - start;
+	if (end >= inode->i_size || len > (~((u32)0) >> 1))
+		len = 0;
+	data->res.count   = data->args.count = (u32)len;
+	data->res.fattr   = &data->fattr;
+	data->res.verf    = &data->verf;
 }
 
 /*
- * Write back all pending writes from one file descriptor..
+ * Commit dirty pages
  */
-int
-nfs_wb_file(struct inode *inode, struct file *file)
-{
-	NFS_WB(inode, req->wb_file == file);
-}
-
-void
-nfs_inval(struct inode *inode)
+static int
+nfs_commit_list(struct list_head *head, int how)
 {
-	nfs_cancel_dirty(inode,0);
+	struct rpc_message	msg;
+	struct rpc_clnt		*clnt;
+	struct nfs_write_data	*data;
+	struct rpc_task         *task;
+	struct nfs_page         *req;
+	int                     flags,
+				async = !(how & FLUSH_SYNC);
+	sigset_t		oldset;
+
+	data = nfs_writedata_alloc();
+	if (!data)
+		goto out_bad;
+	task = &data->task;
+
+	flags = (async) ? RPC_TASK_ASYNC : 0;
+
+	/* Set up the argument struct */
+	nfs_commit_rpcsetup(head, data);
+	req = nfs_list_entry(data->pages.next);
+	clnt = NFS_CLIENT(req->wb_dentry->d_inode);
+
+	rpc_init_task(task, clnt, nfs_commit_done, flags);
+	task->tk_calldata = data;
+	task->tk_release = nfs_writedata_release;
+
+	msg.rpc_proc = NFS3PROC_COMMIT;
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
+
+	dprintk("NFS: %4d initiated commit call\n", task->tk_pid);
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+ out_bad:
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_commit(req);
+		nfs_unlock_request(req);
+	}
+	return -ENOMEM;
 }
 
 /*
- * The following procedures make up the writeback finite state machinery:
- *
- * 1.	Try to lock the page if not yet locked by us,
- *	set up the RPC call info, and pass to the call FSM.
+ * COMMIT call returned
  */
 static void
-nfs_wback_begin(struct rpc_task *task)
+nfs_commit_done(struct rpc_task *task)
 {
-	struct nfs_wreq	*req = (struct nfs_wreq *) task->tk_calldata;
-	struct page	*page = req->wb_page;
-	struct file	*file = req->wb_file;
-	struct dentry	*dentry = file->f_dentry;
+	struct nfs_write_data	*data = (struct nfs_write_data *)task->tk_calldata;
+	struct nfs_writeres	*resp = &data->res;
+	struct nfs_page		*req;
+	struct dentry		*dentry = data->dentry;
+	struct inode		*inode = dentry->d_inode;
 
-	dprintk("NFS: %4d nfs_wback_begin (%s/%s, status=%d flags=%x)\n",
-		task->tk_pid, dentry->d_parent->d_name.name,
-		dentry->d_name.name, task->tk_status, req->wb_flags);
+        dprintk("NFS: %4d nfs_commit_done (status %d)\n",
+                                task->tk_pid, task->tk_status);
 
-	task->tk_status = 0;
+	nfs_write_attributes(inode, resp->fattr);
+	while (!list_empty(&data->pages)) {
+		req = nfs_list_entry(data->pages.next);
+		nfs_list_remove_request(req);
+
+		dprintk("NFS: commit (%s/%s %d@%ld)",
+			req->wb_dentry->d_parent->d_name.name,
+			req->wb_dentry->d_name.name,
+			req->wb_bytes,
+			nfs_page_offset(req->wb_page) + req->wb_offset);
+		if (task->tk_status < 0) {
+			if (req->wb_file)
+				req->wb_file->f_error = task->tk_status;
+			nfs_inode_remove_request(req);
+			dprintk(", error = %d\n", task->tk_status);
+			goto next;
+		}
 
-	/* Setup the task struct for a writeback call */
-	req->wb_flags |= NFS_WRITE_INPROGRESS;
-	req->wb_args.fh     = NFS_FH(dentry);
-	req->wb_args.offset = page->offset + req->wb_offset;
-	req->wb_args.count  = req->wb_bytes;
-	req->wb_args.buffer = (void *) (page_address(page) + req->wb_offset);
+		/* Okay, COMMIT succeeded, apparently. Check the verifier
+		 * returned by the server against all stored verfs. */
+		if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
+			/* We have a match */
+			nfs_inode_remove_request(req);
+			dprintk(" OK\n");
+			goto next;
+		}
+		/* We have a mismatch. Write the page again */
+		dprintk(" mismatch\n");
+		nfs_mark_request_dirty(req);
+	next:
+		nfs_unlock_request(req);
+	}
+}
+#endif
 
-	rpc_call_setup(task, NFSPROC_WRITE, &req->wb_args, &req->wb_fattr, 0);
+int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start,
+		   unsigned int npages, int how)
+{
+	LIST_HEAD(head);
+	int			res,
+				error = 0;
 
-	return;
+	res = nfs_scan_dirty(inode, &head, file, idx_start, npages);
+	if (res)
+		error = nfs_flush_list(inode, &head, how);
+	if (error < 0)
+		return error;
+	return res;
 }
 
-/*
- * 2.	Collect the result
- */
-static void
-nfs_wback_result(struct rpc_task *task)
+int nfs_flush_timeout(struct inode *inode, int how)
 {
-	struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
-	struct file	*file = req->wb_file;
-	struct page	*page = req->wb_page;
-	int		status = task->tk_status;
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
 
-	dprintk("NFS: %4d nfs_wback_result (%s/%s, status=%d, flags=%x)\n",
-		task->tk_pid, dentry->d_parent->d_name.name,
-		dentry->d_name.name, status, req->wb_flags);
-
-	/* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */
-	req->wb_flags |= NFS_WRITE_COMPLETE;
-	req->wb_status = status;
-
-	if (status < 0) {
-		req->wb_flags |= NFS_WRITE_INVALIDATE;
-		file->f_error = status;
-	} else if (!WB_CANCELLED(req)) {
-		struct nfs_fattr *fattr = &req->wb_fattr;
-		/* Update attributes as result of writeback. 
-		 * Beware: when UDP replies arrive out of order, we
-		 * may end up overwriting a previous, bigger file size.
-		 *
-		 * When the file size shrinks we cancel all pending
-		 * writebacks. 
-		 */
-		if (fattr->mtime.seconds >= inode->i_mtime) {
-			if (fattr->size < inode->i_size)
-				fattr->size = inode->i_size;
-
-			/* possible Solaris 2.5 server bug workaround */
-			if (inode->i_ino == fattr->fileid) {
-				/*
-				 * We expect these values to change, and
-				 * don't want to invalidate the caches.
-				 */
-				inode->i_size  = fattr->size;
-				inode->i_mtime = fattr->mtime.seconds;
-				nfs_refresh_inode(inode, fattr);
-			}
-			else
-				printk("nfs_wback_result: inode %ld, got %u?\n",
-					inode->i_ino, fattr->fileid);
-		}
+	pages = nfs_scan_dirty_timeout(inode, &head);
+	if (pages)
+		error = nfs_flush_list(inode, &head, how);
+	if (error < 0)
+		return error;
+	return pages;
+}
+
+#ifdef CONFIG_NFS_V3
+int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start,
+		    unsigned int npages, int how)
+{
+	LIST_HEAD(head);
+	int			res,
+				error = 0;
+
+	res = nfs_scan_commit(inode, &head, file, idx_start, npages);
+	if (res)
+		error = nfs_commit_list(&head, how);
+	if (error < 0)
+		return error;
+	return res;
+}
+
+int nfs_commit_timeout(struct inode *inode, int how)
+{
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
+
+	pages = nfs_scan_commit_timeout(inode, &head);
+	if (pages) {
+		pages += nfs_scan_commit(inode, &head, NULL, 0, 0);
+		error = nfs_commit_list(&head, how);
 	}
+	if (error < 0)
+		return error;
+	return pages;
+}
+#endif
 
-	rpc_release_task(task);
+int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start,
+		  unsigned int npages, int how)
+{
+	int	error,
+		wait;
 
-	if (WB_INVALIDATE(req))
-		clear_bit(PG_uptodate, &page->flags);
+	wait = how & FLUSH_WAIT;
+	how &= ~FLUSH_WAIT;
 
-	__free_page(page);
-	remove_write_request(&NFS_WRITEBACK(inode), req);
-	nr_write_requests--;
-	fput(req->wb_file);
+	if (!inode && file)
+		inode = file->f_dentry->d_inode;
 
-	wake_up(&req->wb_wait);
-	free_write_request(req);
+	do {
+		error = 0;
+		if (wait)
+			error = nfs_wait_on_requests(inode, file, idx_start, npages);
+		if (error == 0)
+			error = nfs_flush_file(inode, file, idx_start, npages, how);
+#ifdef CONFIG_NFS_V3
+		if (error == 0)
+			error = nfs_commit_file(inode, file, idx_start, npages, how);
+#endif
+	} while (error > 0);
+	return error;
 }
+
diff -urN linux-2.2.18-pre8/fs/super.c linux/fs/super.c
--- linux-2.2.18-pre8/fs/super.c	Mon Dec  3 20:24:44 2001
+++ linux/fs/super.c	Mon Dec  3 20:25:21 2001
@@ -304,8 +304,10 @@
 	{ NFS_MOUNT_SOFT, ",soft" },
 	{ NFS_MOUNT_INTR, ",intr" },
 	{ NFS_MOUNT_POSIX, ",posix" },
+	{ NFS_MOUNT_TCP, ",tcp" },
 	{ NFS_MOUNT_NOCTO, ",nocto" },
 	{ NFS_MOUNT_NOAC, ",noac" },
+	{ NFS_MOUNT_NONLM, ",nolock" },
 	{ 0, NULL }
 };
 
@@ -330,6 +332,8 @@
 		}
 		if (!strcmp("nfs", tmp->mnt_sb->s_type->name)) {
 			nfss = &tmp->mnt_sb->u.nfs_sb.s_server;
+			len += sprintf(buf+len, ",v%d", nfss->rpc_ops->version);
+
 			if (nfss->rsize != NFS_DEF_FILE_IO_BUFFER_SIZE) {
 				len += sprintf(buf+len, ",rsize=%d",
 					       nfss->rsize);
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-arc/hardware.h linux/include/asm-arm/arch-arc/hardware.h
--- linux-2.2.18-pre8/include/asm-arm/arch-arc/hardware.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-arc/hardware.h	Mon Dec  3 20:25:24 2001
@@ -49,7 +49,7 @@
 #define SCREEN1_BASE		0x01f88000
 
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 /*
  * for use with inb/outb
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-arc/keyboard.h linux/include/asm-arm/arch-arc/keyboard.h
--- linux-2.2.18-pre8/include/asm-arm/arch-arc/keyboard.h	Mon Aug  9 12:04:41 1999
+++ linux/include/asm-arm/arch-arc/keyboard.h	Mon Dec  3 20:25:24 2001
@@ -29,29 +29,3 @@
 #define kbd_enable_irq()		enable_irq(IRQ_KEYBOARDRX)
 
 #define SYSRQ_KEY	13
-
-/* resource allocation */
-#define KEYBOARD_IRQ                   1
-#define kbd_request_region() request_region(0x60, 16, "keyboard")
-#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
-                                             "keyboard", NULL)
-
-/* How to access the keyboard macros on this platform.  */
-#define kbd_read_input() inb(KBD_DATA_REG)
-#define kbd_read_status() inb(KBD_STATUS_REG)
-#define kbd_write_output(val) outb(val, KBD_DATA_REG)
-#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
-
-/* Some stoneage hardware needs delays after some operations.  */
-#define kbd_pause() do { } while(0)
-
-/*
- * Machine specific bits for the PS/2 driver
- */
-
-#define AUX_IRQ 12
-
-#define aux_request_irq(hand, dev_id)					\
-	request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
-
-#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id)
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-arc/system.h linux/include/asm-arm/arch-arc/system.h
--- linux-2.2.18-pre8/include/asm-arm/arch-arc/system.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/arch-arc/system.h	Mon Dec  3 20:25:24 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-arc/system.h
  *
- * Copyright (c) 1996 Russell King and Dave Gilbert
+ * Copyright (c) 1996-1999 Russell King and Dave Gilbert
  */
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
@@ -23,14 +23,11 @@
 
 #endif
 
+#define arch_do_idle()		do { } while (0)
+
 extern __inline__ void arch_reset(char mode)
 {
 	extern void ecard_reset(int card);
-
-	/*
-	 * Do any cleanups that the processor may require
-	 */
-	processor._proc_fin();
 
 	/*
 	 * Reset all expansion cards.
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-arc/time.h linux/include/asm-arm/arch-arc/time.h
--- linux-2.2.18-pre8/include/asm-arm/arch-arc/time.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-arc/time.h	Mon Dec  3 20:25:24 2001
@@ -167,6 +167,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/hardware.h linux/include/asm-arm/arch-ebsa110/hardware.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/hardware.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa110/hardware.h	Mon Dec  3 20:25:24 2001
@@ -8,7 +8,7 @@
 #ifndef __ASM_ARCH_HARDWARE_H
 #define __ASM_ARCH_HARDWARE_H
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 /*
  * IO definitions
@@ -39,6 +39,8 @@
 
 #define FLUSH_BASE		0xdf000000
 #define PCIO_BASE		0xf0000000
+
+#define PARAMS_BASE		(PAGE_OFFSET + 0x400)
 
 #endif
 
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/io.h linux/include/asm-arm/arch-ebsa110/io.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/io.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/arch-ebsa110/io.h	Mon Dec  3 20:25:24 2001
@@ -16,178 +16,60 @@
 #undef	ARCH_IO_DELAY
 
 /*
- * We use two different types of addressing - PC style addresses, and ARM
- * addresses.  PC style accesses the PC hardware with the normal PC IO
- * addresses, eg 0x3f8 for serial#1.  ARM addresses are 0x80000000+
- * and are translated to the start of IO.  Note that all addresses are
- * shifted left!
+ * Note that the translation here is weird -
+ * the ISA mem space and some peripherals (ethernet and VG468)
+ * appear as a 16-bit memory on a 32-bit bus.  This means that
+ * byte lanes 2 and 3 are not used, and this translation must
+ * be used.
  */
-#define __PORT_PCIO(x)	(!((x) & 0x80000000))
+#define __isa_addr(x)		(((x) & 1) | (((x) & 0xfffffffe)) << 1)
 
-/*
- * Dynamic IO functions - let the compiler
- * optimize the expressions
- */
-#define DECLARE_DYN_OUT(fnsuffix,instr)						\
-extern __inline__ void __out##fnsuffix (unsigned int value, unsigned int port)	\
-{										\
-	unsigned long temp;							\
-	__asm__ __volatile__(							\
-	"tst	%2, #0x80000000\n\t"						\
-	"mov	%0, %4\n\t"							\
-	"addeq	%0, %0, %3\n\t"							\
-	"str" ##instr## "	%1, [%0, %2, lsl #2]	@ out"###fnsuffix	\
-	: "=&r" (temp)								\
-	: "r" (value), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE)	\
-	: "cc");								\
-}
+#define __isa_io_addr(p)	(PCIO_BASE + ((p) << 2))
 
-#define DECLARE_DYN_IN(sz,fnsuffix,instr)					\
-extern __inline__ unsigned sz __in##fnsuffix (unsigned int port)		\
-{										\
-	unsigned long temp, value;						\
-	__asm__ __volatile__(							\
-	"tst	%2, #0x80000000\n\t"						\
-	"mov	%0, %4\n\t"							\
-	"addeq	%0, %0, %3\n\t"							\
-	"ldr" ##instr## "	%1, [%0, %2, lsl #2]	@ in"###fnsuffix	\
-	: "=&r" (temp), "=r" (value)						\
-	: "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE)		\
-	: "cc");								\
-	return (unsigned sz)value;						\
-}
+#define __inb(p)	(*(volatile unsigned char *)__isa_io_addr(p))
+#define __inl(p)	(panic("__inl(%X) called", p),0)
 
-extern __inline__ unsigned int __ioaddr (unsigned int port)			\
-{										\
-	if (__PORT_PCIO(port))							\
-		return (unsigned int)(PCIO_BASE + (port << 2));			\
-	else									\
-		return (unsigned int)(IO_BASE + (port << 2));			\
+extern __inline__ unsigned int __inw(unsigned int port)
+{
+	unsigned int value;
+	__asm__ __volatile__(
+	"ldr%?h	%0, [%1]	@ inw"
+	: "=r" (value)
+	: "r" (__isa_io_addr(port)));
+	return value;
 }
 
-#define DECLARE_IO(sz,fnsuffix,instr)	\
-	DECLARE_DYN_OUT(fnsuffix,instr)	\
-	DECLARE_DYN_IN(sz,fnsuffix,instr)
-
-DECLARE_IO(char,b,"b")
-DECLARE_IO(short,w,"")
-DECLARE_IO(long,l,"")
-
-#undef DECLARE_IO
-#undef DECLARE_DYN_OUT
-#undef DECLARE_DYN_IN
+#define __outb(v,p)	(*(volatile unsigned char *)__isa_io_addr(p) = v)
+#define __outl(v,p)	panic("__outl(%X,%X) called", v, p)
 
-/*
- * Constant address IO functions
- *
- * These have to be macros for the 'J' constraint to work -
- * +/-4096 immediate operand.
- */
-#define __outbc(value,port)							\
-({										\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"strb	%0, [%1, %2]		@ outbc"			\
-		: : "r" (value), "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"strb	%0, [%1, %2]		@ outbc"			\
-		: : "r" (value), "r" (IO_BASE), "r" ((port) << 2));		\
-})
-
-#define __inbc(port)								\
-({										\
-	unsigned char result;							\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"ldrb	%0, [%1, %2]		@ inbc"				\
-		: "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"ldrb	%0, [%1, %2]		@ inbc"				\
-		: "=r" (result) : "r" (IO_BASE), "r" ((port) << 2));		\
-	result;									\
-})
-
-#define __outwc(value,port)							\
-({										\
-	unsigned long v = value;						\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outwc"			\
-		: : "r" (v|v<<16), "r" (PCIO_BASE), "Jr" ((port) << 2));	\
-	else									\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outwc"			\
-		: : "r" (v|v<<16), "r" (IO_BASE), "r" ((port) << 2));		\
-})
-
-#define __inwc(port)								\
-({										\
-	unsigned short result;							\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inwc"				\
-		: "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inwc"				\
-		: "=r" (result) : "r" (IO_BASE), "r" ((port) << 2));		\
-	result & 0xffff;							\
-})
-
-#define __outlc(v,p)								\
-({										\
-	unsigned long v = value;						\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outlc"			\
-		: : "r" (v), "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outlc"			\
-		: : "r" (v), "r" (IO_BASE), "r" ((port) << 2));			\
-})
-
-#define __inlc(port)								\
-({										\
-	unsigned long result;							\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inlc"				\
-		: "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inlc"				\
-		: "=r" (result) : "r" (IO_BASE), "r" ((port) << 2));		\
-	result;									\
-})
-
-#define __ioaddrc(port)								\
-({										\
-	unsigned long addr;							\
-	if (__PORT_PCIO((port)))						\
-		addr = PCIO_BASE + ((port) << 2);				\
-	else									\
-		addr = IO_BASE + ((port) << 2);					\
-	addr;									\
-})
+extern __inline__ unsigned int __outw(unsigned int value, unsigned int port)
+{
+	__asm__ __volatile__(
+	"str%?h	%0, [%1]	@ outw"
+	: : "r" (value), "r" (__isa_io_addr(port)));
+}
+
+#define __ioaddr(p)	__isa_io_addr(p)
 
 /*
- * Translated address IO functions
- *
- * IO address has already been translated to a virtual address
+ * ioremap support - basic support only
  */
-#define outb_t(v,p)								\
-	(*(volatile unsigned char *)(p) = (v))
+#define ioremap(addr,size)	((void *)(addr))
+#define iounmap(addr)
 
-#define inb_t(p)								\
-	(*(volatile unsigned char *)(p))
+#define __isa_mem_addr(x)	((void *)(0xe0000000 + __isa_addr((unsigned long)(x))))
 
-#define outl_t(v,p)								\
-	(*(volatile unsigned long *)(p) = (v))
+#define readb(addr)		(*(volatile unsigned char *)__isa_mem_addr(addr))
+#define readw(addr)		(*(volatile unsigned short *)__isa_mem_addr(addr))
+#define readl(addr)		(*(volatile unsigned long *)__isa_mem_addr(addr))
+
+#define writeb(b,addr)		(*(volatile unsigned char *)__isa_mem_addr(addr) = (b))
+#define writew(b,addr)		(*(volatile unsigned short *)__isa_mem_addr(addr) = (b))
+#define writel(b,addr)		(*(volatile unsigned long *)__isa_mem_addr(addr) = (b))
+
+#define memset_io(a,b,c)	__ebsa110_set_isamem(__isa_mem_addr(a),(b),(c))
+#define memcpy_fromio(a,b,c)	__ebsa110_copy_fromisamem((a),__isa_mem_addr(b),(c))
+#define memcpy_toio(a,b,c)	__ebsa110_copy_toisamem(__isa_mem_addr(a),(b),(c))
 
-#define inl_t(p)								\
-	(*(volatile unsigned long *)(p))
 
 #endif
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/system.h linux/include/asm-arm/arch-ebsa110/system.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/system.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/arch-ebsa110/system.h	Mon Dec  3 20:25:24 2001
@@ -1,17 +1,13 @@
 /*
  * linux/include/asm-arm/arch-ebsa110/system.h
  *
- * Copyright (c) 1996-1998 Russell King.
+ * Copyright (C) 1996-1999 Russell King.
  */
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
-extern __inline__ void arch_reset(char mode)
-{
-	/*
-	 * loop endlessly
-	 */
-	cli();
-}
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
+
+#define arch_reset(mode)	processor.u.armv3v4.reset(0x80000000)
 
 #endif
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/time.h linux/include/asm-arm/arch-ebsa110/time.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa110/time.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa110/time.h	Mon Dec  3 20:25:24 2001
@@ -23,13 +23,13 @@
 #define PIT1_COUNT 0xecbe
 #elif defined(MCLK_47_8)
 /*
- * This should be 0x10AE1, but that doesn't exactly fit.
+ * This should be 0x10B43, but that doesn't exactly fit.
  * We run the timer interrupt at 5ms, and then divide it by
  * two in software...  This is so that the user processes
  * see exactly the same model whichever ARM processor they're
  * running on.
  */
-#define PIT1_COUNT 0x8570
+#define PIT1_COUNT 0x85A1
 #define DIVISOR 2
 #endif
  
@@ -65,6 +65,9 @@
 			do_timer(regs);
 		}
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/hardware.h linux/include/asm-arm/arch-ebsa285/hardware.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/hardware.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa285/hardware.h	Mon Dec  3 20:25:24 2001
@@ -122,8 +122,10 @@
 #define GPIO_RESET		0x001
 
 /* CPLD pins */
-#define CPLD_DSRESET		8
+#define CPLD_DS_ENABLE		8
+#define CPLD_7111_DISABLE	4
 #define CPLD_UNMUTE		2
+#define CPLD_FLASH_WR_ENABLE	1
 
 #ifndef __ASSEMBLY__
 extern void gpio_modify_op(int mask, int set);
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/io.h linux/include/asm-arm/arch-ebsa285/io.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/io.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa285/io.h	Mon Dec  3 20:25:24 2001
@@ -18,132 +18,33 @@
 #undef	ARCH_IO_DELAY
 #define ARCH_READWRITE
 
-/*
- * Dynamic IO functions - let the compiler
- * optimize the expressions
- */
-#define DECLARE_DYN_OUT(fnsuffix,instr,typ)				\
-extern __inline__ void							\
-__out##fnsuffix (unsigned int value, unsigned int port)			\
-{									\
-	__asm__ __volatile__(						\
-	"str%?" ##instr## "	%0, [%1, %2]	@ out"###fnsuffix	\
-	: 								\
-	: "r" (value), "r" (PCIO_BASE), typ (port));			\
-}
+#define __pci_io_addr(x)	(PCIO_BASE + (unsigned int)(x))
 
-#define DECLARE_DYN_IN(sz,fnsuffix,instr,typ)				\
-extern __inline__ unsigned sz						\
-__in##fnsuffix (unsigned int port)					\
-{									\
-	unsigned long value;						\
-	__asm__ __volatile__(						\
-	"ldr%?" ##instr## "	%0, [%1, %2]	@ in"###fnsuffix	\
-	: "=&r" (value)							\
-	: "r" (PCIO_BASE), typ (port));					\
-	return (unsigned sz)value;					\
-}
+#define __inb(p)		(*(volatile unsigned char *)__pci_io_addr(p))
+#define __inl(p)		(*(volatile unsigned long *)__pci_io_addr(p))
 
-extern __inline__ unsigned int __ioaddr (unsigned int port)		\
-{									\
-	return (unsigned int)(PCIO_BASE + port);			\
+extern __inline__ unsigned int __inw(unsigned int port)
+{
+	unsigned int value;
+	__asm__ __volatile__(
+	"ldr%?h	%0, [%1, %2]	@ inw"
+	: "=&r" (value)
+	: "r" (PCIO_BASE), "r" (port));
+	return value;
 }
 
-#define DECLARE_IO(sz,fnsuffix,instr,typ)	\
-	DECLARE_DYN_OUT(fnsuffix,instr,typ)	\
-	DECLARE_DYN_IN(sz,fnsuffix,instr,typ)
-
-DECLARE_IO(char,b,"b","Jr")
-DECLARE_IO(short,w,"h","r")
-DECLARE_IO(long,l,"","Jr")
-
-#undef DECLARE_IO
-#undef DECLARE_DYN_OUT
-#undef DECLARE_DYN_IN
-
-/*
- * Constant address IO functions
- *
- * These have to be macros for the 'J' constraint to work -
- * +/-4096 immediate operand.
- */
-#define __outbc(value,port)						\
-({									\
-	__asm__ __volatile__(						\
-	"str%?b	%0, [%1, %2]				@ outbc"	\
-	:								\
-	: "r" (value), "r" (PCIO_BASE), "Jr" (port));			\
-})
 
-#define __inbc(port)							\
-({									\
-	unsigned char result;						\
-	__asm__ __volatile__(						\
-	"ldr%?b	%0, [%1, %2]				@ inbc"		\
-	: "=r" (result)							\
-	: "r" (PCIO_BASE), "Jr" (port));				\
-	result;								\
-})
-
-#define __outwc(value,port)						\
-({									\
-	__asm__ __volatile__(						\
-	"str%?h	%0, [%1, %2]				@ outwc"	\
-	:								\
-	: "r" (value), "r" (PCIO_BASE), "r" (port));			\
-})
-
-#define __inwc(port)							\
-({									\
-	unsigned short result;						\
-	__asm__ __volatile__(						\
-	"ldr%?h	%0, [%1, %2]				@ inwc"		\
-	: "=r" (result)							\
-	: "r" (PCIO_BASE), "r" (port));					\
-	result & 0xffff;						\
-})
-
-#define __outlc(value,port)						\
-({									\
-	__asm__ __volatile__(						\
-	"str%?	%0, [%1, %2]				@ outlc"	\
-	:								\
-	: "r" (value), "r" (PCIO_BASE), "Jr" (port));			\
-})
+#define __outb(v,p)		(*(volatile unsigned char *)__pci_io_addr(p) = (v))
+#define __outl(v,p)		(*(volatile unsigned long *)__pci_io_addr(p) = (v))
 
-#define __inlc(port)							\
-({									\
-	unsigned long result;						\
-	__asm__ __volatile__(						\
-	"ldr%?	%0, [%1, %2]				@ inlc"		\
-	: "=r" (result)							\
-	: "r" (PCIO_BASE), "Jr" (port));				\
-	result;								\
-})
-
-#define __ioaddrc(port)							\
-({									\
-	unsigned long addr;						\
-	addr = PCIO_BASE + port;					\
-	addr;								\
-})
-
-/*
- * Translated address IO functions
- *
- * IO address has already been translated to a virtual address
- */
-#define outb_t(v,p)							\
-	(*(volatile unsigned char *)(p) = (v))
-
-#define inb_t(p)							\
-	(*(volatile unsigned char *)(p))
-
-#define outl_t(v,p)							\
-	(*(volatile unsigned long *)(p) = (v))
+extern __inline__ void __outw(unsigned int value, unsigned int port)
+{
+	__asm__ __volatile__(
+	"str%?h	%0, [%1, %2]	@ outw"
+	: : "r" (value), "r" (PCIO_BASE), "r" (port));
+}
 
-#define inl_t(p)							\
-	(*(volatile unsigned long *)(p))
+#define __ioaddr(p)	__pci_io_addr(p)
 
 /*
  * ioremap support - validate a PCI memory address,
@@ -160,6 +61,8 @@
  */
 #define IO_FUDGE_FACTOR		PCIMEM_BASE
 
+#define __pci_mem_addr(x)	((void *)(IO_FUDGE_FACTOR + (unsigned long)(x)))
+
 /*
  * ioremap takes a PCI memory address, as specified in
  * linux/Documentation/IO-mapping.txt
@@ -176,30 +79,14 @@
 
 #define ioremap_nocache(iomem_addr,size) ioremap((iomem_addr),(size))
 
-extern void iounmap(void *addr);
-
-#define DECLARE_PCI_WRITE(typ,fnsuffix)					\
-static inline void write##fnsuffix(unsigned typ val, unsigned int addr)	\
-{									\
-	*(volatile unsigned typ *)(IO_FUDGE_FACTOR + addr) = val;	\
-}
-
-#define DECLARE_PCI_READ(typ,fnsuffix)					\
-static inline unsigned typ read##fnsuffix (unsigned int addr)		\
-{									\
-	return *(volatile unsigned typ *)(IO_FUDGE_FACTOR + addr);	\
-}
+#define iounmap(_addr)	do { __iounmap(__pci_mem_addr((_addr))); } while (0)
 
-#define DECLARE_PCI(typ,fnsuffix)					\
-	DECLARE_PCI_WRITE(typ,fnsuffix)					\
-	DECLARE_PCI_READ(typ,fnsuffix)
-
-DECLARE_PCI(char,b)
-DECLARE_PCI(short,w)
-DECLARE_PCI(long,l)
-
-#undef DECLARE_PCI
-#undef DECLARE_PCI_READ
-#undef DECLARE_PCI_WRITE
+#define readb(addr)	(*(volatile unsigned char *)__pci_mem_addr(addr))
+#define readw(addr)	(*(volatile unsigned short *)__pci_mem_addr(addr))
+#define readl(addr)	(*(volatile unsigned long *)__pci_mem_addr(addr))
+
+#define writeb(b,addr)	(*(volatile unsigned char *)__pci_mem_addr(addr) = (b))
+#define writew(b,addr)	(*(volatile unsigned short *)__pci_mem_addr(addr) = (b))
+#define writel(b,addr)	(*(volatile unsigned long *)__pci_mem_addr(addr) = (b))
 
 #endif
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/irq.h linux/include/asm-arm/arch-ebsa285/irq.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/irq.h	Thu May 13 23:16:21 1999
+++ linux/include/asm-arm/arch-ebsa285/irq.h	Mon Dec  3 20:25:24 2001
@@ -159,6 +159,8 @@
 	if (isa_irq != -1) {
 		/*
 		 * Setup, and then probe for an ISA PIC
+		 * If the PIC is not there, then we
+		 * ignore the PIC.
 		 */
 		outb(0x11, PIC_LO);
 		outb(_ISA_IRQ(0), PIC_MASK_LO);	/* IRQ number		*/
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/keyboard.h linux/include/asm-arm/arch-ebsa285/keyboard.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/keyboard.h	Mon Aug  9 12:04:41 1999
+++ linux/include/asm-arm/arch-ebsa285/keyboard.h	Mon Dec  3 20:25:24 2001
@@ -73,7 +73,6 @@
 #define SYSRQ_KEY	0x54
 
 /* resource allocation */
-#define KEYBOARD_IRQ                   1
 #define kbd_request_region() request_region(0x60, 16, "keyboard")
 #define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
                                              "keyboard", NULL)
@@ -90,9 +89,6 @@
 /*
  * Machine specific bits for the PS/2 driver
  */
-
-#define AUX_IRQ 12
-
 #define aux_request_irq(hand, dev_id)					\
 	request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
 
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/memory.h linux/include/asm-arm/arch-ebsa285/memory.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/memory.h	Thu May 13 23:16:21 1999
+++ linux/include/asm-arm/arch-ebsa285/memory.h	Mon Dec  3 20:25:24 2001
@@ -15,8 +15,6 @@
 #ifndef __ASM_ARCH_MMU_H
 #define __ASM_ARCH_MMU_H
 
-#include <linux/config.h>
-
 #if defined(CONFIG_HOST_FOOTBRIDGE)
 
 /*
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/system.h linux/include/asm-arm/arch-ebsa285/system.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/system.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa285/system.h	Mon Dec  3 20:25:24 2001
@@ -8,28 +8,17 @@
 #include <asm/hardware.h>
 #include <asm/leds.h>
 
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
+
 extern __inline__ void arch_reset(char mode)
 {
-	cli();
-
 	if (mode == 's') {
-		__asm__ volatile (
-		"mov	lr, #0x41000000		@ prepare to jump to ROM
-		 mov	r0, #0x130
-		 mcr	p15, 0, r0, c1, c0	@ MMU off
-		 mcr	p15, 0, ip, c7, c7	@ flush caches
-		 mov	pc, lr" : : : "cc");
+		/*
+		 * Jump into ROM
+		 */
+		processor.u.armv3v4.reset(0x41000000);
 	} else {
-		if (machine_is_ebsa285() || machine_is_co285()) {
-			/* To reboot, we set up the 21285 watchdog and
-			 * enable it.  We then wait for it to timeout.
-			 */
-			*CSR_TIMER4_LOAD = 0x8000;
-			*CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
-					   TIMER_CNTL_AUTORELOAD |
-					   TIMER_CNTL_DIV16;
-			*CSR_SA110_CNTL |= 1 << 13;
-		} else if (machine_is_netwinder()) {
+		if (machine_is_netwinder()) {
 			/* open up the SuperIO chip
 			 */
 			outb(0x87, 0x370);
@@ -48,6 +37,15 @@
 			/* set a RED LED and toggle WD_TIMER for rebooting
 			 */
 			outb(0xc4, 0x338);
+		} else {
+			/* To reboot, we set up the 21285 watchdog and
+			 * enable it.  We then wait for it to timeout.
+			 */
+			*CSR_TIMER4_LOAD = 0x8000;
+			*CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
+					   TIMER_CNTL_AUTORELOAD |
+					   TIMER_CNTL_DIV16;
+			*CSR_SA110_CNTL |= 1 << 13;
 		}
 	}
 }
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/time.h linux/include/asm-arm/arch-ebsa285/time.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/time.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa285/time.h	Mon Dec  3 20:25:24 2001
@@ -115,6 +115,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction isa_timer_irq = {
@@ -257,6 +260,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction __ebsa285_data timer1_irq = {
@@ -279,22 +285,20 @@
  */
 extern __inline__ void setup_timer(void)
 {
-	switch(machine_arch_type) {
-	case MACH_TYPE_CO285:
+	if (machine_arch_type == MACH_TYPE_CO285)
 		/*
 		 * Add-in 21285s shouldn't access the RTC
 		 */
 		rtc_base = 0;
-		break;
-
-	default:
+	else
 		rtc_base = 0x70;
-		break;
-	}
 
 	if (rtc_base) {
 		int reg_d, reg_b;
 
+		/*
+		 * Probe for the RTC.
+		 */
 		reg_d = CMOS_READ(RTC_REG_D);
 
 		/*
@@ -314,7 +318,7 @@
 		    CMOS_READ(RTC_REG_B) == reg_b) {
 
 			/*
-			 * Check the battery
+			 * We have a RTC.  Check the battery
 			 */
 			if ((reg_d & 0x80) == 0)
 				printk(KERN_WARNING "RTC: *** warning: CMOS battery bad\n");
@@ -332,8 +336,7 @@
 		xtime.tv_sec = mktime(1970, 1, 1, 0, 0, 0);
 		set_rtc_mmss = set_dummy_time;
 	}
-
-	if (machine_is_ebsa285()) {
+	if (machine_is_ebsa285() || machine_is_co285()) {
 		gettimeoffset = timer1_gettimeoffset;
 
 		*CSR_TIMER1_CLR  = 0;
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/uncompress.h linux/include/asm-arm/arch-ebsa285/uncompress.h
--- linux-2.2.18-pre8/include/asm-arm/arch-ebsa285/uncompress.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-ebsa285/uncompress.h	Mon Dec  3 20:25:24 2001
@@ -1,18 +1,24 @@
 /*
  * linux/include/asm-arm/arch-ebsa285/uncompress.h
  *
- * Copyright (C) 1996,1997,1998 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
 
 /*
  * Note! This could cause problems on the NetWinder
  */
-#define BASE 0x42000160
+#define DC21285_BASE ((volatile unsigned int *)0x42000160)
+#define SER0_BASE    ((volatile unsigned char *)0x7c0003f8)
 
 static __inline__ void putc(char c)
 {
-	while (*((volatile unsigned int *)(BASE + 0x18)) & 8);
-	*((volatile unsigned int *)(BASE)) = c;
+	if (machine_is_netwinder()) {
+		while ((SER0_BASE[5] & 0x60) != 0x60);
+		SER0_BASE[0] = c;
+	} else {
+		while (DC21285_BASE[6] & 8);
+		DC21285_BASE[0] = c;
+	}
 }
 
 /*
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-nexuspci/system.h linux/include/asm-arm/arch-nexuspci/system.h
--- linux-2.2.18-pre8/include/asm-arm/arch-nexuspci/system.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/arch-nexuspci/system.h	Mon Dec  3 20:25:24 2001
@@ -6,6 +6,8 @@
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
+
 extern __inline__ void arch_reset(char mode)
 {
 	/*
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-nexuspci/time.h linux/include/asm-arm/arch-nexuspci/time.h
--- linux-2.2.18-pre8/include/asm-arm/arch-nexuspci/time.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-nexuspci/time.h	Mon Dec  3 20:25:24 2001
@@ -38,6 +38,9 @@
 	readb(UART_BASE + 0x14);
 
 	do_timer(regs);	
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-rpc/hardware.h linux/include/asm-arm/arch-rpc/hardware.h
--- linux-2.2.18-pre8/include/asm-arm/arch-rpc/hardware.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-rpc/hardware.h	Mon Dec  3 20:25:24 2001
@@ -41,7 +41,7 @@
 #define FLUSH_BASE		0xdf000000
 
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 /*
  * for use with inb/outb
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-rpc/keyboard.h linux/include/asm-arm/arch-rpc/keyboard.h
--- linux-2.2.18-pre8/include/asm-arm/arch-rpc/keyboard.h	Mon Aug  9 12:04:41 1999
+++ linux/include/asm-arm/arch-rpc/keyboard.h	Mon Dec  3 20:25:24 2001
@@ -28,32 +28,3 @@
 #define kbd_enable_irq()		enable_irq(IRQ_KEYBOARDRX)
 
 #define SYSRQ_KEY	13
-
-/* resource allocation */
-#define KEYBOARD_IRQ                   1
-#define kbd_request_region() request_region(0x60, 16, "keyboard")
-#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
-                                             "keyboard", NULL)
-
-/* How to access the keyboard macros on this platform.  */
-#define kbd_read_input() inb(KBD_DATA_REG)
-#define kbd_read_status() inb(KBD_STATUS_REG)
-#define kbd_write_output(val) outb(val, KBD_DATA_REG)
-#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
-
-/* Some stoneage hardware needs delays after some operations.  */
-#define kbd_pause() do { } while(0)
-
-#define keyboard_setup()						\
-	request_region(0x60, 16, "keyboard")
-
-/*
- * Machine specific bits for the PS/2 driver
- */
-
-#define AUX_IRQ 12
-
-#define aux_request_irq(hand, dev_id)					\
-	request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
-
-#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id)
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-rpc/system.h linux/include/asm-arm/arch-rpc/system.h
--- linux-2.2.18-pre8/include/asm-arm/arch-rpc/system.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-rpc/system.h	Mon Dec  3 20:25:24 2001
@@ -1,23 +1,27 @@
 /*
  * linux/include/asm-arm/arch-rpc/system.h
  *
- * Copyright (c) 1996 Russell King
+ * Copyright (c) 1996-1999 Russell King
  */
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
 #include <asm/iomd.h>
+#include <asm/io.h>
 
-#define arch_reset(mode) {						\
-	extern void ecard_reset (int card);				\
-	outb (0, IOMD_ROMCR0);						\
-	ecard_reset(-1);						\
-	cli();								\
-	__asm__ __volatile__("msr  spsr, r1;"				\
-			     "mcr  p15, 0, %0, c1, c0, 0;"		\
-			     "movs pc, #0"				\
-			 : 						\
-			 : "r" (processor.u.armv3v4.reset()));		\
-	}
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
 
+extern __inline__ void arch_reset(char mode)
+{
+	extern void ecard_reset(int card);
+
+	ecard_reset(-1);
+
+	outb(0, IOMD_ROMCR0);
+
+	/*
+	 * Jump into the ROM
+	 */
+	processor.u.armv3v4.reset(0);
+}
 #endif
diff -urN linux-2.2.18-pre8/include/asm-arm/arch-rpc/time.h linux/include/asm-arm/arch-rpc/time.h
--- linux-2.2.18-pre8/include/asm-arm/arch-rpc/time.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/arch-rpc/time.h	Mon Dec  3 20:25:24 2001
@@ -151,7 +151,7 @@
 	 */
 	buf[4] &= 0x1f;
 	buf[3] &= 0x3f;
-printk("Year %4d mon %02X day %02X hour %02X min %02X sec %02X\n", year, buf[4], buf[3], buf[2], buf[1], buf[0]);
+
 	for (i = 0; i < 5; i++)
 		BCD_TO_BIN(buf[i]);
 
@@ -175,6 +175,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -urN linux-2.2.18-pre8/include/asm-arm/assembler.h linux/include/asm-arm/assembler.h
--- linux-2.2.18-pre8/include/asm-arm/assembler.h	Tue Jan 20 16:39:42 1998
+++ linux/include/asm-arm/assembler.h	Mon Dec  3 20:25:24 2001
@@ -7,28 +7,5 @@
  * Do not include any C declarations in this file - it is included by
  * assembler source.
  */
-
-/*
- * LOADREGS: multiple register load (ldm) with pc in register list
- *		(takes account of ARM6 not using ^)
- *
- * RETINSTR: return instruction: adds the 's' in at the end of the
- *		instruction if this is not an ARM6
- *
- * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done
- *		implicitly
- *
- * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done
- *		implicitly with ldm ... ^ or movs.
- *
- * These next two need thinking about - can't easily use stack... (see system.S)
- * DISABLEIRQS: disable IRQS in SVC mode
- *
- * ENABLEIRQS: enable IRQS in SVC mode
- *
- * USERMODE: switch to USER mode
- *
- * SVCMODE: switch to SVC mode
- */
-
+#include <asm/proc/ptrace.h>
 #include <asm/proc/assembler.h>
diff -urN linux-2.2.18-pre8/include/asm-arm/checksum.h linux/include/asm-arm/checksum.h
--- linux-2.2.18-pre8/include/asm-arm/checksum.h	Fri May  8 00:42:40 1998
+++ linux/include/asm-arm/checksum.h	Mon Dec  3 20:25:24 2001
@@ -117,12 +117,12 @@
 		   unsigned short proto, unsigned int sum)
 {
 	__asm__("
-	adds	%0, %0, %1
-	adcs	%0, %0, %2
+	adds	%0, %1, %2
 	adcs	%0, %0, %3
+	adcs	%0, %0, %4
 	adc	%0, %0, #0"
 	: "=&r"(sum)
-	: "r" (daddr), "r" (saddr), "r" ((ntohs(len)<<16)+proto*256), "0" (sum));
+	: "r" (sum), "r" (daddr), "r" (saddr), "r" ((ntohs(len)<<16)+proto*256));
 	return sum;
 }	
 /*
diff -urN linux-2.2.18-pre8/include/asm-arm/dec21285.h linux/include/asm-arm/dec21285.h
--- linux-2.2.18-pre8/include/asm-arm/dec21285.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/dec21285.h	Mon Dec  3 20:25:24 2001
@@ -28,6 +28,12 @@
 #define CSR_PCICSRIOBASE	DC21285_IO(0x0014)
 #define CSR_PCISDRAMBASE	DC21285_IO(0x0018)
 #define CSR_PCIROMBASE		DC21285_IO(0x0030)
+#define CSR_MBOX0               DC21285_IO(0x0050)
+#define CSR_MBOX1               DC21285_IO(0x0054)
+#define CSR_MBOX2               DC21285_IO(0x0058)
+#define CSR_MBOX3               DC21285_IO(0x005c)
+#define CSR_DOORBELL            DC21285_IO(0x0060)
+#define CSR_DOORBELL_SETUP      DC21285_IO(0x0064)
 #define CSR_ROMWRITEREG		DC21285_IO(0x0068)
 #define CSR_CSRBASEMASK		DC21285_IO(0x00f8)
 #define CSR_CSRBASEOFFSET	DC21285_IO(0x00fc)
diff -urN linux-2.2.18-pre8/include/asm-arm/elf.h linux/include/asm-arm/elf.h
--- linux-2.2.18-pre8/include/asm-arm/elf.h	Thu Dec 17 09:05:43 1998
+++ linux/include/asm-arm/elf.h	Mon Dec  3 20:25:24 2001
@@ -47,7 +47,8 @@
 /* This yields a mask that user programs can use to figure out what
    instruction set this cpu supports. */
 
-#define ELF_HWCAP	(armidlist[armidindex].hwcap)
+extern unsigned int elf_hwcap;
+#define ELF_HWCAP	(elf_hwcap)
 
 /* This yields a string that ld.so will use to load implementation
    specific libraries for optimization.  This is more specific in
diff -urN linux-2.2.18-pre8/include/asm-arm/hardware.h linux/include/asm-arm/hardware.h
--- linux-2.2.18-pre8/include/asm-arm/hardware.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/hardware.h	Mon Dec  3 20:25:24 2001
@@ -12,7 +12,7 @@
 #include <asm/arch/hardware.h>
 
 #ifdef HAS_EXPMASK
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 #define __EXPMASK(offset)	(((volatile unsigned char *)EXPMASK_BASE)[offset])
 #else
 #define __EXPMASK(offset)	offset
diff -urN linux-2.2.18-pre8/include/asm-arm/io.h linux/include/asm-arm/io.h
--- linux-2.2.18-pre8/include/asm-arm/io.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/io.h	Mon Dec  3 20:25:24 2001
@@ -16,22 +16,14 @@
 
 #ifdef __KERNEL__
 
+#include <linux/types.h>
+
 #ifndef NULL
 #define NULL	((void *) 0)
 #endif
 
-extern void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags);
-
-/*
- * String version of IO memory access ops:
- */
-extern void _memcpy_fromio(void *, unsigned long, unsigned long);
-extern void _memcpy_toio(unsigned long, const void *, unsigned long);
-extern void _memset_io(unsigned long, int, unsigned long);
-
-#define memcpy_fromio(to,from,len)	_memcpy_fromio((to),(unsigned long)(from),(len))
-#define memcpy_toio(to,from,len)	_memcpy_toio((unsigned long)(to),(from),(len))
-#define memset_io(addr,c,len)		_memset_io((unsigned long)(addr),(c),(len))
+extern void * __ioremap(unsigned long offset, size_t size, unsigned long flags);
+extern void __iounmap(void *addr);
 
 #endif
 
@@ -191,18 +183,44 @@
 
 #endif
 
-#ifndef ARCH_READWRITE
-
 /* for panic */
 #include <linux/kernel.h>
 
+#ifndef readb
 #define readb(p)	(panic("readb called, but not implemented"),0)
+#endif
+#ifndef readw
 #define readw(p)	(panic("readw called, but not implemented"),0)
+#endif
+#ifndef readl
 #define readl(p)	(panic("readl called, but not implemented"),0)
+#endif
+#ifndef writeb
 #define writeb(v,p)	panic("writeb called, but not implemented")
+#endif
+#ifndef writew
 #define writew(v,p)	panic("writew called, but not implemented")
+#endif
+#ifndef writel
 #define writel(v,p)	panic("writel called, but not implemented")
+#endif
+
+/*
+ * String version of IO memory access ops:
+ */
+#ifndef memcpy_fromio
+extern void _memcpy_fromio(void *, unsigned long, unsigned long);
+#define memcpy_fromio(to,from,len)	_memcpy_fromio((to),(unsigned long)(from),(len))
+#endif
+
+#ifndef memcpy_toio
+extern void _memcpy_toio(unsigned long, const void *, unsigned long);
+#define memcpy_toio(to,from,len)	_memcpy_toio((unsigned long)(to),(from),(len))
+#endif
 
+#ifndef memset_io
+extern void _memset_io(unsigned long, int, unsigned long);
+#define memset_io(addr,c,len)		_memset_io((unsigned long)(addr),(c),(len))
 #endif
 
 /*
diff -urN linux-2.2.18-pre8/include/asm-arm/ioc.h linux/include/asm-arm/ioc.h
--- linux-2.2.18-pre8/include/asm-arm/ioc.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/ioc.h	Mon Dec  3 20:25:24 2001
@@ -5,7 +5,7 @@
 
 #ifndef IOC_CONTROL
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 #define __IOC(offset)	(IOC_BASE + (offset >> 2))
 #else
 #define __IOC(offset)	offset
diff -urN linux-2.2.18-pre8/include/asm-arm/iomd.h linux/include/asm-arm/iomd.h
--- linux-2.2.18-pre8/include/asm-arm/iomd.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/iomd.h	Mon Dec  3 20:25:24 2001
@@ -1,5 +1,13 @@
+/*
+ * linux/include/asm-arm/iomd.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * This file contains information out the IOMD ASIC used in the
+ * Acorn RiscPC and subsequently integrated into the CLPS7500 chips.
+ */
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 #define __IOMD(offset)	(IO_IOMD_BASE + (offset >> 2))
 #else
 #define __IOMD(offset)	offset
@@ -169,7 +177,7 @@
 #define VDMA_START	IOMD_VIDSTART
 #define VDMA_END	IOMD_VIDEND
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 extern unsigned int vram_half_sam;
 #define video_set_dma(start,end,offset)				\
 do {								\
diff -urN linux-2.2.18-pre8/include/asm-arm/irq.h linux/include/asm-arm/irq.h
--- linux-2.2.18-pre8/include/asm-arm/irq.h	Sat May  8 11:07:16 1999
+++ linux/include/asm-arm/irq.h	Mon Dec  3 20:25:24 2001
@@ -24,8 +24,5 @@
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
-#define __STR(x) #x
-#define STR(x) __STR(x)
-
 #endif
 
diff -urN linux-2.2.18-pre8/include/asm-arm/memc.h linux/include/asm-arm/memc.h
--- linux-2.2.18-pre8/include/asm-arm/memc.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/memc.h	Mon Dec  3 20:25:24 2001
@@ -4,7 +4,7 @@
 #define VDMA_START	1
 #define VDMA_END	2
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 extern void memc_write(unsigned int reg, unsigned long val);
 
 #define video_set_dma(start,end,offset)				\
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armo/assembler.h linux/include/asm-arm/proc-armo/assembler.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armo/assembler.h	Tue Jan 20 16:39:42 1998
+++ linux/include/asm-arm/proc-armo/assembler.h	Mon Dec  3 20:25:24 2001
@@ -6,54 +6,30 @@
  * This file contains arm architecture specific defines
  * for the different processors
  */
-
-/*
- * LOADREGS: multiple register load (ldm) with pc in register list
- *		(takes account of ARM6 not using ^)
- *
- * RETINSTR: return instruction: adds the 's' in at the end of the
- *		instruction if this is not an ARM6
- *
- * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done
- *		implicitly
- *
- * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done
- *		implicitly with ldm ... ^ or movs.
- *
- * These next two need thinking about - can't easily use stack... (see system.S)
- * DISABLEIRQS: disable IRQS in SVC mode
- *
- * ENABLEIRQS: enable IRQS in SVC mode
- *
- * USERMODE: switch to USER mode
- *
- * SVCMODE: switch to SVC mode
- */
-
-#define N_BIT	(1 << 31)
-#define Z_BIT	(1 << 30)
-#define C_BIT	(1 << 29)
-#define V_BIT	(1 << 28)
-
-#define PCMASK	0xfc000003
-
-#ifdef __ASSEMBLER__
-
-#define I_BIT	(1 << 27)
-#define F_BIT	(1 << 26)
-
-#define MODE_USR	0
-#define MODE_FIQ	1
-#define MODE_IRQ	2
-#define MODE_SVC	3
+#ifndef __ASSEMBLY__
+#error "Only include this from assembly code"
+#endif
+
+#define MODE_USR	USR26_MODE
+#define MODE_FIQ	FIQ26_MODE
+#define MODE_IRQ	IRQ26_MODE
+#define MODE_SVC	SVC26_MODE
 
 #define DEFAULT_FIQ	MODE_FIQ
 
+#ifdef __STDC__
 #define LOADREGS(cond, base, reglist...)\
 	ldm##cond	base,reglist^
 
 #define RETINSTR(instr, regs...)\
 	instr##s	regs
+#else
+#define LOADREGS(cond, base, reglist...)\
+	ldm/**/cond	base,reglist^
+
+#define RETINSTR(instr, regs...)\
+	instr/**/s	regs
+#endif
 
 #define MODENOP\
 	mov	r0, r0
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armo/pgtable.h linux/include/asm-arm/proc-armo/pgtable.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armo/pgtable.h	Mon Aug  9 12:04:41 1999
+++ linux/include/asm-arm/proc-armo/pgtable.h	Mon Dec  3 20:25:24 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/proc-armo/pgtable.h
  *
- * Copyright (C) 1995, 1996 Russell King
+ * Copyright (C) 1995-1999 Russell King
  * Modified 18/19-Oct-1997 for two-level page table
  */
 #ifndef __ASM_PROC_PGTABLE_H
@@ -21,6 +21,8 @@
 #define flush_cache_range(mm,start,end)		do { } while (0)
 #define flush_cache_page(vma,vmaddr)		do { } while (0)
 #define flush_page_to_ram(page)			do { } while (0)
+#define clean_cache_range(_start,_end)		do { } while (0)
+#define clean_cache_area(_start,_size)		do { } while (0)
 #define flush_icache_range(start,end)		do { } while (0)
 
 /*
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armo/processor.h linux/include/asm-arm/proc-armo/processor.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armo/processor.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/proc-armo/processor.h	Mon Dec  3 20:25:24 2001
@@ -26,11 +26,12 @@
 	unsigned long r7;
 	unsigned long r8;
 	unsigned long r9;
+	unsigned long sl;
 	unsigned long fp;
 	unsigned long pc;
 };
 
-#define INIT_CSS (struct context_save_struct){ 0, 0, 0, 0, 0, 0, 0, SVC26_MODE }
+#define INIT_CSS (struct context_save_struct){ 0, 0, 0, 0, 0, 0, 0, 0, SVC26_MODE }
 
 typedef struct {
 	void (*put_byte)(void);			/* Special calling convention */
@@ -43,7 +44,7 @@
 	unsigned long (*copy_to_user)(void *to, const void *from, unsigned long sz);
 	unsigned long (*clear_user)(void *addr, unsigned long sz);
 	unsigned long (*strncpy_from_user)(char *to, const char *from, unsigned long sz);
-	unsigned long (*strlen_user)(const char *s);
+	unsigned long (*strnlen_user)(const char *s, long n);
 } uaccess_t;
 
 extern uaccess_t uaccess_user, uaccess_kernel;
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armo/ptrace.h linux/include/asm-arm/proc-armo/ptrace.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armo/ptrace.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/proc-armo/ptrace.h	Mon Dec  3 20:25:24 2001
@@ -1,12 +1,26 @@
 /*
  * linux/include/asm-arm/proc-armo/ptrace.h
  *
- * Copyright (C) 1996 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
-
 #ifndef __ASM_PROC_PTRACE_H
 #define __ASM_PROC_PTRACE_H
 
+#define USR26_MODE	0x00
+#define FIQ26_MODE	0x01
+#define IRQ26_MODE	0x02
+#define SVC26_MODE	0x03
+#define MODE_MASK	0x03
+#define F_BIT		(1 << 26)
+#define I_BIT		(1 << 27)
+#define CC_V_BIT	(1 << 28)
+#define CC_C_BIT	(1 << 29)
+#define CC_Z_BIT	(1 << 30)
+#define CC_N_BIT	(1 << 31)
+#define PCMASK		0xfc000003
+
+#ifndef __ASSEMBLY__
+
 /* this struct defines the way the registers are stored on the
    stack during a system call. */
 
@@ -30,19 +44,9 @@
 #define ARM_r2		uregs[2]
 #define ARM_r1		uregs[1]
 #define ARM_r0		uregs[0]
-#define ARM_ORIG_r0	uregs[16] /* -1 */
+#define ARM_ORIG_r0	uregs[16]
 
-#define USR26_MODE	0x00
-#define FIQ26_MODE	0x01
-#define IRQ26_MODE	0x02
-#define SVC26_MODE	0x03
-#define MODE_MASK	0x03
-#define F_BIT		(1 << 26)
-#define I_BIT		(1 << 27)
-#define CC_V_BIT	(1 << 28)
-#define CC_C_BIT	(1 << 29)
-#define CC_Z_BIT	(1 << 30)
-#define CC_N_BIT	(1 << 31)
+#ifdef __KERNEL__
 
 #define processor_mode(regs) \
 	((regs)->ARM_pc & MODE_MASK)
@@ -70,11 +74,21 @@
  */
 static inline int valid_user_regs(struct pt_regs *regs)
 {
-	if (!user_mode(regs) || regs->ARM_pc & (F_BIT | I_BIT))
+	if (user_mode(regs) &&
+	    (regs->ARM_pc & (F_BIT | I_BIT)) == 0)
 		return 1;
 
+	/*
+	 * force it to be something sensible
+	 */
+	regs->ARM_pc &= ~(MODE_MASK | F_BIT | I_BIT);
+
 	return 0;
 }
+
+#endif	/* __KERNEL__ */
+
+#endif	/* __ASSEMBLY__ */
 
 #endif
 
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armo/semaphore.h linux/include/asm-arm/proc-armo/semaphore.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armo/semaphore.h	Sat May  8 11:06:57 1999
+++ linux/include/asm-arm/proc-armo/semaphore.h	Mon Dec  3 20:25:24 2001
@@ -14,13 +14,13 @@
 	@ atomic down operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%0]
+	and	r0, r0, #0x0c000003
 	subs	lr, lr, #1
 	str	lr, [%0]
-	mov	lr, pc, lsr #28
-	teqp	r0, lr, lsl #28
+	orrmi	r0, r0, #0x80000000	@ set N
+	teqp	r0, #0
 	movmi	r0, %0
 	blmi	" SYMBOL_NAME_STR(__down_failed)
 		:
@@ -39,14 +39,13 @@
 	@ atomic down operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%1]
+	and	r0, r0, #0x0c000003
 	subs	lr, lr, #1
 	str	lr, [%1]
-	mov	lr, pc, lsr #28
 	orrmi	r0, r0, #0x80000000	@ set N
-	teqp	r0, lr, lsl #28
+	teqp	r0, #0
 	movmi	r0, %1
 	movpl	r0, #0
 	blmi	" SYMBOL_NAME_STR(__down_interruptible_failed) "
@@ -64,14 +63,13 @@
 	@ atomic down operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%1]
+	and	r0, r0, #0x0c000003
 	subs	lr, lr, #1
 	str	lr, [%1]
-	mov	lr, pc, lsr #28
 	orrmi	r0, r0, #0x80000000	@ set N
-	teqp	r0, lr, lsl #28
+	teqp	r0, #0
 	movmi	r0, %1
 	movpl	r0, #0
 	blmi	" SYMBOL_NAME_STR(__down_trylock_failed) "
@@ -94,14 +92,13 @@
 	@ atomic up operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%0]
+	and	r0, r0, #0x0c000003
 	adds	lr, lr, #1
 	str	lr, [%0]
-	mov	lr, pc, lsr #28
-	orrls	r0, r0, #0x80000000	@ set N
-	teqp	r0, lr, lsl #28
+	orrle	r0, r0, #0x80000000	@ set N
+	teqp	r0, #0
 	movmi	r0, %0
 	blmi	" SYMBOL_NAME_STR(__up_wakeup)
 		:
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armo/system.h linux/include/asm-arm/proc-armo/system.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armo/system.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/proc-armo/system.h	Mon Dec  3 20:25:24 2001
@@ -105,7 +105,7 @@
 "	bic	%0, %0, #0x0c000000\n"			\
 "	orr	%0, %0, %1\n"				\
 "	teqp	%0, #0\n"				\
-	  : "=r" (temp)					\
+	  : "=&r" (temp)				\
 	  : "r" (x)					\
 	  : "memory");					\
 	} while (0)
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armo/uaccess.h linux/include/asm-arm/proc-armo/uaccess.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armo/uaccess.h	Fri May  8 00:42:40 1998
+++ linux/include/asm-arm/proc-armo/uaccess.h	Mon Dec  3 20:25:24 2001
@@ -136,5 +136,5 @@
 #define __do_strncpy_from_user(dst,src,count,res)			\
 	(res) = current->tss.uaccess->strncpy_from_user(dst,src,count)
 
-#define __do_strlen_user(s,res)						\
-	(res) = current->tss.uaccess->strlen_user(s)
+#define __do_strnlen_user(s,n,res)					\
+	(res) = current->tss.uaccess->strnlen_user(s,n)
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/assembler.h linux/include/asm-arm/proc-armv/assembler.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/assembler.h	Tue Jan 20 16:39:43 1998
+++ linux/include/asm-arm/proc-armv/assembler.h	Mon Dec  3 20:25:24 2001
@@ -6,79 +6,81 @@
  * This file contains arm architecture specific defines
  * for the different processors
  */
+#ifndef __ASSEMBLY__
+#error "Only include this from assembly code"
+#endif
 
-/*
- * LOADREGS: multiple register load (ldm) with pc in register list
- *		(takes account of ARM6 not using ^)
- *
- * RETINSTR: return instruction: adds the 's' in at the end of the
- *		instruction if this is not an ARM6
- *
- * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done
- *		implicitly
- *
- * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done
- *		implicitly with ldm ... ^ or movs.
- *
- * These next two need thinking about - can't easily use stack... (see system.S)
- * DISABLEIRQS: disable IRQS in SVC mode
- *
- * ENABLEIRQS: enable IRQS in SVC mode
- *
- * USERMODE: switch to USER mode
- *
- * SVCMODE: switch to SVC mode
- */
-
-#define N_BIT	(1 << 31)
-#define Z_BIT	(1 << 30)
-#define C_BIT	(1 << 29)
-#define V_BIT	(1 << 28)
-
-#define PCMASK	0
-
-#ifdef __ASSEMBLER__
-
-#define I_BIT	(1 << 7)
-#define F_BIT	(1 << 6)
-
-#define MODE_FIQ26	0x01
-#define MODE_FIQ32	0x11
+#define MODE_USR	USR_MODE
+#define MODE_FIQ	FIQ_MODE
+#define MODE_IRQ	IRQ_MODE
+#define MODE_SVC	SVC_MODE
 
-#define DEFAULT_FIQ	MODE_FIQ32
+#define DEFAULT_FIQ	MODE_FIQ
 
+/*
+ * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc})
+ * RETINSTR - return instruction (eg, mov pc, lr)
+ */
+#ifdef __STDC__
 #define LOADREGS(cond, base, reglist...)\
 	ldm##cond	base,reglist
 
 #define RETINSTR(instr, regs...)\
 	instr	regs
+#else
+#define LOADREGS(cond, base, reglist...)\
+	ldm/**/cond	base,reglist
 
+#define RETINSTR(instr, regs...)\
+	instr		regs
+#endif
+
+/*
+ * No nop required after mode change
+ */
 #define MODENOP
 
+/*
+ * Change to `mode'
+ */
 #define MODE(savereg,tmpreg,mode) \
 	mrs	savereg, cpsr; \
 	bic	tmpreg, savereg, $0x1f; \
 	orr	tmpreg, tmpreg, $mode; \
 	msr	cpsr, tmpreg
 
+/*
+ * Restore mode
+ */
 #define RESTOREMODE(savereg) \
 	msr	cpsr, savereg
 	
+/*
+ * save interrupt state (uses stack)
+ */
 #define SAVEIRQS(tmpreg)\
 	mrs	tmpreg, cpsr; \
 	str	tmpreg, [sp, $-4]!
 
+/*
+ * restore interrupt state (uses stack)
+ */
 #define RESTOREIRQS(tmpreg)\
 	ldr	tmpreg, [sp], $4; \
 	msr	cpsr, tmpreg
 
+/*
+ * disable IRQs
+ */
 #define DISABLEIRQS(tmpreg)\
 	mrs	tmpreg , cpsr; \
 	orr	tmpreg , tmpreg , $I_BIT; \
 	msr	cpsr, tmpreg
 
+/*
+ * enable IRQs
+ */
 #define ENABLEIRQS(tmpreg)\
 	mrs	tmpreg , cpsr; \
 	bic	tmpreg , tmpreg , $I_BIT; \
 	msr	cpsr, tmpreg
-#endif
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/domain.h linux/include/asm-arm/proc-armv/domain.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/domain.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/proc-armv/domain.h	Mon Dec  3 20:25:24 2001
@@ -0,0 +1,46 @@
+/*
+ * linux/include/asm-arm/proc-armv/domain.h
+ *
+ * Copyright (C) 1999 Russell King.
+ */
+#ifndef __ASM_PROC_DOMAIN_H
+#define __ASM_PROC_DOMAIN_H
+
+/*
+ * Domain numbers
+ *
+ *  DOMAIN_IO     - domain 2 includes all IO only
+ *  DOMAIN_KERNEL - domain 1 includes all kernel memory only
+ *  DOMAIN_USER   - domain 0 includes all user memory only
+ */
+#define DOMAIN_USER	0
+#define DOMAIN_KERNEL	1
+#define DOMAIN_TABLE	1
+#define DOMAIN_IO	2
+
+/*
+ * Domain types
+ */
+#define DOMAIN_NOACCESS	0
+#define DOMAIN_CLIENT	1
+#define DOMAIN_MANAGER	3
+
+#define domain_val(dom,type)	((type) << 2*(dom))
+
+#define set_domain(x)					\
+	do {						\
+	__asm__ __volatile__(				\
+	"mcr	p15, 0, %0, c3, c0	@ set domain"	\
+	  : : "r" (x));					\
+	} while (0)
+
+#define modify_domain(dom,type)				\
+	do {						\
+	unsigned int domain = current->tss.domain;	\
+	domain &= ~domain_val(dom, DOMAIN_MANAGER);	\
+	domain |= domain_val(dom, type);		\
+	current->tss.domain = domain;			\
+	set_domain(current->tss.domain);		\
+	} while (0)
+
+#endif
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/pgtable.h linux/include/asm-arm/proc-armv/pgtable.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/pgtable.h	Mon Aug  9 12:04:41 1999
+++ linux/include/asm-arm/proc-armv/pgtable.h	Mon Dec  3 20:25:24 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/proc-armv/pgtable.h
  *
- * Copyright (C) 1995, 1996, 1997 Russell King
+ * Copyright (C) 1995-1999 Russell King
  *
  * 12-Jan-1997	RMK	Altered flushing routines to use function pointers
  *			now possible to combine ARM6, ARM7 and StrongARM versions.
@@ -12,6 +12,7 @@
 #define __ASM_PROC_PGTABLE_H
 
 #include <asm/arch/memory.h>		/* For TASK_SIZE */
+#include <asm/proc/domain.h>
 
 #define LIBRARY_TEXT_START 0x0c000000
 
@@ -149,18 +150,7 @@
 #define VMALLOC_OFFSET	  (8*1024*1024)
 #define VMALLOC_START	  (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
-#define VMALLOC_END       (PAGE_OFFSET + 0x10000000)
-
-
-/*
- * Domains
- */
-#define DOMAIN_USER	0
-#define DOMAIN_KERNEL	1
-#define DOMAIN_TABLE	1
-#define DOMAIN_IO	2
-
-
+#define VMALLOC_END       (PAGE_OFFSET + 0x17f00000)
 
 #undef TEST_VERIFY_AREA
 
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/processor.h linux/include/asm-arm/proc-armv/processor.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/processor.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/proc-armv/processor.h	Mon Dec  3 20:25:24 2001
@@ -12,6 +12,8 @@
 #ifndef __ASM_PROC_PROCESSOR_H
 #define __ASM_PROC_PROCESSOR_H
 
+#include <asm/proc/domain.h>
+
 #define KERNEL_STACK_SIZE	PAGE_SIZE
 
 struct context_save_struct {
@@ -22,14 +24,21 @@
 	unsigned long r7;
 	unsigned long r8;
 	unsigned long r9;
+	unsigned long sl;
 	unsigned long fp;
 	unsigned long pc;
 };
 
-#define INIT_CSS (struct context_save_struct){ SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0 }
+#define INIT_CSS (struct context_save_struct){ SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+#define EXTRA_THREAD_STRUCT						\
+	unsigned int	domain;
+
+#define EXTRA_THREAD_STRUCT_INIT					\
+	, domain_val(DOMAIN_USER, DOMAIN_CLIENT) |			\
+	  domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) |			\
+	  domain_val(DOMAIN_IO, DOMAIN_CLIENT)
 
-#define EXTRA_THREAD_STRUCT
-#define EXTRA_THREAD_STRUCT_INIT
 #define SWAPPER_PG_DIR	(((unsigned long)swapper_pg_dir) - PAGE_OFFSET)
 
 #define start_thread(regs,pc,sp)					\
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/ptrace.h linux/include/asm-arm/proc-armv/ptrace.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/ptrace.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/proc-armv/ptrace.h	Mon Dec  3 20:25:24 2001
@@ -1,12 +1,33 @@
 /*
  * linux/include/asm-arm/proc-armv/ptrace.h
  *
- * Copyright (C) 1996 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
-
 #ifndef __ASM_PROC_PTRACE_H
 #define __ASM_PROC_PTRACE_H
 
+#define USR26_MODE	0x00
+#define FIQ26_MODE	0x01
+#define IRQ26_MODE	0x02
+#define SVC26_MODE	0x03
+#define USR_MODE	0x10
+#define FIQ_MODE	0x11
+#define IRQ_MODE	0x12
+#define SVC_MODE	0x13
+#define ABT_MODE	0x17
+#define UND_MODE	0x1b
+#define SYSTEM_MODE	0x1f
+#define MODE_MASK	0x1f
+#define F_BIT		0x40
+#define I_BIT		0x80
+#define CC_V_BIT	(1 << 28)
+#define CC_C_BIT	(1 << 29)
+#define CC_Z_BIT	(1 << 30)
+#define CC_N_BIT	(1 << 31)
+#define PCMASK		0
+
+#ifndef __ASSEMBLY__
+
 /* this struct defines the way the registers are stored on the
    stack during a system call. */
 
@@ -31,35 +52,12 @@
 #define ARM_r2		uregs[2]
 #define ARM_r1		uregs[1]
 #define ARM_r0		uregs[0]
-#define ARM_ORIG_r0	uregs[17] /* -1 */
+#define ARM_ORIG_r0	uregs[17]
 
-#define USR26_MODE	0x00
-#define FIQ26_MODE	0x01
-#define IRQ26_MODE	0x02
-#define SVC26_MODE	0x03
-#define USR_MODE	0x10
-#define FIQ_MODE	0x11
-#define IRQ_MODE	0x12
-#define SVC_MODE	0x13
-#define ABT_MODE	0x17
-#define UND_MODE	0x1b
-#define SYSTEM_MODE	0x1f
-#define MODE_MASK	0x1f
-#define F_BIT		0x40
-#define I_BIT		0x80
-#define CC_V_BIT	(1 << 28)
-#define CC_C_BIT	(1 << 29)
-#define CC_Z_BIT	(1 << 30)
-#define CC_N_BIT	(1 << 31)
+#ifdef __KERNEL__
 
-#if 0 /* GCC/egcs should be able to optimise this, IMHO */
-#define user_mode(regs)	\
-	((((regs)->ARM_cpsr & MODE_MASK) == USR_MODE) || \
-	 (((regs)->ARM_cpsr & MODE_MASK) == USR26_MODE))
-#else
 #define user_mode(regs)	\
 	(((regs)->ARM_cpsr & 0xf) == 0)
-#endif
 
 #define processor_mode(regs) \
 	((regs)->ARM_cpsr & MODE_MASK)
@@ -81,8 +79,8 @@
  */
 static inline int valid_user_regs(struct pt_regs *regs)
 {
-	if ((regs->ARM_cpsr & 0xf) == 0 ||
-	    (regs->ARM_cpsr & (F_BIT|I_BIT)))
+	if ((regs->ARM_cpsr & 0xf) == 0 &&
+	    (regs->ARM_cpsr & (F_BIT|I_BIT)) == 0)
 		return 1;
 
 	/*
@@ -93,5 +91,8 @@
 	return 0;
 }
 
-#endif
+#endif	/* __KERNEL__ */
 
+#endif	/* __ASSEMBLY__ */
+
+#endif
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/semaphore.h linux/include/asm-arm/proc-armv/semaphore.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/semaphore.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/proc-armv/semaphore.h	Mon Dec  3 20:25:24 2001
@@ -16,12 +16,12 @@
 	@ atomic down operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	subs	%1, %1, #1
-	orrmi	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrmi	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	blmi	" SYMBOL_NAME_STR(__down_failed)
@@ -42,12 +42,12 @@
 	@ atomic down interruptible operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	subs	%1, %1, #1
-	orrmi	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrmi	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	movpl	r0, #0
@@ -68,12 +68,12 @@
 	@ atomic down try lock operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	subs	%1, %1, #1
-	orrmi	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrmi	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	movpl	r0, #0
@@ -100,12 +100,12 @@
 	@ atomic up operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	adds	%1, %1, #1
-	orrls	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrle	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	blmi	" SYMBOL_NAME_STR(__up_wakeup)
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/system.h linux/include/asm-arm/proc-armv/system.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/system.h	Thu Jan  7 15:51:33 1999
+++ linux/include/asm-arm/proc-armv/system.h	Mon Dec  3 20:25:24 2001
@@ -116,7 +116,7 @@
 "	bic	%0, %0, #192\n"					\
 "	orr	%0, %0, %1\n"					\
 "	msr	cpsr, %0"					\
-	  : "=r" (temp)						\
+	  : "=&r" (temp)					\
 	  : "r" (x)						\
 	  : "memory");			\
 	} while (0)
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-armv/uaccess.h linux/include/asm-arm/proc-armv/uaccess.h
--- linux-2.2.18-pre8/include/asm-arm/proc-armv/uaccess.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/proc-armv/uaccess.h	Mon Dec  3 20:25:24 2001
@@ -2,36 +2,8 @@
  * linux/include/asm-arm/proc-armv/uaccess.h
  */
 
-/*
- * The fs functions are implemented on the ARMV3 and V4 architectures
- * using the domain register.
- *
- *  DOMAIN_IO     - domain 2 includes all IO only
- *  DOMAIN_KERNEL - domain 1 includes all kernel memory only
- *  DOMAIN_USER   - domain 0 includes all user memory only
- */
-
 #include <asm/hardware.h>
-
-#define DOMAIN_CLIENT	1
-#define DOMAIN_MANAGER	3
- 
-#define DOMAIN_USER_CLIENT	((DOMAIN_CLIENT) << 0)
-#define DOMAIN_USER_MANAGER	((DOMAIN_MANAGER) << 0)
-
-#define DOMAIN_KERNEL_CLIENT	((DOMAIN_CLIENT) << 2)
-#define DOMAIN_KERNEL_MANAGER	((DOMAIN_MANAGER) << 2)
-
-#define DOMAIN_IO_CLIENT	((DOMAIN_CLIENT) << 4)
-#define DOMAIN_IO_MANAGER	((DOMAIN_MANAGER) << 4)
-
-/*
- * When we want to access kernel memory in the *_user functions,
- * we change the domain register to KERNEL_DS, thus allowing
- * unrestricted access
- */
-#define KERNEL_DOMAIN	(DOMAIN_USER_CLIENT | DOMAIN_KERNEL_MANAGER | DOMAIN_IO_CLIENT)
-#define USER_DOMAIN	(DOMAIN_USER_CLIENT | DOMAIN_KERNEL_CLIENT  | DOMAIN_IO_CLIENT)
+#include <asm/proc/domain.h>
 
 /*
  * Note that this is actually 0x1,0000,0000
@@ -48,8 +20,7 @@
 {
 	current->addr_limit = fs;
 
-	__asm__ __volatile__("mcr	p15, 0, %0, c3, c0" :
-		: "r" (fs ? USER_DOMAIN : KERNEL_DOMAIN));
+	modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER);
 }
 
 /* We use 33-bit arithmetic here... */
@@ -201,6 +172,6 @@
 #define __do_strncpy_from_user(dst,src,count,res)		\
 	(res) = __arch_strncpy_from_user(dst,src,count)
 
-extern unsigned long __arch_strlen_user(const char *s);
-#define __do_strlen_user(s,res)					\
-	(res) = __arch_strlen_user(s)
+extern unsigned long __arch_strnlen_user(const char *s, long n);
+#define __do_strnlen_user(s,n,res)				\
+	(res) = __arch_strnlen_user(s,n)
diff -urN linux-2.2.18-pre8/include/asm-arm/proc-fns.h linux/include/asm-arm/proc-fns.h
--- linux-2.2.18-pre8/include/asm-arm/proc-fns.h	Tue May 11 16:30:36 1999
+++ linux/include/asm-arm/proc-fns.h	Mon Dec  3 20:25:24 2001
@@ -86,7 +86,7 @@
 			/*
 			 * Special stuff for a reset
 			 */
-			unsigned long (*reset)(void);
+			unsigned long (*reset)(unsigned long addr);
 			/*
 			 * flush an icached page
 			 */
@@ -99,6 +99,14 @@
 			 * purge cached data without (necessarily) writing it back
 			 */
 			void (*_cache_purge_area)(unsigned long start, unsigned long end);
+			/*
+			 * Unused
+			 */
+			void (*unused1)(void);
+			/*
+			 * Idle the processor
+			 */
+			int (*_do_idle)(void);
 		} armv3v4;
 		struct {
 			/* MEMC
diff -urN linux-2.2.18-pre8/include/asm-arm/processor.h linux/include/asm-arm/processor.h
--- linux-2.2.18-pre8/include/asm-arm/processor.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/processor.h	Mon Dec  3 20:25:24 2001
@@ -26,10 +26,9 @@
 
 #ifdef __KERNEL__
 
-#include <asm/assembler.h> 
-
 #define NR_DEBUGS	5
 
+#include <asm/ptrace.h>
 #include <asm/arch/processor.h>
 #include <asm/proc/processor.h>
 
diff -urN linux-2.2.18-pre8/include/asm-arm/procinfo.h linux/include/asm-arm/procinfo.h
--- linux-2.2.18-pre8/include/asm-arm/procinfo.h	Sat Jul 18 11:55:25 1998
+++ linux/include/asm-arm/procinfo.h	Mon Dec  3 20:25:24 2001
@@ -1,34 +1,45 @@
 /*
  * linux/include/asm-arm/procinfo.h
  *
- * Copyright (C) 1996 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
-
 #ifndef __ASM_PROCINFO_H
 #define __ASM_PROCINFO_H
 
-#include <asm/proc-fns.h>
+#ifndef __ASSEMBLY__
 
-#ifndef __ASSEMBLER__
+#include <asm/proc-fns.h>
 
-#define HWCAP_SWP	(1 << 0)
-#define HWCAP_HALF	(1 << 1)
+struct proc_info_item {
+	const char	 *manufacturer;
+	const char	 *cpu_name;
+};
 
-struct armversions {
-	const unsigned long id;		/* Processor ID			*/
-	const unsigned long mask;	/* Processor ID mask		*/
-	const char *manu;		/* Manufacturer			*/
-	const char *name;		/* Processor name		*/
-	const char *arch_vsn;		/* Architecture version		*/
-	const char *elf_vsn;		/* ELF library version		*/
-	const int hwcap;		/* ELF HWCAP			*/
-	const struct processor *proc;	/* Processor-specific ASM	*/
+/*
+ * Note!  struct processor is always defined if we're
+ * using MULTI_CPU, otherwise this entry is unused,
+ * but still exists.  NOTE! This structure is used
+ * by assembler code!  Check:
+ *  arch/arm/mm/proc-*.S and arch/arm/kernel/head-armv.S
+ */
+struct proc_info_list {
+	unsigned int	 cpu_val;
+	unsigned int	 cpu_mask;
+	unsigned long	 __cpu_mmu_flags;	/* used by head-armv.S */
+	unsigned long	 __cpu_flush;		/* used by head-armv.S */
+	const char	 *arch_name;
+	const char	 *elf_name;
+	unsigned int	 elf_hwcap;
+	struct proc_info_item *info;
+	struct processor *proc;
 };
 
-extern const struct armversions armidlist[];
-extern int armidindex;
+#endif	/* __ASSEMBLY__ */
 
-#endif
+#define HWCAP_SWP	1
+#define HWCAP_HALF	2
+#define HWCAP_THUMB	4
+#define HWCAP_26BIT	8	/* Play it safe */
 
 #endif
 
diff -urN linux-2.2.18-pre8/include/asm-arm/resource.h linux/include/asm-arm/resource.h
--- linux-2.2.18-pre8/include/asm-arm/resource.h	Tue Jan 20 16:39:43 1998
+++ linux/include/asm-arm/resource.h	Mon Dec  3 20:25:24 2001
@@ -29,7 +29,7 @@
 	{        0, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ MAX_TASKS_PER_USER, MAX_TASKS_PER_USER },	\
-	{ NR_OPEN, NR_OPEN },				\
+	{ INR_OPEN, INR_OPEN },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 }
diff -urN linux-2.2.18-pre8/include/asm-arm/setup.h linux/include/asm-arm/setup.h
--- linux-2.2.18-pre8/include/asm-arm/setup.h	Tue Jan 20 16:39:43 1998
+++ linux/include/asm-arm/setup.h	Mon Dec  3 20:25:24 2001
@@ -1,13 +1,26 @@
 /*
  * include/asm/setup.h
  *
- * Structure passed to kernel to tell it about the hardware it's running on
+ * Structure passed to kernel to tell it about the
+ * hardware it's running on.  See linux/Documentation/arm/Setup
+ * for more info.
  *
- * Copyright (C) 1997,1998 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
 #ifndef __ASMARM_SETUP_H
 #define __ASMARM_SETUP_H
 
+/*
+ * Usage:
+ *  - do not go blindly adding fields, add them at the end
+ *  - when adding fields, don't rely on the address until
+ *    a patch from me has been released
+ *  - unused fields should be zero (for future expansion)
+ *  - this structure is relatively short-lived - only
+ *    guaranteed to contain useful data in setup_arch()
+ */
+#define COMMAND_LINE_SIZE 1024
+
 struct param_struct {
     union {
 	struct {
@@ -33,6 +46,9 @@
 	    unsigned long initrd_start;		/* 64 */
 	    unsigned long initrd_size;		/* 68 */
 	    unsigned long rd_start;		/* 72 */
+	    unsigned long system_rev;		/* 76 */
+	    unsigned long system_serial_low;	/* 80 */
+	    unsigned long system_serial_high;	/* 84 */
 	} s;
 	char unused[256];
     } u1;
@@ -43,7 +59,7 @@
 	    char n[1024 - sizeof(unsigned long)];
 	} s;
     } u2;
-    char commandline[256];
+    char commandline[COMMAND_LINE_SIZE];
 };
 
 #endif
diff -urN linux-2.2.18-pre8/include/asm-arm/socket.h linux/include/asm-arm/socket.h
--- linux-2.2.18-pre8/include/asm-arm/socket.h	Mon Aug  9 12:04:41 1999
+++ linux/include/asm-arm/socket.h	Mon Dec  3 20:25:24 2001
@@ -39,6 +39,7 @@
 #define SO_ATTACH_FILTER        26
 #define SO_DETACH_FILTER        27
 
+#ifdef __KERNEL__
 /* Socket types. */
 #define SOCK_STREAM	1		/* stream (connection) socket	*/
 #define SOCK_DGRAM	2		/* datagram (conn.less) socket	*/
@@ -50,5 +51,6 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#endif
 
 #endif /* _ASM_SOCKET_H */
diff -urN linux-2.2.18-pre8/include/asm-arm/string.h linux/include/asm-arm/string.h
--- linux-2.2.18-pre8/include/asm-arm/string.h	Tue Jan 20 16:39:43 1998
+++ linux/include/asm-arm/string.h	Mon Dec  3 20:25:24 2001
@@ -14,11 +14,24 @@
 #define __HAVE_ARCH_MEMCPY
 #define __HAVE_ARCH_MEMMOVE
 #define __HAVE_ARCH_MEMSET
+#define __HAVE_ARCH_MEMCHR
 
 #define __HAVE_ARCH_MEMZERO
-extern void memzero(void *ptr, int n);
 
-extern void memsetl (unsigned long *, unsigned long, int n);
+extern void __memzero(void *ptr, __kernel_size_t n);
+
+#define memset(p,v,n)							\
+	({								\
+		if ((n) != 0) {						\
+			if (__builtin_constant_p((v)) && (v) == 0)	\
+				__memzero((p),(n));			\
+			else						\
+				memset((p),(v),(n));			\
+		}							\
+		(p);							\
+	})
+
+#define memzero(p,n) ({ if ((n) != 0) __memzero((p),(n)); (p); })
 
 #endif
  
diff -urN linux-2.2.18-pre8/include/asm-arm/system.h linux/include/asm-arm/system.h
--- linux-2.2.18-pre8/include/asm-arm/system.h	Sat May  8 11:07:16 1999
+++ linux/include/asm-arm/system.h	Mon Dec  3 20:25:24 2001
@@ -35,7 +35,7 @@
 
 /*
  * Sort out a definition for machine_arch_type
- * The rules basically are:
+ * The rules are:
  * 1. If one architecture is selected, then all machine_is_xxx()
  *    are constant.
  * 2. If two or more architectures are selected, then the selected
@@ -118,16 +118,8 @@
 #define machine_arch_type	__machine_arch_type
 #endif
 
-/*
- * task_struct isn't always declared - forward-declare it here.
- */
-struct task_struct;
-
 #include <asm/proc-fns.h>
 
-extern void arm_malalignedptr(const char *, void *, volatile void *);
-extern void arm_invalidptr(const char *, int);
-
 #define xchg(ptr,x) \
 	((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
 
@@ -136,11 +128,13 @@
 /*
  * switch_to(prev, next) should switch from task `prev' to `next'
  * `prev' will never be the same as `next'.
- *
- * `next' and `prev' should be struct task_struct, but it isn't always defined
  */
 #define switch_to(prev,next,last) do { last = processor._switch_to(prev,next); } while (0)
 
+extern void arm_malalignedptr(const char *, void *, volatile void *);
+extern void arm_invalidptr(const char *, int);
+extern asmlinkage void __backtrace(void);
+
 /*
  * Include processor dependent parts
  */
@@ -151,8 +145,6 @@
 #define rmb() mb()
 #define wmb() mb()
 #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t");
-
-extern asmlinkage void __backtrace(void);
 
 #endif
 
diff -urN linux-2.2.18-pre8/include/asm-arm/therm.h linux/include/asm-arm/therm.h
--- linux-2.2.18-pre8/include/asm-arm/therm.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/therm.h	Mon Dec  3 20:25:24 2001
@@ -0,0 +1,28 @@
+/*
+ * linux/include/asm-arm/therm.h: Definitions for Dallas Semiconductor
+ *  DS1620 thermometer driver (as used in the Rebel.com NetWinder)
+ */
+#ifndef __ASM_THERM_H
+#define __ASM_THERM_H
+
+/* ioctl numbers for /dev/therm */
+#define CMD_SET_THERMOSTATE	0x53
+#define CMD_GET_THERMOSTATE	0x54
+#define CMD_GET_STATUS		0x56
+#define CMD_GET_TEMPERATURE	0x57
+#define CMD_SET_THERMOSTATE2	0x58
+#define CMD_GET_THERMOSTATE2	0x59
+#define CMD_GET_TEMPERATURE2	0x5a
+#define CMD_GET_FAN		0x5b
+#define CMD_SET_FAN		0x5c
+
+#define FAN_OFF			0
+#define FAN_ON			1
+#define FAN_ALWAYS_ON		2
+
+struct therm {
+	int hi;
+	int lo;
+};
+
+#endif
diff -urN linux-2.2.18-pre8/include/asm-arm/uaccess.h linux/include/asm-arm/uaccess.h
--- linux-2.2.18-pre8/include/asm-arm/uaccess.h	Mon Aug 24 12:29:35 1998
+++ linux/include/asm-arm/uaccess.h	Mon Dec  3 20:25:24 2001
@@ -121,12 +121,14 @@
 	return res;
 }
 
-extern __inline__ long strlen_user (const char *s)
+#define strlen_user(s)	strnlen_user(s, ~0UL >> 1)
+
+extern __inline__ long strnlen_user (const char *s, long n)
 {
 	unsigned long res = 0;
 
 	if (__addr_ok(s))
-		__do_strlen_user (s, res);
+		__do_strnlen_user (s, n, res);
 
 	return res;
 }
diff -urN linux-2.2.18-pre8/include/asm-arm/unaligned.h linux/include/asm-arm/unaligned.h
--- linux-2.2.18-pre8/include/asm-arm/unaligned.h	Tue Jan 20 16:39:43 1998
+++ linux/include/asm-arm/unaligned.h	Mon Dec  3 20:25:24 2001
@@ -1,59 +1,115 @@
-#ifndef __ARM_UNALIGNED_H
-#define __ARM_UNALIGNED_H
+#ifndef __ASM_ARM_UNALIGNED_H
+#define __ASM_ARM_UNALIGNED_H
 
-#define get_unaligned(ptr) \
-	((__typeof__(*(ptr)))__get_unaligned((ptr), sizeof(*(ptr))))
+#include <asm/types.h>
 
-#define put_unaligned(val, ptr) \
-	__put_unaligned((unsigned long)(val), (ptr), sizeof(*(ptr)))
+extern int __bug_unaligned_x(void *ptr);
 
-extern void bad_unaligned_access_length (void);
-	
-extern inline unsigned long __get_unaligned(const void *ptr, size_t size)
+/*
+ * What is the most efficient way of loading/storing an unaligned value?
+ *
+ * That is the subject of this file.  Efficiency here is defined as
+ * minimum code size with minimum register usage for the common cases.
+ * It is currently not believed that long longs are common, so we
+ * trade efficiency for the chars, shorts and longs against the long
+ * longs.
+ *
+ * Current stats with gcc 2.7.2.2 for these functions:
+ *
+ *	ptrsize	get:	code	regs	put:	code	regs
+ *	1		1	1		1	2
+ *	2		3	2		3	2
+ *	4		7	3		7	3
+ *	8		20	6		16	6
+ *
+ * gcc 2.95.1 seems to code differently:
+ *
+ *	ptrsize	get:	code	regs	put:	code	regs
+ *	1		1	1		1	2
+ *	2		3	2		3	2
+ *	4		7	4		7	4
+ *	8		19	8		15	6
+ *
+ * which may or may not be more efficient (depending upon whether
+ * you can afford the extra registers).  Hopefully the gcc 2.95
+ * is inteligent enough to decide if it is better to use the
+ * extra register, but evidence so far seems to suggest otherwise.
+ *
+ * Unfortunately, gcc is not able to optimise the high word
+ * out of long long >> 32, or the low word from long long << 32
+ */
+
+#define __get_unaligned_2(__p)					\
+	(__p[0] | __p[1] << 8)
+
+#define __get_unaligned_4(__p)					\
+	(__p[0] | __p[1] << 8 | __p[2] << 16 | __p[3] << 24)
+
+#define get_unaligned(ptr)					\
+	({							\
+		__typeof__(*(ptr)) __v;				\
+		__u8 *__p = (__u8 *)(ptr);			\
+		switch (sizeof(*(ptr))) {			\
+		case 1:	__v = *(ptr);			break;	\
+		case 2: __v = __get_unaligned_2(__p);	break;	\
+		case 4: __v = __get_unaligned_4(__p);	break;	\
+		case 8: {					\
+				unsigned int __v1, __v2;	\
+				__v2 = __get_unaligned_4((__p+4)); \
+				__v1 = __get_unaligned_4(__p);	\
+				__v = ((unsigned long long)__v2 << 32 | __v1);	\
+			}					\
+			break;					\
+		default: __v = __bug_unaligned_x(__p);	break;	\
+		}						\
+		__v;						\
+	})
+
+
+static inline void __put_unaligned_2(__u32 __v, register __u8 *__p)
+{
+	*__p++ = __v;
+	*__p++ = __v >> 8;
+}
+
+static inline void __put_unaligned_4(__u32 __v, register __u8 *__p)
 {
-    unsigned long val;
-    switch (size) {
-	case 1:
-	    val = *(const unsigned char *)ptr;
-	    break;
-
-	case 2:
-	    val = ((const unsigned char *)ptr)[0] | (((const unsigned char *)ptr)[1] << 8);
-	    break;
-
-	case 4:
-	    val = ((const unsigned char *)ptr)[0]        | (((const unsigned char *)ptr)[1] << 8) |
-		 (((const unsigned char *)ptr)[2]) << 16 | (((const unsigned char *)ptr)[3] << 24);
-	    break;
-
-	default:
-	    bad_unaligned_access_length ();
-    }
-    return val;
+	__put_unaligned_2(__v >> 16, __p + 2);
+	__put_unaligned_2(__v, __p);
 }
 
-extern inline void __put_unaligned(unsigned long val, void *ptr, size_t size)
+static inline void __put_unaligned_8(const unsigned long long __v, register __u8 *__p)
 {
-    switch (size) {
-	case 1:
-	    *(unsigned char *)ptr = val;
-	    break;
-
-	case 2:
-	    ((unsigned char *)ptr)[0] = val;
-	    ((unsigned char *)ptr)[1] = val >> 8;
-	    break;
-
-	case 4:
-	    ((unsigned char *)ptr)[0] = val;
-	    ((unsigned char *)ptr)[1] = val >> 8;
-	    ((unsigned char *)ptr)[2] = val >> 16;
-	    ((unsigned char *)ptr)[3] = val >> 24;
-	    break;
-
-	default:
-	    bad_unaligned_access_length ();
-    }
+	/*
+	 * tradeoff: 8 bytes of stack for all unaligned puts (2
+	 * instructions), or an extra register in the long long
+	 * case - go for the extra register.
+	 */
+	__put_unaligned_4(__v >> 32, __p+4);
+	__put_unaligned_4(__v, __p);
 }
 
+/*
+ * Try to store an unaligned value as efficiently as possible.
+ */
+#define put_unaligned(val,ptr)					\
+	({							\
+		switch (sizeof(*(ptr))) {			\
+		case 1:						\
+			*(ptr) = (val);				\
+			break;					\
+		case 2: __put_unaligned_2((val),(__u8 *)(ptr));	\
+			break;					\
+		case 4:	__put_unaligned_4((val),(__u8 *)(ptr));	\
+			break;					\
+		case 8:	__put_unaligned_8((val),(__u8 *)(ptr)); \
+			break;					\
+		default: __bug_unaligned_x(ptr);		\
+			break;					\
+		}						\
+		(void) 0;					\
+	})
+
+
 #endif
+
diff -urN linux-2.2.18-pre8/include/asm-arm/unistd.h linux/include/asm-arm/unistd.h
--- linux-2.2.18-pre8/include/asm-arm/unistd.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/unistd.h	Mon Dec  3 20:25:24 2001
@@ -59,7 +59,7 @@
 #define __NR_geteuid			(__NR_SYSCALL_BASE+ 49)
 #define __NR_getegid			(__NR_SYSCALL_BASE+ 50)
 #define __NR_acct			(__NR_SYSCALL_BASE+ 51)
-#define __NR_phys			(__NR_SYSCALL_BASE+ 52)
+#define __NR_umount2			(__NR_SYSCALL_BASE+ 52)
 #define __NR_lock			(__NR_SYSCALL_BASE+ 53)
 #define __NR_ioctl			(__NR_SYSCALL_BASE+ 54)
 #define __NR_fcntl			(__NR_SYSCALL_BASE+ 55)
diff -urN linux-2.2.18-pre8/include/linux/adfs_fs.h linux/include/linux/adfs_fs.h
--- linux-2.2.18-pre8/include/linux/adfs_fs.h	Fri Apr 23 21:20:38 1999
+++ linux/include/linux/adfs_fs.h	Mon Dec  3 20:25:24 2001
@@ -2,113 +2,43 @@
 #define _ADFS_FS_H
 
 #include <linux/types.h>
-/*
- * Structures of data on the disk
- */
 
 /*
  * Disc Record at disc address 0xc00
  */
 struct adfs_discrecord {
-    unsigned char  log2secsize;
-    unsigned char  secspertrack;
-    unsigned char  heads;
-    unsigned char  density;
-    unsigned char  idlen;
-    unsigned char  log2bpmb;
-    unsigned char  skew;
-    unsigned char  bootoption;
-    unsigned char  lowsector;
-    unsigned char  nzones;
-    unsigned short zone_spare;
-    unsigned long  root;
-    unsigned long  disc_size;
-    unsigned short disc_id;
-    unsigned char  disc_name[10];
-    unsigned long  disc_type;
-    unsigned long  disc_size_high;
-    unsigned char  log2sharesize:4;
-    unsigned char  unused:4;
-    unsigned char  big_flag:1;
+    __u8  log2secsize;
+    __u8  secspertrack;
+    __u8  heads;
+    __u8  density;
+    __u8  idlen;
+    __u8  log2bpmb;
+    __u8  skew;
+    __u8  bootoption;
+    __u8  lowsector;
+    __u8  nzones;
+    __u16 zone_spare;
+    __u32 root;
+    __u32 disc_size;
+    __u16 disc_id;
+    __u8  disc_name[10];
+    __u32 disc_type;
+    __u32 disc_size_high;
+    __u8  log2sharesize:4;
+    __u8  unused40:4;
+    __u8  big_flag:1;
+    __u8  unused41:1;
+    __u8  nzones_high;
+    __u32 format_version;
+    __u32 root_size;
+    __u8  unused52[60 - 52];
 };
 
 #define ADFS_DISCRECORD		(0xc00)
 #define ADFS_DR_OFFSET		(0x1c0)
 #define ADFS_DR_SIZE		 60
+#define ADFS_DR_SIZE_BITS	(ADFS_DR_SIZE << 3)
 #define ADFS_SUPER_MAGIC	 0xadf5
-#define ADFS_FREE_FRAG		 0
-#define ADFS_BAD_FRAG		 1
-#define ADFS_ROOT_FRAG		 2
-
-/*
- * Directory header
- */
-struct adfs_dirheader {
-	unsigned char startmasseq;
-	unsigned char startname[4];
-};
-
-#define ADFS_NEWDIR_SIZE	2048
-#define ADFS_OLDDIR_SIZE	1024
-#define ADFS_NUM_DIR_ENTRIES	77
-
-/*
- * Directory entries
- */
-struct adfs_direntry {
-	char dirobname[10];
-#define ADFS_NAME_LEN 10
-	__u8 dirload[4];
-	__u8 direxec[4];
-	__u8 dirlen[4];
-	__u8 dirinddiscadd[3];
-	__u8 newdiratts;
-#define ADFS_NDA_OWNER_READ	(1 << 0)
-#define ADFS_NDA_OWNER_WRITE	(1 << 1)
-#define ADFS_NDA_LOCKED		(1 << 2)
-#define ADFS_NDA_DIRECTORY	(1 << 3)
-#define ADFS_NDA_EXECUTE	(1 << 4)
-#define ADFS_NDA_PUBLIC_READ	(1 << 5)
-#define ADFS_NDA_PUBLIC_WRITE	(1 << 6)
-};
-
-#define ADFS_MAX_NAME_LEN	255
-struct adfs_idir_entry {
-	__u32		inode_no;			/* Address		*/
-	__u32		file_id;			/* file id		*/
-	__u32		name_len;			/* name length		*/
-	__u32		size;				/* size			*/
-	__u32		mtime;				/* modification time	*/
-	__u32		filetype;			/* RiscOS file type	*/
-	__u8		mode;				/* internal mode	*/
-	char		name[ADFS_MAX_NAME_LEN];	/* file name		*/
-};
-
-/*
- * Directory tail
- */
-union adfs_dirtail {
-	struct {
-		unsigned char dirlastmask;
-		char dirname[10];
-		unsigned char dirparent[3];
-		char dirtitle[19];
-		unsigned char reserved[14];
-		unsigned char endmasseq;
-		unsigned char endname[4];
-		unsigned char dircheckbyte;
-	} old;
-	struct {
-		unsigned char dirlastmask;
-		unsigned char reserved[2];
-		unsigned char dirparent[3];
-		char dirtitle[19];
-		char dirname[10];
-		unsigned char endmasseq;
-		unsigned char endname[4];
-		unsigned char dircheckbyte;
-	} new;
-};
 
 #ifdef __KERNEL__
 /*
@@ -129,47 +59,8 @@
 	return (result & 0xff) != ptr[511];
 }
 
-/* dir.c */
-extern unsigned int adfs_val (unsigned char *p, int len);
-extern int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp);
-extern int adfs_dir_read (struct inode *inode, struct buffer_head **bhp);
-extern int adfs_dir_check (struct inode *inode, struct buffer_head **bhp,
-			   int buffers, union adfs_dirtail *dtp);
-extern void adfs_dir_free (struct buffer_head **bhp, int buffers);
-extern int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
-			 int buffers, int pos, unsigned long parent_object_id,
-			 struct adfs_idir_entry *ide);
-extern int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
-				int buffers, unsigned int index,
-				struct adfs_idir_entry *ide);
-
-/* inode.c */
-extern int adfs_inode_validate (struct inode *inode);
-extern unsigned long adfs_inode_generate (unsigned long parent_id, int diridx);
-extern unsigned long adfs_inode_objid (struct inode *inode);
-extern unsigned int adfs_parent_bmap (struct inode *inode, int block);
-extern int adfs_bmap (struct inode *inode, int block);
-extern void adfs_read_inode (struct inode *inode);
-
-/* map.c */
-extern int adfs_map_lookup (struct super_block *sb, int frag_id, int offset);
-
-/* namei.c */
-extern struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry);
+#endif
 
-/* super.c */
 extern int init_adfs_fs (void);
-extern void adfs_error (struct super_block *, const char *, const char *, ...);
-
-/*
- * Inodes and file operations
- */
-
-/* dir.c */
-extern struct inode_operations adfs_dir_inode_operations;
-
-/* file.c */
-extern struct inode_operations adfs_file_inode_operations;
-#endif
 
 #endif
diff -urN linux-2.2.18-pre8/include/linux/adfs_fs_i.h linux/include/linux/adfs_fs_i.h
--- linux-2.2.18-pre8/include/linux/adfs_fs_i.h	Tue Jan 20 16:44:58 1998
+++ linux/include/linux/adfs_fs_i.h	Mon Dec  3 20:25:24 2001
@@ -11,7 +11,12 @@
  * adfs file system inode data in memory
  */
 struct adfs_inode_info {
-	unsigned long	file_id;		/* id of fragments containing actual data */
+	unsigned long	parent_id;	/* object id of parent		*/
+	__u32		loadaddr;	/* RISC OS load address		*/
+	__u32		execaddr;	/* RISC OS exec address		*/
+	unsigned int	filetype;	/* RISC OS file type		*/
+	unsigned int	attr;		/* RISC OS permissions		*/
+	int		stamped:1;	/* RISC OS file has date/time	*/
 };
 
 #endif
diff -urN linux-2.2.18-pre8/include/linux/adfs_fs_sb.h linux/include/linux/adfs_fs_sb.h
--- linux-2.2.18-pre8/include/linux/adfs_fs_sb.h	Fri Apr 23 21:26:07 1999
+++ linux/include/linux/adfs_fs_sb.h	Mon Dec  3 20:25:24 2001
@@ -1,33 +1,38 @@
 /*
  *  linux/include/linux/adfs_fs_sb.h
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
 
 #ifndef _ADFS_FS_SB
 #define _ADFS_FS_SB
 
-#include <linux/adfs_fs.h>
+/*
+ * Forward-declare this
+ */
+struct adfs_discmap;
+struct adfs_dir_ops;
 
 /*
- * adfs file system superblock data in memory
+ * ADFS file system superblock data in memory
  */
 struct adfs_sb_info {
-	struct buffer_head *s_sbh;	/* buffer head containing disc record	 */
-	struct adfs_discrecord *s_dr;	/* pointer to disc record in s_sbh	 */
-	uid_t	s_uid;			/* owner uid				 */
-	gid_t	s_gid;			/* owner gid				 */
-	int	s_owner_mask;		/* ADFS Owner perm -> unix perm		 */
-	int	s_other_mask;		/* ADFS Other perm -> unix perm		 */
-	__u16	s_zone_size;		/* size of a map zone in bits		 */
-	__u16	s_ids_per_zone;		/* max. no ids in one zone		 */
-	__u32	s_idlen;		/* length of ID in map			 */
-	__u32	s_map_size;		/* size of a map			 */
-	__u32	s_zonesize;		/* zone size (in map bits)		 */
-	__u32	s_map_block;		/* block address of map			 */
-	struct buffer_head **s_map;	/* bh list containing map		 */
-	__u32	s_root;			/* root disc address			 */
-	__s8	s_map2blk;		/* shift left by this for map->sector	 */
+	struct adfs_discmap *s_map;	/* bh list containing map		 */
+	struct adfs_dir_ops *s_dir;	/* directory operations			 */
+
+	uid_t		s_uid;		/* owner uid				 */
+	gid_t		s_gid;		/* owner gid				 */
+	umode_t		s_owner_mask;	/* ADFS owner perm -> unix perm		 */
+	umode_t		s_other_mask;	/* ADFS other perm -> unix perm		 */
+
+	__u32		s_ids_per_zone;	/* max. no ids in one zone		 */
+	__u32		s_idlen;	/* length of ID in map			 */
+	__u32		s_map_size;	/* sector size of a map			 */
+	unsigned long	s_size;		/* total size (in blocks) of this fs	 */
+	signed int	s_map2blk;	/* shift left by this for map->sector	 */
+	unsigned int	s_log2sharesize;/* log2 share size			 */
+	unsigned int	s_version;	/* disc format version			 */
+	unsigned int	s_namelen;	/* maximum number of characters in name	 */
 };
 
 #endif
diff -urN linux-2.2.18-pre8/include/linux/dcache.h linux/include/linux/dcache.h
--- linux-2.2.18-pre8/include/linux/dcache.h	Tue Jan  4 10:12:24 2000
+++ linux/include/linux/dcache.h	Mon Dec  3 20:25:24 2001
@@ -151,7 +151,7 @@
 /* test whether root is busy without destroying dcache */
 extern int is_root_busy(struct dentry *);
 
-/* test whether we have any submounts in a subdir tree */
+/* test whether we have any submounts */
 extern int have_submounts(struct dentry *);
 
 /*
diff -urN linux-2.2.18-pre8/include/linux/errno.h linux/include/linux/errno.h
--- linux-2.2.18-pre8/include/linux/errno.h	Thu Jun 12 15:28:32 1997
+++ linux/include/linux/errno.h	Mon Dec  3 20:25:24 2001
@@ -11,6 +11,16 @@
 #define ERESTARTNOHAND	514	/* restart if no handler.. */
 #define ENOIOCTLCMD	515	/* No ioctl command */
 
+/* Defined for the NFSv3 protocol */
+#define EBADHANDLE	521	/* Illegal NFS file handle */
+#define ENOTSYNC	522	/* Update synchronization mismatch */
+#define EBADCOOKIE	523	/* Cookie is stale */
+#define ENOTSUPP	524	/* Operation is not supported */
+#define ETOOSMALL	525	/* Buffer or request is too small */
+#define ESERVERFAULT	526	/* An untranslatable error occurred */
+#define EBADTYPE	527	/* Type not supported by server */
+#define EJUKEBOX	528	/* Request initiated, but will not complete before timeout */
+
 #endif
 
 #endif
diff -urN linux-2.2.18-pre8/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.2.18-pre8/include/linux/fs.h	Mon Dec  3 20:24:44 2001
+++ linux/include/linux/fs.h	Mon Dec  3 20:25:24 2001
@@ -469,6 +469,8 @@
 	off_t fl_end;
 
 	void (*fl_notify)(struct file_lock *);	/* unblock callback */
+	void (*fl_insert)(struct file_lock *);	/* lock insertion callback */
+	void (*fl_remove)(struct file_lock *);	/* lock removal callback */
 
 	union {
 		struct nfs_lock_info	nfs_fl;
@@ -626,6 +628,8 @@
 	int (*smap) (struct inode *,int);
 	int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int, int);
 	int (*revalidate) (struct dentry *);
+	int (*prepare_write) (struct file *, struct page *, unsigned, unsigned);
+	int (*sync_page) (struct page *);
 };
 
 struct super_operations {
@@ -810,6 +814,7 @@
 extern void sync_supers(kdev_t dev);
 extern int bmap(struct inode * inode,int block);
 extern int notify_change(struct dentry *, struct iattr *);
+extern int vfs_permission(struct inode * inode,int mask);
 extern int permission(struct inode * inode,int mask);
 extern int get_write_access(struct inode *inode);
 extern void put_write_access(struct inode *inode);
@@ -854,7 +859,10 @@
 extern void iput(struct inode *);
 extern struct inode * igrab(struct inode *inode);
 extern ino_t iunique(struct super_block *, ino_t);
-extern struct inode * iget(struct super_block *, unsigned long);
+
+typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
+extern struct inode * iget4(struct super_block *, unsigned long, find_inode_t, void *);
+extern struct inode * iget(struct super_block *sb, unsigned long ino);
 extern struct inode * iget_in_use (struct super_block *, unsigned long);
 extern void clear_inode(struct inode *);
 extern struct inode * get_empty_inode(void);
diff -urN linux-2.2.18-pre8/include/linux/i2c.h linux/include/linux/i2c.h
--- linux-2.2.18-pre8/include/linux/i2c.h	Tue Jan  4 10:12:24 2000
+++ linux/include/linux/i2c.h	Mon Dec  3 20:25:24 2001
@@ -35,12 +35,13 @@
 #define I2C_DRIVERID_VIDEOTEXT		 3
 #define I2C_DRIVERID_VIDEODECODER	 4
 #define I2C_DRIVERID_VIDEOENCODER	 5
-
+  
 #define I2C_BUSID_BT848		1	/* I2C bus on a BT848 */
 	/* 2 is used in 2.3.x */
 #define I2C_BUSID_BUZ		3	/* I2C bus on a BUZ */
 #define I2C_BUSID_ZORAN		4	/* I2C bus on a Zoran */
-#define I2C_BUSID_SGIVWFB	5	/* Moved to be unique */
+#define I2C_BUSID_CYBER2000	5	/* I2C bus on a Cyber2000 */
+
 
 /*
  * struct for a driver for a i2c chip (tuner, soundprocessor,
diff -urN linux-2.2.18-pre8/include/linux/lockd/lockd.h linux/include/linux/lockd/lockd.h
--- linux-2.2.18-pre8/include/linux/lockd/lockd.h	Tue May 11 10:36:17 1999
+++ linux/include/linux/lockd/lockd.h	Mon Dec  3 20:25:24 2001
@@ -138,12 +138,14 @@
 					struct sockaddr_in *, int, int);
 struct rpc_clnt * nlm_bind_host(struct nlm_host *);
 void		  nlm_rebind_host(struct nlm_host *);
+struct nlm_host * nlm_get_host(struct nlm_host *);
 void		  nlm_release_host(struct nlm_host *);
 void		  nlm_shutdown_hosts(void);
 
 /*
  * Server-side lock handling
  */
+int		  nlmsvc_async_call(struct nlm_rqst *, u32, rpc_action);
 u32		  nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
 					struct nlm_lock *, int, struct nlm_cookie *);
 u32		  nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
diff -urN linux-2.2.18-pre8/include/linux/lockd/nlm.h linux/include/linux/lockd/nlm.h
--- linux-2.2.18-pre8/include/linux/lockd/nlm.h	Mon Apr  7 11:35:31 1997
+++ linux/include/linux/lockd/nlm.h	Mon Dec  3 20:25:24 2001
@@ -10,19 +10,21 @@
 #define LINUX_LOCKD_NLM_H
 
 /* Maximum file offset in file_lock.fl_end */
-#ifdef OFFSET_MAX
-# define NLM_OFFSET_MAX		OFFSET_MAX
-#else
 # define NLM_OFFSET_MAX		((off_t) 0x7fffffff)
-#endif
+# define NLM4_OFFSET_MAX	((s64) ((~(u64)0) >> 1))
 
 /* Return states for NLM */
 enum {
-	NLM_LCK_GRANTED = 0,
-	NLM_LCK_DENIED,
-	NLM_LCK_DENIED_NOLOCKS,
-	NLM_LCK_BLOCKED,
-	NLM_LCK_DENIED_GRACE_PERIOD,
+	NLM_LCK_GRANTED			= 0,
+	NLM_LCK_DENIED			= 1,
+	NLM_LCK_DENIED_NOLOCKS		= 2,
+	NLM_LCK_BLOCKED			= 3,
+	NLM_LCK_DENIED_GRACE_PERIOD	= 4,
+	NLM_DEADLCK			= 5,
+	NLM_ROFS			= 6,
+	NLM_STALE_FH			= 7,
+	NLM_FBIG			= 8,
+	NLM_FAILED			= 9,
 };
 
 #define NLM_PROGRAM		100021
diff -urN linux-2.2.18-pre8/include/linux/lockd/xdr.h linux/include/linux/lockd/xdr.h
--- linux-2.2.18-pre8/include/linux/lockd/xdr.h	Tue May 11 10:36:15 1999
+++ linux/include/linux/lockd/xdr.h	Mon Dec  3 20:25:24 2001
@@ -74,6 +74,7 @@
 #define NLMSVC_XDRSIZE		sizeof(struct nlm_args)
 
 void	nlmxdr_init(void);
+void	nlmxdr_shutdown(void);
 int	nlmsvc_decode_testargs(struct svc_rqst *, u32 *, struct nlm_args *);
 int	nlmsvc_encode_testres(struct svc_rqst *, u32 *, struct nlm_res *);
 int	nlmsvc_decode_lockargs(struct svc_rqst *, u32 *, struct nlm_args *);
diff -urN linux-2.2.18-pre8/include/linux/nfs.h linux/include/linux/nfs.h
--- linux-2.2.18-pre8/include/linux/nfs.h	Sat Mar  6 14:21:13 1999
+++ linux/include/linux/nfs.h	Mon Dec  3 20:25:24 2001
@@ -1,54 +1,60 @@
 /*
  * NFS protocol definitions
+ *
+ * This file contains constants mostly for Version 2 of the protocol,
+ * but also has a couple of NFSv3 bits in (notably the error codes).
  */
 #ifndef _LINUX_NFS_H
 #define _LINUX_NFS_H
 
 #include <linux/sunrpc/msg_prot.h>
 
-#define NFS_PORT	2049
-#define NFS_MAXDATA	8192
-#define NFS_MAXPATHLEN	1024
-#define NFS_MAXNAMLEN	255
-#define NFS_MAXGROUPS	16
-#define NFS_FHSIZE	32
-#define NFS_COOKIESIZE	4
-#define NFS_FIFO_DEV	(-1)
-#define NFSMODE_FMT	0170000
-#define NFSMODE_DIR	0040000
-#define NFSMODE_CHR	0020000
-#define NFSMODE_BLK	0060000
-#define NFSMODE_REG	0100000
-#define NFSMODE_LNK	0120000
-#define NFSMODE_SOCK	0140000
-#define NFSMODE_FIFO	0010000
+#define NFS_PROGRAM		100003
 
-	
+/*
+ * NFS stats. The good thing with these values is that NFSv3 errors are
+ * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
+ * no-one uses anyway), so we can happily mix code as long as we make sure
+ * no NFSv3 errors are returned to NFSv2 clients.
+ * Error codes that have a `--' in the v2 column are not part of the
+ * standard, but seem to be widely used nevertheless.
+ */
 enum nfs_stat {
-	NFS_OK = 0,
-	NFSERR_PERM = 1,
-	NFSERR_NOENT = 2,
-	NFSERR_IO = 5,
-	NFSERR_NXIO = 6,
-	NFSERR_EAGAIN = 11,
-	NFSERR_ACCES = 13,
-	NFSERR_EXIST = 17,
-	NFSERR_XDEV = 18,
-	NFSERR_NODEV = 19,
-	NFSERR_NOTDIR = 20,
-	NFSERR_ISDIR = 21,
-	NFSERR_INVAL = 22,	/* that Sun forgot */
-	NFSERR_FBIG = 27,
-	NFSERR_NOSPC = 28,
-	NFSERR_ROFS = 30,
-	NFSERR_OPNOTSUPP = 45,
-	NFSERR_NAMETOOLONG = 63,
-	NFSERR_NOTEMPTY = 66,
-	NFSERR_DQUOT = 69,
-	NFSERR_STALE = 70,
-	NFSERR_WFLUSH = 99
+	NFS_OK = 0,			/* v2 v3 */
+	NFSERR_PERM = 1,		/* v2 v3 */
+	NFSERR_NOENT = 2,		/* v2 v3 */
+	NFSERR_IO = 5,			/* v2 v3 */
+	NFSERR_NXIO = 6,		/* v2 v3 */
+	NFSERR_EAGAIN = 11,		/* v2 v3 */
+	NFSERR_ACCES = 13,		/* v2 v3 */
+	NFSERR_EXIST = 17,		/* v2 v3 */
+	NFSERR_XDEV = 18,		/*    v3 */
+	NFSERR_NODEV = 19,		/* v2 v3 */
+	NFSERR_NOTDIR = 20,		/* v2 v3 */
+	NFSERR_ISDIR = 21,		/* v2 v3 */
+	NFSERR_INVAL = 22,		/* v2 v3 that Sun forgot */
+	NFSERR_FBIG = 27,		/* v2 v3 */
+	NFSERR_NOSPC = 28,		/* v2 v3 */
+	NFSERR_ROFS = 30,		/* v2 v3 */
+	NFSERR_MLINK = 31,		/*    v3 */
+	NFSERR_OPNOTSUPP = 45,		/* v2 v3 */
+	NFSERR_NAMETOOLONG = 63,	/* v2 v3 */
+	NFSERR_NOTEMPTY = 66,		/* v2 v3 */
+	NFSERR_DQUOT = 69,		/* v2 v3 */
+	NFSERR_STALE = 70,		/* v2 v3 */
+	NFSERR_REMOTE = 71,		/* v2 v3 */
+	NFSERR_WFLUSH = 99,		/* v2    */
+	NFSERR_BADHANDLE = 10001,	/*    v3 */
+	NFSERR_NOT_SYNC = 10002,	/*    v3 */
+	NFSERR_BAD_COOKIE = 10003,	/*    v3 */
+	NFSERR_NOTSUPP = 10004,		/*    v3 */
+	NFSERR_TOOSMALL = 10005,	/*    v3 */
+	NFSERR_SERVERFAULT = 10006,	/*    v3 */
+	NFSERR_BADTYPE = 10007,		/*    v3 */
+	NFSERR_JUKEBOX = 10008		/*    v3 */
 };
 
+/* NFSv2 file types - beware, these are not the same in NFSv3 */
 enum nfs_ftype {
 	NFNON = 0,
 	NFREG = 1,
@@ -61,166 +67,42 @@
 	NFFIFO = 8
 };
 
-struct nfs_fh {
-	char			data[NFS_FHSIZE];
-};
-
-#define NFS_PROGRAM		100003
-#define NFS_VERSION		2
-#define NFSPROC_NULL		0
-#define NFSPROC_GETATTR		1
-#define NFSPROC_SETATTR		2
-#define NFSPROC_ROOT		3
-#define NFSPROC_LOOKUP		4
-#define NFSPROC_READLINK	5
-#define NFSPROC_READ		6
-#define NFSPROC_WRITECACHE	7
-#define NFSPROC_WRITE		8
-#define NFSPROC_CREATE		9
-#define NFSPROC_REMOVE		10
-#define NFSPROC_RENAME		11
-#define NFSPROC_LINK		12
-#define NFSPROC_SYMLINK		13
-#define NFSPROC_MKDIR		14
-#define NFSPROC_RMDIR		15
-#define NFSPROC_READDIR		16
-#define NFSPROC_STATFS		17
 
-/* Mount support for NFSroot */
-#ifdef __KERNEL__
+#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
+/* 
+ * Mount support for NFSroot
+ */
 #define NFS_MNT_PROGRAM		100005
 #define NFS_MNT_VERSION		1
 #define NFS_MNT_PORT		627
 #define NFS_MNTPROC_MNT		1
 #define NFS_MNTPROC_UMNT	3
-#endif
-
-#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
-
-extern struct rpc_program	nfs_program;
-extern struct rpc_stat		nfs_rpcstat;
 
-struct nfs_time {
-	__u32			seconds;
-	__u32			useconds;
-};
-
-struct nfs_fattr {
-	enum nfs_ftype		type;
-	__u32			mode;
-	__u32			nlink;
-	__u32			uid;
-	__u32			gid;
-	__u32			size;
-	__u32			blocksize;
-	__u32			rdev;
-	__u32			blocks;
-	__u32			fsid;
-	__u32			fileid;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-struct nfs_sattr {
-	__u32			mode;
-	__u32			uid;
-	__u32			gid;
-	__u32			size;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-};
-
-struct nfs_fsinfo {
-	__u32			tsize;
-	__u32			bsize;
-	__u32			blocks;
-	__u32			bfree;
-	__u32			bavail;
-};
-
-struct nfs_writeargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	const void *		buffer;
-};
-
-#ifdef NFS_NEED_XDR_TYPES
-
-struct nfs_sattrargs {
-	struct nfs_fh *		fh;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs_diropargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-};
-
-struct nfs_readargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	void *			buffer;
-};
-
-struct nfs_createargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs_renameargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs_linkargs {
-	struct nfs_fh *		fromfh;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs_symlinkargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	const char *		topath;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs_readdirargs {
-	struct nfs_fh *		fh;
-	__u32			cookie;
-	void *			buffer;
-	unsigned int		bufsiz;
-};
-
-struct nfs_diropok {
-	struct nfs_fh *		fh;
-	struct nfs_fattr *	fattr;
-};
+/*
+ * This is really a general kernel constant, but since nothing like
+ * this is defined in the kernel headers, I have to do it here.
+ */
+#define NFS_OFFSET_MAX		LONG_MAX
 
-struct nfs_readres {
-	struct nfs_fattr *	fattr;
-	unsigned int		count;
-};
+/*
+ * These data types are used exlusively by the NFS client implementation.
+ * They support both NFSv2 and NFSv3.
+ */
 
-struct nfs_readlinkres {
-	char **			string;
-	unsigned int *		lenp;
-	unsigned int		maxlen;
-	void *			buffer;
+/*
+ * This is the kernel NFS client file handle representation
+ */
+#define NFS_MAXFHSIZE		64
+struct nfs_fh {
+	unsigned short		size;
+	unsigned char		data[NFS_MAXFHSIZE];
 };
 
-struct nfs_readdirres {
-	void *			buffer;
-	unsigned int		bufsiz;
+enum nfs3_stable_how {
+	NFS_UNSTABLE = 0,
+	NFS_DATA_SYNC = 1,
+	NFS_FILE_SYNC = 2
 };
 
-#endif /* NFS_NEED_XDR_TYPES */
 #endif /* __KERNEL__ */
-
-#endif
+#endif /* _LINUX_NFS_H */
diff -urN linux-2.2.18-pre8/include/linux/nfs2.h linux/include/linux/nfs2.h
--- linux-2.2.18-pre8/include/linux/nfs2.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfs2.h	Mon Dec  3 20:25:24 2001
@@ -0,0 +1,76 @@
+/*
+ * NFS protocol definitions
+ *
+ * This file contains constants for Version 2 of the protocol.
+ */
+#ifndef _LINUX_NFS2_H
+#define _LINUX_NFS2_H
+
+#define NFS2_PORT	2049
+#define NFS2_MAXDATA	8192
+#define NFS2_MAXPATHLEN	1024
+#define NFS2_MAXNAMLEN	255
+#define NFS2_MAXGROUPS	16
+#define NFS2_FHSIZE	32
+#define NFS2_COOKIESIZE	4
+#define NFS2_FIFO_DEV	(-1)
+#define NFS2MODE_FMT	0170000
+#define NFS2MODE_DIR	0040000
+#define NFS2MODE_CHR	0020000
+#define NFS2MODE_BLK	0060000
+#define NFS2MODE_REG	0100000
+#define NFS2MODE_LNK	0120000
+#define NFS2MODE_SOCK	0140000
+#define NFS2MODE_FIFO	0010000
+
+	
+
+
+/* NFSv2 file types - beware, these are not the same in NFSv3 */
+enum nfs2_ftype {
+	NF2NON = 0,
+	NF2REG = 1,
+	NF2DIR = 2,
+	NF2BLK = 3,
+	NF2CHR = 4,
+	NF2LNK = 5,
+	NF2SOCK = 6,
+	NF2BAD = 7,
+	NF2FIFO = 8
+};
+
+struct nfs2_fh {
+	char			data[NFS2_FHSIZE];
+};
+
+/*
+ * Procedure numbers for NFSv2
+ */
+#define NFS2_VERSION		2
+#define NFSPROC_NULL		0
+#define NFSPROC_GETATTR		1
+#define NFSPROC_SETATTR		2
+#define NFSPROC_ROOT		3
+#define NFSPROC_LOOKUP		4
+#define NFSPROC_READLINK	5
+#define NFSPROC_READ		6
+#define NFSPROC_WRITECACHE	7
+#define NFSPROC_WRITE		8
+#define NFSPROC_CREATE		9
+#define NFSPROC_REMOVE		10
+#define NFSPROC_RENAME		11
+#define NFSPROC_LINK		12
+#define NFSPROC_SYMLINK		13
+#define NFSPROC_MKDIR		14
+#define NFSPROC_RMDIR		15
+#define NFSPROC_READDIR		16
+#define NFSPROC_STATFS		17
+
+#define NFS_MNT_PROGRAM		100005
+#define NFS_MNT_VERSION		1
+#define MNTPROC_NULL		0
+#define MNTPROC_MNT		1
+#define MNTPROC_UMNT		3
+#define MNTPROC_UMNTALL		4
+
+#endif /* _LINUX_NFS2_H */
diff -urN linux-2.2.18-pre8/include/linux/nfs3.h linux/include/linux/nfs3.h
--- linux-2.2.18-pre8/include/linux/nfs3.h	Mon Apr  7 11:35:31 1997
+++ linux/include/linux/nfs3.h	Mon Dec  3 20:25:25 2001
@@ -4,15 +4,12 @@
 #ifndef _LINUX_NFS3_H
 #define _LINUX_NFS3_H
 
-#include <linux/sunrpc/msg_prot.h>
-#include <linux/nfs.h>
-
 #define NFS3_PORT		2049
-#define NFS3_MAXDATA		8192
+#define NFS3_MAXDATA		32768
 #define NFS3_MAXPATHLEN		PATH_MAX
 #define NFS3_MAXNAMLEN		NAME_MAX
 #define NFS3_MAXGROUPS		16
-#define NFS3_FHSIZE		NFS_FHSIZE
+#define NFS3_FHSIZE		64
 #define NFS3_COOKIESIZE		4
 #define NFS3_FIFO_DEV		(-1)
 #define NFS3MODE_FMT		0170000
@@ -24,39 +21,30 @@
 #define NFS3MODE_SOCK		0140000
 #define NFS3MODE_FIFO		0010000
 
-	
-enum nfs3_stat {
-	NFS3_OK			= 0,
-	NFS3ERR_PERM		= 1,
-	NFS3ERR_NOENT		= 2,
-	NFS3ERR_IO		= 5,
-	NFS3ERR_NXIO		= 6,
-	NFS3ERR_EAGAIN		= 11,
-	NFS3ERR_ACCES		= 13,
-	NFS3ERR_EXIST		= 17,
-	NFS3ERR_XDEV		= 18,	/* new in NFSv3 */
-	NFS3ERR_NODEV		= 19,
-	NFS3ERR_NOTDIR		= 20,
-	NFS3ERR_ISDIR		= 21,
-	NFS3ERR_INVAL		= 22,	/* new in NFSv3 */
-	NFS3ERR_FBIG		= 27,
-	NFS3ERR_NOSPC		= 28,
-	NFS3ERR_ROFS		= 30,
-	NFS3ERR_MLINK		= 31,	/* new in NFSv3 */
-	NFS3ERR_NAMETOOLONG	= 63,
-	NFS3ERR_NOTEMPTY	= 66,
-	NFS3ERR_DQUOT		= 69,
-	NFS3ERR_STALE		= 70,
-	NFS3ERR_REMOTE		= 71,	/* new in NFSv3 */
-	NFS3ERR_BADHANDLE	= 10001,/* ditto */
-	NFS3ERR_NOT_SYNC	= 10002,/* ditto */
-	NFS3ERR_BAD_COOKIE	= 10003,/* ditto */
-	NFS3ERR_NOTSUPP		= 10004,/* ditto */
-	NFS3ERR_TOOSMALL	= 10005,/* ditto */
-	NFS3ERR_SERVERFAULT	= 10006,/* ditto */
-	NFS3ERR_BADTYPE		= 10007,/* ditto */
-	NFS3ERR_JUKEBOX		= 10008,/* ditto */
-};
+/* Flags for access() call */
+#define NFS3_ACCESS_READ	0x0001
+#define NFS3_ACCESS_LOOKUP	0x0002
+#define NFS3_ACCESS_MODIFY	0x0004
+#define NFS3_ACCESS_EXTEND	0x0008
+#define NFS3_ACCESS_DELETE	0x0010
+#define NFS3_ACCESS_EXECUTE	0x0020
+
+/* Flags for create mode */
+enum nfs3_createmode {
+	NFS3_CREATE_UNCHECKED = 0,
+	NFS3_CREATE_GUARDED = 1,
+	NFS3_CREATE_EXCLUSIVE =	2
+};
+
+/* NFSv3 file system properties */
+#define NFS3_FSF_LINK		0x0001
+#define NFS3_FSF_SYMLINK	0x0002
+#define NFS3_FSF_HOMOGENEOUS	0x0008
+#define NFS3_FSF_CANSETTIME	0x0010
+/* Some shorthands. See fs/nfsd/nfs3proc.c */
+#define NFS3_FSF_DEFAULT	0x001B
+#define NFS3_FSF_BILLYBOY	0x0018
+#define NFS3_FSF_READONLY	0x0008
 
 enum nfs3_ftype {
 	NF3NON  = 0,
@@ -71,182 +59,41 @@
 };
 
 #define NFS3_VERSION		3
-#define NFSPROC_NULL		0
-#define NFSPROC_GETATTR		1
-#define NFSPROC_SETATTR		2
-#define NFSPROC_ROOT		3
-#define NFSPROC_LOOKUP		4
-#define NFSPROC_READLINK	5
-#define NFSPROC_READ		6
-#define NFSPROC_WRITECACHE	7
-#define NFSPROC_WRITE		8
-#define NFSPROC_CREATE		9
-#define NFSPROC_REMOVE		10
-#define NFSPROC_RENAME		11
-#define NFSPROC_LINK		12
-#define NFSPROC_SYMLINK		13
-#define NFSPROC_MKDIR		14
-#define NFSPROC_RMDIR		15
-#define NFSPROC_READDIR		16
-#define NFSPROC_STATFS		17
+#define NFS3PROC_NULL		0
+#define NFS3PROC_GETATTR	1
+#define NFS3PROC_SETATTR	2
+#define NFS3PROC_LOOKUP		3
+#define NFS3PROC_ACCESS		4
+#define NFS3PROC_READLINK	5
+#define NFS3PROC_READ		6
+#define NFS3PROC_WRITE		7
+#define NFS3PROC_CREATE		8
+#define NFS3PROC_MKDIR		9
+#define NFS3PROC_SYMLINK	10
+#define NFS3PROC_MKNOD		11
+#define NFS3PROC_REMOVE		12
+#define NFS3PROC_RMDIR		13
+#define NFS3PROC_RENAME		14
+#define NFS3PROC_LINK		15
+#define NFS3PROC_READDIR	16
+#define NFS3PROC_READDIRPLUS	17
+#define NFS3PROC_FSSTAT		18
+#define NFS3PROC_FSINFO		19
+#define NFS3PROC_PATHCONF	20
+#define NFS3PROC_COMMIT		21
+
+#define NFS_MNT3_PROGRAM	100005
+#define NFS_MNT3_VERSION	3
+#define MOUNTPROC3_NULL		0
+#define MOUNTPROC3_MNT		1
+#define MOUNTPROC3_UMNT		3
+#define MOUNTPROC3_UMNTALL	4
 
 #if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
 
-struct nfs3_fh {
-	__u32			size;
-	__u8			data[NFS3_FHSIZE];
-};
-
-struct nfs3_fattr {
-	enum nfs3_ftype		type;
-	__u32			mode;
-	__u32			nlink;
-	__u32			uid;
-	__u32			gid;
-	__u64			size;
-	__u64			used;
-	__u32			rdev_maj;
-	__u32			rdev_min;
-	__u32			fsid;
-	__u32			fileid;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-struct nfs3_wcc_attr {
-	__u64			size;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-struct nfs3_wcc_data {
-	struct nfs3_wcc_attr	before;
-	struct nfs3_wcc_attr	after;
-};
-
-struct nfs3_sattr {
-	__u32			valid;
-	__u32			mode;
-	__u32			uid;
-	__u32			gid;
-	__u64			size;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-};
-
-struct nfs3_entry {
-	__u32			fileid;
-	char *			name;
-	unsigned int		length;
-	__u32			cookie;
-	__u32			eof;
-};
-
-struct nfs3_fsinfo {
-	__u32			tsize;
-	__u32			bsize;
-	__u32			blocks;
-	__u32			bfree;
-	__u32			bavail;
-};
-
-#ifdef NFS_NEED_XDR_TYPES
-
-struct nfs3_sattrargs {
-	struct nfs_fh *		fh;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs3_diropargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-};
-
-struct nfs3_readargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	void *			buffer;
-};
-
-struct nfs3_writeargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	const void *		buffer;
-};
+/* Number of 32bit words in post_op_attr */
+#define NFS3_POST_OP_ATTR_WORDS	22
+ 
 
-struct nfs3_createargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs3_renameargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs3_linkargs {
-	struct nfs_fh *		fromfh;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs3_symlinkargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	const char *		topath;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs3_readdirargs {
-	struct nfs_fh *		fh;
-	__u32			cookie;
-	void *			buffer;
-	unsigned int		bufsiz;
-};
-
-struct nfs3_diropok {
-	struct nfs_fh *		fh;
-	struct nfs_fattr *	fattr;
-};
-
-struct nfs3_readres {
-	struct nfs_fattr *	fattr;
-	unsigned int		count;
-};
-
-struct nfs3_readlinkres {
-	char **			string;
-	unsigned int *		lenp;
-	unsigned int		maxlen;
-	void *			buffer;
-};
-
-struct nfs3_readdirres {
-	void *			buffer;
-	unsigned int		bufsiz;
-};
-
-/*
- * The following are for NFSv3
- */
-struct nfs3_fh {
-	__u32			size;
-	__u8			data[NFS3_FHSIZE]
-};
-
-struct nfs3_wcc_attr {
-	__u64			size;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-#endif /* NFS_NEED_XDR_TYPES */
 #endif /* __KERNEL__ */
-
-#endif
+#endif /* _LINUX_NFS3_H */
diff -urN linux-2.2.18-pre8/include/linux/nfs_flushd.h linux/include/linux/nfs_flushd.h
--- linux-2.2.18-pre8/include/linux/nfs_flushd.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfs_flushd.h	Mon Dec  3 20:25:25 2001
@@ -0,0 +1,64 @@
+#ifndef NFS_CLUSTER_H
+#define NFS_CLUSTER_H
+
+
+
+#ifdef __KERNEL__
+#include <linux/nfs_fs_sb.h>
+
+/*
+ * Counters of total number and pending number of requests.
+ * When the total number of requests exceeds the soft limit, we start
+ * flushing out requests. If it exceeds the hard limit, we stall until
+ * it drops again.
+ */
+#define MAX_REQUEST_SOFT        192
+#define MAX_REQUEST_HARD        256
+
+/*
+ * Maximum number of requests per write cluster.
+ * 32 requests per cluster account for 128K of data on an intel box.
+ * Note: it's a good idea to make this number smaller than MAX_REQUEST_SOFT.
+ *
+ * For 100Mbps Ethernet, 128 pages (i.e. 256K) per cluster gives much
+ * better performance.
+ */
+#define REQUEST_HASH_SIZE	16
+#define REQUEST_NR(off)		((off) >> PAGE_CACHE_SHIFT)
+#define REQUEST_HASH(ino, off)	(((ino) ^ REQUEST_NR(off)) & (REQUEST_HASH_SIZE - 1))
+
+
+/*
+ * Functions
+ */
+extern int		nfs_reqlist_alloc(struct nfs_server *);
+extern void		nfs_reqlist_free(struct nfs_server *);
+extern int		nfs_reqlist_init(struct nfs_server *);
+extern void		nfs_reqlist_exit(struct nfs_server *);
+extern void		inode_schedule_scan(struct inode *, unsigned long);
+extern void		inode_remove_flushd(struct inode *);
+extern void		nfs_wake_flushd(void);
+
+/*
+ * This is the per-mount writeback cache.
+ */
+struct nfs_reqlist {
+	unsigned int		nr_requests;
+	unsigned long		runat;
+	struct wait_queue	*request_wait;
+
+	/* The async RPC task that is responsible for scanning the
+	 * requests.
+	 */
+	struct rpc_task		*task;		/* request flush task */
+
+	/* Authentication flavor handle for this NFS client */
+	struct rpc_auth		*auth;
+
+	/* The list of all inodes with pending writebacks.  */
+	struct inode		*inodes;
+};
+
+#endif
+
+#endif
diff -urN linux-2.2.18-pre8/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- linux-2.2.18-pre8/include/linux/nfs_fs.h	Tue May 11 10:35:46 1999
+++ linux/include/linux/nfs_fs.h	Mon Dec  3 20:25:25 2001
@@ -9,13 +9,21 @@
 #ifndef _LINUX_NFS_FS_H
 #define _LINUX_NFS_FS_H
 
+#ifdef __KERNEL__
 #include <linux/signal.h>
 #include <linux/sched.h>
+#include <linux/kernel.h>
 #include <linux/in.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
 
 #include <linux/sunrpc/sched.h>
+#endif /* __KERNEL__ */
+
 #include <linux/nfs.h>
-#include <linux/nfs_mount.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_xdr.h>
 
 /*
  * Enable debugging support for nfs client.
@@ -25,48 +33,45 @@
 # define NFS_DEBUG
 #endif
 
-/*
- * NFS_MAX_DIRCACHE controls the number of simultaneously cached
- * directory chunks. Each chunk holds the list of nfs_entry's returned
- * in a single readdir call in a memory region of size PAGE_SIZE.
- *
- * Note that at most server->rsize bytes of the cache memory are used.
- */
-#define NFS_MAX_DIRCACHE		16
-
-#define NFS_MAX_FILE_IO_BUFFER_SIZE	16384
-#define NFS_DEF_FILE_IO_BUFFER_SIZE	4096
+#define NFS_MAX_FILE_IO_BUFFER_SIZE	32768
+#define NFS_DEF_FILE_IO_BUFFER_SIZE	1024
 
 /*
  * The upper limit on timeouts for the exponential backoff algorithm.
  */
-#define NFS_MAX_RPC_TIMEOUT		(6*HZ)
-
-/*
- * Size of the lookup cache in units of number of entries cached.
- * It is better not to make this too large although the optimum
- * depends on a usage and environment.
- */
-#define NFS_LOOKUP_CACHE_SIZE		64
+#define NFS_READ_DELAY			(2*HZ)
+#define NFS_WRITEBACK_DELAY		(5*HZ)
+#define NFS_WRITEBACK_LOCKDELAY		(60*HZ)
+#define NFS_COMMIT_DELAY		(5*HZ)
 
 /*
  * superblock magic number for NFS
  */
 #define NFS_SUPER_MAGIC			0x6969
+#define NFS_FILE_MAGIC			0xA4F0
 
+#ifdef __KERNEL__
+/*
+ * Convenience macros
+ */
 #define NFS_FH(dentry)			((struct nfs_fh *) ((dentry)->d_fsdata))
 #define NFS_DSERVER(dentry)		(&(dentry)->d_sb->u.nfs_sb.s_server)
 #define NFS_SERVER(inode)		(&(inode)->i_sb->u.nfs_sb.s_server)
 #define NFS_CLIENT(inode)		(NFS_SERVER(inode)->client)
+#define NFS_PROTO(inode)		(NFS_SERVER(inode)->rpc_ops)
+#define NFS_REQUESTLIST(inode)		(NFS_SERVER(inode)->rw_requests)
 #define NFS_ADDR(inode)			(RPC_PEERADDR(NFS_CLIENT(inode)))
 #define NFS_CONGESTED(inode)		(RPC_CONGESTED(NFS_CLIENT(inode)))
-
+#define NFS_COOKIEVERF(inode)		((inode)->u.nfs_i.cookieverf)
 #define NFS_READTIME(inode)		((inode)->u.nfs_i.read_cache_jiffies)
-#define NFS_OLDMTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_CACHE_CTIME(inode)		((inode)->u.nfs_i.read_cache_ctime)
+#define NFS_CACHE_MTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_CACHE_ATIME(inode)		((inode)->u.nfs_i.read_cache_atime)
+#define NFS_CACHE_ISIZE(inode)		((inode)->u.nfs_i.read_cache_isize)
+#define NFS_NEXTSCAN(inode)		((inode)->u.nfs_i.nextscan)
 #define NFS_CACHEINV(inode) \
 do { \
-	NFS_READTIME(inode) = jiffies - 1000000; \
-	NFS_OLDMTIME(inode) = 0; \
+	NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
 } while (0)
 #define NFS_ATTRTIMEO(inode)		((inode)->u.nfs_i.attrtimeo)
 #define NFS_MINATTRTIMEO(inode) \
@@ -75,10 +80,18 @@
 #define NFS_MAXATTRTIMEO(inode) \
 	(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \
 			       : NFS_SERVER(inode)->acregmax)
+#define NFS_ATTRTIMEO_UPDATE(inode)	((inode)->u.nfs_i.attrtimeo_timestamp)
 
 #define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
-#define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATE)
-#define NFS_WRITEBACK(inode)		((inode)->u.nfs_i.writeback)
+#define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
+
+#define NFS_FILEID(inode)	((inode)->u.nfs_i.fileid)
+#define NFS_FSID(inode)		((inode)->u.nfs_i.fsid)
+
+#define NFS_FILE(file)		((struct nfs_file *)(file)->private_data)
+
+/* Inode Flags */
+#define NFS_USE_READDIRPLUS(inode)	((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0)
 
 /*
  * These are the default flags for swap requests
@@ -88,116 +101,81 @@
 /* Flags in the RPC client structure */
 #define NFS_CLNTF_BUFSIZE	0x0001	/* readdir buffer in longwords */
 
-#ifdef __KERNEL__
+#define NFS_RW_SYNC		0x0001	/* O_SYNC handling */
+#define NFS_RW_SWAP		0x0002	/* This is a swap request */
 
 /*
- * This struct describes a file region to be written.
- * It's kind of a pity we have to keep all these lists ourselves, rather
- * than sticking an extra pointer into struct page.
- */
-struct nfs_wreq {
-	struct rpc_listitem	wb_list;	/* linked list of req's */
-	struct rpc_task		wb_task;	/* RPC task */
-	struct file *		wb_file;	/* dentry referenced */
-	struct page *		wb_page;	/* page to be written */
-	struct wait_queue *	wb_wait;	/* wait for completion */
-	unsigned int		wb_offset;	/* offset within page */
-	unsigned int		wb_bytes;	/* dirty range */
-	unsigned int		wb_count;	/* user count */
-	int			wb_status;
-	pid_t			wb_pid;		/* owner process */
-	unsigned short		wb_flags;	/* status flags */
-
-	struct nfs_writeargs	wb_args;	/* NFS RPC stuff */
-	struct nfs_fattr	wb_fattr;	/* file attributes */
-};
+ * When flushing a cluster of dirty pages, there can be different
+ * strategies:
+ */
+#define FLUSH_AGING		0	/* only flush old buffers */
+#define FLUSH_SYNC		1	/* file being synced, or contention */
+#define FLUSH_WAIT		2	/* wait for completion */
+#define FLUSH_STABLE		4	/* commit to stable storage */
 
-#define WB_NEXT(req)		((struct nfs_wreq *) ((req)->wb_list.next))
 
 /*
- * Various flags for wb_flags
+ * Structure for file->private_data;
  */
-#define NFS_WRITE_CANCELLED	0x0004	/* has been cancelled */
-#define NFS_WRITE_UNCOMMITTED	0x0008	/* written but uncommitted (NFSv3) */
-#define NFS_WRITE_INVALIDATE	0x0010	/* invalidate after write */
-#define NFS_WRITE_INPROGRESS	0x0100	/* RPC call in progress */
-#define NFS_WRITE_COMPLETE	0x0200	/* RPC call completed */
-
-#define WB_CANCELLED(req)	((req)->wb_flags & NFS_WRITE_CANCELLED)
-#define WB_UNCOMMITTED(req)	((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
-#define WB_INVALIDATE(req)	((req)->wb_flags & NFS_WRITE_INVALIDATE)
-#define WB_INPROGRESS(req)	((req)->wb_flags & NFS_WRITE_INPROGRESS)
-#define WB_COMPLETE(req)	((req)->wb_flags & NFS_WRITE_COMPLETE)
-
-/*
- * linux/fs/nfs/proc.c
- */
-extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fattr *fattr);
-extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_sattr *sattr, struct nfs_fattr *fattr);
-extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_fh *fhandle,
-			struct nfs_fattr *fattr);
-extern int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-			void **p0, char **string, unsigned int *len,
-			unsigned int maxlen);
-extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
-			int swap, unsigned long offset, unsigned int count,
-			void *buffer, struct nfs_fattr *fattr);
-extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
-			int swap, unsigned long offset, unsigned int count,
-			const void *buffer, struct nfs_fattr *fattr);
-extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr);
-extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name);
-extern int nfs_proc_rename(struct nfs_server *server,
-			struct nfs_fh *old_dir, const char *old_name,
-			struct nfs_fh *new_dir, const char *new_name);
-extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fh *dir, const char *name);
-extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, const char *path,
-			struct nfs_sattr *sattr);
-extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr);
-extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name);
-extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-			u32 cookie, unsigned int size, __u32 *entry);
-extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fsinfo *res);
+struct nfs_file {
+	unsigned long		magic;		/* Magic number */
+	struct rpc_cred*	cred;		/* RPC Credentials */
+};
+
+static inline int
+nfs_check_file(struct file *file)
+{
+	if (NFS_FILE(file)->magic != NFS_FILE_MAGIC) {
+		printk(KERN_ERR "NFS: corrupt file structure!\n");
+		return 0;
+	}
+	return 1;
+}
 
+static inline
+unsigned long nfs_page_offset(struct page *page)
+{
+        return page->offset;
+}
+
+static inline
+unsigned long page_index(struct page *page)
+{
+	return page->offset >> PAGE_CACHE_SHIFT;
+}
 
 /*
  * linux/fs/nfs/inode.c
  */
-extern struct super_block *nfs_read_super(struct super_block *, void *, int);
-extern int init_nfs_fs(void);
 extern struct inode *nfs_fhget(struct dentry *, struct nfs_fh *,
-				struct nfs_fattr *);
-extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
-extern int nfs_revalidate(struct dentry *);
-extern int nfs_open(struct inode *, struct file *);
-extern int nfs_release(struct inode *, struct file *);
-extern int _nfs_revalidate_inode(struct nfs_server *, struct dentry *);
+			       struct nfs_fattr *);
+extern struct super_block *nfs_read_super(struct super_block *, void *, int);
+extern int	init_nfs_fs(void);
+extern void	nfs_zap_caches(struct inode *);
+extern int	nfs_revalidate(struct dentry *);
+extern int	nfs_open(struct inode *, struct file *);
+extern int	nfs_release(struct inode *, struct file *);
+extern int	__nfs_revalidate_inode(struct dentry *);
+extern int	nfs_refresh_inode(struct inode *, struct nfs_fattr *);
+extern int	nfs_wait_on_inode(struct inode *, int flag);
+extern void	nfs_unlock_inode(struct inode *);
+extern int	nfs_update_atime(struct dentry *);
+
 
 /*
  * linux/fs/nfs/file.c
  */
 extern struct inode_operations nfs_file_inode_operations;
+extern int	nfs_permission(struct inode *i, int msk);
 
 /*
  * linux/fs/nfs/dir.c
  */
 extern struct inode_operations nfs_dir_inode_operations;
 extern struct dentry_operations nfs_dentry_operations;
-extern void nfs_free_dircache(void);
-extern void nfs_invalidate_dircache(struct inode *);
-extern void nfs_invalidate_dircache_sb(struct super_block *);
+extern struct nfs_fh *nfs_fh_alloc(void);
+extern void nfs_fh_free(struct nfs_fh *);
+
 
 /*
  * linux/fs/nfs/symlink.c
@@ -210,53 +188,167 @@
 extern int nfs_lock(struct file *, int, struct file_lock *);
 
 /*
+ * linux/fs/nfs/unlink.c
+ */
+extern int  nfs_async_unlink(struct dentry *);
+extern void nfs_complete_unlink(struct dentry *);
+
+/*
  * linux/fs/nfs/write.c
  */
 extern int  nfs_writepage(struct file *, struct page *);
-extern int  nfs_check_failed_request(struct inode *);
-
+extern int  nfs_flush_incompatible(struct file *, struct page *);
+extern int  nfs_updatepage(struct file *, struct page *,
+                        unsigned long, unsigned int, int);
 /*
  * Try to write back everything synchronously (but check the
  * return value!)
  */
-extern int  nfs_wb_all(struct inode *);
-extern int  nfs_wb_page(struct inode *, struct page *);
-extern int  nfs_wb_file(struct inode *, struct file *);
+extern int  nfs_sync_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_flush_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_commit_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_flush_timeout(struct inode *, int);
+extern int  nfs_commit_timeout(struct inode *, int);
+
+static inline int
+nfs_have_read(struct inode *inode)
+{
+	return !list_empty(&inode->u.nfs_i.read);
+}
+
+static inline int
+nfs_have_writebacks(struct inode *inode)
+{
+	return !list_empty(&inode->u.nfs_i.writeback);
+}
+
+static inline int
+nfs_wb_all(struct inode *inode)
+{
+	int error = nfs_sync_file(inode, 0, 0, 0, FLUSH_WAIT);
+	return (error < 0) ? error : 0;
+}
+
+/*
+ * Write back all requests on one page - we do this before reading it.
+ */
+static inline int
+nfs_wb_page(struct inode *inode, struct page* page)
+{
+	int error = nfs_sync_file(inode, 0, page_index(page), 1, FLUSH_WAIT | FLUSH_STABLE);
+	return (error < 0) ? error : 0;
+}
+
+/*
+ * Write back all pending writes for one user.. 
+ */
+static inline int
+nfs_wb_file(struct inode *inode, struct file *file)
+{
+	int error = nfs_sync_file(inode, file, 0, 0, FLUSH_WAIT);
+	return (error < 0) ? error : 0;
+}
 
 /*
- * Invalidate write-backs, possibly trying to write them
- * back first..
+ * Lock the page of an asynchronous request
  */
-extern void nfs_inval(struct inode *);
-extern int  nfs_updatepage(struct file *, struct page *, unsigned long, unsigned int, int);
+static __inline__ int
+nfs_lock_page(struct page *page)
+{
+	return !test_and_set_bit(PG_locked, &page->flags);
+}
+
+static __inline__ void
+nfs_unlock_page(struct page *page)
+{
+        clear_bit(PG_locked, &page->flags);
+        wake_up(&page->wait);
+}
 
 /*
  * linux/fs/nfs/read.c
  */
 extern int  nfs_readpage(struct file *, struct page *);
+extern int  nfs_pagein_inode(struct inode *, unsigned long, unsigned int);
+extern int  nfs_pagein_timeout(struct inode *);
+
+/*
+ * linux/fs/nfs2xdr.c
+ */
+extern u32 *nfs_decode_dirent(u32 *, struct nfs_entry *, int);
+
+/*
+ * linux/fs/nfs2xdr.c
+ */
+extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
 
 /*
  * linux/fs/mount_clnt.c
  * (Used only by nfsroot module)
  */
 extern int  nfs_mount(struct sockaddr_in *, char *, struct nfs_fh *);
+extern int  nfs3_mount(struct sockaddr_in *, char *, struct nfs_fh *);
 
 /*
  * inline functions
  */
 static inline int
-nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
+nfs_revalidate_inode(struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
-	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
+	if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
 		return 0;
-	return _nfs_revalidate_inode(server, dentry);
+	return __nfs_revalidate_inode(dentry);
+}
+
+static inline off_t
+nfs_size_to_off_t(__u64 size)
+{
+	return (size > (__u64)LONG_MAX) ? (off_t)LONG_MAX : (off_t) size;
+}
+
+static inline ino_t
+nfs_fileid_to_ino_t(u64 fileid)
+{
+	ino_t ino = (unsigned long) fileid;
+	if (sizeof(unsigned long) < sizeof(u64))
+		ino ^= fileid >> (sizeof(u64)-sizeof(unsigned long)) * 8;
+	return ino;
+}
+
+static inline time_t
+nfs_time_to_secs(__u64 time)
+{
+	return (time_t)(time >> 32);
+}
+
+static __inline__ struct rpc_cred *
+nfs_file_cred(struct file *file)
+{
+	if (!NFS_FILE(file) || !nfs_check_file(file)) {
+		printk("nfs_file_cred: invalid file!\n");
+		return NULL;
+	}
+	return NFS_FILE(file)->cred;
 }
 
 /* NFS root */
 
 extern int nfs_root_mount(struct super_block *sb);
 
+#define nfs_wait_event(clnt, wq, condition)				\
+({									\
+	int __retval = 0;						\
+	if (clnt->cl_intr) {						\
+		sigset_t oldmask;					\
+		rpc_clnt_sigmask(clnt, &oldmask);			\
+		__retval = wait_event_interruptible(wq, condition);	\
+		rpc_clnt_sigunmask(clnt, &oldmask);			\
+	} else								\
+		wait_event(wq, condition);				\
+	__retval;							\
+})
+
 #endif /* __KERNEL__ */
 
 /*
@@ -270,6 +362,7 @@
 #define NFSDBG_XDR		0x0020
 #define NFSDBG_FILE		0x0040
 #define NFSDBG_ROOT		0x0080
+#define NFSDBG_PARANOID		0x0100
 #define NFSDBG_ALL		0xFFFF
 
 #ifdef __KERNEL__
@@ -278,6 +371,11 @@
 #  define ifdebug(fac)		if (nfs_debug & NFSDBG_##fac)
 # else
 #  define ifdebug(fac)		if (0)
+# endif
+# ifdef NFS_PARANOIA
+#  define nfsparanoid(args...)	dfprintk(PARANOID, ##args)
+# else
+#  define nfsparanoid(args...)	do { } while (0)
 # endif
 #endif /* __KERNEL */
 
diff -urN linux-2.2.18-pre8/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h
--- linux-2.2.18-pre8/include/linux/nfs_fs_i.h	Sat Mar  6 15:04:35 1999
+++ linux/include/linux/nfs_fs_i.h	Mon Dec  3 20:25:25 2001
@@ -1,4 +1,4 @@
-#ifndef _NFS_FS_I
+#ifndef _NFS_FS_i
 #define _NFS_FS_I
 
 #include <linux/nfs.h>
@@ -16,6 +16,12 @@
 	struct pipe_inode_info	pipeinfo;
 
 	/*
+	 * The 64bit fileid
+	 */
+	__u64 fsid;
+	__u64 fileid;
+
+	/*
 	 * Various flags
 	 */
 	unsigned short		flags;
@@ -38,22 +44,68 @@
 	 *	mtime != read_cache_mtime
 	 */
 	unsigned long		read_cache_jiffies;
-	unsigned long		read_cache_mtime;
+	__u64			read_cache_ctime;
+	__u64			read_cache_mtime;
+	__u64			read_cache_atime;
+	__u64			read_cache_isize;
 	unsigned long		attrtimeo;
+	unsigned long		attrtimeo_timestamp;
+
+	/*
+	 * This is the cookie verifier used for NFSv3 readdir
+	 * operations
+	 */
+	__u32			cookieverf[2];
 
 	/*
-	 * This is the list of dirty unwritten pages.
-	 * NFSv3 will want to add a list for written but uncommitted
-	 * pages.
+	 * This is the list of dirty pages.
 	 */
-	struct nfs_wreq *	writeback;
+	struct list_head	read;
+	struct list_head	dirty;
+	struct list_head	commit;
+	struct list_head	writeback;
+
+	unsigned int		nread,
+				ndirty,
+				ncommit,
+				npages;
+
+	/* Flush daemon info */
+	struct inode		*hash_next,
+				*hash_prev;
+	unsigned long		nextscan;
 };
 
 /*
  * Legal inode flag values
  */
-#define NFS_INO_REVALIDATE	0x0001		/* revalidating attrs */
+#define NFS_INO_LOCKED          0x0001          /* locked for revalidation */
+#define NFS_INO_ADVISE_RDPLUS   0x0002          /* advise readdirplus */
+#define NFS_INO_REVALIDATING    0x0004          /* in nfs_revalidate() */
+#define NFS_INO_INVALIDATE      0x0008          /* zap cache on next occasion */
 #define NFS_IS_SNAPSHOT		0x0010		/* a snapshot file */
+#define NFS_INO_STALE		0x0020		/* We suspect inode is stale */
+#define NFS_INO_FLUSH		0x0040		/* inode is due for flushing */
+
+/*
+ * NFS ACL info.
+ * This information will be used by nfs_permission() in the obvious fashion,
+ * but also helps the RPC engine to select whether to try the operation first
+ * with the effective or real uid/gid first.
+ *
+ * For NFSv2, this info is obtained by just trying the operation in
+ * question and updating the ACL info according to the result.
+ * For NFSv3, the access() call is used to fill in the permission bits.
+ *
+ * Not yet used.
+ */
+struct nfs_acl_info {
+	struct nfs_acl_info *	acl_next;
+	unsigned long		acl_read_time;
+	uid_t			acl_uid;
+	gid_t			acl_gid;
+	unsigned int		acl_bits;
+};
 
 /*
  * NFS lock info
@@ -61,6 +113,7 @@
 struct nfs_lock_info {
 	u32		state;
 	u32		flags;
+	struct nlm_host	*host;
 };
 
 /*
diff -urN linux-2.2.18-pre8/include/linux/nfs_fs_sb.h linux/include/linux/nfs_fs_sb.h
--- linux-2.2.18-pre8/include/linux/nfs_fs_sb.h	Tue May 11 10:35:43 1999
+++ linux/include/linux/nfs_fs_sb.h	Mon Dec  3 20:25:25 2001
@@ -1,7 +1,6 @@
 #ifndef _NFS_FS_SB
 #define _NFS_FS_SB
 
-#include <linux/nfs.h>
 #include <linux/in.h>
 
 /*
@@ -9,23 +8,32 @@
  */
 struct nfs_server {
 	struct rpc_clnt *	client;		/* RPC client handle */
+	struct nfs_rpc_ops *	rpc_ops;	/* NFS protocol vector */
 	int			flags;		/* various flags */
-	int			rsize;		/* read size */
-	int			wsize;		/* write size */
+	unsigned int		rsize;		/* read size */
+	unsigned int		rpages;		/* read size (in pages) */
+	unsigned int		wsize;		/* write size */
+	unsigned int		wpages;		/* write size (in pages) */
+	unsigned int		dtsize;		/* readdir size */
 	unsigned int		bsize;		/* server block size */
 	unsigned int		acregmin;	/* attr cache timeouts */
 	unsigned int		acregmax;
 	unsigned int		acdirmin;
 	unsigned int		acdirmax;
+	unsigned int		namelen;
 	char *			hostname;	/* remote hostname */
+	struct nfs_dircache *	dircache;	/* readdir cache info */
+	struct nfs_reqlist *	rw_requests;    /* async read/write requests */
 };
 
+
 /*
  * nfs super-block data in memory
  */
 struct nfs_sb_info {
-	struct nfs_server	s_server;
-	struct nfs_fh		s_root;
+	struct nfs_server	s_server;	/* NFS server info */
+	unsigned int		s_fhsize;	/* File handle size */
+	struct nfs_fh *		s_root;		/* The root file handle */
 };
 
 #endif
diff -urN linux-2.2.18-pre8/include/linux/nfs_mount.h linux/include/linux/nfs_mount.h
--- linux-2.2.18-pre8/include/linux/nfs_mount.h	Mon Apr  7 11:35:31 1997
+++ linux/include/linux/nfs_mount.h	Mon Dec  3 20:25:25 2001
@@ -8,6 +8,9 @@
  *
  *  structure passed from user-space to kernel-space during an nfs mount
  */
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_fs.h>
 
 /*
  * WARNING!  Do not delete or change the order of these fields.  If
@@ -15,13 +18,17 @@
  * tracks which fields are present.  This will ensure some measure of
  * mount-to-kernel version compatibility.  Some of these aren't used yet
  * but here they are anyway.
+ *
+ * Lugging around two file handles is inevitable if we want to pass
+ * version 3 handles while maintaining compatibility. We may want to
+ * break it for the 2.2 kernel eventually...
  */
-#define NFS_MOUNT_VERSION	3
+#define NFS_MOUNT_VERSION	4
 
 struct nfs_mount_data {
 	int		version;		/* 1 */
 	int		fd;			/* 1 */
-	struct nfs_fh	root;			/* 1 */
+	struct nfs2_fh	old_root;		/* 1 */
 	int		flags;			/* 1 */
 	int		rsize;			/* 1 */
 	int		wsize;			/* 1 */
@@ -35,6 +42,7 @@
 	char		hostname[256];		/* 1 */
 	int		namlen;			/* 2 */
 	unsigned int	bsize;			/* 3 */
+	struct nfs_fh	root;			/* 4 */
 };
 
 /* bits in the flags field */
@@ -49,5 +57,7 @@
 #define NFS_MOUNT_VER3		0x0080	/* 3 */
 #define NFS_MOUNT_KERBEROS	0x0100	/* 3 */
 #define NFS_MOUNT_NONLM		0x0200	/* 3 */
- 
+#define NFS_MOUNT_BROKEN_SUID	0x0400	/* 4 */
+#define NFS_MOUNT_FLAGMASK	0xFFFF
+
 #endif
diff -urN linux-2.2.18-pre8/include/linux/nfs_page.h linux/include/linux/nfs_page.h
--- linux-2.2.18-pre8/include/linux/nfs_page.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfs_page.h	Mon Dec  3 20:25:25 2001
@@ -0,0 +1,100 @@
+/*
+ * linux/include/linux/nfs_page.h
+ *
+ * Copyright (C) 2000 Trond Myklebust
+ *
+ * NFS page cache wrapper.
+ */
+
+#ifndef _LINUX_NFS_PAGE_H
+#define _LINUX_NFS_PAGE_H
+
+
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/wait.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/nfs_xdr.h>
+
+/*
+ * Valid flags for a dirty buffer
+ */
+#define PG_BUSY			0x0001
+
+struct nfs_page {
+	struct list_head	wb_hash,	/* Inode */
+				wb_list,	/* Defines state of page: */
+				*wb_list_head;	/*      read/write/commit */
+	struct file		*wb_file;
+	struct dentry		*wb_dentry;
+	struct rpc_cred		*wb_cred;
+	struct page		*wb_page;	/* page to read in/write out */
+	struct wait_queue	*wb_wait;	/* wait queue */
+	unsigned long		wb_timeout;	/* when to read/write/commit */
+	unsigned int		wb_offset,	/* Offset of read/write */
+				wb_bytes,	/* Length of request */
+				wb_count,	/* reference count */
+				wb_flags;
+	struct nfs_writeverf	wb_verf;	/* Commit cookie */
+};
+
+#define NFS_WBACK_BUSY(req)	((req)->wb_flags & PG_BUSY)
+
+extern	struct nfs_page *nfs_create_request(struct file *file,
+					    struct page *page,
+					    unsigned int offset,
+					    unsigned int count);
+extern	void nfs_release_request(struct nfs_page *req);
+
+
+extern	void nfs_list_add_request(struct nfs_page *req,
+				  struct list_head *head);
+extern	void nfs_list_remove_request(struct nfs_page *req);
+
+extern	int nfs_scan_list_timeout(struct list_head *head,
+				  struct list_head *dst,
+				  struct inode *inode);
+extern	int nfs_scan_list(struct list_head *src, struct list_head *dst,
+			  struct file *file, unsigned long idx_start,
+			  unsigned int npages);
+extern	int nfs_coalesce_requests(struct list_head *src, struct list_head *dst,
+				  unsigned int maxpages);
+
+/*
+ * Lock the page of an asynchronous request
+ */
+static __inline__ int
+nfs_lock_request(struct nfs_page *req)
+{
+	if (NFS_WBACK_BUSY(req))
+		return 0;
+	req->wb_count++;
+	req->wb_flags |= PG_BUSY;
+	return 1;
+}
+
+static __inline__ void
+nfs_unlock_request(struct nfs_page *req)
+{
+	if (!NFS_WBACK_BUSY(req)) {
+		printk(KERN_ERR "NFS: Invalid unlock attempted\n");
+		return;
+	}
+	req->wb_flags &= ~PG_BUSY;
+	wake_up(&req->wb_wait);
+	nfs_release_request(req);
+}
+
+static __inline__ struct nfs_page *
+nfs_list_entry(struct list_head *head)
+{
+	return list_entry(head, struct nfs_page, wb_list);
+}
+
+static __inline__ struct nfs_page *
+nfs_inode_wb_entry(struct list_head *head)
+{
+	return list_entry(head, struct nfs_page, wb_hash);
+}
+
+#endif /* _LINUX_NFS_PAGE_H */
diff -urN linux-2.2.18-pre8/include/linux/nfs_xdr.h linux/include/linux/nfs_xdr.h
--- linux-2.2.18-pre8/include/linux/nfs_xdr.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfs_xdr.h	Mon Dec  3 20:25:25 2001
@@ -0,0 +1,387 @@
+#ifndef _LINUX_NFS_XDR_H
+#define _LINUX_NFS_XDR_H
+
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
+#include <linux/dcache.h>
+
+extern struct rpc_program	nfs_program;
+extern struct rpc_stat		nfs_rpcstat;
+
+struct nfs_fattr {
+	unsigned short		valid;		/* which fields are valid */
+	__u64			pre_size;	/* pre_op_attr.size	  */
+	__u64			pre_mtime;	/* pre_op_attr.mtime	  */
+	__u64			pre_ctime;	/* pre_op_attr.ctime	  */
+	enum nfs_ftype		type;		/* always use NFSv2 types */
+	__u32			mode;
+	__u32			nlink;
+	__u32			uid;
+	__u32			gid;
+	__u64			size;
+	union {
+		struct {
+			__u32	blocksize;
+			__u32	blocks;
+		} nfs2;
+		struct {
+			__u64	used;
+		} nfs3;
+	} du;
+	__u32			rdev;
+	__u64			fsid;
+	__u64			fileid;
+	__u64			atime;
+	__u64			mtime;
+	__u64			ctime;
+};
+
+#define NFS_ATTR_WCC		0x0001		/* pre-op WCC data    */
+#define NFS_ATTR_FATTR		0x0002		/* post-op attributes */
+#define NFS_ATTR_FATTR_V3	0x0004		/* NFSv3 attributes */
+
+/*
+ * Info on the file system
+ */
+struct nfs_fsinfo {
+	__u32			rtmax;	/* max.  read transfer size */
+	__u32			rtpref;	/* pref. read transfer size */
+	__u32			rtmult;	/* reads should be multiple of this */
+	__u32			wtmax;	/* max.  write transfer size */
+	__u32			wtpref;	/* pref. write transfer size */
+	__u32			wtmult;	/* writes should be multiple of this */
+	__u32			dtpref;	/* pref. readdir transfer size */
+	__u64			maxfilesize;
+	__u64			bsize;	/* block size */
+	__u64			tbytes;	/* total size in bytes */
+	__u64			fbytes;	/* # of free bytes */
+	__u64			abytes;	/* # of bytes available to user */
+	__u64			tfiles;	/* # of files */
+	__u64			ffiles;	/* # of free files */
+	__u64			afiles;	/* # of files available to user */
+	__u32			linkmax;/* max # of hard links */
+	__u32			namelen;/* max name length */
+};
+
+/* Arguments to the read call.
+ * Note that NFS_READ_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+ */
+#define NFS_READ_MAXIOV 8
+
+struct nfs_readargs {
+	struct nfs_fh *		fh;
+	__u64			offset;
+	__u32			count;
+	unsigned int            nriov;
+	struct iovec            iov[NFS_READ_MAXIOV];
+};
+
+struct nfs_readres {
+	struct nfs_fattr *	fattr;
+	unsigned int		count;
+	int                     eof;
+};
+
+/* Arguments to the write call.
+ * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+ */
+#define NFS_WRITE_MAXIOV        8
+struct nfs_writeargs {
+	struct nfs_fh *		fh;
+	__u32			offset;
+	__u32			count;
+	enum nfs3_stable_how	stable;
+	unsigned int		nriov;
+	struct iovec		iov[NFS_WRITE_MAXIOV];
+};
+
+struct nfs_writeverf {
+	enum nfs3_stable_how	committed;
+	__u32			verifier[2];
+};
+
+struct nfs_writeres {
+	struct nfs_fattr *	fattr;
+	struct nfs_writeverf *	verf;
+	__u32			count;
+};
+
+/*
+ * Argument struct for decode_entry function
+ */
+struct nfs_entry {
+	__u64			ino;
+	__u64			cookie,
+				prev_cookie;
+	const char *		name;
+	unsigned int		len;
+	int			eof;
+	struct nfs_fh		fh;
+	struct nfs_fattr	fattr;
+};
+
+/*
+ * The following types are for NFSv2 only.
+ */
+struct nfs_sattrargs {
+	struct nfs_fh *		fh;
+	struct iattr *		sattr;
+};
+
+struct nfs_diropargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+};
+
+struct nfs_createargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	struct iattr *		sattr;
+};
+
+struct nfs_renameargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs_linkargs {
+	struct nfs_fh *		fromfh;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs_symlinkargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	const char *		topath;
+	unsigned int		tolen;
+	struct iattr *		sattr;
+};
+
+struct nfs_readdirargs {
+	struct nfs_fh *		fh;
+	__u32			cookie;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs_diropok {
+	struct nfs_fh *		fh;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs_readlinkargs {
+	struct nfs_fh *		fh;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs_readlinkres {
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs_readdirres {
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs3_sattrargs {
+	struct nfs_fh *		fh;
+	struct iattr *		sattr;
+	unsigned int		guard;
+	__u64			guardtime;
+};
+
+struct nfs3_diropargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+};
+
+struct nfs3_accessargs {
+	struct nfs_fh *		fh;
+	__u32			access;
+};
+
+struct nfs3_createargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	struct iattr *		sattr;
+	enum nfs3_createmode	createmode;
+	__u32			verifier[2];
+};
+
+struct nfs3_mkdirargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	struct iattr *		sattr;
+};
+
+struct nfs3_symlinkargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	const char *		topath;
+	unsigned int		tolen;
+	struct iattr *		sattr;
+};
+
+struct nfs3_mknodargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	enum nfs3_ftype		type;
+	struct iattr *		sattr;
+	dev_t			rdev;
+};
+
+struct nfs3_renameargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs3_linkargs {
+	struct nfs_fh *		fromfh;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs3_readdirargs {
+	struct nfs_fh *		fh;
+	__u64			cookie;
+	__u32			verf[2];
+	void *			buffer;
+	unsigned int		bufsiz;
+	int			plus;
+};
+
+struct nfs3_diropres {
+	struct nfs_fattr *	dir_attr;
+	struct nfs_fh *		fh;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs3_accessres {
+	struct nfs_fattr *	fattr;
+	__u32			access;
+};
+
+struct nfs3_readlinkargs {
+	struct nfs_fh *		fh;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs3_readlinkres {
+	struct nfs_fattr *	fattr;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs3_renameres {
+	struct nfs_fattr *	fromattr;
+	struct nfs_fattr *	toattr;
+};
+
+struct nfs3_linkres {
+	struct nfs_fattr *	dir_attr;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs3_readdirres {
+	struct nfs_fattr *	dir_attr;
+	__u32 *			verf;
+	void *			buffer;
+	unsigned int		bufsiz;
+	int			plus;
+};
+
+/*
+ * RPC procedure vector for NFSv2/NFSv3 demuxing
+ */
+struct nfs_rpc_ops {
+	int	version;		/* Protocol version */
+
+	int	(*getroot)(struct nfs_server *,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*getattr)(struct dentry *, struct nfs_fattr *);
+	int	(*setattr)(struct dentry *, struct nfs_fattr *, struct iattr *);
+	int	(*lookup)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*access)(struct dentry *, int fmode, struct nfs_fattr *, int);
+	int	(*readlink)(struct dentry *, struct nfs_fattr *,
+			void *buffer, unsigned int buflen);
+	int	(*read)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			int flags, unsigned long offset,
+			unsigned int count, void *buffer, int *eofp);
+	int	(*write)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			int flags, unsigned long offset,
+			unsigned int count, void *buffer,
+			struct nfs_writeverf *verfp);
+	int	(*commit)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			unsigned long, unsigned int);
+	int	(*create)(struct dentry *, struct nfs_fattr *,
+			struct qstr *, struct iattr *, int flags,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*remove)(struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*unlink_setup)	(struct rpc_message *,
+			struct dentry *, struct qstr *);
+	void	(*unlink_done)	(struct dentry *, struct rpc_message *);
+	int	(*rename)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*link)(struct dentry *, struct nfs_fattr *,
+			struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*symlink)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct qstr *, struct iattr *,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*mkdir)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct iattr *, struct nfs_fh *, struct nfs_fattr *);
+	int	(*rmdir)(struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*readdir)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			__u64 cookie, void *, unsigned int size, int plus);
+	int	(*mknod)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct iattr *, dev_t,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*statfs)(struct nfs_server *, struct nfs_fh *,
+			struct nfs_fsinfo *);
+	__u32 *	(*decode_dirent)(__u32 *, struct nfs_entry *, int plus);
+};
+
+/*
+ * 	NFS_CALL(getattr, inode, (fattr));
+ * into
+ *	NFS_PROTO(inode)->getattr(fattr);
+ */
+#define NFS_CALL(op, inode, args)	NFS_PROTO(inode)->op args
+
+/*
+ * Function vectors etc. for the NFS client
+ */
+extern struct nfs_rpc_ops	nfs_v2_clientops;
+extern struct nfs_rpc_ops	nfs_v3_clientops;
+extern struct rpc_version	nfs_version2;
+extern struct rpc_version	nfs_version3;
+extern struct rpc_program	nfs_program;
+
+#endif
diff -urN linux-2.2.18-pre8/include/linux/nfsd/const.h linux/include/linux/nfsd/const.h
--- linux-2.2.18-pre8/include/linux/nfsd/const.h	Tue May 11 10:36:12 1999
+++ linux/include/linux/nfsd/const.h	Mon Dec  3 20:25:25 2001
@@ -16,6 +16,10 @@
 #include <linux/fs.h>
 #include <linux/nfs.h>
 
+#define NFS_FHSIZE		32
+#define NFS_MAXPATHLEN		1024
+#define NFS_MAXNAMLEN		255
+
 /*
  * Maximum protocol version supported by knfsd
  */
diff -urN linux-2.2.18-pre8/include/linux/pagemap.h linux/include/linux/pagemap.h
--- linux-2.2.18-pre8/include/linux/pagemap.h	Tue Jan  4 10:12:25 2000
+++ linux/include/linux/pagemap.h	Mon Dec  3 20:25:25 2001
@@ -153,4 +153,9 @@
 extern void update_vm_cache_conditional(struct inode *, unsigned long, const char *, int, unsigned long);
 extern void update_vm_cache(struct inode *, unsigned long, const char *, int);
 
+typedef int filler_t(void *, struct page*);
+
+extern struct page *read_cache_page(struct inode *, unsigned long,
+				filler_t *, void *);
+
 #endif
diff -urN linux-2.2.18-pre8/include/linux/pci.h linux/include/linux/pci.h
--- linux-2.2.18-pre8/include/linux/pci.h	Mon Dec  3 20:24:44 2001
+++ linux/include/linux/pci.h	Mon Dec  3 20:25:25 2001
@@ -751,6 +751,9 @@
 #define PCI_VENDOR_ID_INTERG		0x10ea
 #define PCI_DEVICE_ID_INTERG_1680	0x1680
 #define PCI_DEVICE_ID_INTERG_1682	0x1682
+#define PCI_DEVICE_ID_INTERG_2000	0x2000
+#define PCI_DEVICE_ID_INTERG_2010	0x2010
+#define PCI_DEVICE_ID_INTERG_5000	0x5000
 
 #define PCI_VENDOR_ID_REALTEK		0x10ec
 #define PCI_DEVICE_ID_REALTEK_8029	0x8029
diff -urN linux-2.2.18-pre8/include/linux/personality.h linux/include/linux/personality.h
--- linux-2.2.18-pre8/include/linux/personality.h	Mon Aug  9 12:04:41 1999
+++ linux/include/linux/personality.h	Mon Dec  3 20:25:25 2001
@@ -27,6 +27,7 @@
 #define PER_IRIX32              (0x0009 | STICKY_TIMEOUTS) /* IRIX5 32-bit     */
 #define PER_IRIXN32             (0x000a | STICKY_TIMEOUTS) /* IRIX6 new 32-bit */
 #define PER_IRIX64              (0x000b | STICKY_TIMEOUTS) /* IRIX6 64-bit     */
+#define PER_RISCOS		(0x000c)
 
 /* Prototype for an lcall7 syscall handler. */
 typedef void (*lcall7_func)(struct pt_regs *);
diff -urN linux-2.2.18-pre8/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux-2.2.18-pre8/include/linux/proc_fs.h	Mon Dec  3 20:24:44 2001
+++ linux/include/linux/proc_fs.h	Mon Dec  3 20:25:25 2001
@@ -213,6 +213,7 @@
 	PROC_SCSI_I2O,
 	PROC_SCSI_3W_XXXX,
 	PROC_SCSI_USB_SCSI,
+	PROC_SCSI_CPQFCTS,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
diff -urN linux-2.2.18-pre8/include/linux/sunrpc/auth.h linux/include/linux/sunrpc/auth.h
--- linux-2.2.18-pre8/include/linux/sunrpc/auth.h	Tue May 11 10:35:45 1999
+++ linux/include/linux/sunrpc/auth.h	Mon Dec  3 20:25:25 2001
@@ -64,10 +64,10 @@
 	struct rpc_auth *	(*create)(struct rpc_clnt *);
 	void			(*destroy)(struct rpc_auth *);
 
-	struct rpc_cred *	(*crcreate)(struct rpc_task *);
+	struct rpc_cred *	(*crcreate)(int);
 	void			(*crdestroy)(struct rpc_cred *);
 
-	int			(*crmatch)(struct rpc_task *, struct rpc_cred*);
+	int			(*crmatch)(struct rpc_cred *, int);
 	u32 *			(*crmarshal)(struct rpc_task *, u32 *, int);
 	int			(*crrefresh)(struct rpc_task *);
 	u32 *			(*crvalidate)(struct rpc_task *, u32 *);
@@ -83,10 +83,14 @@
 int			rpcauth_unregister(struct rpc_authops *);
 struct rpc_auth *	rpcauth_create(unsigned int, struct rpc_clnt *);
 void			rpcauth_destroy(struct rpc_auth *);
-struct rpc_cred *	rpcauth_lookupcred(struct rpc_task *);
+struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, int);
+struct rpc_cred *	rpcauth_bindcred(struct rpc_task *);
 void			rpcauth_holdcred(struct rpc_task *);
-void			rpcauth_releasecred(struct rpc_task *);
-int			rpcauth_matchcred(struct rpc_task *, struct rpc_cred *);
+void			rpcauth_releasecred(struct rpc_auth *,
+					    struct rpc_cred *);
+void			rpcauth_unbindcred(struct rpc_task *);
+int			rpcauth_matchcred(struct rpc_auth *,
+					  struct rpc_cred *, int);
 u32 *			rpcauth_marshcred(struct rpc_task *, u32 *);
 u32 *			rpcauth_checkverf(struct rpc_task *, u32 *);
 int			rpcauth_refreshcred(struct rpc_task *);
diff -urN linux-2.2.18-pre8/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h
--- linux-2.2.18-pre8/include/linux/sunrpc/clnt.h	Tue May 11 10:36:15 1999
+++ linux/include/linux/sunrpc/clnt.h	Mon Dec  3 20:25:25 2001
@@ -30,7 +30,7 @@
  * The high-level client handle
  */
 struct rpc_clnt {
-	unsigned int		cl_users;	/* number of references */
+	atomic_t		cl_users;	/* number of references */
 	struct rpc_xprt *	cl_xprt;	/* transport */
 	struct rpc_procinfo *	cl_procinfo;	/* procedure info */
 	u32			cl_maxproc;	/* max procedure number */
@@ -45,6 +45,7 @@
 				cl_chatty   : 1,/* be verbose */
 				cl_autobind : 1,/* use getport() */
 				cl_binding  : 1,/* doing a getport() */
+				cl_droppriv : 1,/* enable NFS suid hack */
 				cl_oneshot  : 1,/* dispose after use */
 				cl_dead     : 1;/* abandoned */
 	unsigned int		cl_flags;	/* misc client flags */
@@ -111,21 +112,23 @@
 void		rpc_getport(struct rpc_task *, struct rpc_clnt *);
 int		rpc_register(u32, u32, int, unsigned short, int *);
 
-int		rpc_call(struct rpc_clnt *clnt, u32 proc,
-				void *argp, void *resp, int flags);
-int		rpc_call_async(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags);
-void		rpc_call_setup(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags);
-int		rpc_do_call(struct rpc_clnt *clnt, u32 proc,
-				void *argp, void *resp, int flags,
-				rpc_action callback, void *clntdata);
+void		rpc_call_setup(struct rpc_task *, struct rpc_message *, int);
+
+int		rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg,
+			       int flags, rpc_action callback, void *clntdata);
+int		rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg,
+			      int flags);
 void		rpc_restart_call(struct rpc_task *);
 void		rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
 void		rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
 
-#define rpc_call(clnt, proc, argp, resp, flags)	\
-		rpc_do_call(clnt, proc, argp, resp, flags, NULL, NULL)
+static __inline__
+int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
+{
+	struct rpc_message msg = { proc, argp, resp, NULL };
+	return rpc_call_sync(clnt, &msg, flags);
+}
+		
 
 extern __inline__ void
 rpc_set_timeout(struct rpc_clnt *clnt, unsigned int retr, unsigned long incr)
diff -urN linux-2.2.18-pre8/include/linux/sunrpc/sched.h linux/include/linux/sunrpc/sched.h
--- linux-2.2.18-pre8/include/linux/sunrpc/sched.h	Tue May 11 10:35:45 1999
+++ linux/include/linux/sunrpc/sched.h	Mon Dec  3 20:25:25 2001
@@ -11,6 +11,8 @@
 
 #include <linux/timer.h>
 #include <linux/tqueue.h>
+#include <asm/errno.h>
+#include <asm/spinlock.h>
 #include <linux/sunrpc/types.h>
 
 /*
@@ -20,6 +22,16 @@
 #undef  CONFIG_RPC_FASTSCHED
 
 /*
+ * This is the actual RPC procedure call info.
+ */
+struct rpc_message {
+	__u32			rpc_proc;	/* Procedure number */
+	void *			rpc_argp;	/* Arguments */
+	void *			rpc_resp;	/* Result */
+	struct rpc_cred *	rpc_cred;	/* Credentials */
+};
+
+/*
  * This is the RPC task struct
  */
 struct rpc_task {
@@ -30,19 +42,17 @@
 #endif
 	struct rpc_task *	tk_next_task;	/* global list of tasks */
 	struct rpc_task *	tk_prev_task;	/* global list of tasks */
+	struct rpc_task *	tk_parent;	/* parent task */
 	struct rpc_clnt *	tk_client;	/* RPC client */
 	struct rpc_rqst *	tk_rqstp;	/* RPC request */
-	struct rpc_cred *	tk_cred;	/* RPC credentials */
-	int			tk_status;	/* result of last operation */
+	volatile int		tk_status;	/* result of last operation */
 	struct rpc_wait_queue *	tk_rpcwait;	/* RPC wait queue we're on */
 
 	/*
 	 * RPC call state
 	 */
-	__u32			tk_proc;	/* procedure number */
+	struct rpc_message	tk_msg;		/* RPC call info */
 	__u32 *			tk_buffer;	/* XDR buffer */
-	void *			tk_argp;	/* argument storage */
-	void *			tk_resp;	/* result storage */
 	__u8			tk_garb_retry,
 				tk_cred_retry,
 				tk_suid_retry;
@@ -52,9 +62,11 @@
 	 * action	next procedure for async tasks
 	 * exit		exit async task and report to caller
 	 */
+	void			(*tk_timeout_fn)(struct rpc_task *);
 	void			(*tk_callback)(struct rpc_task *);
 	void			(*tk_action)(struct rpc_task *);
 	void			(*tk_exit)(struct rpc_task *);
+	void			(*tk_release)(struct rpc_task *);
 	void *			tk_calldata;
 
 	/*
@@ -65,9 +77,14 @@
 	struct timer_list	tk_timer;	/* kernel timer */
 	struct wait_queue *	tk_wait;	/* sync: sleep on this q */
 	unsigned long		tk_timeout;	/* timeout for rpc_sleep() */
-	unsigned short		tk_flags;	/* misc flags */
+	unsigned int		tk_flags;	/* misc flags */
+	unsigned int		tk_lock;	/* Task lock counter */
+	unsigned char		tk_active   : 1,/* Task has been activated */
+				tk_wakeup   : 1;/* Task waiting to wake up */
+	volatile unsigned char	tk_running  : 1,/* Task is running */
+				tk_sleeping : 1;/* Task is truly asleep */
 #ifdef RPC_DEBUG
-	unsigned short		tk_pid;		/* debugging aid */
+	unsigned int		tk_pid;		/* debugging aid */
 #endif
 };
 #define tk_auth			tk_client->cl_auth
@@ -78,27 +95,27 @@
 /*
  * RPC task flags
  */
-#define RPC_TASK_RUNNING	0x0001		/* is running */
-#define RPC_TASK_ASYNC		0x0002		/* is an async task */
-#define RPC_TASK_CALLBACK	0x0004		/* invoke callback */
-#define RPC_TASK_SWAPPER	0x0008		/* is swapping in/out */
-#define RPC_TASK_SETUID		0x0010		/* is setuid process */
-#define RPC_TASK_CHILD		0x0020		/* is child of other task */
-#define RPC_CALL_REALUID	0x0040		/* try using real uid */
-#define RPC_CALL_MAJORSEEN	0x0080		/* major timeout seen */
-#define RPC_TASK_ROOTCREDS	0x0100		/* force root creds */
-#define RPC_TASK_DYNAMIC	0x0200		/* task was kmalloc'ed */
-#define RPC_TASK_KILLED		0x0400		/* task was killed */
-#define RPC_TASK_NFSWRITE	0x1000		/* an NFS writeback */
+#define RPC_TASK_ASYNC		0x0001		/* is an async task */
+#define RPC_TASK_SWAPPER	0x0002		/* is swapping in/out */
+#define RPC_TASK_SETUID		0x0004		/* is setuid process */
+#define RPC_TASK_CHILD		0x0008		/* is child of other task */
+#define RPC_CALL_REALUID	0x0010		/* try using real uid */
+#define RPC_CALL_MAJORSEEN	0x0020		/* major timeout seen */
+#define RPC_TASK_ROOTCREDS	0x0040		/* force root creds */
+#define RPC_TASK_DYNAMIC	0x0080		/* task was kmalloc'ed */
+#define RPC_TASK_KILLED		0x0100		/* task was killed */
+#define RPC_TASK_PRIORITY	0x0200		/* resched when waking up */
 
-#define RPC_IS_RUNNING(t)	((t)->tk_flags & RPC_TASK_RUNNING)
 #define RPC_IS_ASYNC(t)		((t)->tk_flags & RPC_TASK_ASYNC)
 #define RPC_IS_SETUID(t)	((t)->tk_flags & RPC_TASK_SETUID)
 #define RPC_IS_CHILD(t)		((t)->tk_flags & RPC_TASK_CHILD)
 #define RPC_IS_SWAPPER(t)	((t)->tk_flags & RPC_TASK_SWAPPER)
-#define RPC_DO_CALLBACK(t)	((t)->tk_flags & RPC_TASK_CALLBACK)
 #define RPC_DO_ROOTOVERRIDE(t)	((t)->tk_flags & RPC_TASK_ROOTCREDS)
 #define RPC_ASSASSINATED(t)	((t)->tk_flags & RPC_TASK_KILLED)
+#define RPC_IS_RUNNING(t)	((t)->tk_running)
+#define RPC_IS_SLEEPING(t)	((t)->tk_sleeping)
+#define RPC_IS_ACTIVATED(t)	((t)->tk_active)
+#define RPC_DO_CALLBACK(t)	((t)->tk_callback != NULL)
 
 /*
  * RPC synchronization objects
@@ -110,6 +127,7 @@
 #endif
 };
 
+#define RPC_WAITQ_EMPTY(q)	((q)->task == NULL)
 #ifndef RPC_DEBUG
 # define RPC_INIT_WAITQ(name)	((struct rpc_wait_queue) { NULL })
 #else
@@ -125,51 +143,60 @@
 					rpc_action exitfunc, int flags);
 void		rpc_release_task(struct rpc_task *);
 void		rpc_killall_tasks(struct rpc_clnt *);
-void		rpc_execute(struct rpc_task *);
+int		rpc_execute(struct rpc_task *);
 void		rpc_run_child(struct rpc_task *parent, struct rpc_task *child,
 					rpc_action action);
-int		rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *);
+int		rpc_add_wait_queue(struct rpc_wait_queue *,
+				     struct rpc_task *);
 void		rpc_remove_wait_queue(struct rpc_task *);
 void		rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
 					rpc_action action, rpc_action timer);
-void		rpc_cond_wait(struct rpc_wait_queue *, struct rpc_task *,
-					unsigned char *,
-					rpc_action action, rpc_action timer);
+void		rpc_sleep_locked(struct rpc_wait_queue *, struct rpc_task *,
+				 rpc_action action, rpc_action timer);
+void		rpc_add_timer(struct rpc_task *task, rpc_action timer);
 void		rpc_wake_up_task(struct rpc_task *);
 void		rpc_wake_up(struct rpc_wait_queue *);
 struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *);
 void		rpc_wake_up_status(struct rpc_wait_queue *, int);
-void		rpc_add_timer(struct rpc_task *, rpc_action);
-void		rpc_del_timer(struct rpc_task *);
+int		__rpc_lock_task(struct rpc_task *);
+void		rpc_unlock_task(struct rpc_task *);
 void		rpc_delay(struct rpc_task *, unsigned long);
 void *		rpc_allocate(unsigned int flags, unsigned int);
 void		rpc_free(void *);
 int		rpciod_up(void);
 void		rpciod_down(void);
 void		rpciod_wake_up(void);
-void		rpciod_tcp_dispatcher(void);
 #ifdef RPC_DEBUG
 void		rpc_show_tasks(void);
 #endif
 
-extern __inline__ void *
+static __inline__ void *
 rpc_malloc(struct rpc_task *task, unsigned int size)
 {
 	return rpc_allocate(task->tk_flags, size);
 }
 
-extern __inline__ void
+static __inline__ void
 rpc_exit(struct rpc_task *task, int status)
 {
 	task->tk_status = status;
 	task->tk_action = NULL;
 }
 
+static __inline__ void
+rpc_kill(struct rpc_task *task, int status)
+{
+        rpc_exit(task, status);
+        rpc_wake_up_task(task);
+}
+
+extern spinlock_t rpc_queue_lock;
+
 #ifdef RPC_DEBUG
-extern __inline__ char *
+static __inline__ char *
 rpc_qname(struct rpc_wait_queue *q)
 {
-	return q->name? q->name : "unknown";
+	return q? (q->name? q->name : "unknown") : "none";
 }
 #endif
 
diff -urN linux-2.2.18-pre8/include/linux/sunrpc/svcsock.h linux/include/linux/sunrpc/svcsock.h
--- linux-2.2.18-pre8/include/linux/sunrpc/svcsock.h	Tue Jan  4 10:12:25 2000
+++ linux/include/linux/sunrpc/svcsock.h	Mon Dec  3 20:25:25 2001
@@ -25,13 +25,13 @@
 
 	struct svc_serv *	sk_server;	/* service for this socket */
 	atomic_t		sk_inuse;	/* use count */
-	unsigned char		sk_busy;	/* enqueued/receiving */
-	unsigned char		sk_conn;	/* conn pending */
-	unsigned char		sk_close;	/* dead or dying */
-	int			sk_data;	/* data pending */
-	unsigned int		sk_temp : 1,	/* temp socket */
+	volatile int		sk_conn;	/* conn pending */
+	volatile int		sk_data;	/* data pending */
+	volatile unsigned char	sk_busy : 1,	/* enqueued/receiving */
+				sk_close: 1,	/* dead or dying */
 				sk_qued : 1,	/* on serv->sk_sockets */
 				sk_dead : 1;	/* socket closed */
+	unsigned char		sk_temp : 1;	/* temp socket */
 	int			(*sk_recvfrom)(struct svc_rqst *rqstp);
 	int			(*sk_sendto)(struct svc_rqst *rqstp);
 
diff -urN linux-2.2.18-pre8/include/linux/sunrpc/types.h linux/include/linux/sunrpc/types.h
--- linux-2.2.18-pre8/include/linux/sunrpc/types.h	Tue May 11 10:35:44 1999
+++ linux/include/linux/sunrpc/types.h	Mon Dec  3 20:25:25 2001
@@ -58,6 +58,7 @@
 	}
 	if (*q == item)
 		*q = next;
+	item->prev = item->next = 0;
 }
 
 #define rpc_insert_list(q, i) \
diff -urN linux-2.2.18-pre8/include/linux/sunrpc/xdr.h linux/include/linux/sunrpc/xdr.h
--- linux-2.2.18-pre8/include/linux/sunrpc/xdr.h	Sun Dec 27 22:18:30 1998
+++ linux/include/linux/sunrpc/xdr.h	Mon Dec  3 20:25:25 2001
@@ -1,7 +1,7 @@
 /*
  * include/linux/sunrpc/xdr.h
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef _SUNRPC_XDR_H_
@@ -59,13 +59,32 @@
 /*
  * Miscellaneous XDR helper functions
  */
-u32 *	xdr_encode_string(u32 *p, const char *s);
+u32 *	xdr_encode_string(u32 *p, const char *s, int len);
 u32 *	xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen);
 u32 *	xdr_encode_netobj(u32 *p, const struct xdr_netobj *);
 u32 *	xdr_decode_netobj(u32 *p, struct xdr_netobj *);
 u32 *	xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len);
 
 /*
+ * Decode 64bit quantities (NFSv3 support)
+ */
+static inline u32 *
+xdr_encode_hyper(u32 *p, __u64 val)
+{
+	*p++ = htonl(val >> 32);
+	*p++ = htonl(val & 0xFFFFFFFF);
+	return p;
+}
+
+static inline u32 *
+xdr_decode_hyper(u32 *p, __u64 *valp)
+{
+	*valp  = ((__u64) ntohl(*p++)) << 32;
+	*valp |= ntohl(*p++);
+	return p;
+}
+
+/*
  * Adjust iovec to reflect end of xdr'ed data (RPC client XDR)
  */
 static inline int
@@ -73,6 +92,9 @@
 {
 	return iov->iov_len = ((u8 *) p - (u8 *) iov->iov_base);
 }
+
+void xdr_shift_iovec(struct iovec *, int, size_t);
+void xdr_zero_iovec(struct iovec *, int, size_t);
 
 #endif /* __KERNEL__ */
 
diff -urN linux-2.2.18-pre8/include/linux/sunrpc/xprt.h linux/include/linux/sunrpc/xprt.h
--- linux-2.2.18-pre8/include/linux/sunrpc/xprt.h	Tue Jan  4 10:12:25 2000
+++ linux/include/linux/sunrpc/xprt.h	Mon Dec  3 20:25:25 2001
@@ -9,15 +9,17 @@
 #ifndef _LINUX_SUNRPC_XPRT_H
 #define _LINUX_SUNRPC_XPRT_H
 
+#include <linux/timer.h>
 #include <linux/uio.h>
 #include <linux/socket.h>
 #include <linux/in.h>
+#include <linux/list.h>
 #include <linux/sunrpc/sched.h>
 
 /*
  * Maximum number of iov's we use.
  */
-#define MAX_IOVEC	8
+#define MAX_IOVEC	10
 
 /*
  * The transport code maintains an estimate on the maximum number of out-
@@ -44,10 +46,10 @@
 #define RPC_MAXCWND		(RPC_MAXCONG * RPC_CWNDSCALE)
 #define RPC_INITCWND		RPC_CWNDSCALE
 #define RPCXPRT_CONGESTED(xprt) \
-	((xprt)->cong >= ((xprt)->nocong? RPC_MAXCWND : (xprt)->cwnd))
+	((xprt)->cong >= (xprt)->cwnd)
 
 /* Default timeout values */
-#define RPC_MAX_UDP_TIMEOUT	(6*HZ)
+#define RPC_MAX_UDP_TIMEOUT	(60*HZ)
 #define RPC_MAX_TCP_TIMEOUT	(600*HZ)
 
 /* RPC call and reply header size as number of 32bit words (verifier
@@ -96,8 +98,7 @@
 	struct rpc_task *	rq_task;	/* RPC task data */
 	__u32			rq_xid;		/* request XID */
 	struct rpc_rqst *	rq_next;	/* free list */
-	unsigned char		rq_gotit;	/* reply received */
-	unsigned char		rq_damaged;	/* being received */
+	volatile unsigned char	rq_received : 1;/* receive completed */
 
 	/*
 	 * For authentication (e.g. auth_des)
@@ -122,14 +123,6 @@
 #define rq_rlen			rq_rcv_buf.io_len
 
 struct rpc_xprt {
-	struct rpc_xprt *	link;		/* list of all clients */
-	struct rpc_xprt *	rx_pending;	/* receive pending list */
-	struct rpc_xprt *	tx_pending;	/* transmit pending list */
-	
-	int 			rx_pending_flag;/* are we on the rcv pending list ? */
-	int 			tx_pending_flag;/* are we on the xmit pending list ? */
-
-	struct file *		file;		/* VFS layer */
 	struct socket *		sock;		/* BSD socket layer */
 	struct sock *		inet;		/* INET layer */
 
@@ -147,61 +140,92 @@
 	struct rpc_wait_queue	reconn;		/* waiting for reconnect */
 	struct rpc_rqst *	free;		/* free slots */
 	struct rpc_rqst		slot[RPC_MAXREQS];
-	unsigned char		connected;	/* TCP: connected */
-	unsigned char		write_space;	/* TCP: can send */
-	unsigned int		shutdown   : 1,	/* being shut down */
-				nocong	   : 1,	/* no congestion control */
-				stream     : 1,	/* TCP */
-				tcp_more   : 1,	/* more record fragments */
-				connecting : 1;	/* being reconnected */
+	volatile unsigned char	connected   : 1,/* TCP: connected */
+				write_space : 1;/* TCP: can send */
+	unsigned char		nocong	    : 1,/* no congestion control */
+				stream      : 1,/* TCP */
+				shutdown    : 1,/* being shut down */
+				tcp_more    : 1,/* more record fragments */
+				connecting  : 1;/* being reconnected */
+
+	unsigned long		tcp_timeout;	/* TCP timeout window */
+	struct timer_list	tcp_timer;	/* TCP timeout */
 
 	/*
 	 * State of TCP reply receive stuff
 	 */
-	union {					/* record marker & XID */
-		u32		header[2];
-		u8 		data[8];
-	}			tcp_recm;
-	struct rpc_rqst *	tcp_rqstp;
-	struct iovec		tcp_iovec[MAX_IOVEC];
-	u32			tcp_total;	/* overall record length */
-	u32			tcp_reclen;	/* fragment length */
-	u32			tcp_offset;	/* fragment offset */
-	u32			tcp_copied;	/* copied to request */
+	u32			tcp_recm;	/* Fragment header */
+	u32			tcp_xid;	/* Current XID */
+	unsigned int		tcp_reclen,	/* fragment length */
+				tcp_offset,	/* fragment offset */
+				tcp_copied;	/* copied to request */
+	struct list_head	rx_pending;	/* receive pending list */
 
 	/*
-	 * TCP send stuff
+	 * Send stuff
 	 */
-	struct rpc_iov		snd_buf;	/* send buffer */
 	struct rpc_task *	snd_task;	/* Task blocked in send */
-	u32			snd_sent;	/* Bytes we have sent */
 
 
 	void			(*old_data_ready)(struct sock *, int);
 	void			(*old_state_change)(struct sock *);
 	void			(*old_write_space)(struct sock *);
+
+	struct wait_queue *	cong_wait;
 };
-#define tcp_reclen		tcp_recm.header[0]
-#define tcp_xid			tcp_recm.header[1]
 
 #ifdef __KERNEL__
 
-struct rpc_xprt *	xprt_create(struct file *socket,
-					struct sockaddr_in *addr,
-					struct rpc_timeout *toparms);
 struct rpc_xprt *	xprt_create_proto(int proto, struct sockaddr_in *addr,
 					struct rpc_timeout *toparms);
 int			xprt_destroy(struct rpc_xprt *);
+void			xprt_shutdown(struct rpc_xprt *);
 void			xprt_default_timeout(struct rpc_timeout *, int);
 void			xprt_set_timeout(struct rpc_timeout *, unsigned int,
 					unsigned long);
 
 int			xprt_reserve(struct rpc_task *);
+int			xprt_down_transmit(struct rpc_task *);
 void			xprt_transmit(struct rpc_task *);
+void			xprt_up_transmit(struct rpc_task *);
 void			xprt_receive(struct rpc_task *);
 int			xprt_adjust_timeout(struct rpc_timeout *);
 void			xprt_release(struct rpc_task *);
 void			xprt_reconnect(struct rpc_task *);
+void			__rpciod_tcp_dispatcher(void);
+
+extern struct list_head	rpc_xprt_pending;
+
+static inline
+int xprt_tcp_pending(void)
+{
+	return !list_empty(&rpc_xprt_pending);
+}
+
+static inline
+void rpciod_tcp_dispatcher(void)
+{
+	if (xprt_tcp_pending())
+		__rpciod_tcp_dispatcher();
+}
+
+static inline
+int xprt_connected(struct rpc_xprt *xprt)
+{
+	return xprt->connected;
+}
+
+static inline
+void xprt_set_connected(struct rpc_xprt *xprt)
+{
+	xprt->connected = 1;
+}
+
+static inline
+void xprt_clear_connected(struct rpc_xprt *xprt)
+{
+	xprt->connected = 0;
+}
 
 #endif /* __KERNEL__*/
 
diff -urN linux-2.2.18-pre8/include/scsi/scsi.h linux/include/scsi/scsi.h
--- linux-2.2.18-pre8/include/scsi/scsi.h	Mon Mar 15 16:11:31 1999
+++ linux/include/scsi/scsi.h	Mon Dec  3 20:25:25 2001
@@ -205,6 +205,9 @@
 /* Used to get the bus number for a device */
 #define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
 
+/* Used to get Fibre Channel WWN and port_id from device */
+#define SCSI_IOCTL_FC_TARGET_ADDRESS 0x5387
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
diff -urN linux-2.2.18-pre8/include/scsi/scsi_ioctl.h linux/include/scsi/scsi_ioctl.h
--- linux-2.2.18-pre8/include/scsi/scsi_ioctl.h	Fri May  1 11:19:58 1998
+++ linux/include/scsi/scsi_ioctl.h	Mon Dec  3 20:25:25 2001
@@ -32,6 +32,13 @@
 	__u32 host_unique_id;
 } Scsi_Idlun;
 
+/* Fibre Channel WWN, port_id struct */
+typedef struct scsi_fctargaddress
+{
+	__u32 host_port_id;
+	unsigned char host_wwn[8]; // include NULL term.
+} Scsi_FCTargAddress;
+
 extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
 extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
 extern int scsi_ioctl_send_command(Scsi_Device *dev,
diff -urN linux-2.2.18-pre8/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-2.2.18-pre8/kernel/ksyms.c	Mon Dec  3 20:24:44 2001
+++ linux/kernel/ksyms.c	Mon Dec  3 20:25:25 2001
@@ -126,6 +126,7 @@
 EXPORT_SYMBOL(igrab);
 EXPORT_SYMBOL(iunique);
 EXPORT_SYMBOL(iget);
+EXPORT_SYMBOL(iget4);
 EXPORT_SYMBOL(iget_in_use);
 EXPORT_SYMBOL(iput);
 EXPORT_SYMBOL(__namei);
@@ -156,6 +157,7 @@
 EXPORT_SYMBOL(invalidate_inode_pages);
 EXPORT_SYMBOL(truncate_inode_pages);
 EXPORT_SYMBOL(fsync_dev);
+EXPORT_SYMBOL(vfs_permission);
 EXPORT_SYMBOL(permission);
 EXPORT_SYMBOL(inode_setattr);
 EXPORT_SYMBOL(inode_change_ok);
@@ -195,8 +197,15 @@
 EXPORT_SYMBOL(vfs_unlink);
 EXPORT_SYMBOL(vfs_rename);
 EXPORT_SYMBOL(__pollwait);
+EXPORT_SYMBOL(read_cache_page);
 EXPORT_SYMBOL(ROOT_DEV);
 EXPORT_SYMBOL(inode_generation_count);
+
+EXPORT_SYMBOL(page_cache_size);
+EXPORT_SYMBOL(page_hash_table);
+EXPORT_SYMBOL(page_hash_mask);
+EXPORT_SYMBOL(page_hash_bits);
+EXPORT_SYMBOL(__wait_on_page);
 
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 EXPORT_SYMBOL(do_nfsservctl);
diff -urN linux-2.2.18-pre8/mm/filemap.c linux/mm/filemap.c
--- linux-2.2.18-pre8/mm/filemap.c	Mon Dec  3 20:24:44 2001
+++ linux/mm/filemap.c	Mon Dec  3 20:25:25 2001
@@ -55,6 +55,15 @@
 static inline void 
 make_pio_request(struct file *, unsigned long, unsigned long);
 
+static inline int sync_page(struct page *page)
+{
+	struct inode *inode = page->inode;
+
+	if (inode && inode->i_op && inode->i_op->sync_page)
+		return inode->i_op->sync_page(page);
+	run_task_queue(&tq_disk);
+	return 0;
+}
 
 /*
  * Invalidate the pages of an inode, removing all pages that aren't
@@ -350,7 +359,7 @@
 	add_wait_queue(&page->wait, &wait);
 repeat:
 	tsk->state = TASK_UNINTERRUPTIBLE;
-	run_task_queue(&tq_disk);
+	sync_page(page);
 	if (PageLocked(page)) {
 		schedule();
 		goto repeat;
@@ -621,7 +630,8 @@
 {
 	struct dentry *dentry = filp->f_dentry;
 	struct inode *inode = dentry->d_inode;
-	size_t pos, pgpos, page_cache;
+	unsigned long page_cache;
+	size_t pos, pgpos;
 	int reada_ok;
 	int max_readahead = get_max_readahead(inode);
 
@@ -1473,6 +1483,71 @@
 	return error;
 }
 
+static inline
+struct page *__read_cache_page(struct inode *inode, 
+			       unsigned long offset,
+			       int (*filler)(void *,struct page*),
+			       void *data)
+{
+	struct page **hash = page_hash(inode, offset);
+	struct page *page;
+	unsigned long cached_page = 0;
+	int err;
+
+	offset &= PAGE_CACHE_MASK;
+repeat:
+	page = __find_page(inode, offset, *hash);
+	if (!page) {
+		if (!cached_page) {
+			cached_page = page_cache_alloc();
+			if (!cached_page)
+				return ERR_PTR(-ENOMEM);
+			goto repeat;
+		}
+		page = page_cache_entry(cached_page);
+		cached_page = 0;
+		add_to_page_cache(page, inode, offset, hash);
+		set_bit(PG_locked, &page->flags);
+		err = filler(data, page);
+		if (err < 0) {
+			page_cache_release(page);
+			page = ERR_PTR(err);
+		}
+	}
+	if (cached_page)
+		page_cache_free(cached_page);
+	return page;
+}
+
+/*
+ * Read into the page cache. If a page already exists,
+ * and Page_Uptodate() is not set, try to fill the page.
+ */
+struct page *read_cache_page(struct inode *inode,
+				unsigned long offset,
+				int (*filler)(void *,struct page*),
+				void *data)
+{
+	struct page *page = __read_cache_page(inode, offset, filler, data);
+	int err;
+
+	if (IS_ERR(page) || PageUptodate(page))
+		goto out;
+
+	wait_on_page(page);
+	if (PageUptodate(page))
+		goto out;
+
+	set_bit(PG_locked, &page->flags);
+	err = filler(data, page);
+	if (err < 0) {
+		page_cache_release(page);
+		page = ERR_PTR(err);
+	}
+ out:
+	return page;
+}
+
 /*
  * Write to a file through the page cache. This is mainly for the
  * benefit of NFS and possibly other network-based file systems.
@@ -1569,6 +1644,11 @@
 		wait_on_page(page);
 		set_bit(PG_locked, &page->flags);
 
+		if (inode->i_op->prepare_write)
+			status = inode->i_op->prepare_write(file, page, offset, bytes);
+		if (status < 0)
+			goto unlock;
+
 		/*
 		 * Do the real work.. If the writer ends up delaying the write,
 		 * the writer needs to increment the page use counts until he
@@ -1583,6 +1663,7 @@
 		if (bytes)
 			status = inode->i_op->updatepage(file, page, offset, bytes, sync);
 
+ unlock:
 		/* Mark it unlocked again and drop the page.. */
 		clear_bit(PG_locked, &page->flags);
 		wake_up(&page->wait);
diff -urN linux-2.2.18-pre8/net/sunrpc/auth.c linux/net/sunrpc/auth.c
--- linux-2.2.18-pre8/net/sunrpc/auth.c	Mon Mar 22 08:00:15 1999
+++ linux/net/sunrpc/auth.c	Mon Dec  3 20:25:25 2001
@@ -78,6 +78,15 @@
 	auth->au_nextgc = jiffies + (auth->au_expire >> 1);
 }
 
+static inline void
+rpcauth_crdestroy(struct rpc_auth *auth, struct rpc_cred *cred)
+{
+	if (auth->au_ops->crdestroy)
+		auth->au_ops->crdestroy(cred);
+	else
+		rpc_free(cred);
+}
+
 /*
  * Clear the RPC credential cache
  */
@@ -107,17 +116,14 @@
 rpcauth_gc_credcache(struct rpc_auth *auth)
 {
 	struct rpc_cred	**q, *cred, *free = NULL;
-	int		i, safe = 0;
+	int		i;
 
 	dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
 	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
 		q = &auth->au_credcache[i];
 		while ((cred = *q) != NULL) {
-			if (++safe > 500) {
-				printk("RPC: rpcauth_gc_credcache looping!\n");
-				break;
-			}
-			if (!cred->cr_count && time_before(cred->cr_expire, jiffies)) {
+			if (!cred->cr_count &&
+			    time_before(cred->cr_expire, jiffies)) {
 				*q = cred->cr_next;
 				cred->cr_next = free;
 				free = cred;
@@ -128,7 +134,7 @@
 	}
 	while ((cred = free) != NULL) {
 		free = cred->cr_next;
-		rpc_free(cred);
+		rpcauth_crdestroy(auth, cred);
 	}
 	auth->au_nextgc = jiffies + auth->au_expire;
 }
@@ -136,7 +142,7 @@
 /*
  * Insert credential into cache
  */
-inline void
+void
 rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
 {
 	int		nr;
@@ -144,28 +150,29 @@
 	nr = (cred->cr_uid % RPC_CREDCACHE_NR);
 	cred->cr_next = auth->au_credcache[nr];
 	auth->au_credcache[nr] = cred;
-	cred->cr_expire = jiffies + auth->au_expire;
 	cred->cr_count++;
+	cred->cr_expire = jiffies + auth->au_expire;
 }
 
 /*
  * Look up a process' credentials in the authentication cache
  */
 static struct rpc_cred *
-rpcauth_lookup_credcache(struct rpc_task *task)
+rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags)
 {
-	struct rpc_auth	*auth = task->tk_auth;
 	struct rpc_cred	**q, *cred = NULL;
-	int		nr;
+	int		nr = 0;
 
-	nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR);
+	if (!(taskflags & RPC_TASK_ROOTCREDS))
+		nr = current->uid % RPC_CREDCACHE_NR;
 
 	if (time_before(auth->au_nextgc, jiffies))
 		rpcauth_gc_credcache(auth);
 
 	q = &auth->au_credcache[nr];
 	while ((cred = *q) != NULL) {
-		if (auth->au_ops->crmatch(task, cred)) {
+		if (!(cred->cr_flags & RPCAUTH_CRED_DEAD) &&
+		    auth->au_ops->crmatch(cred, taskflags)) {
 			*q = cred->cr_next;
 			break;
 		}
@@ -173,7 +180,7 @@
 	}
 
 	if (!cred)
-		cred = auth->au_ops->crcreate(task);
+		cred = auth->au_ops->crcreate(taskflags);
 
 	if (cred)
 		rpcauth_insert_credcache(auth, cred);
@@ -184,7 +191,7 @@
 /*
  * Remove cred handle from cache
  */
-static inline void
+static void
 rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
 {
 	struct rpc_cred	**q, *cr;
@@ -195,65 +202,86 @@
 	while ((cr = *q) != NULL) {
 		if (cred == cr) {
 			*q = cred->cr_next;
-			return;
+			cred->cr_next = NULL;
+			break;
 		}
 		q = &cred->cr_next;
 	}
 }
 
 struct rpc_cred *
-rpcauth_lookupcred(struct rpc_task *task)
+rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
 {
+	dprintk("RPC:     looking up %s cred\n",
+		auth->au_ops->au_name);
+	return rpcauth_lookup_credcache(auth, taskflags);
+}
+
+struct rpc_cred *
+rpcauth_bindcred(struct rpc_task *task)
+{
+	struct rpc_auth *auth = task->tk_auth;
+
 	dprintk("RPC: %4d looking up %s cred\n",
 		task->tk_pid, task->tk_auth->au_ops->au_name);
-	return task->tk_cred = rpcauth_lookup_credcache(task);
+	task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, task->tk_flags);
+	if (task->tk_msg.rpc_cred == 0)
+		task->tk_status = -ENOMEM;
+	return task->tk_msg.rpc_cred;
 }
 
 int
-rpcauth_matchcred(struct rpc_task *task, struct rpc_cred *cred)
+rpcauth_matchcred(struct rpc_auth *auth, struct rpc_cred *cred, int taskflags)
 {
-	struct rpc_auth	*auth = task->tk_auth;
-
-	dprintk("RPC: %4d matching %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
-	return auth->au_ops->crmatch(task, cred);
+	dprintk("RPC:     matching %s cred %d\n",
+		auth->au_ops->au_name, taskflags);
+	return auth->au_ops->crmatch(cred, taskflags);
 }
 
 void
 rpcauth_holdcred(struct rpc_task *task)
 {
 	dprintk("RPC: %4d holding %s cred %p\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
-	if (task->tk_cred)
-		task->tk_cred->cr_count++;
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
+	if (task->tk_msg.rpc_cred) {
+		task->tk_msg.rpc_cred->cr_count++;
+		task->tk_msg.rpc_cred->cr_expire = jiffies + task->tk_auth->au_expire;
+	}
 }
 
 void
-rpcauth_releasecred(struct rpc_task *task)
+rpcauth_releasecred(struct rpc_auth *auth, struct rpc_cred *cred)
 {
-	struct rpc_auth	*auth = task->tk_auth;
-	struct rpc_cred	*cred;
-
-	dprintk("RPC: %4d releasing %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
-	if ((cred = task->tk_cred) != NULL) {
+	if (cred != NULL && cred->cr_count > 0) {
 		cred->cr_count--;
 		if (cred->cr_flags & RPCAUTH_CRED_DEAD) {
 			rpcauth_remove_credcache(auth, cred);
 			if (!cred->cr_count)
-				auth->au_ops->crdestroy(cred);
+				rpcauth_crdestroy(auth, cred);
 		}
-		task->tk_cred = NULL;
 	}
 }
 
+void
+rpcauth_unbindcred(struct rpc_task *task)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
+
+	dprintk("RPC: %4d releasing %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, cred);
+
+	rpcauth_releasecred(auth, cred);
+	task->tk_msg.rpc_cred = NULL;
+}
+
 u32 *
 rpcauth_marshcred(struct rpc_task *task, u32 *p)
 {
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d marshaling %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	return auth->au_ops->crmarshal(task, p,
 				task->tk_flags & RPC_CALL_REALUID);
 }
@@ -264,7 +292,7 @@
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d validating %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	return auth->au_ops->crvalidate(task, p);
 }
 
@@ -274,7 +302,7 @@
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d refreshing %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	task->tk_status = auth->au_ops->crrefresh(task);
 	return task->tk_status;
 }
@@ -283,14 +311,14 @@
 rpcauth_invalcred(struct rpc_task *task)
 {
 	dprintk("RPC: %4d invalidating %s cred %p\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
-	if (task->tk_cred)
-		task->tk_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
+	if (task->tk_msg.rpc_cred)
+		task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
 }
 
 int
 rpcauth_uptodatecred(struct rpc_task *task)
 {
-	return !(task->tk_cred) ||
-		(task->tk_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
+	return !(task->tk_msg.rpc_cred) ||
+		(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
 }
diff -urN linux-2.2.18-pre8/net/sunrpc/auth_null.c linux/net/sunrpc/auth_null.c
--- linux-2.2.18-pre8/net/sunrpc/auth_null.c	Mon Mar 22 08:00:15 1999
+++ linux/net/sunrpc/auth_null.c	Mon Dec  3 20:25:25 2001
@@ -38,6 +38,7 @@
 nul_destroy(struct rpc_auth *auth)
 {
 	dprintk("RPC: destroying NULL authenticator %p\n", auth);
+	rpcauth_free_credcache(auth);
 	rpc_free(auth);
 }
 
@@ -45,15 +46,12 @@
  * Create NULL creds for current process
  */
 static struct rpc_cred *
-nul_create_cred(struct rpc_task *task)
+nul_create_cred(int flags)
 {
 	struct rpc_cred	*cred;
 
-	if (!(cred = (struct rpc_cred *) rpc_malloc(task, sizeof(*cred)))) {
-		task->tk_status = -ENOMEM;
+	if (!(cred = (struct rpc_cred *) rpc_allocate(flags, sizeof(*cred))))
 		return NULL;
-	}
-
 	cred->cr_count = 0;
 	cred->cr_flags = RPCAUTH_CRED_UPTODATE;
 
@@ -73,7 +71,7 @@
  * Match cred handle against current process
  */
 static int
-nul_match(struct rpc_task *task, struct rpc_cred *cred)
+nul_match(struct rpc_cred *cred, int taskflags)
 {
 	return 1;
 }
@@ -110,10 +108,12 @@
 		printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
 		return NULL;
 	}
-	if ((n = ntohl(*p++)) != 0) {
+	if ((n = ntohl(*p++)) > 400) {
 		printk("RPC: bad verf size: %ld\n", (unsigned long) n);
 		return NULL;
 	}
+	if (n)
+		p += XDR_QUADLEN(n);
 
 	return p;
 }
diff -urN linux-2.2.18-pre8/net/sunrpc/auth_unix.c linux/net/sunrpc/auth_unix.c
--- linux-2.2.18-pre8/net/sunrpc/auth_unix.c	Mon Mar 22 08:00:15 1999
+++ linux/net/sunrpc/auth_unix.c	Mon Dec  3 20:25:25 2001
@@ -27,7 +27,7 @@
 
 #define UNX_CRED_EXPIRE		(60 * HZ)
 
-#define UNX_WRITESLACK		(21 + (UNX_MAXNODENAME >> 2))
+#define UNX_WRITESLACK		(9 + NFS_NGROUPS + (UNX_MAXNODENAME >> 2))
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_AUTH
@@ -60,7 +60,7 @@
 }
 
 static struct rpc_cred *
-unx_create_cred(struct rpc_task *task)
+unx_create_cred(int flags)
 {
 	struct unx_cred	*cred;
 	int		i;
@@ -68,14 +68,12 @@
 	dprintk("RPC:      allocating UNIX cred for uid %d gid %d\n",
 				current->uid, current->gid);
 
-	if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred)))) {
-		task->tk_status = -ENOMEM;
+	if (!(cred = (struct unx_cred *) rpc_allocate(flags, sizeof(*cred))))
 		return NULL;
-	}
 
 	cred->uc_count = 0;
 	cred->uc_flags = RPCAUTH_CRED_UPTODATE;
-	if (RPC_DO_ROOTOVERRIDE(task)) {
+	if (flags & RPC_TASK_ROOTCREDS) {
 		cred->uc_uid = cred->uc_fsuid = 0;
 		cred->uc_gid = cred->uc_fsgid = 0;
 		cred->uc_gids[0] = NOGROUP;
@@ -116,7 +114,7 @@
 	cred->uc_fsgid = gid;
 	cred->uc_gids[0] = (gid_t) NOGROUP;
 
-	return task->tk_cred = (struct rpc_cred *) cred;
+	return task->tk_msg.rpc_cred = (struct rpc_cred *) cred;
 }
 
 static void
@@ -131,12 +129,12 @@
  * request root creds (e.g. for NFS swapping).
  */
 static int
-unx_match(struct rpc_task * task, struct rpc_cred *rcred)
+unx_match(struct rpc_cred *rcred, int taskflags)
 {
 	struct unx_cred	*cred = (struct unx_cred *) rcred;
 	int		i;
 
-	if (!RPC_DO_ROOTOVERRIDE(task)) {
+	if (!(taskflags & RPC_TASK_ROOTCREDS)) {
 		int groups;
 
 		if (cred->uc_uid != current->uid
@@ -166,7 +164,7 @@
 unx_marshal(struct rpc_task *task, u32 *p, int ruid)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
-	struct unx_cred	*cred = (struct unx_cred *) task->tk_cred;
+	struct unx_cred	*cred = (struct unx_cred *) task->tk_msg.rpc_cred;
 	u32		*base, *hold;
 	int		i, n;
 
@@ -182,7 +180,8 @@
 	memcpy(p, clnt->cl_nodename, n);
 	p += (n + 3) >> 2;
 
-	if (ruid) {
+	/* Note: we don't use real uid if it involves raising priviledge */
+	if (ruid && cred->uc_uid != 0 && cred->uc_gid != 0) {
 		*p++ = htonl((u32) cred->uc_uid);
 		*p++ = htonl((u32) cred->uc_gid);
 	} else {
@@ -207,7 +206,7 @@
 static int
 unx_refresh(struct rpc_task *task)
 {
-	task->tk_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
+	task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
 	return task->tk_status = -EACCES;
 }
 
@@ -216,7 +215,7 @@
 {
 	u32		n = ntohl(*p++);
 
-	if (n != RPC_AUTH_NULL && n != RPC_AUTH_UNIX && n != RPC_AUTH_SHORT) {
+	if (n != RPC_AUTH_NULL && n != RPC_AUTH_SHORT) {
 		printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
 		return NULL;
 	}
@@ -224,8 +223,12 @@
 		printk("RPC: giant verf size: %ld\n", (unsigned long) n);
 		return NULL;
 	}
-	task->tk_auth->au_rslack = (n >> 2) + 2;
-	p += (n >> 2);
+	if (n) {
+		n = XDR_QUADLEN(n);
+		if (n + 2 > task->tk_auth->au_rslack)
+			task->tk_auth->au_rslack = n + 2;
+		p += n;
+	}
 
 	return p;
 }
diff -urN linux-2.2.18-pre8/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- linux-2.2.18-pre8/net/sunrpc/clnt.c	Tue Jan  4 10:12:27 2000
+++ linux/net/sunrpc/clnt.c	Mon Dec  3 20:25:25 2001
@@ -29,11 +29,12 @@
 #include <linux/malloc.h>
 #include <linux/in.h>
 #include <linux/utsname.h>
+#include <linux/interrupt.h>
 
 #include <linux/sunrpc/clnt.h>
 
 
-#define RPC_SLACK_SPACE		1024	/* total overkill */
+#define RPC_SLACK_SPACE		512	/* total overkill */
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_CALL
@@ -42,14 +43,13 @@
 static struct wait_queue *	destroy_wait = NULL;
 
 
-static void	call_bind(struct rpc_task *task);
 static void	call_reserve(struct rpc_task *task);
 static void	call_reserveresult(struct rpc_task *task);
 static void	call_allocate(struct rpc_task *task);
 static void	call_encode(struct rpc_task *task);
 static void	call_decode(struct rpc_task *task);
+static void	call_bind(struct rpc_task *task);
 static void	call_transmit(struct rpc_task *task);
-static void	call_receive(struct rpc_task *task);
 static void	call_status(struct rpc_task *task);
 static void	call_refresh(struct rpc_task *task);
 static void	call_refreshresult(struct rpc_task *task);
@@ -76,6 +76,12 @@
 	dprintk("RPC: creating %s client for %s (xprt %p)\n",
 		program->name, servname, xprt);
 
+	xdr_init();
+
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
+
 	if (!xprt)
 		goto out;
 	if (vers >= program->nrvers || !(version = program->version[vers]))
@@ -85,6 +91,7 @@
 	if (!clnt)
 		goto out_no_clnt;
 	memset(clnt, 0, sizeof(*clnt));
+	atomic_set(&clnt->cl_users, 0);
 
 	clnt->cl_xprt     = xprt;
 	clnt->cl_procinfo = version->procs;
@@ -94,7 +101,7 @@
 	clnt->cl_port     = xprt->addr.sin_port;
 	clnt->cl_prog     = program->number;
 	clnt->cl_vers     = version->number;
-	clnt->cl_prot     = IPPROTO_UDP;
+	clnt->cl_prot     = xprt->prot;
 	clnt->cl_stats    = program->stats;
 	clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait");
 
@@ -113,10 +120,10 @@
 	return clnt;
 
 out_no_clnt:
-	printk("RPC: out of memory in rpc_create_client\n");
+	printk(KERN_INFO "RPC: out of memory in rpc_create_client\n");
 	goto out;
 out_no_auth:
-	printk("RPC: Couldn't create auth handle (flavor %d)\n",
+	printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %d)\n",
 		flavor);
 	rpc_free(clnt);
 	clnt = NULL;
@@ -134,10 +141,10 @@
 {
 	dprintk("RPC: shutting down %s client for %s\n",
 		clnt->cl_protname, clnt->cl_server);
-	while (clnt->cl_users) {
+	while (atomic_read(&clnt->cl_users)) {
 #ifdef RPC_DEBUG
-		printk("rpc_shutdown_client: client %s, tasks=%d\n",
-			clnt->cl_protname, clnt->cl_users);
+		dprintk("RPC: rpc_shutdown_client: client %s, tasks=%d\n",
+			clnt->cl_protname, atomic_read(&clnt->cl_users));
 #endif
 		/* Don't let rpc_release_client destroy us */
 		clnt->cl_oneshot = 0;
@@ -176,14 +183,10 @@
 rpc_release_client(struct rpc_clnt *clnt)
 {
 	dprintk("RPC:      rpc_release_client(%p, %d)\n",
-				clnt, clnt->cl_users);
-	if (clnt->cl_users) {
-		if (--(clnt->cl_users) > 0)
-			return;
-	} else
-		printk("rpc_release_client: %s client already free??\n",
-			clnt->cl_protname);
+				clnt, atomic_read(&clnt->cl_users));
 
+	if (!atomic_dec_and_test(&clnt->cl_users))
+		return;
 	wake_up(&destroy_wait);
 	if (clnt->cl_oneshot || clnt->cl_dead)
 		rpc_destroy_client(clnt);
@@ -195,7 +198,6 @@
 static void
 rpc_default_callback(struct rpc_task *task)
 {
-	rpc_release_task(task);
 }
 
 /*
@@ -236,42 +238,73 @@
 /*
  * New rpc_call implementation
  */
-int
-rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
-				int flags, rpc_action func, void *data)
+int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
 {
 	struct rpc_task	my_task, *task = &my_task;
 	sigset_t	oldset;
-	int		async, status;
+	int		status;
 
 	/* If this client is slain all further I/O fails */
 	if (clnt->cl_dead) 
 		return -EIO;
 
+	if (flags & RPC_TASK_ASYNC) {
+		printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
+		flags &= ~RPC_TASK_ASYNC;
+	}
+
 	rpc_clnt_sigmask(clnt, &oldset);		
 
 	/* Create/initialize a new RPC task */
-	if ((async = (flags & RPC_TASK_ASYNC)) != 0) {
-		if (!func)
-			func = rpc_default_callback;
-		status = -ENOMEM;
-		if (!(task = rpc_new_task(clnt, func, flags)))
-			goto out;
-		task->tk_calldata = data;
-	} else {
-		rpc_init_task(task, clnt, NULL, flags);
+	rpc_init_task(task, clnt, NULL, flags);
+	rpc_call_setup(task, msg, 0);
+
+	/* Set up the call info struct and execute the task */
+	if (task->tk_status == 0)
+		status = rpc_execute(task);
+	else {
+		status = task->tk_status;
+		rpc_release_task(task);
 	}
 
-	/* Bind the user cred, set up the call info struct and
-	 * execute the task */
-	if (rpcauth_lookupcred(task) != NULL) {
-		rpc_call_setup(task, proc, argp, resp, 0);
-		rpc_execute(task);
-	} else
-		async = 0;
+	rpc_clnt_sigunmask(clnt, &oldset);		
+
+	return status;
+}
 
-	status = 0;
-	if (!async) {
+/*
+ * New rpc_call implementation
+ */
+int
+rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
+	       rpc_action callback, void *data)
+{
+	struct rpc_task	*task;
+	sigset_t	oldset;
+	int		status;
+
+	/* If this client is slain all further I/O fails */
+	if (clnt->cl_dead) 
+		return -EIO;
+
+	flags |= RPC_TASK_ASYNC;
+
+	rpc_clnt_sigmask(clnt, &oldset);		
+
+	/* Create/initialize a new RPC task */
+	if (!callback)
+		callback = rpc_default_callback;
+	status = -ENOMEM;
+	if (!(task = rpc_new_task(clnt, callback, flags)))
+		goto out;
+	task->tk_calldata = data;
+
+	rpc_call_setup(task, msg, 0);
+
+	/* Set up the call info struct and execute the task */
+	if (task->tk_status == 0)
+		status = rpc_execute(task);
+	else {
 		status = task->tk_status;
 		rpc_release_task(task);
 	}
@@ -284,17 +317,24 @@
 
 
 void
-rpc_call_setup(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags)
+rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags)
 {
-	task->tk_action = call_bind;
-	task->tk_proc   = proc;
-	task->tk_argp   = argp;
-	task->tk_resp   = resp;
+	task->tk_msg   = *msg;
 	task->tk_flags |= flags;
+	/* Bind the user cred */
+	if (task->tk_msg.rpc_cred != NULL) {
+		rpcauth_holdcred(task);
+	} else
+		rpcauth_bindcred(task);
+
+	if (task->tk_status == 0)
+		task->tk_action = call_reserve;
+	else
+		task->tk_action = NULL;
 
 	/* Increment call count */
-	rpcproc_count(task->tk_client, proc)++;
+	if (task->tk_msg.rpc_proc < task->tk_client->cl_maxproc)
+		rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
 }
 
 /*
@@ -304,26 +344,11 @@
 void
 rpc_restart_call(struct rpc_task *task)
 {
-	if (task->tk_flags & RPC_TASK_KILLED) {
-		rpc_release_task(task);
+	if (RPC_ASSASSINATED(task))
 		return;
-	}
-	task->tk_action = call_bind;
-	rpcproc_count(task->tk_client, task->tk_proc)++;
-}
-
-/*
- * 0.	Get the server port number if not yet set
- */
-static void
-call_bind(struct rpc_task *task)
-{
-	struct rpc_clnt	*clnt = task->tk_client;
 
 	task->tk_action = call_reserve;
-	task->tk_status = 0;
-	if (!clnt->cl_port)
-		rpc_getport(task, clnt);
+	rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
 }
 
 /*
@@ -334,25 +359,22 @@
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 
-	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
-	if (!clnt->cl_port) {
-		printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n",
-			clnt->cl_protname, clnt->cl_server,
-			clnt->cl_softrtry? "giving up" : "retrying");
-		if (!clnt->cl_softrtry) {
-			rpc_delay(task, 5*HZ);
-			return;
-		}
+	if (task->tk_msg.rpc_proc >= clnt->cl_maxproc) {
+		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
+			clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc);
 		rpc_exit(task, -EIO);
 		return;
 	}
+
+	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
 	if (!rpcauth_uptodatecred(task)) {
 		task->tk_action = call_refresh;
 		return;
 	}
+
+	task->tk_status  = 0;
 	task->tk_action  = call_reserveresult;
 	task->tk_timeout = clnt->cl_timeout.to_resrvval;
-	task->tk_status  = 0;
 	clnt->cl_stats->rpccnt++;
 	xprt_reserve(task);
 }
@@ -363,6 +385,8 @@
 static void
 call_reserveresult(struct rpc_task *task)
 {
+	int status = task->tk_status;
+
 	dprintk("RPC: %4d call_reserveresult (status %d)\n",
 				task->tk_pid, task->tk_status);
 	/*
@@ -376,25 +400,27 @@
 
 	if (task->tk_status >= 0) {
 		task->tk_action = call_allocate;
-		goto out;
-	} else if (task->tk_status == -EAGAIN) {
+		return;
+	}
+
+	task->tk_status = 0;
+	switch (status) {
+	case -EAGAIN:
+	case -ENOBUFS:
 		task->tk_timeout = task->tk_client->cl_timeout.to_resrvval;
-		task->tk_status = 0;
-		xprt_reserve(task);
-		goto out;
-	} else if (task->tk_status == -ETIMEDOUT) {
+		task->tk_action = call_reserve;
+		break;
+	case -ETIMEDOUT:
 		dprintk("RPC: task timed out\n");
 		task->tk_action = call_timeout;
-		goto out;
-	} else {
-		task->tk_action = NULL;
-	}
-	if (!task->tk_rqstp) {
-		printk("RPC: task has no request, exit EIO\n");
-		rpc_exit(task, -EIO);
+		break;
+	default:
+		if (!task->tk_rqstp) {
+			printk(KERN_INFO "RPC: task has no request, exit EIO\n");
+			rpc_exit(task, -EIO);
+		} else
+			rpc_exit(task, status);
 	}
-out:
-	return;
 }
 
 /*
@@ -415,16 +441,15 @@
 
 	/* FIXME: compute buffer requirements more exactly using
 	 * auth->au_wslack */
-	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc) + RPC_SLACK_SPACE;
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE;
 
-	if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL)
+	if ((task->tk_buffer = rpc_malloc(task, bufsiz << 1)) != NULL)
 		return;
-	printk("RPC: buffer allocation failed for task %p\n", task); 
+	printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task); 
 
-	if (!signalled()) {
-		xprt_release(task);
-		task->tk_action = call_reserve;
-		rpc_delay(task, HZ);
+	if (RPC_IS_ASYNC(task) || !(task->tk_client->cl_intr && signalled())) {
+		task->tk_action = call_allocate;
+		rpc_delay(task, HZ>>4);
 		return;
 	}
 
@@ -447,36 +472,26 @@
 	dprintk("RPC: %4d call_encode (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
-	task->tk_action = call_transmit;
+	task->tk_action = call_bind;
 
 	/* Default buffer setup */
-	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc)+RPC_SLACK_SPACE;
-	req->rq_svec[0].iov_base = task->tk_buffer;
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE;
+	req->rq_svec[0].iov_base = (void *)task->tk_buffer;
 	req->rq_svec[0].iov_len  = bufsiz;
 	req->rq_slen		 = 0;
 	req->rq_snr		 = 1;
-	req->rq_rvec[0].iov_base = task->tk_buffer;
+	req->rq_rvec[0].iov_base = (void *)((char *)task->tk_buffer + bufsiz);
 	req->rq_rvec[0].iov_len  = bufsiz;
 	req->rq_rlen		 = bufsiz;
 	req->rq_rnr		 = 1;
 
-	if (task->tk_proc > clnt->cl_maxproc) {
-		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
-			clnt->cl_protname, clnt->cl_vers, task->tk_proc);
-		rpc_exit(task, -EIO);
-		return;
-	}
-
-	/* Zero buffer so we have automatic zero-padding of opaque & string */
-	memset(task->tk_buffer, 0, bufsiz);
-
 	/* Encode header and provided arguments */
-	encode = rpcproc_encode(clnt, task->tk_proc);
+	encode = rpcproc_encode(clnt, task->tk_msg.rpc_proc);
 	if (!(p = call_header(task))) {
-		printk("RPC: call_header failed, exit EIO\n");
+		printk(KERN_INFO "RPC: call_header failed, exit EIO\n");
 		rpc_exit(task, -EIO);
 	} else
-	if ((status = encode(req, p, task->tk_argp)) < 0) {
+	if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) {
 		printk(KERN_WARNING "%s: can't encode arguments: %d\n",
 				clnt->cl_protname, -status);
 		rpc_exit(task, status);
@@ -484,42 +499,60 @@
 }
 
 /*
- * 4.	Transmit the RPC request
+ * 4.	Get the server port number if not yet set
  */
 static void
-call_transmit(struct rpc_task *task)
+call_bind(struct rpc_task *task)
 {
-	dprintk("RPC: %4d call_transmit (status %d)\n", 
+	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_xprt *xprt = task->tk_xprt;
+
+	task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_reconnect;
+
+	if (!clnt->cl_port) {
+		task->tk_action = call_reconnect;
+		task->tk_timeout = clnt->cl_timeout.to_maxval;
+		rpc_getport(task, clnt);
+	}
+}
+
+/*
+ * 4a.	Reconnect to the RPC server (TCP case)
+ */
+static void
+call_reconnect(struct rpc_task *task)
+{
+	struct rpc_clnt *clnt = task->tk_client;
+
+	dprintk("RPC: %4d call_reconnect status %d\n",
 				task->tk_pid, task->tk_status);
 
-	task->tk_action = call_receive;
-	task->tk_status = 0;
-	xprt_transmit(task);
+	task->tk_action = call_transmit;
+	if (task->tk_status < 0 || !clnt->cl_xprt->stream)
+		return;
+	clnt->cl_stats->netreconn++;
+	xprt_reconnect(task);
 }
 
 /*
- * 5.	Wait for the RPC reply
+ * 5.	Transmit the RPC request, and wait for reply
  */
 static void
-call_receive(struct rpc_task *task)
+call_transmit(struct rpc_task *task)
 {
-	dprintk("RPC: %4d call_receive (status %d)\n", 
-		task->tk_pid, task->tk_status);
+	struct rpc_clnt	*clnt = task->tk_client;
+
+	dprintk("RPC: %4d call_transmit (status %d)\n", 
+				task->tk_pid, task->tk_status);
 
 	task->tk_action = call_status;
-	/* In case of error, evaluate status */
 	if (task->tk_status < 0)
 		return;
-
-	/* If we have no decode function, this means we're performing
-	 * a void call (a la lockd message passing). */
-	if (!rpcproc_decode(task->tk_client, task->tk_proc)) {
-		rpc_remove_wait_queue(task); /* remove from xprt_pending */
+	xprt_transmit(task);
+	if (!rpcproc_decode(clnt, task->tk_msg.rpc_proc)) {
 		task->tk_action = NULL;
-		return;
+		rpc_wake_up_task(task);
 	}
-
-	xprt_receive(task);
 }
 
 /*
@@ -529,6 +562,7 @@
 call_status(struct rpc_task *task)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_xprt	*xprt = clnt->cl_xprt;
 	struct rpc_rqst	*req;
 	int		status = task->tk_status;
 
@@ -537,28 +571,42 @@
 
 	if (status >= 0) {
 		task->tk_action = call_decode;
-	} else if (status == -ETIMEDOUT) {
+		return;
+	}
+
+	task->tk_status = 0;
+	req = task->tk_rqstp;
+	switch(status) {
+	case -ETIMEDOUT:
 		task->tk_action = call_timeout;
-	} else if (status == -EAGAIN) {
-		if (!(req = task->tk_rqstp))
-			task->tk_action = call_reserve;
-		else if (!task->tk_buffer)
-			task->tk_action = call_allocate;
-		else if (req->rq_damaged)
-			task->tk_action = call_encode;
-		else
-			task->tk_action = call_transmit;
-	} else if (status == -ENOTCONN) {
-		task->tk_action = call_reconnect;
-	} else if (status == -ECONNREFUSED && clnt->cl_autobind) {
-		task->tk_action = call_bind;
-		clnt->cl_port = 0;
-	} else {
+		break;
+	case -ECONNREFUSED:
+	case -ENOTCONN:
+		req->rq_bytes_sent = 0;
+		if (clnt->cl_autobind || !clnt->cl_port) {
+			clnt->cl_port = 0;
+			task->tk_action = call_bind;
+			break;
+		}
+		if (xprt->stream) {
+			task->tk_action = call_reconnect;
+			break;
+		}
+		/*
+		 * Sleep and dream of an open connection
+		 */
+		task->tk_timeout = 5 * HZ;
+		rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+	case -ENOMEM:
+	case -EAGAIN:
+		task->tk_action = call_transmit;
+		clnt->cl_stats->rpcretrans++;
+		break;
+	default:
 		if (clnt->cl_chatty)
 			printk("%s: RPC call returned error %d\n",
-				clnt->cl_protname, -status);
-		task->tk_action = NULL;
-		return;
+			       clnt->cl_protname, -status);
+		rpc_exit(task, status);
 	}
 }
 
@@ -581,38 +629,42 @@
 				task->tk_pid);
 			goto minor_timeout;
 		}
-		to->to_initval <<= 1;
-		if (to->to_initval > to->to_maxval)
-			to->to_initval = to->to_maxval;
+		to->to_retries = clnt->cl_timeout.to_retries;
+	} else {
+		printk(KERN_NOTICE "%s: task %d can't get a request slot\n",
+		       clnt->cl_protname, task->tk_pid);
+		goto minor_timeout;
 	}
 
 	dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid);
 	if (clnt->cl_softrtry) {
 		if (clnt->cl_chatty && !task->tk_exit)
-			printk("%s: server %s not responding, timed out\n",
+			printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
 				clnt->cl_protname, clnt->cl_server);
 		rpc_exit(task, -EIO);
 		return;
 	}
+
 	if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
 		task->tk_flags |= RPC_CALL_MAJORSEEN;
-		if (req)
-			printk("%s: server %s not responding, still trying\n",
-				clnt->cl_protname, clnt->cl_server);
-		else 
-			printk("%s: task %d can't get a request slot\n",
-				clnt->cl_protname, task->tk_pid);
+		printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
+		       clnt->cl_protname, clnt->cl_server);
+	} else if (clnt->cl_chatty) {
+		printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
+		       clnt->cl_protname, clnt->cl_server);
 	}
+
 	if (clnt->cl_autobind)
 		clnt->cl_port = 0;
 
 minor_timeout:
-	if (!clnt->cl_port) {
-		task->tk_action = call_bind;
-	} else if (!req) {
+	if (!req)
 		task->tk_action = call_reserve;
-	} else if (req->rq_damaged) {
-		task->tk_action = call_encode;
+	else if (!clnt->cl_port) {
+		task->tk_action = call_bind;
+		clnt->cl_stats->rpcretrans++;
+	} else if (clnt->cl_xprt->stream && !xprt_connected(clnt->cl_xprt)) {
+		task->tk_action = call_reconnect;
 		clnt->cl_stats->rpcretrans++;
 	} else {
 		task->tk_action = call_transmit;
@@ -622,23 +674,6 @@
 }
 
 /*
- * 6b.	Reconnect to the RPC server (TCP case)
- */
-static void
-call_reconnect(struct rpc_task *task)
-{
-	dprintk("RPC: %4d call_reconnect status %d\n",
-				task->tk_pid, task->tk_status);
-	if (task->tk_status == 0) {
-		task->tk_action = call_status;
-		task->tk_status = -EAGAIN;
-		return;
-	}
-	task->tk_client->cl_stats->netreconn++;
-	xprt_reconnect(task);
-}
-
-/*
  * 7.	Decode the RPC reply
  */
 static void
@@ -646,20 +681,20 @@
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 	struct rpc_rqst	*req = task->tk_rqstp;
-	kxdrproc_t	decode = rpcproc_decode(clnt, task->tk_proc);
+	kxdrproc_t	decode = rpcproc_decode(clnt, task->tk_msg.rpc_proc);
 	u32		*p;
 
 	dprintk("RPC: %4d call_decode (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
 	if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) {
-		printk("%s: server %s OK\n",
+		printk(KERN_NOTICE "%s: server %s OK\n",
 			clnt->cl_protname, clnt->cl_server);
 		task->tk_flags &= ~RPC_CALL_MAJORSEEN;
 	}
 
 	if (task->tk_status < 12) {
-		printk("%s: too small RPC reply size (%d bytes)\n",
+		printk(KERN_WARNING "%s: too small RPC reply size (%d bytes)\n",
 			clnt->cl_protname, task->tk_status);
 		rpc_exit(task, -EIO);
 		return;
@@ -673,18 +708,21 @@
 	 * The following is an NFS-specific hack to cater for setuid
 	 * processes whose uid is mapped to nobody on the server.
 	 */
-	if (task->tk_client->cl_prog == 100003 && 
+	if (task->tk_client->cl_droppriv &&
             (ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
-		if (RPC_IS_SETUID(task) && (task->tk_suid_retry)--) {
+		if (RPC_IS_SETUID(task) && task->tk_suid_retry) {
 			dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
 			task->tk_flags ^= RPC_CALL_REALUID;
 			task->tk_action = call_encode;
+			task->tk_suid_retry--;
 			return;
 		}
 	}
 
 	task->tk_action = NULL;
-	task->tk_status = decode(req, p, task->tk_resp);
+
+	if (decode)
+		task->tk_status = decode(req, p, task->tk_msg.rpc_resp);
 	dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
 					task->tk_status);
 }
@@ -713,10 +751,9 @@
 	dprintk("RPC: %4d call_refreshresult (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
-	if (task->tk_status < 0) {
-		task->tk_status = -EACCES;
-		task->tk_action = NULL;
-	} else
+	if (task->tk_status < 0)
+		rpc_exit(task, -EACCES);
+	else
 		task->tk_action = call_reserve;
 }
 
@@ -728,17 +765,18 @@
 {
 	struct rpc_clnt *clnt = task->tk_client;
 	struct rpc_xprt *xprt = clnt->cl_xprt;
-	u32		*p = task->tk_buffer;
+	struct rpc_rqst	*req = task->tk_rqstp;
+	u32		*p = req->rq_svec[0].iov_base;
 
 	/* FIXME: check buffer size? */
 	if (xprt->stream)
 		*p++ = 0;		/* fill in later */
-	*p++ = task->tk_rqstp->rq_xid;	/* XID */
+	*p++ = req->rq_xid;		/* XID */
 	*p++ = htonl(RPC_CALL);		/* CALL */
 	*p++ = htonl(RPC_VERSION);	/* RPC version */
 	*p++ = htonl(clnt->cl_prog);	/* program number */
 	*p++ = htonl(clnt->cl_vers);	/* program version */
-	*p++ = htonl(task->tk_proc);	/* procedure */
+	*p++ = htonl(task->tk_msg.rpc_proc);	/* procedure */
 	return rpcauth_marshcred(task, p);
 }
 
@@ -748,25 +786,26 @@
 static u32 *
 call_verify(struct rpc_task *task)
 {
-	u32	*p = task->tk_buffer, n;
+	u32	*p = task->tk_rqstp->rq_rvec[0].iov_base, n;
 
 	p += 1;	/* skip XID */
 
 	if ((n = ntohl(*p++)) != RPC_REPLY) {
-		printk("call_verify: not an RPC reply: %x\n", n);
+		printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n);
 		goto garbage;
 	}
 	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
 		int	error = -EACCES;
 
 		if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
-			printk("call_verify: RPC call rejected: %x\n", n);
+			printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
 		} else
 		switch ((n = ntohl(*p++))) {
 		case RPC_AUTH_REJECTEDCRED:
 		case RPC_AUTH_REJECTEDVERF:
-			if (!task->tk_cred_retry--)
+			if (!task->tk_cred_retry)
 				break;
+			task->tk_cred_retry--;
 			dprintk("RPC: %4d call_verify: retry stale creds\n",
 							task->tk_pid);
 			rpcauth_invalcred(task);
@@ -775,17 +814,18 @@
 		case RPC_AUTH_BADCRED:
 		case RPC_AUTH_BADVERF:
 			/* possibly garbled cred/verf? */
-			if (!task->tk_garb_retry--)
+			if (!task->tk_garb_retry)
 				break;
+			task->tk_garb_retry--;
 			dprintk("RPC: %4d call_verify: retry garbled creds\n",
 							task->tk_pid);
 			task->tk_action = call_encode;
 			return NULL;
 		case RPC_AUTH_TOOWEAK:
-			printk("call_verify: server requires stronger "
+			printk(KERN_NOTICE "call_verify: server requires stronger "
 			       "authentication.\n");
 		default:
-			printk("call_verify: unknown auth error: %x\n", n);
+			printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
 			error = -EIO;
 		}
 		dprintk("RPC: %4d call_verify: call rejected %d\n",
@@ -794,7 +834,7 @@
 		return NULL;
 	}
 	if (!(p = rpcauth_checkverf(task, p))) {
-		printk("call_verify: auth check failed\n");
+		printk(KERN_WARNING "call_verify: auth check failed\n");
 		goto garbage;		/* bad verifier, retry */
 	}
 	switch ((n = ntohl(*p++))) {
@@ -803,19 +843,20 @@
 	case RPC_GARBAGE_ARGS:
 		break;			/* retry */
 	default:
-		printk("call_verify: server accept status: %x\n", n);
+		printk(KERN_WARNING "call_verify: server accept status: %x\n", n);
 		/* Also retry */
 	}
 
 garbage:
 	dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
 	task->tk_client->cl_stats->rpcgarbage++;
-	if (task->tk_garb_retry--) {
-		printk("RPC: garbage, retrying %4d\n", task->tk_pid);
+	if (task->tk_garb_retry) {
+		printk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid);
 		task->tk_action = call_encode;
+		task->tk_garb_retry--;
 		return NULL;
 	}
-	printk("RPC: garbage, exit EIO\n");
+	printk(KERN_WARNING "RPC: garbage, exit EIO\n");
 	rpc_exit(task, -EIO);
 	return NULL;
 }
diff -urN linux-2.2.18-pre8/net/sunrpc/pmap_clnt.c linux/net/sunrpc/pmap_clnt.c
--- linux-2.2.18-pre8/net/sunrpc/pmap_clnt.c	Sun Oct 12 10:17:46 1997
+++ linux/net/sunrpc/pmap_clnt.c	Mon Dec  3 20:25:25 2001
@@ -41,6 +41,7 @@
 {
 	struct rpc_portmap *map = &clnt->cl_pmap;
 	struct sockaddr_in *sap = &clnt->cl_xprt->addr;
+	struct rpc_message msg = { PMAP_GETPORT, map, &clnt->cl_port, NULL };
 	struct rpc_clnt	*pmap_clnt;
 	struct rpc_task	*child;
 
@@ -66,7 +67,7 @@
 		goto bailout;
 
 	/* Setup the call info struct */
-	rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0);
+	rpc_call_setup(child, &msg, 0);
 
 	/* ... and run the child task */
 	rpc_run_child(task, child, pmap_getport_done);
@@ -121,7 +122,7 @@
 		task->tk_action = NULL;
 	} else if (clnt->cl_port == 0) {
 		/* Program not registered */
-		task->tk_status = -EACCES;
+		task->tk_status = -EPROTONOSUPPORT;
 		task->tk_action = NULL;
 	} else {
 		/* byte-swap port number first */
diff -urN linux-2.2.18-pre8/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- linux-2.2.18-pre8/net/sunrpc/sched.c	Mon Sep  4 10:39:29 2000
+++ linux/net/sunrpc/sched.c	Mon Dec  3 20:25:25 2001
@@ -31,7 +31,7 @@
  */
 #define GFP_RPC			GFP_NFS
 
-static void			__rpc_default_timer(struct rpc_task *task);
+static void			rpc_default_timer(struct rpc_task *task);
 static void			rpciod_killall(void);
 
 /*
@@ -68,12 +68,110 @@
 static int			rpc_inhibit = 0;
 
 /*
+ * Spinlock for wait queues. Access to the latter has to be interrupt-safe
+ * since we want to wake up tasks from sk->write_space().
+ */
+spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED;
+
+/*
  * This is the last-ditch buffer for NFS swap requests
  */
 static u32			swap_buffer[PAGE_SIZE >> 2];
 static int			swap_buffer_used = 0;
 
 /*
+ * Make allocation of the swap_buffer SMP-safe
+ */
+static __inline__ int rpc_lock_swapbuf(void)
+{
+	return !test_and_set_bit(1, &swap_buffer_used);
+}
+static __inline__ void rpc_unlock_swapbuf(void)
+{
+	clear_bit(1, &swap_buffer_used);
+}
+
+/*
+ * Disable the timer for a given RPC task. Should be called with
+ * rpc_queue_lock and bh_disabled in order to avoid races within
+ * rpc_run_timer().
+ */
+static inline void
+__rpc_disable_timer(struct rpc_task *task)
+{
+	dprintk("RPC: %4d disabling timer\n", task->tk_pid);
+	task->tk_timeout_fn = NULL;
+	task->tk_timeout = 0;
+}
+
+/*
+ * Run a timeout function.
+ * We use the callback in order to allow __rpc_wake_up_task()
+ * and friends to disable the timer synchronously on SMP systems
+ * without calling del_timer_sync(). The latter could cause a
+ * deadlock if called while we're holding spinlocks...
+ */
+static void
+rpc_run_timer(struct rpc_task *task)
+{
+	void (*callback)(struct rpc_task *);
+	unsigned long	oldflags;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	callback = task->tk_timeout_fn;
+	task->tk_timeout_fn = NULL;
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+	if (callback) {
+		dprintk("RPC: %4d running timer\n", task->tk_pid);
+		callback(task);
+	}
+}
+
+/*
+ * Set up a timer for the current task.
+ */
+static inline void
+__rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+	if (!task->tk_timeout)
+		return;
+
+	dprintk("RPC: %4d setting alarm for %lu ms\n",
+			task->tk_pid, task->tk_timeout * 1000 / HZ);
+
+	if (timer)
+		task->tk_timeout_fn = timer;
+	else
+		task->tk_timeout_fn = rpc_default_timer;
+	mod_timer(&task->tk_timer, jiffies + task->tk_timeout);
+}
+
+/*
+ * Set up a timer for an already sleeping task.
+ */
+void rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+	unsigned long	oldflags;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	if (!(RPC_IS_RUNNING(task) || task->tk_wakeup))
+		__rpc_add_timer(task, timer);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+}
+
+/*
+ * Delete any timer for the current task.
+ */
+static inline void
+rpc_delete_timer(struct rpc_task *task)
+{
+	if (timer_pending(&task->tk_timer)) {
+		dprintk("RPC: %4d deleting timer\n", task->tk_pid);
+		del_timer(&task->tk_timer);
+	}
+}
+
+/*
  * Add new request to wait queue.
  *
  * Swapper tasks always get inserted at the head of the queue.
@@ -81,16 +179,17 @@
  * improve overall performance.
  * Everyone else gets appended to the queue to ensure proper FIFO behavior.
  */
-int
-rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
+static inline int
+__rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
 {
-	if (task->tk_rpcwait) {
-		if (task->tk_rpcwait != queue)
-		{
-			printk(KERN_WARNING "RPC: doubly enqueued task!\n");
-			return -EWOULDBLOCK;
-		}
+	if (task->tk_rpcwait == queue)
 		return 0;
+
+	if (task->tk_rpcwait) {
+		printk(KERN_WARNING "RPC: task already queued!\n");
+		dprintk("task already on %s, to be added to %s\n",
+			rpc_qname(task->tk_rpcwait), rpc_qname(queue));
+		return -EWOULDBLOCK;
 	}
 	if (RPC_IS_SWAPPER(task))
 		rpc_insert_list(&queue->task, task);
@@ -104,17 +203,30 @@
 	return 0;
 }
 
+int
+rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task)
+{
+	unsigned long	oldflags;
+	int		result;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	result = __rpc_add_wait_queue(q, task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+	return result;
+}
+
 /*
  * Remove request from queue.
- * Note: must be called with interrupts disabled.
+ * Note: must be called with spin lock held.
  */
-void
-rpc_remove_wait_queue(struct rpc_task *task)
+static inline void
+__rpc_remove_wait_queue(struct rpc_task *task)
 {
-	struct rpc_wait_queue *queue;
+	struct rpc_wait_queue *queue = task->tk_rpcwait;
 
-	if (!(queue = task->tk_rpcwait))
+	if (!queue)
 		return;
+
 	rpc_remove_list(&queue->task, task);
 	task->tk_rpcwait = NULL;
 
@@ -122,73 +234,63 @@
 				task->tk_pid, queue, rpc_qname(queue));
 }
 
-/*
- * Set up a timer for the current task.
- */
-inline void
-rpc_add_timer(struct rpc_task *task, rpc_action timer)
+void
+rpc_remove_wait_queue(struct rpc_task *task)
 {
-	unsigned long	expires = jiffies + task->tk_timeout;
-
-	dprintk("RPC: %4d setting alarm for %lu ms\n",
-			task->tk_pid, task->tk_timeout * 1000 / HZ);
-	if (!timer)
-		timer = __rpc_default_timer;
-	if (time_before(expires, jiffies)) {
-		printk(KERN_ERR "RPC: bad timeout value %ld - setting to 10 sec!\n",
-					task->tk_timeout);
-		expires = jiffies + 10 * HZ;
-	}
-	task->tk_timer.expires  = expires;
-	task->tk_timer.data     = (unsigned long) task;
-	task->tk_timer.function = (void (*)(unsigned long)) timer;
-	task->tk_timer.prev     = NULL;
-	task->tk_timer.next     = NULL;
-	add_timer(&task->tk_timer);
-}
+	unsigned long	oldflags;
 
-/*
- * Delete any timer for the current task.
- * Must be called with interrupts off.
- */
-inline void
-rpc_del_timer(struct rpc_task *task)
-{
-	if (task->tk_timeout) {
-		dprintk("RPC: %4d deleting timer\n", task->tk_pid);
-		del_timer(&task->tk_timer);
-		task->tk_timeout = 0;
-	}
+	if (!task->tk_rpcwait)
+		return;
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_remove_wait_queue(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
  * Make an RPC task runnable.
  *
  * Note: If the task is ASYNC, this must be called with 
- * interrupts disabled to protect the wait queue operation.
+ * spin lock held in order to protect the wait queue operation.
  */
 static inline void
-rpc_make_runnable(struct rpc_task *task)
+__rpc_make_runnable(struct rpc_task *task)
 {
 	if (task->tk_timeout) {
 		printk(KERN_ERR "RPC: task w/ running timer in rpc_make_runnable!!\n");
 		return;
 	}
-	task->tk_flags |= RPC_TASK_RUNNING;
+	task->tk_running = 1;
 	if (RPC_IS_ASYNC(task)) {
-		int status;
-		status = rpc_add_wait_queue(&schedq, task);
-		if (status)
-		{
-			printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
-			task->tk_status = status;
+		if (RPC_IS_SLEEPING(task)) {
+			int status;
+			status = __rpc_add_wait_queue(&schedq, task);
+			if (status < 0) {
+				printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
+				task->tk_status = status;
+			} else
+				task->tk_sleeping = 0;
 		}
 		wake_up(&rpciod_idle);
 	} else {
+		task->tk_sleeping = 0;
 		wake_up(&task->tk_wait);
 	}
 }
 
+/*
+ * Place a newly initialized task on the schedq.
+ */
+static inline void
+__rpc_schedule_run(struct rpc_task *task)
+{
+	/* Don't run a child twice! */
+	if (RPC_IS_ACTIVATED(task))
+		return;
+	task->tk_active = 1;
+	task->tk_sleeping = 1;
+	__rpc_make_runnable(task);
+}
+
 
 /*
  *	For other people who may need to wake the I/O daemon
@@ -198,9 +300,7 @@
 void rpciod_wake_up(void)
 {
 	if(rpciod_pid==0)
-	{
 		printk(KERN_ERR "rpciod: wot no daemon?\n");
-	}
 	wake_up(&rpciod_idle);
 }
 
@@ -214,33 +314,32 @@
 __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
 			rpc_action action, rpc_action timer)
 {
-	unsigned long	oldflags;
 	int status;
 
 	dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid,
 				rpc_qname(q), jiffies);
 
-	/*
-	 * Protect the execution below.
-	 */
-	save_flags(oldflags); cli();
+	if (!RPC_IS_ASYNC(task) && !RPC_IS_ACTIVATED(task)) {
+		printk(KERN_ERR "RPC: Inactive synchronous task put to sleep!\n");
+		return;
+	}
+
+	/* Mark the task as being activated if so needed */
+	if (!RPC_IS_ACTIVATED(task)) {
+		task->tk_active = 1;
+		task->tk_sleeping = 1;
+	}
 
-	status = rpc_add_wait_queue(q, task);
-	if (status)
-	{
+	status = __rpc_add_wait_queue(q, task);
+	if (status) {
 		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
 		task->tk_status = status;
-		task->tk_flags |= RPC_TASK_RUNNING;
-	}
-	else
-	{
+	} else {
+		task->tk_running = 0;
 		task->tk_callback = action;
-		if (task->tk_timeout)
-			rpc_add_timer(task, timer);
-		task->tk_flags &= ~RPC_TASK_RUNNING;
+		__rpc_add_timer(task, timer);
 	}
 
-	restore_flags(oldflags);
 	return;
 }
 
@@ -248,17 +347,39 @@
 rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
 				rpc_action action, rpc_action timer)
 {
+	unsigned long	oldflags;
+
+	/*
+	 * Protect the queue operations.
+	 */
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	__rpc_sleep_on(q, task, action, timer);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+}
+
+void
+rpc_sleep_locked(struct rpc_wait_queue *q, struct rpc_task *task,
+		 rpc_action action, rpc_action timer)
+{
+	unsigned long	oldflags;
+
+	/*
+	 * Protect the queue operations.
+	 */
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_sleep_on(q, task, action, timer);
+	__rpc_lock_task(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
- * Wake up a single task -- must be invoked with bottom halves off.
+ * Wake up a single task -- must be invoked with spin lock held.
  *
  * It would probably suffice to cli/sti the del_timer and remove_wait_queue
  * operations individually.
  */
-static void
-__rpc_wake_up(struct rpc_task *task)
+static inline void
+__rpc_wake_up_task(struct rpc_task *task)
 {
 	dprintk("RPC: %4d __rpc_wake_up (now %ld inh %d)\n",
 					task->tk_pid, jiffies, rpc_inhibit);
@@ -267,16 +388,32 @@
 	if (task->tk_magic != 0xf00baa) {
 		printk(KERN_ERR "RPC: attempt to wake up non-existing task!\n");
 		rpc_debug = ~0;
+		rpc_show_tasks();
 		return;
 	}
 #endif
-	rpc_del_timer(task);
-	if (task->tk_rpcwait != &schedq)
-		rpc_remove_wait_queue(task);
-	if (!RPC_IS_RUNNING(task)) {
-		task->tk_flags |= RPC_TASK_CALLBACK;
-		rpc_make_runnable(task);
+	/* Has the task been executed yet? If not, we cannot wake it up! */
+	if (!RPC_IS_ACTIVATED(task)) {
+		printk(KERN_ERR "RPC: Inactive task (%p) being woken up!\n", task);
+		return;
 	}
+	if (RPC_IS_RUNNING(task))
+		return;
+
+	__rpc_disable_timer(task);
+	if (task->tk_rpcwait != &schedq)
+		__rpc_remove_wait_queue(task);
+
+	/* If the task has been locked, then set tk_wakeup so that
+	 * rpc_unlock_task() wakes us up... */
+	if (task->tk_lock) {
+		task->tk_wakeup = 1;
+		return;
+	} else
+		task->tk_wakeup = 0;
+
+	__rpc_make_runnable(task);
+
 	dprintk("RPC:      __rpc_wake_up done\n");
 }
 
@@ -284,12 +421,12 @@
  * Default timeout handler if none specified by user
  */
 static void
-__rpc_default_timer(struct rpc_task *task)
+rpc_default_timer(struct rpc_task *task)
 {
-	dprintk("RPC: %d timeout (default timer)\n", task->tk_pid);
+	dprintk("RPC: %4d timeout (default timer)\n", task->tk_pid);
 	task->tk_status = -ETIMEDOUT;
 	task->tk_timeout = 0;
-	__rpc_wake_up(task);
+	rpc_wake_up_task(task);
 }
 
 /*
@@ -300,26 +437,38 @@
 {
 	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
-	__rpc_wake_up(task);
-	restore_flags(oldflags);
+	if (RPC_IS_RUNNING(task))
+		return;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_wake_up_task(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
  * Wake up the next task on the wait queue.
  */
-struct rpc_task *
-rpc_wake_up_next(struct rpc_wait_queue *queue)
+static inline struct rpc_task *
+__rpc_wake_up_next(struct rpc_wait_queue *queue)
 {
-	unsigned long	oldflags;
 	struct rpc_task	*task;
 
 	dprintk("RPC:      wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue));
-	save_flags(oldflags); cli();
 	if ((task = queue->task) != 0)
-		__rpc_wake_up(task);
-	restore_flags(oldflags);
+		__rpc_wake_up_task(task);
+
+	return task;
+}
+
+struct rpc_task *
+rpc_wake_up_next(struct rpc_wait_queue *queue)
+{
+	struct rpc_task	*task;
+	unsigned long	oldflags;
 
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	task = __rpc_wake_up_next(queue);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 	return task;
 }
 
@@ -331,10 +480,10 @@
 {
 	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	while (queue->task)
-		__rpc_wake_up(queue->task);
-	restore_flags(oldflags);
+		__rpc_wake_up_task(queue->task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
@@ -346,30 +495,63 @@
 	struct rpc_task	*task;
 	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	while ((task = queue->task) != NULL) {
 		task->tk_status = status;
-		__rpc_wake_up(task);
+		__rpc_wake_up_task(task);
 	}
-	restore_flags(oldflags);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+}
+
+/*
+ * Lock down a sleeping task to prevent it from waking up
+ * and disappearing from beneath us.
+ *
+ * This function should always be called with the
+ * rpc_queue_lock held.
+ */
+int
+__rpc_lock_task(struct rpc_task *task)
+{
+	if (!RPC_IS_RUNNING(task))
+		return ++task->tk_lock;
+	return 0;
+}
+
+static inline void
+__rpc_unlock_task(struct rpc_task *task)
+{
+	if (task->tk_lock && !--task->tk_lock && task->tk_wakeup)
+		__rpc_wake_up_task(task);
+}
+
+void
+rpc_unlock_task(struct rpc_task *task)
+{
+	unsigned long	oldflags;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_unlock_task(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
  * Run a task at a later time
  */
-static void	__rpc_atrun(struct rpc_task *);
+static void	rpc_atrun(struct rpc_task *);
 void
 rpc_delay(struct rpc_task *task, unsigned long delay)
 {
 	task->tk_timeout = delay;
-	rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun);
+	rpc_sleep_on(&delay_queue, task, NULL, rpc_atrun);
 }
 
 static void
-__rpc_atrun(struct rpc_task *task)
+rpc_atrun(struct rpc_task *task)
 {
 	task->tk_status = 0;
-	__rpc_wake_up(task);
+	task->tk_timeout = 0;
+	rpc_wake_up_task(task);
 }
 
 /*
@@ -389,15 +571,15 @@
 		return 0;
 	}
 
+restarted:
 	while (1) {
 		/*
 		 * Execute any pending callback.
 		 */
-		if (task->tk_flags & RPC_TASK_CALLBACK) {
+		if (RPC_DO_CALLBACK(task)) {
 			/* Define a callback save pointer */
 			void (*save_callback)(struct rpc_task *);
 	
-			task->tk_flags &= ~RPC_TASK_CALLBACK;
 			/* 
 			 * If a callback exists, save it, reset it,
 			 * call it.
@@ -405,11 +587,9 @@
 			 * another callback set within the callback handler
 			 * - Dave
 			 */
-			if (task->tk_callback) {
-				save_callback=task->tk_callback;
-				task->tk_callback=NULL;
-				save_callback(task);
-			}
+			save_callback=task->tk_callback;
+			task->tk_callback=NULL;
+			save_callback(task);
 		}
 
 		/*
@@ -418,6 +598,10 @@
 		 * by someone else.
 		 */
 		if (RPC_IS_RUNNING(task)) {
+			/*
+			 * Garbage collection of pending timers...
+			 */
+			rpc_delete_timer(task);
 			if (!task->tk_action)
 				break;
 			task->tk_action(task);
@@ -425,86 +609,98 @@
 
 		/*
 		 * Check whether task is sleeping.
-		 * Note that if the task may go to sleep in tk_action,
+		 * Note that if the task goes to sleep in tk_action,
 		 * and the RPC reply arrives before we get here, it will
 		 * have state RUNNING, but will still be on schedq.
+		 * 27/9/99: The above has been attempted fixed by
+		 *          introduction of task->tk_sleeping.
 		 */
-		save_flags(oldflags); cli();
-		if (RPC_IS_RUNNING(task)) {
-			if (task->tk_rpcwait == &schedq)
-				rpc_remove_wait_queue(task);
-		} else while (!RPC_IS_RUNNING(task)) {
+		spin_lock_irqsave(&rpc_queue_lock, oldflags);
+		if (!RPC_IS_RUNNING(task)) {
+			task->tk_sleeping = 1;
 			if (RPC_IS_ASYNC(task)) {
-				restore_flags(oldflags);
+				spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 				return 0;
 			}
+		} else
+			task->tk_sleeping = 0;
+		spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 
+		while (RPC_IS_SLEEPING(task)) {
 			/* sync task: sleep here */
 			dprintk("RPC: %4d sync task going to sleep\n",
 							task->tk_pid);
-			if (current->pid == rpciod_pid)
-				printk(KERN_ERR "RPC: rpciod waiting on sync task!\n");
+			if (current->pid == rpciod_pid) {
+				printk(KERN_ERR "RPC: rpciod waiting on sync task %4d!\n", task->tk_pid);
+				rpc_show_tasks();
+			}
 
-			__wait_event(task->tk_wait, RPC_IS_RUNNING(task));
+			__wait_event(task->tk_wait, !RPC_IS_SLEEPING(task));
+			dprintk("RPC: %4d sync task resuming\n", task->tk_pid);
 
 			/*
-			 * When the task received a signal, remove from
-			 * any queues etc, and make runnable again.
+			 * When a sync task receives a signal, it exits with
+			 * -ERESTARTSYS. In order to catch any callbacks that
+			 * clean up after sleeping on some queue, we don't
+			 * break the loop here, but go around once more.
 			 */
-			if (signalled()) { 
-				cli(); 
-				__rpc_wake_up(task);
+			if (task->tk_client->cl_intr && signalled()) {
+				dprintk("RPC: %4d got signal\n", task->tk_pid);
+				task->tk_flags |= RPC_TASK_KILLED;
+				rpc_exit(task, -ERESTARTSYS);
+				rpc_wake_up_task(task);
 			}
-
-			dprintk("RPC: %4d sync task resuming\n",
-							task->tk_pid);
-		}
-		restore_flags(oldflags);
-
-		/*
-		 * When a sync task receives a signal, it exits with
-		 * -ERESTARTSYS. In order to catch any callbacks that
-		 * clean up after sleeping on some queue, we don't
-		 * break the loop here, but go around once more.
-		 */
-		if (!RPC_IS_ASYNC(task) && signalled()) {
-			dprintk("RPC: %4d got signal\n", task->tk_pid);
-			rpc_exit(task, -ERESTARTSYS);
 		}
 	}
 
 	dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
 	if (task->tk_exit) {
-		status = task->tk_status;
 		task->tk_exit(task);
+		/* If tk_action is non-null, the user wants us to restart */
+		if (task->tk_action) {
+			if (!RPC_ASSASSINATED(task)) {
+					/* Release RPC slot and buffer memory */
+					if (task->tk_rqstp)
+						xprt_release(task);
+					if (task->tk_buffer) {
+						rpc_free(task->tk_buffer);
+						task->tk_buffer = NULL;
+					}
+					goto restarted;
+			}
+			printk(KERN_ERR "RPC: dead task tries to walk away.\n");
+		}
 	}
 
+	/* Save the task exit status */
+	status = task->tk_status;
+
+	/* Release all resources associated with the task */
+	rpc_release_task(task);
 	return status;
 }
 
 /*
  * User-visible entry point to the scheduler.
- * The recursion protection is for debugging. It should go away once
- * the code has stabilized.
+ *
+ * This may be called recursively if e.g. an async NFS task updates
+ * the attributes and finds that dirty pages must be flushed.
  */
-void
+int
 rpc_execute(struct rpc_task *task)
 {
-	static int	executing = 0;
-	int		incr = RPC_IS_ASYNC(task)? 1 : 0;
-
-	if (incr) {
-		if (rpc_inhibit) {
-			printk(KERN_INFO "RPC: execution inhibited!\n");
-			return;
-		}
-		if (executing)
-			printk(KERN_WARNING "RPC: %d tasks executed\n", executing);
+	if (rpc_inhibit) {
+		printk(KERN_INFO "RPC: execution inhibited!\n");
+		return -EIO;
+	}
+	task->tk_running = 1;
+	if (task->tk_active) {
+		printk(KERN_ERR "RPC: active task was run twice!\n");
+		return -EWOULDBLOCK;
 	}
+	task->tk_active = 1;
 	
-	executing += incr;
-	__rpc_execute(task);
-	executing -= incr;
+	return __rpc_execute(task);
 }
 
 /*
@@ -514,31 +710,36 @@
 __rpc_schedule(void)
 {
 	struct rpc_task	*task;
-	int		count = 0;
 	unsigned long	oldflags;
-	int need_resched = current->need_resched;
+	int		count = 0;
 
 	dprintk("RPC:      rpc_schedule enter\n");
-	save_flags(oldflags);
 	while (1) {
-		cli();
-		if (!(task = schedq.task))
+		/* Ensure equal rights for tcp tasks... */
+		rpciod_tcp_dispatcher();
+
+		spin_lock_irqsave(&rpc_queue_lock, oldflags);
+		if (!(task = schedq.task)) {
+			spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 			break;
-		rpc_del_timer(task);
-		rpc_remove_wait_queue(task);
-		task->tk_flags |= RPC_TASK_RUNNING;
-		restore_flags(oldflags);
+		}
+		if (task->tk_lock) {
+			spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+			printk(KERN_ERR "RPC: Locked task was scheduled !!!!\n");
+			rpc_debug = ~0;
+			rpc_show_tasks();
+			break;
+		}
+		__rpc_remove_wait_queue(task);
+		spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 
 		__rpc_execute(task);
 
-		if (++count >= 200) {
+		if (++count >= 200 || current->need_resched) {
+			schedule();
 			count = 0;
-			need_resched = 1;
 		}
-		if (need_resched)
-			schedule();
 	}
-	restore_flags(oldflags);
 	dprintk("RPC:      rpc_schedule leave\n");
 }
 
@@ -579,7 +780,8 @@
 			dprintk("RPC:      allocated buffer %p\n", buffer);
 			return buffer;
 		}
-		if ((flags & RPC_TASK_SWAPPER) && !swap_buffer_used++) {
+		if ((flags & RPC_TASK_SWAPPER) && size <= sizeof(swap_buffer)
+		 && rpc_lock_swapbuf()) {
 			dprintk("RPC:      used last-ditch swap buffer\n");
 			return swap_buffer;
 		}
@@ -587,6 +789,7 @@
 			return NULL;
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(HZ>>4);
+		current->state = TASK_RUNNING;
 	} while (!signalled());
 
 	return NULL;
@@ -599,20 +802,24 @@
 		kfree(buffer);
 		return;
 	}
-	swap_buffer_used = 0;
+	rpc_unlock_swapbuf();
 }
 
 /*
  * Creation and deletion of RPC task structures
  */
-inline void
+void
 rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
 				rpc_action callback, int flags)
 {
 	memset(task, 0, sizeof(*task));
+	init_timer(&task->tk_timer);
+	task->tk_timer.data     = (unsigned long) task;
+	task->tk_timer.function = (void (*)(unsigned long)) rpc_run_timer;
+
 	task->tk_client = clnt;
-	task->tk_flags  = RPC_TASK_RUNNING | flags;
 	task->tk_exit   = callback;
+	task->tk_flags  = flags;
 	if (current->uid != current->fsuid || current->gid != current->fsgid)
 		task->tk_flags |= RPC_TASK_SETUID;
 
@@ -621,6 +828,11 @@
 	task->tk_cred_retry = 2;
 	task->tk_suid_retry = 1;
 
+#ifdef RPC_DEBUG
+	task->tk_magic = 0xf00baa;
+	task->tk_pid = rpc_task_id++;
+#endif
+
 	/* Add to global list of all tasks */
 	task->tk_next_task = all_tasks;
 	task->tk_prev_task = NULL;
@@ -629,14 +841,11 @@
 	all_tasks = task;
 
 	if (clnt)
-		clnt->cl_users++;
+		atomic_inc(&clnt->cl_users);
 
-#ifdef RPC_DEBUG
-	task->tk_magic = 0xf00baa;
-	task->tk_pid = rpc_task_id++;
-#endif
-	dprintk("RPC: %4d new task procpid %d\n", task->tk_pid,
-				current->pid);
+	dprintk("RPC: %d new task procpid %d%s\n",
+			task->tk_pid, current->pid,
+			(flags & RPC_TASK_DYNAMIC) ? " (alloc)" : "");
 }
 
 /*
@@ -653,10 +862,7 @@
 	if (!task)
 		goto cleanup;
 
-	rpc_init_task(task, clnt, callback, flags);
-
-	dprintk("RPC: %4d allocated task\n", task->tk_pid);
-	task->tk_flags |= RPC_TASK_DYNAMIC;
+	rpc_init_task(task, clnt, callback, flags | RPC_TASK_DYNAMIC);
 out:
 	return task;
 
@@ -664,8 +870,8 @@
 	/* Check whether to release the client */
 	if (clnt) {
 		printk("rpc_new_task: failed, users=%d, oneshot=%d\n",
-			clnt->cl_users, clnt->cl_oneshot);
-		clnt->cl_users++; /* pretend we were used ... */
+			atomic_read(&clnt->cl_users), clnt->cl_oneshot);
+		atomic_inc(&clnt->cl_users); /* pretend we were used ... */
 		rpc_release_client(clnt);
 	}
 	goto out;
@@ -675,9 +881,19 @@
 rpc_release_task(struct rpc_task *task)
 {
 	struct rpc_task	*next, *prev;
+	unsigned long	oldflags;
 
 	dprintk("RPC: %4d release task\n", task->tk_pid);
 
+#ifdef RPC_DEBUG
+	if (task->tk_magic != 0xf00baa) {
+		printk(KERN_ERR "RPC: attempt to release a non-existing task!\n");
+		rpc_debug = ~0;
+		rpc_show_tasks();
+		return;
+	}
+#endif
+
 	/* Remove from global task list */
 	prev = task->tk_prev_task;
 	next = task->tk_next_task;
@@ -687,12 +903,29 @@
 		prev->tk_next_task = next;
 	else
 		all_tasks = next;
+	task->tk_next_task = task->tk_prev_task = NULL;
+
+	/* Protect the execution below. */
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+
+	/* Disable timer to prevent zombie wakeup */
+	__rpc_disable_timer(task);
+
+	/* Remove from any wait queue we're still on */
+	__rpc_remove_wait_queue(task);
+
+	task->tk_active = 0;
+
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+
+	/* Synchronously delete any running timer */
+	rpc_delete_timer(task);
 
 	/* Release resources */
 	if (task->tk_rqstp)
 		xprt_release(task);
-	if (task->tk_cred)
-		rpcauth_releasecred(task);
+	if (task->tk_msg.rpc_cred)
+		rpcauth_unbindcred(task);
 	if (task->tk_buffer) {
 		rpc_free(task->tk_buffer);
 		task->tk_buffer = NULL;
@@ -708,9 +941,12 @@
 
 	if (task->tk_flags & RPC_TASK_DYNAMIC) {
 		dprintk("RPC: %4d freeing task\n", task->tk_pid);
+		if (task->tk_release)
+			task->tk_release(task);
 		task->tk_flags &= ~RPC_TASK_DYNAMIC;
 		rpc_free(task);
-	}
+	} else if (task->tk_release)
+			task->tk_release(task);
 }
 
 /*
@@ -719,14 +955,18 @@
  * parent task may already have gone away
  */
 static inline struct rpc_task *
-rpc_find_parent(struct rpc_task *child)
+__rpc_find_parent(struct rpc_task *child)
 {
-	struct rpc_task	*temp, *parent;
+	struct rpc_task	*head, *parent;
 
-	parent = (struct rpc_task *) child->tk_calldata;
-	for (temp = childq.task; temp; temp = temp->tk_next) {
-		if (temp == parent)
-			return parent;
+	parent = child->tk_parent;
+	if ((head = childq.task) != NULL) {
+		struct rpc_task	*task = head;
+		do {
+			if (task == parent)
+				return parent;
+			task = task->tk_next;
+		} while (task != head);
 	}
 	return NULL;
 }
@@ -735,12 +975,14 @@
 rpc_child_exit(struct rpc_task *child)
 {
 	struct rpc_task	*parent;
+	unsigned long	oldflags;
 
-	if ((parent = rpc_find_parent(child)) != NULL) {
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	if ((parent = __rpc_find_parent(child)) != NULL) {
 		parent->tk_status = child->tk_status;
-		rpc_wake_up_task(parent);
+		__rpc_wake_up_task(parent);
 	}
-	rpc_release_task(child);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
@@ -755,7 +997,7 @@
 	if (!task)
 		goto fail;
 	task->tk_exit = rpc_child_exit;
-	task->tk_calldata = parent;
+	task->tk_parent = parent;
 	return task;
 
 fail:
@@ -766,13 +1008,13 @@
 void
 rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func)
 {
-	unsigned long oldflags;
+	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
-	rpc_make_runnable(child);
-	restore_flags(oldflags);
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	/* N.B. Is it possible for the child to have already finished? */
-	rpc_sleep_on(&childq, task, func, NULL);
+	__rpc_sleep_on(&childq, task, func, NULL);
+	__rpc_schedule_run(child);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
@@ -785,8 +1027,9 @@
 	struct rpc_task	**q, *rovr;
 
 	dprintk("RPC:      killing all tasks for client %p\n", clnt);
-	/* N.B. Why bother to inhibit? Nothing blocks here ... */
-	rpc_inhibit++;
+	/*
+	 * Spin lock all_tasks to prevent changes...
+	 */
 	for (q = &all_tasks; (rovr = *q); q = &rovr->tk_next_task) {
 		if (!clnt || rovr->tk_client == clnt) {
 			rovr->tk_flags |= RPC_TASK_KILLED;
@@ -794,11 +1037,16 @@
 			rpc_wake_up_task(rovr);
 		}
 	}
-	rpc_inhibit--;
 }
 
 static struct semaphore rpciod_running = MUTEX_LOCKED;
 
+static inline int
+rpciod_task_pending(void)
+{
+	return schedq.task != NULL || xprt_tcp_pending();
+}
+
 /*
  * This is the rpciod kernel thread
  */
@@ -806,11 +1054,12 @@
 rpciod(void *ptr)
 {
 	struct wait_queue **assassin = (struct wait_queue **) ptr;
-	unsigned long	oldflags;
 	int		rounds = 0;
 
 	MOD_INC_USE_COUNT;
+
 	lock_kernel();
+
 	/*
 	 * Let our maker know we're running ...
 	 */
@@ -837,22 +1086,17 @@
 		}
 		__rpc_schedule();
 
-		if (++rounds >= 64) {	/* safeguard */
+		if (++rounds >= 64 || current->need_resched) {	/* safeguard */
 			schedule();
 			rounds = 0;
 		}
-		save_flags(oldflags); cli();
-		dprintk("RPC: rpciod running checking dispatch\n");
-		rpciod_tcp_dispatcher();
 
-		if (!schedq.task) {
+		if (!rpciod_task_pending()) {
 			dprintk("RPC: rpciod back to sleep\n");
-			interruptible_sleep_on(&rpciod_idle);
+			wait_event_interruptible(rpciod_idle, rpciod_task_pending());
 			dprintk("RPC: switch to rpciod\n");
-			rpciod_tcp_dispatcher();
 			rounds = 0;
 		}
-		restore_flags(oldflags);
 	}
 
 	dprintk("RPC: rpciod shutdown commences\n");
@@ -882,6 +1126,7 @@
 			dprintk("rpciod_killall: waiting for tasks to exit\n");
 			current->state = TASK_INTERRUPTIBLE;
 			schedule_timeout(1);
+			current->state = TASK_RUNNING;
 		}
 	}
 
@@ -953,6 +1198,7 @@
 	current->sigpending = 0;
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(1);
+	current->state = TASK_RUNNING;
 	/*
 	 * Display a message if we're going to wait longer.
 	 */
@@ -972,36 +1218,23 @@
 	MOD_DEC_USE_COUNT;
 }
 
-#ifdef RPC_DEBUG
-#include <linux/nfs_fs.h>
 void rpc_show_tasks(void)
 {
 	struct rpc_task *t = all_tasks, *next;
-	struct nfs_wreq *wreq;
 
-	if (!t)
+	t = all_tasks;
+	if (!t) {
 		return;
+	}
 	printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout "
 		"-rpcwait -action- --exit--\n");
 	for (; t; t = next) {
 		next = t->tk_next_task;
 		printk("%05d %04d %04x %06d %8p %6d %8p %08ld %8s %8p %8p\n",
-			t->tk_pid, t->tk_proc, t->tk_flags, t->tk_status,
+			t->tk_pid, t->tk_msg.rpc_proc, t->tk_flags, t->tk_status,
 			t->tk_client, t->tk_client->cl_prog,
 			t->tk_rqstp, t->tk_timeout,
 			t->tk_rpcwait ? rpc_qname(t->tk_rpcwait) : " <NULL> ",
 			t->tk_action, t->tk_exit);
-
-		if (!(t->tk_flags & RPC_TASK_NFSWRITE))
-			continue;
-		/* NFS write requests */
-		wreq = (struct nfs_wreq *) t->tk_calldata;
-		printk("     NFS: flgs=%08x, pid=%d, pg=%p, off=(%d, %d)\n",
-			wreq->wb_flags, wreq->wb_pid, wreq->wb_page,
-			wreq->wb_offset, wreq->wb_bytes);
-		printk("          name=%s/%s\n",
-			wreq->wb_file->f_dentry->d_parent->d_name.name,
-			wreq->wb_file->f_dentry->d_name.name);
 	}
 }
-#endif
diff -urN linux-2.2.18-pre8/net/sunrpc/stats.c linux/net/sunrpc/stats.c
--- linux-2.2.18-pre8/net/sunrpc/stats.c	Sat Jan  2 17:55:06 1999
+++ linux/net/sunrpc/stats.c	Mon Dec  3 20:25:25 2001
@@ -128,6 +128,7 @@
 {
 	struct proc_dir_entry	*ent;
 
+	rpc_proc_init();
 	dprintk("RPC: registering /proc/net/rpc/%s\n", name);
 	ent = create_proc_entry(name, 0, proc_net_rpc);
 	ent->read_proc = issvc? svc_proc_read : rpc_proc_read;
@@ -174,6 +175,9 @@
 			proc_net_rpc = ent;
 		}
 	}
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
 }
 
 void
@@ -184,6 +188,9 @@
 		proc_net_rpc = NULL;
 		remove_proc_entry("net/rpc", 0);
 	}
+#ifdef RPC_DEBUG
+	rpc_unregister_sysctl();
+#endif
 }
 
 #ifdef MODULE
@@ -206,9 +213,6 @@
 int
 init_module(void)
 {
-#ifdef RPC_DEBUG
-	rpc_register_sysctl();
-#endif
 	rpc_proc_init();
 	return 0;
 }
@@ -216,9 +220,6 @@
 void
 cleanup_module(void)
 {
-#ifdef RPC_DEBUG
-	rpc_unregister_sysctl();
-#endif
 	rpc_proc_exit();
 }
 #endif
diff -urN linux-2.2.18-pre8/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c
--- linux-2.2.18-pre8/net/sunrpc/sunrpc_syms.c	Wed Aug 19 16:16:04 1998
+++ linux/net/sunrpc/sunrpc_syms.c	Mon Dec  3 20:25:25 2001
@@ -26,13 +26,17 @@
 EXPORT_SYMBOL(rpc_allocate);
 EXPORT_SYMBOL(rpc_free);
 EXPORT_SYMBOL(rpc_execute);
+EXPORT_SYMBOL(rpc_new_task);
 EXPORT_SYMBOL(rpc_init_task);
 EXPORT_SYMBOL(rpc_release_task);
 EXPORT_SYMBOL(rpc_sleep_on);
+EXPORT_SYMBOL(rpc_wake_up);
 EXPORT_SYMBOL(rpc_wake_up_next);
 EXPORT_SYMBOL(rpc_wake_up_task);
+EXPORT_SYMBOL(rpc_wake_up_status);
 EXPORT_SYMBOL(rpc_new_child);
 EXPORT_SYMBOL(rpc_run_child);
+EXPORT_SYMBOL(rpciod_wake_up);
 EXPORT_SYMBOL(rpciod_down);
 EXPORT_SYMBOL(rpciod_up);
 
@@ -41,7 +45,8 @@
 EXPORT_SYMBOL(rpc_destroy_client);
 EXPORT_SYMBOL(rpc_shutdown_client);
 EXPORT_SYMBOL(rpc_killall_tasks);
-EXPORT_SYMBOL(rpc_do_call);
+EXPORT_SYMBOL(rpc_call_sync);
+EXPORT_SYMBOL(rpc_call_async);
 EXPORT_SYMBOL(rpc_call_setup);
 EXPORT_SYMBOL(rpc_clnt_sigmask);
 EXPORT_SYMBOL(rpc_clnt_sigunmask);
@@ -60,7 +65,9 @@
 EXPORT_SYMBOL(rpcauth_free_credcache);
 EXPORT_SYMBOL(rpcauth_insert_credcache);
 EXPORT_SYMBOL(rpcauth_lookupcred);
+EXPORT_SYMBOL(rpcauth_bindcred);
 EXPORT_SYMBOL(rpcauth_matchcred);
+EXPORT_SYMBOL(rpcauth_holdcred);
 EXPORT_SYMBOL(rpcauth_releasecred);
 
 /* RPC server stuff */
@@ -93,6 +100,9 @@
 EXPORT_SYMBOL(xdr_encode_netobj);
 EXPORT_SYMBOL(xdr_zero);
 EXPORT_SYMBOL(xdr_one);
+EXPORT_SYMBOL(xdr_two);
+EXPORT_SYMBOL(xdr_shift_iovec);
+EXPORT_SYMBOL(xdr_zero_iovec);
 
 /* RPC errors */
 EXPORT_SYMBOL(rpc_success);
diff -urN linux-2.2.18-pre8/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- linux-2.2.18-pre8/net/sunrpc/svcsock.c	Tue Jan  4 10:12:27 2000
+++ linux/net/sunrpc/svcsock.c	Mon Dec  3 20:25:25 2001
@@ -344,8 +344,9 @@
 		return;
 	dprintk("svc: socket %p(inet %p), count=%d, busy=%d\n",
 		svsk, sk, count, svsk->sk_busy);
-	svsk->sk_data = 1;
+	svsk->sk_data++;
 	svc_sock_enqueue(svsk);
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
@@ -358,19 +359,10 @@
 	struct svc_serv	*serv = svsk->sk_server;
 	struct sk_buff	*skb;
 	u32		*data;
-	int		err, len;
+	int		err, len, recvd = svsk->sk_data;
 
-	svsk->sk_data = 0;
-	while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) {
-		svc_sock_received(svsk, 0);
-		if (err == -EAGAIN)
-			return err;
-		/* possibly an icmp error */
-		dprintk("svc: recvfrom returned error %d\n", -err);
-	}
-
-	/* There may be more data */
-	svsk->sk_data = 1;
+	if ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL)
+		goto out_err;
 
 	len  = skb->len - sizeof(struct udphdr);
 	data = (u32 *) (skb->h.raw + sizeof(struct udphdr));
@@ -390,15 +382,24 @@
 #else
 	rqstp->rq_addr.sin_addr.s_addr = skb->saddr;
 #endif
+	memset(rqstp->rq_addr.sin_zero, 0, sizeof(rqstp->rq_addr.sin_zero));
 
 	if (serv->sv_stats)
 		serv->sv_stats->netudpcnt++;
 
 	/* One down, maybe more to go... */
 	svsk->sk_sk->stamp = skb->stamp;
-	svc_sock_received(svsk, 0);
+	svc_sock_received(svsk, 1);
 
 	return len;
+ out_err:
+	if (err != -EAGAIN) {
+		/* possibly an icmp error */
+		dprintk("svc: recvfrom returned error %d\n", -err);
+		svc_sock_received(svsk, 0);
+	} else
+		svc_sock_received(svsk, recvd);
+	return -EAGAIN;
 }
 
 static int
@@ -458,6 +459,7 @@
 	}
 	svsk->sk_conn++;
 	svc_sock_enqueue(svsk);
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
@@ -477,6 +479,7 @@
 	}
 	svsk->sk_close = 1;
 	svc_sock_enqueue(svsk);
+	wake_up_interruptible(sk->sleep);
 }
 
 static void
diff -urN linux-2.2.18-pre8/net/sunrpc/sysctl.c linux/net/sunrpc/sysctl.c
--- linux-2.2.18-pre8/net/sunrpc/sysctl.c	Thu Jan  7 09:28:47 1999
+++ linux/net/sunrpc/sysctl.c	Mon Dec  3 20:25:25 2001
@@ -99,9 +99,8 @@
 			left--, p++;
 		*(unsigned int *) table->data = value;
 		/* Display the RPC tasks on writing to rpc_debug */
-		if (table->ctl_name == CTL_RPCDEBUG) {
+		if (table->ctl_name == CTL_RPCDEBUG)
 			rpc_show_tasks();
-		}
 	} else {
 		if (!access_ok(VERIFY_WRITE, buffer, left))
 			return -EFAULT;
diff -urN linux-2.2.18-pre8/net/sunrpc/xdr.c linux/net/sunrpc/xdr.c
--- linux-2.2.18-pre8/net/sunrpc/xdr.c	Mon Apr  7 11:35:33 1997
+++ linux/net/sunrpc/xdr.c	Mon Dec  3 20:25:25 2001
@@ -9,6 +9,7 @@
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/string.h>
+#include <linux/kernel.h>
 #include <linux/in.h>
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/msg_prot.h>
@@ -84,11 +85,13 @@
 }
 
 u32 *
-xdr_encode_string(u32 *p, const char *string)
+xdr_encode_string(u32 *p, const char *string, int len)
 {
-	int len = strlen(string);
-	int quadlen = XDR_QUADLEN(len);
+	int quadlen;
 
+	if (len < 0)
+		len = strlen(string);
+	quadlen = XDR_QUADLEN(len);
 	p[quadlen] = 0;
 	*p++ = htonl(len);
 	memcpy(p, string, len);
@@ -116,3 +119,51 @@
 	return p + XDR_QUADLEN(len);
 }
 
+/*
+ * Realign the iovec if the server missed out some reply elements
+ * (such as post-op attributes,...)
+ * Note: This is a simple implementation that assumes that
+ *            len <= iov->iov_len !!!
+ *       The RPC header (assumed to be the 1st element in the iov array)
+ *            is not shifted.
+ */
+void xdr_shift_iovec(struct iovec *iov, int nr, size_t len)
+{
+	struct iovec *pvec;
+
+	for (pvec = iov + nr - 1; nr > 1; nr--, pvec--) {
+		struct iovec *svec = pvec - 1;
+
+		if (len > pvec->iov_len) {
+			printk(KERN_DEBUG "RPC: Urk! Large shift of short iovec.\n");
+			return;
+		}
+		memmove((char *)pvec->iov_base + len, pvec->iov_base,
+			pvec->iov_len - len);
+
+		if (len > svec->iov_len) {
+			printk(KERN_DEBUG "RPC: Urk! Large shift of short iovec.\n");
+			return;
+		}
+		memcpy(pvec->iov_base,
+		       (char *)svec->iov_base + svec->iov_len - len, len);
+	}
+}
+
+/*
+ * Zero the last n bytes in an iovec array of 'nr' elements
+ */
+void xdr_zero_iovec(struct iovec *iov, int nr, size_t n)
+{
+	struct iovec *pvec;
+
+	for (pvec = iov + nr - 1; n && nr > 0; nr--, pvec--) {
+		if (n < pvec->iov_len) {
+			memset((char *)pvec->iov_base + pvec->iov_len - n, 0, n);
+			n = 0;
+		} else {
+			memset(pvec->iov_base, 0, pvec->iov_len);
+			n -= pvec->iov_len;
+		}
+	}
+}
diff -urN linux-2.2.18-pre8/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- linux-2.2.18-pre8/net/sunrpc/xprt.c	Tue Jan  4 10:12:27 2000
+++ linux/net/sunrpc/xprt.c	Mon Dec  3 20:25:25 2001
@@ -31,7 +31,7 @@
  *  primitives that `transparently' work for processes as well as async
  *  tasks that rely on callbacks.
  *
- *  Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ *  Copyright (C) 1995-1997, Olaf Kirch <okir@monad.swb.de>
  *
  *  TCP callback races fixes (C) 1998 Red Hat Software <alan@redhat.com>
  *  TCP send fixes (C) 1998 Red Hat Software <alan@redhat.com>
@@ -56,17 +56,20 @@
 #include <linux/file.h>
 
 #include <net/sock.h>
+#include <net/checksum.h>
 
 #include <asm/uaccess.h>
 
-#define SOCK_HAS_USER_DATA
+/* Following value should be > 32k + RPC overhead */
+#define XPRT_MIN_WRITE_SPACE (35000 + MIN_WRITE_SPACE)
+#define RPCXPRT_TIMEOUT 300*HZ
 
 /*
  * Local variables
  */
-#ifndef SOCK_HAS_USER_DATA
-static struct rpc_xprt *	sock_list = NULL;
-#endif
+
+/* Spinlock for critical sections in the code. */
+spinlock_t xprt_sock_lock = SPIN_LOCK_UNLOCKED;
 
 #ifdef RPC_DEBUG
 # undef  RPC_DEBUG_DATA
@@ -82,13 +85,15 @@
  * Local functions
  */
 static void	xprt_request_init(struct rpc_task *, struct rpc_xprt *);
-static void	xprt_transmit_status(struct rpc_task *task);
-static void	xprt_receive_status(struct rpc_task *task);
-static void	xprt_reserve_status(struct rpc_task *task);
-static void	xprt_reconn_timeout(struct rpc_task *task);
+static void	do_xprt_transmit(struct rpc_task *);
+static void	__xprt_reserve_status(struct rpc_task *task);
+static void	__xprt_disconnect(struct rpc_xprt *);
 static void	xprt_reconn_status(struct rpc_task *task);
-static struct socket *xprt_create_socket(int, struct sockaddr_in *,
-					struct rpc_timeout *);
+static struct socket *xprt_create_socket(int, struct rpc_timeout *);
+static int	xprt_bind_socket(struct rpc_xprt *, struct socket *);
+static void	__xprt_append_pending(struct rpc_xprt *);
+static void	__xprt_remove_pending(struct rpc_xprt *);
+static int	__xprt_clear_backlog(struct rpc_xprt *xprt);
 
 #ifdef RPC_DEBUG_DATA
 /*
@@ -127,98 +132,88 @@
 static inline struct rpc_xprt *
 xprt_from_sock(struct sock *sk)
 {
-#ifndef SOCK_HAS_USER_DATA
-	struct rpc_xprt		*xprt;
-
-	for (xprt = sock_list; xprt && sk != xprt->inet; xprt = xprt->link)
-		;
-	return xprt;
-#else
 	return (struct rpc_xprt *) sk->user_data;
-#endif
 }
 
 /*
  *	Adjust the iovec to move on 'n' bytes
  */
  
-extern inline void xprt_move_iov(struct msghdr *msg, struct iovec *niv, int amount)
+extern inline void
+xprt_move_iov(struct msghdr *msg, struct iovec *niv, unsigned amount)
 {
 	struct iovec *iv=msg->msg_iov;
+	int i;
 	
 	/*
 	 *	Eat any sent iovecs
 	 */
-
-	while(iv->iov_len < amount)
-	{
-		amount-=iv->iov_len;
+	while (iv->iov_len <= amount) {
+		amount -= iv->iov_len;
 		iv++;
 		msg->msg_iovlen--;
 	}
-	
-	msg->msg_iov=niv;
-	
+
 	/*
 	 *	And chew down the partial one
 	 */
-
 	niv[0].iov_len = iv->iov_len-amount;
 	niv[0].iov_base =((unsigned char *)iv->iov_base)+amount;
 	iv++;
-	
+
 	/*
 	 *	And copy any others
 	 */
-	 
-	for(amount=1;amount<msg->msg_iovlen; amount++)
-	{
-		niv[amount]=*iv++;
-	}
+	for(i = 1; i < msg->msg_iovlen; i++)
+		niv[i]=*iv++;
+
+	msg->msg_iov=niv;
 }
  
 /*
  * Write data to socket.
  */
 
-static inline int
-xprt_sendmsg(struct rpc_xprt *xprt)
+static int
+xprt_sendmsg(struct rpc_xprt *xprt, struct rpc_rqst *req)
 {
 	struct socket	*sock = xprt->sock;
 	struct msghdr	msg;
 	mm_segment_t	oldfs;
 	int		result;
+	int		slen = req->rq_slen - req->rq_bytes_sent;
 	struct iovec	niv[MAX_IOVEC];
 
+	if (slen <= 0)
+		return 0;
+
+	if (!sock)
+		return -ENOTCONN;
+
 	xprt_pktdump("packet data:",
-				xprt->snd_buf.io_vec->iov_base,
-				xprt->snd_buf.io_vec->iov_len);
+				req->rq_svec->iov_base,
+				req->rq_svec->iov_len);
 
-	msg.msg_flags   = MSG_DONTWAIT;
-	msg.msg_iov	= xprt->snd_buf.io_vec;
-	msg.msg_iovlen	= xprt->snd_buf.io_nr;
+	msg.msg_flags   = MSG_DONTWAIT|MSG_NOSIGNAL;
+	msg.msg_iov	= req->rq_svec;
+	msg.msg_iovlen	= req->rq_snr;
 	msg.msg_name	= (struct sockaddr *) &xprt->addr;
 	msg.msg_namelen = sizeof(xprt->addr);
 	msg.msg_control = NULL;
 	msg.msg_controllen = 0;
 
 	/* Dont repeat bytes */
-	
-	if(xprt->snd_sent)
-		xprt_move_iov(&msg, niv, xprt->snd_sent);
-		
+	if (req->rq_bytes_sent)
+		xprt_move_iov(&msg, niv, req->rq_bytes_sent);
+
 	oldfs = get_fs(); set_fs(get_ds());
-	result = sock_sendmsg(sock, &msg, xprt->snd_buf.io_len);
+	result = sock_sendmsg(sock, &msg, slen);
 	set_fs(oldfs);
 
-	dprintk("RPC:      xprt_sendmsg(%d) = %d\n",
-				xprt->snd_buf.io_len, result);
+	dprintk("RPC:      xprt_sendmsg(%d) = %d\n", slen, result);
 
-	if (result >= 0) {
-		xprt->snd_buf.io_len -= result;
-		xprt->snd_sent += result;
+	if (result >= 0)
 		return result;
-	}
 
 	switch (result) {
 	case -ECONNREFUSED:
@@ -227,13 +222,17 @@
 		 */
 		break;
 	case -EAGAIN:
-		return 0;
-	case -ENOTCONN: case -EPIPE:
+		if (sock->flags & SO_NOSPACE)
+			result = -ENOMEM;
+		break;
+	case -ENOTCONN:
+	case -EPIPE:
 		/* connection broken */
+		if (xprt->stream)
+			result = -ENOTCONN;
 		break;
 	default:
 		printk(KERN_NOTICE "RPC: sendmsg returned error %d\n", -result);
-		result = 0;
 	}
 	return result;
 }
@@ -241,41 +240,33 @@
 /*
  * Read data from socket
  */
-static inline int
-xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, int len)
+static int
+xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, unsigned len, unsigned shift)
 {
 	struct socket	*sock = xprt->sock;
-	struct sockaddr_in sin;
 	struct msghdr	msg;
 	mm_segment_t	oldfs;
+	struct iovec	niv[MAX_IOVEC];
 	int		result;
 
-#if LINUX_VERSION_CODE >= 0x020100
-	msg.msg_flags   = MSG_DONTWAIT;
-	msg.msg_iov	= iov;
-	msg.msg_iovlen	= nr;
-	msg.msg_name	= &sin;
-	msg.msg_namelen = sizeof(sin);
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
+	if (!sock)
+		return -ENOTCONN;
 
-	oldfs = get_fs(); set_fs(get_ds());
-	result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
-	set_fs(oldfs);
-#else
-	int		alen = sizeof(sin);
-	msg.msg_flags   = 0;
+	msg.msg_flags   = MSG_DONTWAIT|MSG_NOSIGNAL;
 	msg.msg_iov	= iov;
 	msg.msg_iovlen	= nr;
-	msg.msg_name	= &sin;
-	msg.msg_namelen = sizeof(sin);
+	msg.msg_name	= NULL;
+	msg.msg_namelen = 0;
 	msg.msg_control = NULL;
 	msg.msg_controllen = 0;
 
+	/* Adjust the iovec if we've already filled it */
+	if (shift)
+		xprt_move_iov(&msg, niv, shift);
+
 	oldfs = get_fs(); set_fs(get_ds());
-	result = sock->ops->recvmsg(sock, &msg, len, 1, 0, &alen);
+	result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
 	set_fs(oldfs);
-#endif
 
 	dprintk("RPC:      xprt_recvmsg(iov %p, len %d) = %d\n",
 						iov, len, result);
@@ -288,7 +279,7 @@
  * We use a time-smoothed congestion estimator to avoid heavy oscillation.
  */
 static void
-xprt_adjust_cwnd(struct rpc_xprt *xprt, int result)
+__xprt_adjust_cwnd(struct rpc_xprt *xprt, int result)
 {
 	unsigned long	cwnd = xprt->cwnd;
 
@@ -304,14 +295,14 @@
 			cwnd = RPC_MAXCWND;
 		else
 			pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd);
-		xprt->congtime = jiffies + ((cwnd * HZ) << 2) / RPC_CWNDSCALE;
+		xprt->congtime = jiffies + ((cwnd * HZ/10) << 2) / RPC_CWNDSCALE;
 		dprintk("RPC:      cong %08lx, cwnd was %08lx, now %08lx, "
 			"time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
 			(xprt->congtime-jiffies)*1000/HZ);
 	} else if (result == -ETIMEDOUT) {
 		if ((cwnd >>= 1) < RPC_CWNDSCALE)
 			cwnd = RPC_CWNDSCALE;
-		xprt->congtime = jiffies + ((cwnd * HZ) << 3) / RPC_CWNDSCALE;
+		xprt->congtime = jiffies + ((cwnd * HZ/10) << 3) / RPC_CWNDSCALE;
 		dprintk("RPC:      cong %ld, cwnd was %ld, now %ld, "
 			"time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
 			(xprt->congtime-jiffies)*1000/HZ);
@@ -327,21 +318,82 @@
 int
 xprt_adjust_timeout(struct rpc_timeout *to)
 {
-	if (to->to_exponential)
-		to->to_current <<= 1;
-	else
-		to->to_current += to->to_increment;
-	if (to->to_maxval && to->to_current >= to->to_maxval) {
-		to->to_current = to->to_maxval;
-		to->to_retries = 0;
+	if (to->to_retries > 0) {
+		if (to->to_exponential)
+			to->to_current <<= 1;
+		else
+			to->to_current += to->to_increment;
+		if (to->to_maxval && to->to_current >= to->to_maxval)
+			to->to_current = to->to_maxval;
+	} else {
+		if (to->to_exponential)
+			to->to_initval <<= 1;
+		else
+			to->to_initval += to->to_increment;
+		if (to->to_maxval && to->to_initval >= to->to_maxval)
+			to->to_initval = to->to_maxval;
+		to->to_current = to->to_initval;
 	}
+
 	if (!to->to_current) {
 		printk(KERN_WARNING "xprt_adjust_timeout: to_current = 0!\n");
 		to->to_current = 5 * HZ;
 	}
 	pprintk("RPC: %lu %s\n", jiffies,
 			to->to_retries? "retrans" : "timeout");
-	return (to->to_retries)--;
+	return to->to_retries-- > 0;
+}
+
+static inline void
+__xprt_disable_tcp_timer(struct rpc_xprt *xprt)
+{
+	xprt->tcp_timeout = 0;
+}
+
+static inline void
+__xprt_delete_tcp_timer(struct rpc_xprt *xprt)
+{
+	if (timer_pending(&xprt->tcp_timer))
+		del_timer(&xprt->tcp_timer);
+}
+
+/*
+ * Safe for use outside BH contexts.
+ */
+static inline void
+xprt_delete_tcp_timer(struct rpc_xprt *xprt)
+{
+	start_bh_atomic();
+	__xprt_delete_tcp_timer(xprt);
+	end_bh_atomic();
+}
+
+static void
+xprt_tcp_timeout(struct rpc_xprt *xprt)
+{
+	if (xprt->tcp_timeout) {
+		__xprt_disable_tcp_timer(xprt);
+		__xprt_disconnect(xprt);
+	}
+}
+
+static inline void
+__xprt_add_tcp_timer(struct rpc_xprt *xprt, long timeout)
+{
+	if ((xprt->tcp_timeout = timeout) == 0)
+		return;
+	mod_timer(&xprt->tcp_timer, jiffies + xprt->tcp_timeout);
+}
+
+/*
+ * Safe for use outside BH contexts.
+ */
+static inline void
+xprt_add_tcp_timer(struct rpc_xprt *xprt, long timeout)
+{
+	start_bh_atomic();
+	__xprt_add_tcp_timer(xprt, timeout);
+	end_bh_atomic();
 }
 
 /*
@@ -350,24 +402,39 @@
 static void
 xprt_close(struct rpc_xprt *xprt)
 {
-	struct sock	*sk = xprt->inet;
+	struct socket	*sock;
+	struct sock	*sk;
+
+	start_bh_atomic();
+	if (!(sock = xprt->sock)) {
+		end_bh_atomic();
+		return;
+	}
+
+	__xprt_disable_tcp_timer(xprt);
+	xprt_clear_connected(xprt);
+	__xprt_remove_pending(xprt);
+	rpc_wake_up_status(&xprt->pending, -ENOTCONN);
+
+	sk = xprt->inet;
+	xprt->inet = NULL;
+	xprt->sock = NULL;
 
-#ifdef SOCK_HAS_USER_DATA
 	sk->user_data    = NULL;
-#endif
 	sk->data_ready   = xprt->old_data_ready;
 	sk->state_change = xprt->old_state_change;
 	sk->write_space  = xprt->old_write_space;
 
-	if (xprt->file)
-		fput(xprt->file);
-	else
-		sock_release(xprt->sock);
+	end_bh_atomic();
+
+	xprt_delete_tcp_timer(xprt);
+
+	sock_release(sock);
 	/*
 	 *	TCP doesnt require the rpciod now - other things may
 	 *	but rpciod handles that not us.
 	 */
-	if(xprt->stream && !xprt->connecting)
+	if(xprt->stream)
 		rpciod_down();
 }
 
@@ -375,12 +442,11 @@
  * Mark a transport as disconnected
  */
 static void
-xprt_disconnect(struct rpc_xprt *xprt)
+__xprt_disconnect(struct rpc_xprt *xprt)
 {
 	dprintk("RPC:      disconnected transport %p\n", xprt);
-	rpc_wake_up_status(&xprt->pending, -ENOTCONN);
-	rpc_wake_up_status(&xprt->sending, -ENOTCONN);
-	xprt->connected = 0;
+	xprt_clear_connected(xprt);
+	__xprt_append_pending(xprt);
 }
 
 /*
@@ -390,151 +456,145 @@
 xprt_reconnect(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
-	struct socket	*sock;
-	struct sock	*inet;
+	struct socket	*sock = xprt->sock;
+	struct sock	*inet = xprt->inet;
 	int		status;
 
 	dprintk("RPC: %4d xprt_reconnect %p connected %d\n",
-				task->tk_pid, xprt, xprt->connected);
-	task->tk_status = 0;
+				task->tk_pid, xprt, xprt_connected(xprt));
+	if (xprt->shutdown)
+		return;
+
+	if (!xprt->stream)
+		return;
+
+	if (!xprt->addr.sin_port) {
+		task->tk_status = -EIO;
+		return;
+	}
 
 	if (xprt->connecting) {
-		task->tk_timeout = xprt->timeout.to_maxval;
+		task->tk_timeout = 0;
 		rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
 		return;
 	}
 	xprt->connecting = 1;
 
-	/* Create an unconnected socket */
-	if (!(sock = xprt_create_socket(xprt->prot, NULL, &xprt->timeout)))
-		goto defer;
-
-#if LINUX_VERSION_CODE >= 0x020100
-	inet = sock->sk;
-#else
-	inet = (struct sock *) sock->data;
-#endif
-	inet->data_ready   = xprt->inet->data_ready;
-	inet->state_change = xprt->inet->state_change;
-	inet->write_space  = xprt->inet->write_space;
-#ifdef SOCK_HAS_USER_DATA
-	inet->user_data    = xprt;
-#endif
-
-	dprintk("RPC: %4d closing old socket\n", task->tk_pid);
-	xprt_disconnect(xprt);
-	xprt_close(xprt);
+	status = -ENOTCONN;
+	if (!inet) {
+		/* Create an unconnected socket */
+		if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout)))
+			goto defer;
+		xprt_bind_socket(xprt, sock);
+		inet = sock->sk;
+	}
 
-	/* Reset to new socket and default congestion */
-	xprt->sock = sock;
-	xprt->inet = inet;
-	xprt->cwnd = RPC_INITCWND;
+	xprt->tcp_offset = 0;
+	xprt->tcp_copied = 0;
+	xprt->tcp_more = 0;
 
 	/* Now connect it asynchronously. */
 	dprintk("RPC: %4d connecting new socket\n", task->tk_pid);
 	status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
 				sizeof(xprt->addr), O_NONBLOCK);
+
+	xprt_add_tcp_timer(xprt, RPCXPRT_TIMEOUT);
+
 	if (status < 0) {
-		if (status != -EINPROGRESS && status != -EALREADY) {
+		switch (status) {
+		case -EALREADY:
+		case -EINPROGRESS:
+			status = 0;
+			break;
+		case -EISCONN:
+		case -EPIPE:
+			status = 0;
+			xprt_close(xprt);
+			goto defer;
+		default:
 			printk("RPC: TCP connect error %d!\n", -status);
+			xprt_close(xprt);
 			goto defer;
 		}
 
 		dprintk("RPC: %4d connect status %d connected %d\n",
-				task->tk_pid, status, xprt->connected);
-		task->tk_timeout = 60 * HZ;
+				task->tk_pid, status, xprt_connected(xprt));
 
 		start_bh_atomic();
-		if (!xprt->connected) {
-			rpc_sleep_on(&xprt->reconn, task,
-				xprt_reconn_status, xprt_reconn_timeout);
+		if (!xprt_connected(xprt)) {
+			task->tk_timeout = xprt->timeout.to_maxval;
+			rpc_sleep_on(&xprt->reconn, task, xprt_reconn_status, NULL);
 			end_bh_atomic();
 			return;
 		}
 		end_bh_atomic();
 	}
-
-	xprt->connecting = 0;
-	rpc_wake_up(&xprt->reconn);
-	return;
-
 defer:
-	task->tk_timeout = 30 * HZ;
-	rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
 	xprt->connecting = 0;
+	if (status < 0) {
+		rpc_delay(task, 5*HZ);
+		task->tk_status = -ENOTCONN;
+	}
+	rpc_wake_up(&xprt->reconn);
 }
 
 /*
- * Reconnect status
+ * Reconnect done
  */
 static void
 xprt_reconn_status(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
 
-	dprintk("RPC: %4d xprt_reconn_status %d\n",
-				task->tk_pid, task->tk_status);
-	if (!xprt->connected && task->tk_status != -ETIMEDOUT) {
-		task->tk_timeout = 30 * HZ;
-		rpc_sleep_on(&xprt->reconn, task, NULL, xprt_reconn_timeout);
-	}
-}
-
-/*
- * Reconnect timeout. We just mark the transport as not being in the
- * process of reconnecting, and leave the rest to the upper layers.
- */
-static void
-xprt_reconn_timeout(struct rpc_task *task)
-{
-	dprintk("RPC: %4d xprt_reconn_timeout %d\n",
-				task->tk_pid, task->tk_status);
-	task->tk_status = -ENOTCONN;
-	task->tk_xprt->connecting = 0;
-	task->tk_timeout = 0;
-	rpc_wake_up_task(task);
+	xprt->connecting = 0;
+	rpc_wake_up(&xprt->reconn);
 }
 
 /*
- * Look up the RPC request corresponding to a reply.
+ * Look up the RPC request corresponding to a reply, and then lock it.
  */
-static inline struct rpc_rqst *
+static struct rpc_rqst *
 xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid)
 {
 	struct rpc_task	*head, *task;
 	struct rpc_rqst	*req;
+	unsigned long	oldflags;
 	int		safe = 0;
 
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	if ((head = xprt->pending.task) != NULL) {
 		task = head;
 		do {
 			if ((req = task->tk_rqstp) && req->rq_xid == xid)
-				return req;
+				goto out;
 			task = task->tk_next;
 			if (++safe > 100) {
 				printk("xprt_lookup_rqst: loop in Q!\n");
-				return NULL;
+				goto out_bad;
 			}
 		} while (task != head);
 	}
 	dprintk("RPC:      unknown XID %08x in reply.\n", xid);
-	return NULL;
+ out_bad:
+	req = NULL;
+ out:
+	if (req && !__rpc_lock_task(req->rq_task))
+		req = NULL;
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+	return req;
 }
 
 /*
  * Complete reply received.
  * The TCP code relies on us to remove the request from xprt->pending.
  */
-static inline void
-xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
+static void
+__xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
 {
 	struct rpc_task	*task = req->rq_task;
 
-	req->rq_rlen   = copied;
-	req->rq_gotit  = 1;
-
 	/* Adjust congestion window */
-	xprt_adjust_cwnd(xprt, copied);
+	__xprt_adjust_cwnd(xprt, copied);
 
 #ifdef RPC_PROFILE
 	/* Profile only reads for now */
@@ -555,19 +615,31 @@
 	}
 #endif
 
-	/* ... and wake up the process. */
 	dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied);
 	task->tk_status = copied;
+	req->rq_received = 1;
 
+	/* ... and wake up the process. */
 	rpc_wake_up_task(task);
 	return;
 }
 
 /*
+ * Safe for use outside BH contexts.
+ */
+static inline void
+xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
+{
+	start_bh_atomic();
+	__xprt_complete_rqst(xprt, req, copied);
+	end_bh_atomic();
+}
+
+/*
  * Input handler for RPC replies. Called from a bottom half and hence
  * atomic.
  */
-static inline void
+static void
 udp_data_ready(struct sock *sk, int len)
 {
 	struct rpc_task	*task;
@@ -578,26 +650,34 @@
 	int		err, repsize, copied;
 
 	dprintk("RPC:      udp_data_ready...\n");
-	if (!(xprt = xprt_from_sock(sk)))
-		return;
+	if (!(xprt = xprt_from_sock(sk))) {
+		printk("RPC:      udp_data_ready request not found!\n");
+		goto wake;
+	}
+
 	dprintk("RPC:      udp_data_ready client %p\n", xprt);
 
 	if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL)
-		return;
-	repsize = skb->len - 8;	/* don't account for UDP header */
+		goto wake;
 
+	if (xprt->shutdown)
+		goto dropit;
+
+	repsize = skb->len - sizeof(struct udphdr);
 	if (repsize < 4) {
 		printk("RPC: impossible RPC reply size %d!\n", repsize);
 		goto dropit;
 	}
 
-	/* Look up the request corresponding to the given XID */
-	if (!(rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + 8))))
+	/* Look up and lock the request corresponding to the given XID */
+	rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + sizeof(struct udphdr)));
+	if (!rovr)
 		goto dropit;
 	task = rovr->rq_task;
 
-	dprintk("RPC: %4d received reply\n", task->tk_pid);
-	xprt_pktdump("packet data:", (u32 *) (skb->h.raw+8), repsize);
+	dprintk("RPC: %4d received reply, (%d bytes)\n", task->tk_pid, repsize);
+	xprt_pktdump("packet data:",
+		     (u32 *) (skb->h.raw+sizeof(struct udphdr)), repsize);
 
 	if ((copied = rovr->rq_rlen) > repsize)
 		copied = repsize;
@@ -605,226 +685,284 @@
 	/* Okay, we have it. Copy datagram... */
 	memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0]));
 	/* This needs to stay tied with the usermode skb_copy_dagram... */
-	memcpy_tokerneliovec(iov, skb->data+8, copied);
+	memcpy_tokerneliovec(iov, skb->h.raw + sizeof(struct udphdr), copied);
 
-	xprt_complete_rqst(xprt, rovr, copied);
+	__xprt_complete_rqst(xprt, rovr, copied);
 
-dropit:
+	rpc_unlock_task(task);
+ dropit:
 	skb_free_datagram(sk, skb);
-	return;
+ wake:
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
- * TCP record receive routine
- * This is not the most efficient code since we call recvfrom twice--
- * first receiving the record marker and XID, then the data.
- * 
- * The optimal solution would be a RPC support in the TCP layer, which
- * would gather all data up to the next record marker and then pass us
- * the list of all TCP segments ready to be copied.
+ * TCP read fragment marker
  */
 static inline int
-tcp_input_record(struct rpc_xprt *xprt)
+tcp_read_fraghdr(struct rpc_xprt *xprt)
 {
-	struct rpc_rqst	*req;
-	struct iovec	*iov;
 	struct iovec	riov;
-	u32		offset;
-	int		result, maxcpy, reclen, avail, want;
+	int		want, result;
 
-	dprintk("RPC:      tcp_input_record\n");
-	offset = xprt->tcp_offset;
-	result = -EAGAIN;
-	if (offset < 4 || (!xprt->tcp_more && offset < 8)) {
-		want = (xprt->tcp_more? 4 : 8) - offset;
-		dprintk("RPC:      reading header (%d bytes)\n", want);
-		riov.iov_base = xprt->tcp_recm.data + offset;
+	if (xprt->tcp_offset >= xprt->tcp_reclen + sizeof(xprt->tcp_recm)) {
+		xprt->tcp_offset = 0;
+		xprt->tcp_reclen = 0;
+	}
+	if (xprt->tcp_offset >= sizeof(xprt->tcp_recm))
+		goto done;
+
+	want = sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+	dprintk("RPC:      reading header (%d bytes)\n", want);
+	do {
+		riov.iov_base = ((u8*) &xprt->tcp_recm) + xprt->tcp_offset;
 		riov.iov_len  = want;
-		result = xprt_recvmsg(xprt, &riov, 1, want);
-		if (!result)
-		{
-			dprintk("RPC: empty TCP record.\n");
-			return -ENOTCONN;
-		}
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
 		if (result < 0)
-			goto done;
-		offset += result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
+			return result;
+		xprt->tcp_offset += result;
+		want -= result;
+	} while (want);
 
-		/* Get the record length and mask out the more_fragments bit */
-		reclen = ntohl(xprt->tcp_reclen);
-		dprintk("RPC:      reclen %08x\n", reclen);
-		xprt->tcp_more = (reclen & 0x80000000)? 0 : 1;
-		reclen &= 0x7fffffff;
-		xprt->tcp_total += reclen;
-		xprt->tcp_reclen = reclen;
-
-		dprintk("RPC:      got xid %08x reclen %d morefrags %d\n",
-			xprt->tcp_xid, xprt->tcp_reclen, xprt->tcp_more);
-		if (!xprt->tcp_copied
-		 && (req = xprt_lookup_rqst(xprt, xprt->tcp_xid))) {
-			iov = xprt->tcp_iovec;
-			memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
-#if 0
-*(u32 *)iov->iov_base = req->rq_xid;
-#endif
-			iov->iov_base += 4;
-			iov->iov_len  -= 4;
-			xprt->tcp_copied = 4;
-			xprt->tcp_rqstp  = req;
-		}
-	} else {
-		reclen = xprt->tcp_reclen;
-	}
+	/* Is this another fragment in the last message */
+	if (!xprt->tcp_more)
+		xprt->tcp_copied = 0; /* No, so we're reading a new message */
+
+	/* Get the record length and mask out the last fragment bit */
+	xprt->tcp_reclen = ntohl(xprt->tcp_recm);
+	xprt->tcp_more = (xprt->tcp_reclen & 0x80000000) ? 0 : 1;
+	xprt->tcp_reclen &= 0x7fffffff;
+
+	dprintk("RPC:      New record reclen %d morefrags %d\n",
+				   xprt->tcp_reclen, xprt->tcp_more);
+ done:
+	return xprt->tcp_reclen + sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+}
+
+/*
+ * TCP read xid
+ */
+static inline int
+tcp_read_xid(struct rpc_xprt *xprt, int avail)
+{
+	struct iovec	riov;
+	int		want, result;
+
+	if (xprt->tcp_copied >= sizeof(xprt->tcp_xid) || !avail)
+		goto done;
+	want = MIN(sizeof(xprt->tcp_xid) - xprt->tcp_copied, avail);
+	do {
+		dprintk("RPC:      reading xid (%d bytes)\n", want);
+		riov.iov_base = ((u8*) &xprt->tcp_xid) + xprt->tcp_copied;
+		riov.iov_len  = want;
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
+		if (result < 0)
+			return result;
+		xprt->tcp_copied += result;
+		xprt->tcp_offset += result;
+		want  -= result;
+		avail -= result;
+	} while (want);
+ done:
+	return avail;
+}
 
-	avail = reclen - (offset - 4);
-	if ((req = xprt->tcp_rqstp) && req->rq_xid == xprt->tcp_xid
-	 && req->rq_task->tk_rpcwait == &xprt->pending) {
-		want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+/*
+ * TCP read and complete request
+ */
+static inline int
+tcp_read_request(struct rpc_xprt *xprt, struct rpc_rqst *req, int avail)
+{
+	int	want, result;
 
+	if (req->rq_rlen <= xprt->tcp_copied || !avail)
+		goto done;
+	want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+	do {
 		dprintk("RPC: %4d TCP receiving %d bytes\n",
-					req->rq_task->tk_pid, want);
-		result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want);
-		if (!result && want)
-			result = -EAGAIN;
+			req->rq_task->tk_pid, want);
+
+		result = xprt_recvmsg(xprt, req->rq_rvec, req->rq_rnr, want, xprt->tcp_copied);
 		if (result < 0)
-			goto done;
+			return result;
 		xprt->tcp_copied += result;
-		offset += result;
+		xprt->tcp_offset += result;
 		avail  -= result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
+		want   -= result;
+	} while (want);
 
-		maxcpy = MIN(req->rq_rlen, xprt->tcp_total);
-		if (xprt->tcp_copied == maxcpy && !xprt->tcp_more) {
-			dprintk("RPC: %4d received reply complete\n",
-					req->rq_task->tk_pid);
-			xprt_complete_rqst(xprt, req, xprt->tcp_total);
-			xprt->tcp_copied = 0;
-			xprt->tcp_rqstp  = NULL;
-		}
-		/* Request must be re-encoded before retransmit */
-		req->rq_damaged = 1;
-	}
+ done:
+	if (req->rq_rlen > xprt->tcp_copied && xprt->tcp_more)
+		return avail;
+	dprintk("RPC: %4d received reply complete\n", req->rq_task->tk_pid);
+	xprt_complete_rqst(xprt, req, xprt->tcp_copied);
 
-	/* Skip over any trailing bytes on short reads */
-	while (avail) {
-		static u8	dummy[64];
+	return avail;
+}
+
+/*
+ * TCP discard extra bytes from a short read
+ */
+static inline int
+tcp_read_discard(struct rpc_xprt *xprt, int avail)
+{
+	struct iovec	riov;
+	static u8	dummy[64];
+	int		want, result = 0;
 
+	while (avail) {
 		want = MIN(avail, sizeof(dummy));
 		riov.iov_base = dummy;
 		riov.iov_len  = want;
 		dprintk("RPC:      TCP skipping %d bytes\n", want);
-		result = xprt_recvmsg(xprt, &riov, 1, want);
-		if (!result && want)
-			result=-EAGAIN;
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
 		if (result < 0)
-			goto done;
-		offset += result;
+			return result;
+		xprt->tcp_offset += result;
 		avail  -= result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
 	}
-	if (!xprt->tcp_more)
-		xprt->tcp_total = 0;
-	offset = 0;
-
-done:
-	dprintk("RPC:      tcp_input_record done (off %d total %d copied %d)\n",
-			offset, xprt->tcp_total, xprt->tcp_copied);
-	xprt->tcp_offset = offset;
-	return result;
+	return avail;
 }
 
-static __inline__ void tcp_output_record(struct rpc_xprt *xprt)
+/*
+ * TCP record receive routine
+ * This is not the most efficient code since we call recvfrom thrice--
+ * first receiving the record marker, then the XID, then the data.
+ * 
+ * The optimal solution would be a RPC support in the TCP layer, which
+ * would gather all data up to the next record marker and then pass us
+ * the list of all TCP segments ready to be copied.
+ */
+static int
+tcp_input_record(struct rpc_xprt *xprt)
 {
-	if(xprt->snd_sent && xprt->snd_task)
-		dprintk("RPC: write space\n");
-	if(xprt->write_space == 0)
-	{
-		xprt->write_space = 1;
-		if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task))
-		{
-			if(xprt->snd_sent)
-				dprintk("RPC: Write wakeup snd_sent =%d\n",
-					xprt->snd_sent);
-			rpc_wake_up_task(xprt->snd_task);			
-		}
+	struct rpc_rqst	*req = NULL;
+	struct rpc_task	*task = NULL;
+	int		avail, result;
+
+	dprintk("RPC:      tcp_input_record\n");
+
+	if (xprt->shutdown)
+		return -EIO;
+	if (!xprt_connected(xprt))
+		return -ENOTCONN;
+
+	/* Read in a new fragment marker if necessary */
+	/* Can we ever really expect to get completely empty fragments? */
+	if ((result = tcp_read_fraghdr(xprt)) <= 0)
+		return result;
+	avail = result;
+
+	/* Read in the xid if necessary */
+	if ((result = tcp_read_xid(xprt, avail)) <= 0)
+		return result;
+	avail = result;
+
+	/* Find and lock the request corresponding to this xid */
+	req = xprt_lookup_rqst(xprt, xprt->tcp_xid);
+	if (req) {
+		task = req->rq_task;
+		/* Read in the request data */
+		result = tcp_read_request(xprt,  req, avail);
+		rpc_unlock_task(task);
+		if (result < 0)
+			return result;
+		avail = result;
 	}
+
+	/* Skip over any trailing bytes on short reads */
+	if ((result = tcp_read_discard(xprt, avail)) < 0)
+		return result;
+
+	dprintk("RPC:      tcp_input_record done (off %d reclen %d copied %d)\n",
+			xprt->tcp_offset, xprt->tcp_reclen, xprt->tcp_copied);
+	result = xprt->tcp_reclen;
+	return result;
 }
 
 /*
  *	TCP task queue stuff
  */
- 
-static struct rpc_xprt *rpc_rx_xprt_pending = NULL;	/* Chain by rx_pending of rpc_xprt's */
-static struct rpc_xprt *rpc_tx_xprt_pending = NULL;	/* Chain by tx_pending of rpc_xprt's */
-
-/*
- *	This is protected from tcp_data_ready and the stack as its run
- *	inside of the RPC I/O daemon
- */
+LIST_HEAD(rpc_xprt_pending);	/* List of xprts having pending tcp requests */
 
-void rpciod_tcp_dispatcher(void)
+static inline
+void tcp_rpciod_queue(void)
 {
-	struct rpc_xprt *xprt;
-	int result;
+	rpciod_wake_up();
+}
+
+static void
+__xprt_append_pending(struct rpc_xprt *xprt)
+{
+	if (list_empty(&xprt->rx_pending)) {
+		list_add(&xprt->rx_pending, rpc_xprt_pending.prev);
+		dprintk("RPC:     xprt queue %p\n", xprt);
+		tcp_rpciod_queue();
+	}
+}
+
+static void
+__xprt_remove_pending(struct rpc_xprt *xprt)
+{
+	if (!list_empty(&xprt->rx_pending)) {
+		list_del(&xprt->rx_pending);
+		INIT_LIST_HEAD(&xprt->rx_pending);
+	}
+}
+
+static void
+xprt_remove_pending(struct rpc_xprt *xprt)
+{
+	start_bh_atomic();
+	__xprt_remove_pending(xprt);
+	end_bh_atomic();
+}
+
+static inline
+struct rpc_xprt *xprt_remove_pending_next(void)
+{
+	struct rpc_xprt	*xprt = NULL;
+
+	start_bh_atomic();
+	if (!list_empty(&rpc_xprt_pending)) {
+		xprt = list_entry(rpc_xprt_pending.next, struct rpc_xprt, rx_pending);
+		list_del(&xprt->rx_pending);
+		INIT_LIST_HEAD(&xprt->rx_pending);
+	}
+	end_bh_atomic();
+	return xprt;
+}
+
+/*
+ *	This is protected from tcp_data_ready and the stack as its run
+ *	inside of the RPC I/O daemon
+ */
+void
+__rpciod_tcp_dispatcher(void)
+{
+	struct rpc_xprt *xprt;
+	int safe_retry = 0, result;
 
 	dprintk("rpciod_tcp_dispatcher: Queue Running\n");
-	
+
 	/*
 	 *	Empty each pending socket
 	 */
-	 
-	while((xprt=rpc_rx_xprt_pending)!=NULL)
-	{
-		int safe_retry=0;
-		
-		rpc_rx_xprt_pending=xprt->rx_pending;
-		xprt->rx_pending_flag=0;
-		
+	while ((xprt = xprt_remove_pending_next()) != NULL) {
 		dprintk("rpciod_tcp_dispatcher: Processing %p\n", xprt);
-		
-		do 
-		{
-			if (safe_retry++ > 50)
-				break;
-			result = tcp_input_record(xprt);
-		}
-		while (result >= 0);
-	
-		switch (result) {
-			case -EAGAIN:
-				continue;
-			case -ENOTCONN:
-			case -EPIPE:
-				xprt_disconnect(xprt);
-				continue;
-			default:
-				printk(KERN_WARNING "RPC: unexpected error %d from tcp_input_record\n",
-					result);
-		}
-	}
 
-	while((xprt=rpc_tx_xprt_pending)!=NULL)
-	{
-		rpc_tx_xprt_pending = xprt->tx_pending;
-		xprt->tx_pending_flag = 0;
-		tcp_output_record(xprt);
-	}
-}
+		do {
+			result = tcp_input_record(xprt);
+		} while (result >= 0);
 
+		if (!xprt_connected(xprt) && !xprt->connecting)
+			xprt_close(xprt);
 
-extern inline void tcp_rpciod_queue(void)
-{
-	rpciod_wake_up();
+		if (safe_retry++ > 200) {
+			schedule();
+			safe_retry = 0;
+		}
+	}
 }
 
 /*
@@ -839,37 +977,22 @@
 	struct rpc_xprt	*xprt;
 
 	dprintk("RPC:      tcp_data_ready...\n");
-	if (!(xprt = xprt_from_sock(sk)))
-	{
+	if (!(xprt = xprt_from_sock(sk))) {
 		printk("Not a socket with xprt %p\n", sk);
-		return;
+		goto wake;
 	}
+
+	if (xprt->shutdown)
+		goto wake;
+
+	__xprt_append_pending(xprt);
+
 	dprintk("RPC:      tcp_data_ready client %p\n", xprt);
 	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
-				sk->state, xprt->connected,
+				sk->state, xprt_connected(xprt),
 				sk->dead, sk->zapped);
-	/*
-	 *	If we are not waiting for the RPC bh run then
-	 *	we are now
-	 */
-	if (!xprt->rx_pending_flag)
-	{
-		int start_queue=0;
-
-		dprintk("RPC:     xprt queue %p\n", rpc_rx_xprt_pending);
-		if(rpc_rx_xprt_pending==NULL)
-			start_queue=1;
-		xprt->rx_pending_flag=1;
-		xprt->rx_pending=rpc_rx_xprt_pending;
-		rpc_rx_xprt_pending=xprt;
-		if (start_queue)
-		  {
-		    tcp_rpciod_queue();
-		    start_queue=0;
-		  }
-	}
-	else
-		dprintk("RPC:     xprt queued already %p\n", xprt);
+ wake:
+	wake_up_interruptible(sk->sleep);
 }
 
 
@@ -879,41 +1002,88 @@
 	struct rpc_xprt	*xprt;
 
 	if (!(xprt = xprt_from_sock(sk)))
-		return;
+		goto wake;
 	dprintk("RPC:      tcp_state_change client %p...\n", xprt);
 	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
-				sk->state, xprt->connected,
+				sk->state, xprt_connected(xprt),
 				sk->dead, sk->zapped);
 
-	if (sk->state == TCP_ESTABLISHED && !xprt->connected) {
-		xprt->connected = 1;
-		xprt->connecting = 0;
+	switch (sk->state) {
+	case TCP_ESTABLISHED:
+		xprt_set_connected(xprt);
+		if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
+			rpc_wake_up_task(xprt->snd_task);
 		rpc_wake_up(&xprt->reconn);
-	} else if (sk->zapped) {
-		rpc_wake_up_status(&xprt->pending, -ENOTCONN);
-		rpc_wake_up_status(&xprt->sending, -ENOTCONN);
-		rpc_wake_up_status(&xprt->reconn,  -ENOTCONN);
+		break;
+	case TCP_SYN_SENT:
+	case TCP_SYN_RECV:
+		break;
+	default:
+		__xprt_disconnect(xprt);
+		break;
 	}
+ wake:
+	wake_up_interruptible(sk->sleep);
 }
 
+/*
+ * The following 2 routines allow a task to sleep while socket memory is
+ * low.
+ */
 static void
 tcp_write_space(struct sock *sk)
 {
 	struct rpc_xprt	*xprt;
+	unsigned long	oldflags;
 
 	if (!(xprt = xprt_from_sock(sk)))
 		return;
-	if (!xprt->tx_pending_flag) {
-		int start_queue = 0;
+	if (xprt->shutdown)
+		return;
 
-		if (rpc_tx_xprt_pending == NULL)
-			start_queue = 1;
-		xprt->tx_pending_flag = 1;
-		xprt->tx_pending = rpc_tx_xprt_pending;
-		rpc_tx_xprt_pending = xprt;
-		if (start_queue)
-			tcp_rpciod_queue();
-	}
+	/* Wait until we have enough socket memory */
+	if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE))
+		return;
+
+	spin_lock_irqsave(&xprt_sock_lock, oldflags);
+	if (xprt->write_space)
+		goto out_unlock;
+
+	xprt->write_space = 1;
+
+	if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
+		rpc_wake_up_task(xprt->snd_task);
+ out_unlock:
+	spin_unlock_irqrestore(&xprt_sock_lock, oldflags);
+	wake_up_interruptible(sk->sleep);
+}
+
+static void
+udp_write_space(struct sock *sk)
+{
+	struct rpc_xprt *xprt;
+	unsigned long	oldflags;
+
+	if (!(xprt = xprt_from_sock(sk)))
+		return;
+	if (xprt->shutdown)
+		return;
+
+	/* Wait until we have enough socket memory */
+	if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE))
+		return;
+
+	spin_lock_irqsave(&xprt_sock_lock, oldflags);
+	if (xprt->write_space)
+		goto out_unlock;
+
+	xprt->write_space = 1;
+
+	if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
+		rpc_wake_up_task(xprt->snd_task);
+ out_unlock:
+	spin_unlock_irqrestore(&xprt_sock_lock, oldflags);
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
@@ -924,9 +1094,8 @@
 {
 	struct rpc_rqst	*req = task->tk_rqstp;
 
-	if (req) {
-		xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT);
-	}
+	if (req)
+		__xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT);
 
 	dprintk("RPC: %4d xprt_timer (%s request)\n",
 		task->tk_pid, req ? "pending" : "backlogged");
@@ -936,32 +1105,47 @@
 	rpc_wake_up_task(task);
 }
 
+
 /*
- * (Partly) transmit the RPC packet
- * Note that task->tk_status is either 0 or negative on return.
- * Only when the reply is received will the status be set to a
- * positive value.
+ * Serialize access to sockets, in order to prevent different
+ * requests from interfering with each other.
  */
-static inline int
-xprt_transmit_some(struct rpc_xprt *xprt, struct rpc_task *task)
+int
+xprt_down_transmit(struct rpc_task *task)
 {
+	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
 	struct rpc_rqst	*req = task->tk_rqstp;
-	int		result;
 
-	task->tk_status = 0;
-	if ((result = xprt_sendmsg(xprt)) >= 0) {
-		if (!xprt->snd_buf.io_len || !xprt->stream) {
-			rpc_wake_up_next(&xprt->sending);
-			return req->rq_slen;
-		}
-		result = -EAGAIN;
-	} else if (xprt->stream) {
-		if (result == -ENOTCONN || result == -EPIPE) {
-			xprt_disconnect(xprt);
-			result = -ENOTCONN;
-		}
+	xprt_delete_tcp_timer(xprt);
+	if (xprt->snd_task && xprt->snd_task != task) {
+		dprintk("RPC: %4d TCP write queue full (task %d)\n",
+			task->tk_pid, xprt->snd_task->tk_pid);
+		task->tk_timeout = 0;
+		task->tk_status = -EAGAIN;
+		rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+	} else if (!xprt->snd_task) {
+		xprt->snd_task = task;
+#ifdef RPC_PROFILE
+		req->rq_xtime = jiffies;
+#endif
+		req->rq_bytes_sent = 0;
+	}
+	return xprt->snd_task == task;
+}
+
+/*
+ * Releases the socket for use by other requests.
+ */
+void
+xprt_up_transmit(struct rpc_task *task)
+{
+	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
+
+	if (xprt->snd_task && xprt->snd_task == task) {
+		xprt->snd_task = NULL;
+		if (!rpc_wake_up_next(&xprt->sending) && xprt->stream)
+			xprt_add_tcp_timer(xprt, RPCXPRT_TIMEOUT);
 	}
-	return task->tk_status = result;
 }
 
 /*
@@ -971,129 +1155,137 @@
 void
 xprt_transmit(struct rpc_task *task)
 {
-	struct rpc_timeout *timeo;
 	struct rpc_rqst	*req = task->tk_rqstp;
 	struct rpc_xprt	*xprt = req->rq_xprt;
-	int status;
 
-	dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, 
-				*(u32 *)(req->rq_svec[0].iov_base));
+	dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, req->rq_xid);
 
-	if (xprt->shutdown) {
+	if (xprt->shutdown)
 		task->tk_status = -EIO;
+
+	if (!xprt_connected(xprt))
+		task->tk_status = -ENOTCONN;
+
+	if (task->tk_status < 0)
 		return;
-	}
 
-	/* If we're not already in the process of transmitting our call,
-	 * set up everything as needed. */
-	if (xprt->snd_task != task) {
-		/* Write the record marker */
-		if (xprt->stream) {
-			u32	marker;
+	/* set up everything as needed. */
+	/* Write the record marker */
+	if (xprt->stream) {
+		u32	*marker = req->rq_svec[0].iov_base;
 
-			if (!xprt->connected) {
-				task->tk_status = -ENOTCONN;
-				return;
-			}
-			marker = htonl(0x80000000|(req->rq_slen-4));
-			*((u32 *) req->rq_svec[0].iov_base) = marker;
-		}
+		*marker = htonl(0x80000000|(req->rq_slen-sizeof(*marker)));
+	}
 
-		/* Reset timeout parameters */
-		timeo = &req->rq_timeout;
-		if (timeo->to_retries < 0) {
-			dprintk("RPC: %4d xprt_transmit reset timeo\n",
-						task->tk_pid);
-			timeo->to_retries = xprt->timeout.to_retries;
-			timeo->to_current = timeo->to_initval;
-		}
+	if (!xprt_down_transmit(task))
+		return;
 
-#ifdef RPC_PROFILE
-		req->rq_xtime = jiffies;
-#endif
-		req->rq_gotit = 0;
+	do_xprt_transmit(task);
+}
+
+static void
+do_xprt_transmit(struct rpc_task *task)
+{
+	struct rpc_rqst	*req = task->tk_rqstp;
+	struct rpc_xprt	*xprt = req->rq_xprt;
+	unsigned long	oldflags;
+	int status, retry = 0;
 
-		if (xprt->snd_task) {
-			dprintk("RPC: %4d TCP write queue full (task %d)\n",
-					task->tk_pid, xprt->snd_task->tk_pid);
-			rpc_sleep_on(&xprt->sending, task,
-					xprt_transmit_status, NULL);
-			return;
-		}
-		xprt->snd_buf  = req->rq_snd_buf;
-		xprt->snd_task = task;
-		xprt->snd_sent = 0;
-	}
 
 	/* For fast networks/servers we have to put the request on
 	 * the pending list now:
+	 * Note that we don't want the task timing out during the
+	 * call to xprt_sendmsg(), so we initially disable the timeout,
+	 * and then reset it later...
 	 */
-	start_bh_atomic();
-	status = rpc_add_wait_queue(&xprt->pending, task);
-	if (!status)
-		task->tk_callback = NULL;
-	end_bh_atomic();
-
-	if (status)
-	{
-		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
-		task->tk_status = status;
-		return;
-	}
+	xprt_receive(task);
 
 	/* Continue transmitting the packet/record. We must be careful
 	 * to cope with writespace callbacks arriving _after_ we have
 	 * called xprt_sendmsg().
 	 */
-	while (1) {
+	for (;;) {
+		status = -ENOTCONN;
+		if (!xprt->inet)
+			break;
+
 		xprt->write_space = 0;
-		if (xprt_transmit_some(xprt, task) != -EAGAIN) {
-			dprintk("RPC: %4d xmit complete\n", task->tk_pid);
-			xprt->snd_task = NULL;
-			return;
+		status = -ENOMEM;
+		if (sock_wspace(xprt->inet) < req->rq_slen + MIN_WRITE_SPACE)
+			break;
+		status = xprt_sendmsg(xprt, req);
+
+		if (status < 0)
+			break;
+
+		if (xprt->stream) {
+			req->rq_bytes_sent += status;
+
+			if (req->rq_bytes_sent >= req->rq_slen)
+				goto out_receive;
+		} else {
+			if (status >= req->rq_slen)
+				goto out_receive;
+			status = -ENOMEM;
+			break;
 		}
 
-		/*d*/dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
-				task->tk_pid, xprt->snd_buf.io_len,
+		dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
+				task->tk_pid, req->rq_slen - req->rq_bytes_sent,
 				req->rq_slen);
-		task->tk_status = 0;
-		start_bh_atomic();
+
+		status = -EAGAIN;
+		if (retry++ > 50)
+			break;
+	}
+	rpc_unlock_task(task);
+
+	/* Note: at this point, task->tk_sleeping has not yet been set,
+	 *	 hence there is no danger of the waking up task being put on
+	 *	 schedq, and being picked up by a parallel run of rpciod().
+	 */
+	rpc_wake_up_task(task);
+	if (!RPC_IS_RUNNING(task))
+		goto out_release;
+	if (req->rq_received)
+		goto out_release;
+
+	task->tk_status = status;
+
+	switch (status) {
+	case -ENOMEM:
+		/* Protect against (udp|tcp)_write_space */
+		spin_lock_irqsave(&xprt_sock_lock, oldflags);
 		if (!xprt->write_space) {
-			/* Remove from pending */
-			rpc_remove_wait_queue(task);
-			rpc_sleep_on(&xprt->sending, task,
-					xprt_transmit_status, NULL);
-			end_bh_atomic();
+			task->tk_timeout = req->rq_timeout.to_current;
+			rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+		}
+		spin_unlock_irqrestore(&xprt_sock_lock, oldflags);
+		return;
+	case -EAGAIN:
+	case -ECONNREFUSED:
+	case -ENOTCONN:
+		/* Keep holding the socket if it is blocked */
+		if (!xprt->stream) {
+			rpc_delay(task, HZ>>4);
 			return;
 		}
-		end_bh_atomic();
+	default:
+		goto out_release;
 	}
-}
-
-/*
- * This callback is invoked when the sending task is forced to sleep
- * because the TCP write buffers are full
- */
-static void
-xprt_transmit_status(struct rpc_task *task)
-{
-	struct rpc_xprt	*xprt = task->tk_client->cl_xprt;
 
-	dprintk("RPC: %4d transmit_status %d\n", task->tk_pid, task->tk_status);
-	if (xprt->snd_task == task) 
-	{
-		if (task->tk_status < 0)
-		{
-			xprt->snd_task = NULL;
-			xprt_disconnect(xprt);
-		}
-		else
-			xprt_transmit(task);
-	}
+ out_receive:
+	dprintk("RPC: %4d xmit complete\n", task->tk_pid);
+	/* Set the task's receive timeout value */
+	task->tk_timeout = req->rq_timeout.to_current;
+	rpc_add_timer(task, xprt_timer);
+	rpc_unlock_task(task);
+ out_release:
+	xprt_up_transmit(task);
 }
 
 /*
- * Wait for the reply to our call.
+ * Queue the task for a reply to our call.
  * When the callback is invoked, the congestion window should have
  * been updated already.
  */
@@ -1104,34 +1296,10 @@
 	struct rpc_xprt	*xprt = req->rq_xprt;
 
 	dprintk("RPC: %4d xprt_receive\n", task->tk_pid);
-	if (xprt->connected == 0) {
-		task->tk_status = -ENOTCONN;
-		return;
-	}
-
-	/*
-	 * Wait until rq_gotit goes non-null, or timeout elapsed.
-	 */
-	task->tk_timeout = req->rq_timeout.to_current;
-
-	start_bh_atomic();
-	if (!req->rq_gotit) {
-		rpc_sleep_on(&xprt->pending, task,
-				xprt_receive_status, xprt_timer);
-	}
-	end_bh_atomic();
 
-	dprintk("RPC: %4d xprt_receive returns %d\n",
-				task->tk_pid, task->tk_status);
-}
-
-static void
-xprt_receive_status(struct rpc_task *task)
-{
-	struct rpc_xprt	*xprt = task->tk_xprt;
-
-	if (xprt->stream && xprt->tcp_rqstp == task->tk_rqstp)
-		xprt->tcp_rqstp = NULL;
+	req->rq_received= 0;
+	task->tk_timeout = 0;
+	rpc_sleep_locked(&xprt->pending, task, NULL, NULL);
 }
 
 /*
@@ -1148,15 +1316,18 @@
 
 	dprintk("RPC: %4d xprt_reserve cong = %ld cwnd = %ld\n",
 				task->tk_pid, xprt->cong, xprt->cwnd);
-	if ((!RPCXPRT_CONGESTED(xprt) && xprt->free)) {
-		xprt_reserve_status(task);
+	start_bh_atomic();
+	__xprt_reserve_status(task);
+	if (task->tk_rqstp) {
 		task->tk_timeout = 0;
 	} else if (!task->tk_timeout) {
 		task->tk_status = -ENOBUFS;
 	} else {
 		dprintk("RPC:      xprt_reserve waiting on backlog\n");
-		rpc_sleep_on(&xprt->backlog, task, xprt_reserve_status, NULL);
+		task->tk_status = -EAGAIN;
+		rpc_sleep_on(&xprt->backlog, task, NULL, NULL);
 	}
+	end_bh_atomic();
 	dprintk("RPC: %4d xprt_reserve returns %d\n",
 				task->tk_pid, task->tk_status);
 	return task->tk_status;
@@ -1166,7 +1337,7 @@
  * Reservation callback
  */
 static void
-xprt_reserve_status(struct rpc_task *task)
+__xprt_reserve_status(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct rpc_rqst	*req;
@@ -1177,40 +1348,25 @@
 		/* NOP */
 	} else if (task->tk_rqstp) {
 		/* We've already been given a request slot: NOP */
-	} else if (!RPCXPRT_CONGESTED(xprt)) {
+	} else {
+		if (RPCXPRT_CONGESTED(xprt) || !(req = xprt->free))
+			goto out_nofree;
 		/* OK: There's room for us. Grab a free slot and bump
 		 * congestion value */
-		req = xprt->free;
-		if (!req)
-			goto bad_list;
-		if (req->rq_xid)
-			goto bad_used;
 		xprt->free     = req->rq_next;
+		req->rq_next   = NULL;
 		xprt->cong    += RPC_CWNDSCALE;
 		task->tk_rqstp = req;
-		req->rq_next   = NULL;
 		xprt_request_init(task, xprt);
-	} else {
-		task->tk_status = -EAGAIN;
-	}
 
-	if (xprt->free && !RPCXPRT_CONGESTED(xprt))
-		rpc_wake_up_next(&xprt->backlog);
+		if (xprt->free)
+			__xprt_clear_backlog(xprt);
+	}
 
 	return;
 
-bad_list:
-	printk(KERN_ERR 
-		"RPC: %4d inconsistent free list (cong %ld cwnd %ld)\n",
-		task->tk_pid, xprt->cong, xprt->cwnd);
-	rpc_debug = ~0;
-	goto bummer;
-bad_used:
-	printk(KERN_ERR "RPC: used rqst slot %p on free list!\n", req);
-bummer:
-	task->tk_status = -EIO;
-	xprt->free = NULL;
-	return;
+out_nofree:
+	task->tk_status = -EAGAIN;
 }
 
 /*
@@ -1227,7 +1383,6 @@
 
 	dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid);
 	task->tk_status = 0;
-	req->rq_gotit	= 0;
 	req->rq_timeout = xprt->timeout;
 	req->rq_task	= task;
 	req->rq_xprt    = xprt;
@@ -1245,6 +1400,7 @@
 	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct rpc_rqst	*req;
 
+	xprt_up_transmit(task);
 	if (!(req = task->tk_rqstp))
 		return;
 	task->tk_rqstp = NULL;
@@ -1252,46 +1408,15 @@
 
 	dprintk("RPC: %4d release request %p\n", task->tk_pid, req);
 
-	/* remove slot from queue of pending */
 	start_bh_atomic();
-	if (task->tk_rpcwait) {
-		printk("RPC: task of released request still queued!\n");
-#ifdef RPC_DEBUG
-		printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait));
-#endif
-		rpc_del_timer(task);
-		rpc_remove_wait_queue(task);
-	}
-	end_bh_atomic();
+	req->rq_next = xprt->free;
+	xprt->free   = req;
 
 	/* Decrease congestion value. */
 	xprt->cong -= RPC_CWNDSCALE;
 
-#if 0
-	/* If congestion threshold is not yet reached, pass on the request slot.
-	 * This looks kind of kludgy, but it guarantees backlogged requests
-	 * are served in order.
-	 * N.B. This doesn't look completely safe, as the task is still
-	 * on the backlog list after wake-up.
-	 */
-	if (!RPCXPRT_CONGESTED(xprt)) {
-		struct rpc_task	*next = rpc_wake_up_next(&xprt->backlog);
-
-		if (next && next->tk_rqstp == 0) {
-			xprt->cong += RPC_CWNDSCALE;
-			next->tk_rqstp = req;
-			xprt_request_init(next, xprt);
-			return;
-		}
-	}
-#endif
-
-	req->rq_next = xprt->free;
-	xprt->free   = req;
-
-	/* If not congested, wake up the next backlogged process */
-	if (!RPCXPRT_CONGESTED(xprt))
-		rpc_wake_up_next(&xprt->backlog);
+	__xprt_clear_backlog(xprt);
+	end_bh_atomic();
 }
 
 /*
@@ -1303,7 +1428,7 @@
 	if (proto == IPPROTO_UDP)
 		xprt_set_timeout(to, 5,  5 * HZ);
 	else
-		xprt_set_timeout(to, 5, 15 * HZ);
+		xprt_set_timeout(to, 5, 60 * HZ);
 }
 
 /*
@@ -1330,56 +1455,36 @@
 {
 	struct rpc_xprt	*xprt;
 	struct rpc_rqst	*req;
-	struct sock	*inet;
 	int		i;
 
 	dprintk("RPC:      setting up %s transport...\n",
 				proto == IPPROTO_UDP? "UDP" : "TCP");
 
-#if LINUX_VERSION_CODE >= 0x020100
-	inet = sock->sk;
-#else
-	inet = (struct sock *) sock->data;
-#endif
-
 	if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL)
 		return NULL;
 	memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */
 
-	xprt->file = NULL;
-	xprt->sock = sock;
-	xprt->inet = inet;
 	xprt->addr = *ap;
 	xprt->prot = proto;
 	xprt->stream = (proto == IPPROTO_TCP)? 1 : 0;
-	xprt->cwnd = RPC_INITCWND;
-#ifdef SOCK_HAS_USER_DATA
-	inet->user_data = xprt;
-#else
-	xprt->link = sock_list;
-	sock_list = xprt;
-#endif
-	xprt->old_data_ready = inet->data_ready;
-	xprt->old_state_change = inet->state_change;
-	xprt->old_write_space = inet->write_space;
-	if (proto == IPPROTO_UDP) {
-		inet->data_ready = udp_data_ready;
-	} else {
-		inet->data_ready = tcp_data_ready;
-		inet->state_change = tcp_state_change;
-		inet->write_space = tcp_write_space;
+	if (xprt->stream) {
+		xprt->cwnd = RPC_MAXCWND;
 		xprt->nocong = 1;
-	}
-	xprt->connected = 1;
+	} else
+		xprt->cwnd = RPC_INITCWND;
+	xprt->congtime = jiffies;
 
 	/* Set timeout parameters */
 	if (to) {
 		xprt->timeout = *to;
 		xprt->timeout.to_current = to->to_initval;
 		xprt->timeout.to_resrvval = to->to_maxval << 1;
-	} else {
+	} else
 		xprt_default_timeout(&xprt->timeout, xprt->prot);
-	}
+
+	init_timer(&xprt->tcp_timer);
+	xprt->tcp_timer.data = (unsigned long)xprt;
+	xprt->tcp_timer.function = (void(*)(unsigned long)) xprt_tcp_timeout;
 
 	xprt->pending = RPC_INIT_WAITQ("xprt_pending");
 	xprt->sending = RPC_INIT_WAITQ("xprt_sending");
@@ -1392,50 +1497,15 @@
 	req->rq_next = NULL;
 	xprt->free = xprt->slot;
 
+	INIT_LIST_HEAD(&xprt->rx_pending);
+
 	dprintk("RPC:      created transport %p\n", xprt);
 	
-	/*
-	 *	TCP requires the rpc I/O daemon is present
-	 */
-	if(proto==IPPROTO_TCP)
-		rpciod_up();
+	xprt_bind_socket(xprt, sock);
 	return xprt;
 }
 
 /*
- * Create and initialize an RPC client given an open file.
- * This is obsolete now.
- */
-#if 0
-struct rpc_xprt *
-xprt_create(struct file *file, struct sockaddr_in *ap, struct rpc_timeout *to)
-{
-	struct rpc_xprt	*xprt;
-	struct socket	*sock;
-	int		proto;
-
-	if (!file) {
-		printk("RPC: file == NULL in xprt_create!\n");
-		return NULL;
-	}
-
-	sock = &file->f_inode->u.socket_i;
-	if (sock->ops->family != PF_INET) {
-		printk(KERN_WARNING "RPC: only INET sockets supported\n");
-		return NULL;
-	}
-
-	proto = (sock->type == SOCK_DGRAM)? IPPROTO_UDP : IPPROTO_TCP;
-	if ((xprt = xprt_setup(sock, proto, ap, to)) != NULL) {
-		xprt->file = file;
-		file->f_count++;
-	}
-
-	return xprt;
-}
-#endif
-
-/*
  * Bind to a reserved port
  */
 static inline int
@@ -1459,38 +1529,66 @@
 	return err;
 }
 
+static int 
+xprt_bind_socket(struct rpc_xprt *xprt, struct socket *sock)
+{
+	struct sock	*sk = sock->sk;
+
+	if (xprt->inet)
+		return -EBUSY;
+
+	start_bh_atomic();
+	sk->user_data = xprt;
+	xprt->old_data_ready = sk->data_ready;
+	xprt->old_state_change = sk->state_change;
+	xprt->old_write_space = sk->write_space;
+	if (xprt->prot == IPPROTO_UDP) {
+		sk->data_ready = udp_data_ready;
+		sk->write_space = udp_write_space;
+		xprt_set_connected(xprt);
+	} else {
+		sk->data_ready = tcp_data_ready;
+		sk->state_change = tcp_state_change;
+		sk->write_space = tcp_write_space;
+		xprt_clear_connected(xprt);
+	}
+
+	/* Reset to new socket */
+	xprt->sock = sock;
+	xprt->inet = sk;
+	end_bh_atomic();
+	/*
+	 *	TCP requires the rpc I/O daemon is present
+	 */
+	if(xprt->stream)
+		rpciod_up();
+
+	return 0;
+}
+
 /*
  * Create a client socket given the protocol and peer address.
  */
 static struct socket *
-xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
+xprt_create_socket(int proto, struct rpc_timeout *to)
 {
 	struct socket	*sock;
 	int		type, err;
 
-	dprintk("RPC:      xprt_create_socket(%08x, %s %d)\n",
-			   sap? ntohl(sap->sin_addr.s_addr) : 0,
+	dprintk("RPC:      xprt_create_socket(%s %d)\n",
 			   (proto == IPPROTO_UDP)? "udp" : "tcp", proto);
 
 	type = (proto == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
+
 	if ((err = sock_create(PF_INET, type, proto, &sock)) < 0) {
 		printk("RPC: can't create socket (%d).\n", -err);
 		goto failed;
 	}
 
-	/* If the caller has root privs, bind to a reserved port */
-	if (!current->fsuid && xprt_bindresvport(sock) < 0)
+	/* If the caller has the capability, bind to a reserved port */
+	if (capable(CAP_NET_BIND_SERVICE) && xprt_bindresvport(sock) < 0)
 		goto failed;
 
-	if (type == SOCK_STREAM && sap) {
-		err = sock->ops->connect(sock, (struct sockaddr *) sap,
-						sizeof(*sap), 0);
-		if (err < 0) {
-			printk("RPC: TCP connect failed (%d).\n", -err);
-			goto failed;
-		}
-	}
-
 	return sock;
 
 failed:
@@ -1509,7 +1607,7 @@
 
 	dprintk("RPC:      xprt_create_proto called\n");
 
-	if (!(sock = xprt_create_socket(proto, sap, to)))
+	if (!(sock = xprt_create_socket(proto, to)))
 		return NULL;
 
 	if (!(xprt = xprt_setup(sock, proto, sap, to)))
@@ -1529,6 +1627,20 @@
 	rpc_wake_up(&xprt->pending);
 	rpc_wake_up(&xprt->backlog);
 	rpc_wake_up(&xprt->reconn);
+	wake_up(&xprt->cong_wait);
+}
+
+/*
+ * Clear the xprt backlog queue
+ */
+static int
+__xprt_clear_backlog(struct rpc_xprt *xprt)
+{
+	if (RPCXPRT_CONGESTED(xprt))
+		return 0;
+	rpc_wake_up_next(&xprt->backlog);
+	wake_up(&xprt->cong_wait);
+	return 1;
 }
 
 /*
@@ -1537,20 +1649,11 @@
 int
 xprt_destroy(struct rpc_xprt *xprt)
 {
-#ifndef SOCK_HAS_USER_DATA
-	struct rpc_xprt	**q;
-
-	for (q = &sock_list; *q && *q != xprt; q = &((*q)->link))
-		;
-	if (!*q) {
-		printk(KERN_WARNING "xprt_destroy: unknown socket!\n");
-		return -EIO;	/* why is there no EBUGGYSOFTWARE */
-	}
-	*q = xprt->link;
-#endif
-
 	dprintk("RPC:      destroying transport %p\n", xprt);
+	xprt_shutdown(xprt);
 	xprt_close(xprt);
+	xprt_delete_tcp_timer(xprt);
+	xprt_remove_pending(xprt);
 	kfree(xprt);
 
 	return 0;
